about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.gitignore6
-rw-r--r--bam.lua4
-rw-r--r--data/countryflags/AD.pngbin0 -> 2741 bytes
-rw-r--r--data/countryflags/AE.pngbin0 -> 816 bytes
-rw-r--r--data/countryflags/AF.pngbin0 -> 2631 bytes
-rw-r--r--data/countryflags/AG.pngbin0 -> 2161 bytes
-rw-r--r--data/countryflags/AI.pngbin0 -> 2544 bytes
-rw-r--r--data/countryflags/AL.pngbin0 -> 2347 bytes
-rw-r--r--data/countryflags/AM.pngbin0 -> 740 bytes
-rw-r--r--data/countryflags/AO.pngbin0 -> 1739 bytes
-rw-r--r--data/countryflags/AR.pngbin1571 -> 1519 bytes
-rw-r--r--data/countryflags/AS.pngbin0 -> 4005 bytes
-rw-r--r--data/countryflags/AT.pngbin1072 -> 798 bytes
-rw-r--r--data/countryflags/AU.pngbin3842 -> 2472 bytes
-rw-r--r--data/countryflags/AW.pngbin0 -> 1280 bytes
-rw-r--r--data/countryflags/AX.pngbin0 -> 985 bytes
-rw-r--r--data/countryflags/AZ.pngbin0 -> 1288 bytes
-rw-r--r--data/countryflags/BA.pngbin2868 -> 2175 bytes
-rw-r--r--data/countryflags/BB.pngbin0 -> 1354 bytes
-rw-r--r--data/countryflags/BD.pngbin0 -> 1145 bytes
-rw-r--r--data/countryflags/BE.pngbin879 -> 678 bytes
-rw-r--r--data/countryflags/BF.pngbin0 -> 1073 bytes
-rw-r--r--data/countryflags/BG.pngbin1006 -> 789 bytes
-rw-r--r--data/countryflags/BH.pngbin0 -> 1493 bytes
-rw-r--r--data/countryflags/BI.pngbin0 -> 2643 bytes
-rw-r--r--data/countryflags/BJ.pngbin0 -> 851 bytes
-rw-r--r--data/countryflags/BL.pngbin0 -> 6881 bytes
-rw-r--r--data/countryflags/BM.pngbin0 -> 3329 bytes
-rw-r--r--data/countryflags/BN.pngbin0 -> 3910 bytes
-rw-r--r--data/countryflags/BO.pngbin0 -> 1674 bytes
-rw-r--r--data/countryflags/BR.pngbin3905 -> 2682 bytes
-rw-r--r--data/countryflags/BS.pngbin0 -> 1216 bytes
-rw-r--r--data/countryflags/BT.pngbin0 -> 4193 bytes
-rw-r--r--data/countryflags/BW.pngbin0 -> 719 bytes
-rw-r--r--data/countryflags/BY.pngbin2728 -> 1869 bytes
-rw-r--r--data/countryflags/BZ.pngbin0 -> 5185 bytes
-rw-r--r--data/countryflags/CA.pngbin1885 -> 1436 bytes
-rw-r--r--data/countryflags/CC.pngbin0 -> 2056 bytes
-rw-r--r--data/countryflags/CD.pngbin0 -> 2868 bytes
-rw-r--r--data/countryflags/CF.pngbin0 -> 1148 bytes
-rw-r--r--data/countryflags/CG.pngbin0 -> 1580 bytes
-rw-r--r--data/countryflags/CH.pngbin1099 -> 896 bytes
-rw-r--r--data/countryflags/CI.pngbin0 -> 818 bytes
-rw-r--r--data/countryflags/CK.pngbin0 -> 3372 bytes
-rw-r--r--data/countryflags/CL.pngbin1422 -> 984 bytes
-rw-r--r--data/countryflags/CM.pngbin0 -> 1104 bytes
-rw-r--r--data/countryflags/CN.pngbin1720 -> 1331 bytes
-rw-r--r--data/countryflags/CO.pngbin993 -> 784 bytes
-rw-r--r--data/countryflags/CR.pngbin0 -> 1531 bytes
-rw-r--r--data/countryflags/CU.pngbin0 -> 1632 bytes
-rw-r--r--data/countryflags/CV.pngbin0 -> 1668 bytes
-rw-r--r--data/countryflags/CW.pngbin0 -> 1138 bytes
-rw-r--r--data/countryflags/CX.pngbin0 -> 2560 bytes
-rw-r--r--data/countryflags/CY.pngbin0 -> 1905 bytes
-rw-r--r--data/countryflags/CZ.pngbin1815 -> 962 bytes
-rw-r--r--data/countryflags/DE.pngbin863 -> 666 bytes
-rw-r--r--data/countryflags/DJ.pngbin0 -> 1758 bytes
-rw-r--r--data/countryflags/DK.pngbin1207 -> 863 bytes
-rw-r--r--data/countryflags/DM.pngbin0 -> 2237 bytes
-rw-r--r--data/countryflags/DO.pngbin0 -> 1646 bytes
-rw-r--r--data/countryflags/DZ.pngbin0 -> 1605 bytes
-rw-r--r--data/countryflags/EC.pngbin0 -> 2589 bytes
-rw-r--r--data/countryflags/EE.pngbin887 -> 632 bytes
-rw-r--r--data/countryflags/EG.pngbin1662 -> 1406 bytes
-rw-r--r--data/countryflags/EH.pngbin0 -> 1516 bytes
-rw-r--r--data/countryflags/ER.pngbin0 -> 3335 bytes
-rw-r--r--data/countryflags/ES.pngbin2406 -> 2174 bytes
-rw-r--r--data/countryflags/ET.pngbin0 -> 2587 bytes
-rw-r--r--data/countryflags/FI.pngbin1071 -> 832 bytes
-rw-r--r--data/countryflags/FJ.pngbin0 -> 3222 bytes
-rw-r--r--data/countryflags/FK.pngbin0 -> 4080 bytes
-rw-r--r--data/countryflags/FM.pngbin0 -> 1434 bytes
-rw-r--r--data/countryflags/FO.pngbin0 -> 994 bytes
-rw-r--r--data/countryflags/FR.pngbin1037 -> 814 bytes
-rw-r--r--data/countryflags/GA.pngbin0 -> 857 bytes
-rw-r--r--data/countryflags/GB.pngbin4786 -> 2870 bytes
-rw-r--r--data/countryflags/GD.pngbin0 -> 2669 bytes
-rw-r--r--data/countryflags/GE.pngbin0 -> 1595 bytes
-rw-r--r--data/countryflags/GF.pngbin0 -> 1637 bytes
-rw-r--r--data/countryflags/GG.pngbin0 -> 1143 bytes
-rw-r--r--data/countryflags/GH.pngbin0 -> 1152 bytes
-rw-r--r--data/countryflags/GI.pngbin0 -> 2466 bytes
-rw-r--r--data/countryflags/GL.pngbin0 -> 1130 bytes
-rw-r--r--data/countryflags/GM.pngbin0 -> 789 bytes
-rw-r--r--data/countryflags/GN.pngbin0 -> 822 bytes
-rw-r--r--data/countryflags/GP.pngbin0 -> 1180 bytes
-rw-r--r--data/countryflags/GQ.pngbin0 -> 1599 bytes
-rw-r--r--data/countryflags/GR.pngbin1439 -> 1011 bytes
-rw-r--r--data/countryflags/GS.pngbin0 -> 4830 bytes
-rw-r--r--data/countryflags/GT.pngbin0 -> 1910 bytes
-rw-r--r--data/countryflags/GU.pngbin0 -> 2242 bytes
-rw-r--r--data/countryflags/GW.pngbin0 -> 1138 bytes
-rw-r--r--data/countryflags/GY.pngbin0 -> 2993 bytes
-rw-r--r--data/countryflags/HK.pngbin0 -> 2097 bytes
-rw-r--r--data/countryflags/HN.pngbin0 -> 1224 bytes
-rw-r--r--data/countryflags/HR.pngbin2614 -> 2320 bytes
-rw-r--r--data/countryflags/HT.pngbin0 -> 1577 bytes
-rw-r--r--data/countryflags/HU.pngbin1027 -> 838 bytes
-rw-r--r--data/countryflags/ID.pngbin1017 -> 787 bytes
-rw-r--r--data/countryflags/IE.pngbin0 -> 847 bytes
-rw-r--r--data/countryflags/IL.pngbin2110 -> 1565 bytes
-rw-r--r--data/countryflags/IM.pngbin0 -> 2053 bytes
-rw-r--r--data/countryflags/IN.pngbin1841 -> 1518 bytes
-rw-r--r--data/countryflags/IO.pngbin0 -> 7189 bytes
-rw-r--r--data/countryflags/IQ.pngbin0 -> 1469 bytes
-rw-r--r--data/countryflags/IR.pngbin3227 -> 2604 bytes
-rw-r--r--data/countryflags/IS.pngbin0 -> 1017 bytes
-rw-r--r--data/countryflags/IT.pngbin1022 -> 861 bytes
-rw-r--r--data/countryflags/JE.pngbin0 -> 3269 bytes
-rw-r--r--data/countryflags/JM.pngbin0 -> 2407 bytes
-rw-r--r--data/countryflags/JO.pngbin0 -> 1048 bytes
-rw-r--r--data/countryflags/JP.pngbin0 -> 1397 bytes
-rw-r--r--data/countryflags/KE.pngbin0 -> 2149 bytes
-rw-r--r--data/countryflags/KG.pngbin0 -> 3085 bytes
-rw-r--r--data/countryflags/KH.pngbin0 -> 2424 bytes
-rw-r--r--data/countryflags/KI.pngbin0 -> 4268 bytes
-rw-r--r--data/countryflags/KM.pngbin0 -> 1804 bytes
-rw-r--r--data/countryflags/KN.pngbin0 -> 2524 bytes
-rw-r--r--data/countryflags/KP.pngbin0 -> 1618 bytes
-rw-r--r--data/countryflags/KR.pngbin0 -> 2860 bytes
-rw-r--r--data/countryflags/KW.pngbin0 -> 1119 bytes
-rw-r--r--data/countryflags/KY.pngbin0 -> 3989 bytes
-rw-r--r--data/countryflags/KZ.pngbin4339 -> 3021 bytes
-rw-r--r--data/countryflags/LA.pngbin0 -> 1033 bytes
-rw-r--r--data/countryflags/LB.pngbin0 -> 1918 bytes
-rw-r--r--data/countryflags/LC.pngbin0 -> 1904 bytes
-rw-r--r--data/countryflags/LI.pngbin0 -> 1598 bytes
-rw-r--r--data/countryflags/LK.pngbin0 -> 4089 bytes
-rw-r--r--data/countryflags/LR.pngbin0 -> 1268 bytes
-rw-r--r--data/countryflags/LS.pngbin0 -> 1378 bytes
-rw-r--r--data/countryflags/LT.pngbin991 -> 758 bytes
-rw-r--r--data/countryflags/LU.pngbin1089 -> 832 bytes
-rw-r--r--data/countryflags/LV.pngbin1052 -> 771 bytes
-rw-r--r--data/countryflags/LY.pngbin0 -> 634 bytes
-rw-r--r--data/countryflags/MA.pngbin0 -> 1667 bytes
-rw-r--r--data/countryflags/MC.pngbin0 -> 764 bytes
-rw-r--r--data/countryflags/MD.pngbin0 -> 2601 bytes
-rw-r--r--data/countryflags/ME.pngbin0 -> 3243 bytes
-rw-r--r--data/countryflags/MF.pngbin0 -> 1623 bytes
-rw-r--r--data/countryflags/MG.pngbin0 -> 894 bytes
-rw-r--r--data/countryflags/MH.pngbin0 -> 3355 bytes
-rw-r--r--data/countryflags/MK.pngbin0 -> 2768 bytes
-rw-r--r--data/countryflags/ML.pngbin0 -> 832 bytes
-rw-r--r--data/countryflags/MM.pngbin0 -> 1550 bytes
-rw-r--r--data/countryflags/MN.pngbin0 -> 1718 bytes
-rw-r--r--data/countryflags/MO.pngbin0 -> 2064 bytes
-rw-r--r--data/countryflags/MP.pngbin0 -> 5386 bytes
-rw-r--r--data/countryflags/MQ.pngbin0 -> 3036 bytes
-rw-r--r--data/countryflags/MR.pngbin0 -> 1559 bytes
-rw-r--r--data/countryflags/MS.pngbin0 -> 2919 bytes
-rw-r--r--data/countryflags/MT.pngbin0 -> 1263 bytes
-rw-r--r--data/countryflags/MU.pngbin0 -> 843 bytes
-rw-r--r--data/countryflags/MV.pngbin0 -> 1140 bytes
-rw-r--r--data/countryflags/MW.pngbin0 -> 2357 bytes
-rw-r--r--data/countryflags/MX.pngbin2486 -> 2211 bytes
-rw-r--r--data/countryflags/MY.pngbin0 -> 2080 bytes
-rw-r--r--data/countryflags/MZ.pngbin0 -> 2087 bytes
-rw-r--r--data/countryflags/NA.pngbin0 -> 3339 bytes
-rw-r--r--data/countryflags/NC.pngbin0 -> 2547 bytes
-rw-r--r--data/countryflags/NE.pngbin0 -> 1060 bytes
-rw-r--r--data/countryflags/NF.pngbin0 -> 2791 bytes
-rw-r--r--data/countryflags/NG.pngbin0 -> 771 bytes
-rw-r--r--data/countryflags/NI.pngbin0 -> 1463 bytes
-rw-r--r--data/countryflags/NL.pngbin1064 -> 825 bytes
-rw-r--r--data/countryflags/NO.pngbin1327 -> 975 bytes
-rw-r--r--data/countryflags/NP.pngbin0 -> 2085 bytes
-rw-r--r--data/countryflags/NR.pngbin0 -> 1294 bytes
-rw-r--r--data/countryflags/NU.pngbin0 -> 2806 bytes
-rw-r--r--data/countryflags/NZ.pngbin0 -> 2166 bytes
-rw-r--r--data/countryflags/OM.pngbin0 -> 1496 bytes
-rw-r--r--data/countryflags/PA.pngbin0 -> 1385 bytes
-rw-r--r--data/countryflags/PE.pngbin0 -> 794 bytes
-rw-r--r--data/countryflags/PF.pngbin0 -> 1967 bytes
-rw-r--r--data/countryflags/PG.pngbin0 -> 2315 bytes
-rw-r--r--data/countryflags/PH.pngbin2843 -> 2109 bytes
-rw-r--r--data/countryflags/PK.pngbin2180 -> 1638 bytes
-rw-r--r--data/countryflags/PL.pngbin988 -> 753 bytes
-rw-r--r--data/countryflags/PM.pngbin0 -> 7849 bytes
-rw-r--r--data/countryflags/PN.pngbin0 -> 4692 bytes
-rw-r--r--data/countryflags/PR.pngbin0 -> 1670 bytes
-rw-r--r--data/countryflags/PT.pngbin2940 -> 2653 bytes
-rw-r--r--data/countryflags/PW.pngbin0 -> 1375 bytes
-rw-r--r--data/countryflags/PY.pngbin0 -> 1584 bytes
-rw-r--r--data/countryflags/QA.pngbin0 -> 1193 bytes
-rw-r--r--data/countryflags/RE.pngbin0 -> 1816 bytes
-rw-r--r--data/countryflags/RO.pngbin1002 -> 805 bytes
-rw-r--r--data/countryflags/RS.pngbin3666 -> 3054 bytes
-rw-r--r--data/countryflags/RU.pngbin989 -> 719 bytes
-rw-r--r--data/countryflags/RW.pngbin0 -> 1557 bytes
-rw-r--r--data/countryflags/SA.pngbin3662 -> 2605 bytes
-rw-r--r--data/countryflags/SB.pngbin0 -> 2399 bytes
-rw-r--r--data/countryflags/SC.pngbin0 -> 2184 bytes
-rw-r--r--data/countryflags/SD.pngbin0 -> 1015 bytes
-rw-r--r--data/countryflags/SE.pngbin1098 -> 849 bytes
-rw-r--r--data/countryflags/SG.pngbin0 -> 1407 bytes
-rw-r--r--data/countryflags/SH.pngbin0 -> 3167 bytes
-rw-r--r--data/countryflags/SI.pngbin0 -> 1507 bytes
-rw-r--r--data/countryflags/SK.pngbin2280 -> 1836 bytes
-rw-r--r--data/countryflags/SL.pngbin0 -> 836 bytes
-rw-r--r--data/countryflags/SM.pngbin0 -> 2585 bytes
-rw-r--r--data/countryflags/SN.pngbin0 -> 1108 bytes
-rw-r--r--data/countryflags/SO.pngbin0 -> 1181 bytes
-rw-r--r--data/countryflags/SR.pngbin0 -> 1260 bytes
-rw-r--r--data/countryflags/SS.pngbin0 -> 1437 bytes
-rw-r--r--data/countryflags/ST.pngbin0 -> 1348 bytes
-rw-r--r--data/countryflags/SV.pngbin1893 -> 1717 bytes
-rw-r--r--data/countryflags/SX.pngbin0 -> 2708 bytes
-rw-r--r--data/countryflags/SY.pngbin0 -> 1171 bytes
-rw-r--r--data/countryflags/SZ.pngbin0 -> 2458 bytes
-rw-r--r--data/countryflags/TC.pngbin0 -> 2398 bytes
-rw-r--r--data/countryflags/TD.pngbin0 -> 805 bytes
-rw-r--r--data/countryflags/TF.pngbin0 -> 1729 bytes
-rw-r--r--data/countryflags/TG.pngbin0 -> 1208 bytes
-rw-r--r--data/countryflags/TH.pngbin0 -> 797 bytes
-rw-r--r--data/countryflags/TJ.pngbin0 -> 1597 bytes
-rw-r--r--data/countryflags/TK.pngbin0 -> 2242 bytes
-rw-r--r--data/countryflags/TL.pngbin0 -> 1379 bytes
-rw-r--r--data/countryflags/TM.pngbin0 -> 3311 bytes
-rw-r--r--data/countryflags/TN.pngbin0 -> 1733 bytes
-rw-r--r--data/countryflags/TO.pngbin0 -> 832 bytes
-rw-r--r--data/countryflags/TR.pngbin2117 -> 1622 bytes
-rw-r--r--data/countryflags/TT.pngbin0 -> 1938 bytes
-rw-r--r--data/countryflags/TV.pngbin0 -> 3266 bytes
-rw-r--r--data/countryflags/TW.pngbin0 -> 1126 bytes
-rw-r--r--data/countryflags/TZ.pngbin0 -> 2300 bytes
-rw-r--r--data/countryflags/UA.pngbin938 -> 777 bytes
-rw-r--r--data/countryflags/UG.pngbin0 -> 1360 bytes
-rw-r--r--data/countryflags/US.pngbin3280 -> 2401 bytes
-rw-r--r--data/countryflags/UY.pngbin0 -> 2188 bytes
-rw-r--r--data/countryflags/UZ.pngbin0 -> 1385 bytes
-rw-r--r--data/countryflags/VA.pngbin0 -> 2547 bytes
-rw-r--r--data/countryflags/VC.pngbin0 -> 1495 bytes
-rw-r--r--data/countryflags/VE.pngbin0 -> 1342 bytes
-rw-r--r--data/countryflags/VG.pngbin0 -> 3211 bytes
-rw-r--r--data/countryflags/VI.pngbin0 -> 5088 bytes
-rw-r--r--data/countryflags/VN.pngbin0 -> 1278 bytes
-rw-r--r--data/countryflags/VU.pngbin0 -> 2410 bytes
-rw-r--r--data/countryflags/WF.pngbin0 -> 1263 bytes
-rw-r--r--data/countryflags/WS.pngbin0 -> 1216 bytes
-rw-r--r--data/countryflags/XEN.pngbin1192 -> 914 bytes
-rw-r--r--data/countryflags/XNI.pngbin3701 -> 2212 bytes
-rw-r--r--data/countryflags/XSC.pngbin3766 -> 2225 bytes
-rw-r--r--data/countryflags/XWA.pngbin6362 -> 4827 bytes
-rw-r--r--data/countryflags/YE.pngbin0 -> 699 bytes
-rw-r--r--data/countryflags/ZA.pngbin3270 -> 2255 bytes
-rw-r--r--data/countryflags/ZM.pngbin0 -> 1272 bytes
-rw-r--r--data/countryflags/ZW.pngbin0 -> 2070 bytes
-rw-r--r--data/countryflags/index.txt1534
-rw-r--r--data/editor/desert_main.rules227
-rw-r--r--data/editor/grass_main.rules225
-rw-r--r--data/editor/jungle_main.rules266
-rw-r--r--data/editor/winter_main.rules177
-rw-r--r--data/languages/belarusian.txt679
-rw-r--r--data/languages/bosnian.txt29
-rw-r--r--data/languages/brazilian_portuguese.txt679
-rw-r--r--data/languages/bulgarian.txt60
-rw-r--r--data/languages/czech.txt73
-rw-r--r--data/languages/danish.txt14
-rw-r--r--data/languages/dutch.txt39
-rw-r--r--data/languages/finnish.txt31
-rw-r--r--data/languages/french.txt33
-rw-r--r--data/languages/german.txt31
-rw-r--r--data/languages/hungarian.txt679
-rw-r--r--data/languages/index.txt35
-rw-r--r--data/languages/italian.txt480
-rw-r--r--data/languages/norwegian.txt31
-rw-r--r--data/languages/polish.txt480
-rw-r--r--data/languages/portuguese.txt233
-rw-r--r--data/languages/romanian.txt31
-rw-r--r--data/languages/russian.txt261
-rw-r--r--data/languages/serbian.txt423
-rw-r--r--data/languages/slovak.txt73
-rw-r--r--data/languages/spanish.txt218
-rw-r--r--data/languages/swedish.txt51
-rw-r--r--data/languages/turkish.txt679
-rw-r--r--data/languages/ukrainian.txt27
-rw-r--r--data/mapres/jungle_deathtiles.pngbin264060 -> 263805 bytes
-rw-r--r--data/mapres/jungle_doodads.pngbin237695 -> 237659 bytes
-rw-r--r--datasrc/compile.py4
-rw-r--r--datasrc/content.py36
-rw-r--r--datasrc/datatypes.py4
-rw-r--r--datasrc/network.py2
-rw-r--r--other/sdl/vc2005libs/SDL.dllbin324096 -> 343552 bytes
-rw-r--r--other/sdl/vc2005libs/SDL.libbin42702 -> 42698 bytes
-rw-r--r--other/sdl/vc2005libs/SDLmain.libbin8264 -> 22852 bytes
-rw-r--r--scripts/build.py7
-rw-r--r--scripts/cmd5.py2
-rwxr-xr-xscripts/copyright.py5
-rw-r--r--scripts/tw_api.py311
-rw-r--r--scripts/update_localization.py11
-rw-r--r--src/base/detect.h45
-rw-r--r--src/base/math.h11
-rw-r--r--src/base/system.c202
-rw-r--r--src/base/system.h41
-rw-r--r--src/engine/client.h2
-rw-r--r--src/engine/client/client.cpp240
-rw-r--r--src/engine/client/client.h35
-rw-r--r--src/engine/client/friends.cpp39
-rw-r--r--src/engine/client/friends.h1
-rw-r--r--src/engine/client/graphics.cpp100
-rw-r--r--src/engine/client/graphics.h3
-rw-r--r--src/engine/client/serverbrowser.cpp116
-rw-r--r--src/engine/client/sound.cpp22
-rw-r--r--src/engine/client/sound.h4
-rw-r--r--src/engine/client/text.cpp3
-rw-r--r--src/engine/console.h36
-rw-r--r--src/engine/demo.h2
-rw-r--r--src/engine/external/pnglite/pnglite.c4
-rw-r--r--src/engine/external/pnglite/pnglite.h2
-rw-r--r--src/engine/friends.h8
-rw-r--r--src/engine/masterserver.h1
-rw-r--r--src/engine/server.h6
-rw-r--r--src/engine/server/register.h6
-rw-r--r--src/engine/server/server.cpp219
-rw-r--r--src/engine/server/server.h28
-rw-r--r--src/engine/serverbrowser.h5
-rw-r--r--src/engine/shared/config_variables.h18
-rw-r--r--src/engine/shared/console.cpp319
-rw-r--r--src/engine/shared/console.h36
-rw-r--r--src/engine/shared/datafile.cpp117
-rw-r--r--src/engine/shared/datafile.h18
-rw-r--r--src/engine/shared/demo.cpp26
-rw-r--r--src/engine/shared/demo.h2
-rw-r--r--src/engine/shared/econ.cpp173
-rw-r--r--src/engine/shared/econ.h50
-rw-r--r--src/engine/shared/filecollection.cpp186
-rw-r--r--src/engine/shared/filecollection.h35
-rw-r--r--src/engine/shared/huffman.cpp4
-rw-r--r--src/engine/shared/mapchecker.h2
-rw-r--r--src/engine/shared/masterserver.cpp90
-rw-r--r--src/engine/shared/network.h92
-rw-r--r--src/engine/shared/network_console.cpp219
-rw-r--r--src/engine/shared/network_console_conn.cpp186
-rw-r--r--src/engine/shared/network_server.cpp16
-rw-r--r--src/engine/shared/protocol.h6
-rw-r--r--src/engine/sound.h2
-rw-r--r--src/game/client/animstate.cpp12
-rw-r--r--src/game/client/animstate.h20
-rw-r--r--src/game/client/components/chat.cpp66
-rw-r--r--src/game/client/components/console.cpp58
-rw-r--r--src/game/client/components/console.h7
-rw-r--r--src/game/client/components/countryflags.cpp29
-rw-r--r--src/game/client/components/countryflags.h15
-rw-r--r--src/game/client/components/debughud.cpp8
-rw-r--r--src/game/client/components/effects.cpp2
-rw-r--r--src/game/client/components/emoticon.cpp8
-rw-r--r--src/game/client/components/hud.cpp8
-rw-r--r--src/game/client/components/maplayers.cpp20
-rw-r--r--src/game/client/components/menus.cpp84
-rw-r--r--src/game/client/components/menus.h30
-rw-r--r--src/game/client/components/menus_browser.cpp215
-rw-r--r--src/game/client/components/menus_demo.cpp18
-rw-r--r--src/game/client/components/menus_ingame.cpp2
-rw-r--r--src/game/client/components/menus_settings.cpp69
-rw-r--r--src/game/client/components/scoreboard.cpp2
-rw-r--r--src/game/client/components/sounds.cpp19
-rw-r--r--src/game/client/components/sounds.h1
-rw-r--r--src/game/client/components/spectator.cpp29
-rw-r--r--src/game/client/gameclient.cpp44
-rw-r--r--src/game/client/gameclient.h2
-rw-r--r--src/game/client/lineinput.cpp2
-rw-r--r--src/game/client/render.cpp4
-rw-r--r--src/game/client/render.h7
-rw-r--r--src/game/client/render_map.cpp18
-rw-r--r--src/game/client/ui.cpp7
-rw-r--r--src/game/client/ui.h1
-rw-r--r--src/game/editor/auto_map.cpp202
-rw-r--r--src/game/editor/auto_map.h54
-rw-r--r--src/game/editor/editor.cpp (renamed from src/game/editor/ed_editor.cpp)446
-rw-r--r--src/game/editor/editor.h (renamed from src/game/editor/ed_editor.h)49
-rw-r--r--src/game/editor/io.cpp (renamed from src/game/editor/ed_io.cpp)55
-rw-r--r--src/game/editor/layer_game.cpp (renamed from src/game/editor/ed_layer_game.cpp)4
-rw-r--r--src/game/editor/layer_quads.cpp (renamed from src/game/editor/ed_layer_quads.cpp)24
-rw-r--r--src/game/editor/layer_tiles.cpp (renamed from src/game/editor/ed_layer_tiles.cpp)45
-rw-r--r--src/game/editor/popups.cpp (renamed from src/game/editor/ed_popups.cpp)76
-rw-r--r--src/game/gamecore.cpp48
-rw-r--r--src/game/mapitems.h9
-rw-r--r--src/game/server/entities/character.cpp107
-rw-r--r--src/game/server/entities/character.h2
-rw-r--r--src/game/server/entities/laser.cpp1
-rw-r--r--src/game/server/eventhandler.cpp2
-rw-r--r--src/game/server/gamecontext.cpp382
-rw-r--r--src/game/server/gamecontext.h24
-rw-r--r--src/game/server/gamecontroller.cpp218
-rw-r--r--src/game/server/gamecontroller.h10
-rw-r--r--src/game/server/gamemodes/ctf.cpp41
-rw-r--r--src/game/server/gamemodes/ctf.h1
-rw-r--r--src/game/server/gamemodes/dm.cpp1
-rw-r--r--src/game/server/gamemodes/mod.cpp2
-rw-r--r--src/game/server/gamemodes/tdm.cpp1
-rw-r--r--src/game/server/gamemodes/zcatch.cpp230
-rw-r--r--src/game/server/gamemodes/zcatch.hpp33
-rw-r--r--src/game/server/gameworld.cpp1
-rw-r--r--src/game/server/player.cpp137
-rw-r--r--src/game/server/player.h23
-rw-r--r--src/game/variables.h18
-rw-r--r--src/game/version.h4
-rw-r--r--src/tools/dilate.cpp24
-rw-r--r--src/tools/tileset_borderfix.cpp25
399 files changed, 10721 insertions, 3353 deletions
diff --git a/.gitignore b/.gitignore
index 75e610f5..35d317c7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,3 +20,9 @@ teeworlds*
 teeworlds_srv*
 tileset_borderfix*
 versionsrv*
+
+zcatch_srv*
+config.cfg
+.project
+.cproject
+
diff --git a/bam.lua b/bam.lua
index 9c827d0d..006ba20f 100644
--- a/bam.lua
+++ b/bam.lua
@@ -106,7 +106,7 @@ AddDependency(network_source, network_header)
 AddDependency(client_content_source, client_content_header)
 AddDependency(server_content_source, server_content_header)
 
-nethash = CHash("src/game/generated/nethash.c", "src/engine/shared/protocol.h", "src/game/generated/protocol.h", "src/game/tuning.h", "src/game/gamecore.cpp", network_header)
+nethash = CHash("src/game/generated/nethash.cpp", "src/engine/shared/protocol.h", "src/game/generated/protocol.h", "src/game/tuning.h", "src/game/gamecore.cpp", network_header)
 
 client_link_other = {}
 client_depends = {}
@@ -243,7 +243,7 @@ function build(settings)
 		engine, client, game_editor, zlib, pnglite, wavpack,
 		client_link_other, client_osxlaunch)
 
-	server_exe = Link(server_settings, "teeworlds_srv", engine, server,
+	server_exe = Link(server_settings, "zcatch_srv", engine, server,
 		game_shared, game_server, zlib, server_link_other)
 
 	serverlaunch = {}
diff --git a/data/countryflags/AD.png b/data/countryflags/AD.png
new file mode 100644
index 00000000..9977c009
--- /dev/null
+++ b/data/countryflags/AD.png
Binary files differdiff --git a/data/countryflags/AE.png b/data/countryflags/AE.png
new file mode 100644
index 00000000..7be66c75
--- /dev/null
+++ b/data/countryflags/AE.png
Binary files differdiff --git a/data/countryflags/AF.png b/data/countryflags/AF.png
new file mode 100644
index 00000000..e7f20d1c
--- /dev/null
+++ b/data/countryflags/AF.png
Binary files differdiff --git a/data/countryflags/AG.png b/data/countryflags/AG.png
new file mode 100644
index 00000000..22fdded8
--- /dev/null
+++ b/data/countryflags/AG.png
Binary files differdiff --git a/data/countryflags/AI.png b/data/countryflags/AI.png
new file mode 100644
index 00000000..9056a2b2
--- /dev/null
+++ b/data/countryflags/AI.png
Binary files differdiff --git a/data/countryflags/AL.png b/data/countryflags/AL.png
new file mode 100644
index 00000000..acc74619
--- /dev/null
+++ b/data/countryflags/AL.png
Binary files differdiff --git a/data/countryflags/AM.png b/data/countryflags/AM.png
new file mode 100644
index 00000000..139c30d9
--- /dev/null
+++ b/data/countryflags/AM.png
Binary files differdiff --git a/data/countryflags/AO.png b/data/countryflags/AO.png
new file mode 100644
index 00000000..5b3ca638
--- /dev/null
+++ b/data/countryflags/AO.png
Binary files differdiff --git a/data/countryflags/AR.png b/data/countryflags/AR.png
index 0ce5f92b..878458c7 100644
--- a/data/countryflags/AR.png
+++ b/data/countryflags/AR.png
Binary files differdiff --git a/data/countryflags/AS.png b/data/countryflags/AS.png
new file mode 100644
index 00000000..b4add724
--- /dev/null
+++ b/data/countryflags/AS.png
Binary files differdiff --git a/data/countryflags/AT.png b/data/countryflags/AT.png
index c1fbe9d4..13c6a599 100644
--- a/data/countryflags/AT.png
+++ b/data/countryflags/AT.png
Binary files differdiff --git a/data/countryflags/AU.png b/data/countryflags/AU.png
index 361f22e4..6e334645 100644
--- a/data/countryflags/AU.png
+++ b/data/countryflags/AU.png
Binary files differdiff --git a/data/countryflags/AW.png b/data/countryflags/AW.png
new file mode 100644
index 00000000..9c89e68c
--- /dev/null
+++ b/data/countryflags/AW.png
Binary files differdiff --git a/data/countryflags/AX.png b/data/countryflags/AX.png
new file mode 100644
index 00000000..d7fb2236
--- /dev/null
+++ b/data/countryflags/AX.png
Binary files differdiff --git a/data/countryflags/AZ.png b/data/countryflags/AZ.png
new file mode 100644
index 00000000..49bcb835
--- /dev/null
+++ b/data/countryflags/AZ.png
Binary files differdiff --git a/data/countryflags/BA.png b/data/countryflags/BA.png
index 3a77a439..21397f63 100644
--- a/data/countryflags/BA.png
+++ b/data/countryflags/BA.png
Binary files differdiff --git a/data/countryflags/BB.png b/data/countryflags/BB.png
new file mode 100644
index 00000000..051b3032
--- /dev/null
+++ b/data/countryflags/BB.png
Binary files differdiff --git a/data/countryflags/BD.png b/data/countryflags/BD.png
new file mode 100644
index 00000000..1488cf27
--- /dev/null
+++ b/data/countryflags/BD.png
Binary files differdiff --git a/data/countryflags/BE.png b/data/countryflags/BE.png
index 409e8552..b6135ec6 100644
--- a/data/countryflags/BE.png
+++ b/data/countryflags/BE.png
Binary files differdiff --git a/data/countryflags/BF.png b/data/countryflags/BF.png
new file mode 100644
index 00000000..fd1fa109
--- /dev/null
+++ b/data/countryflags/BF.png
Binary files differdiff --git a/data/countryflags/BG.png b/data/countryflags/BG.png
index 7f9a6590..f323e5d0 100644
--- a/data/countryflags/BG.png
+++ b/data/countryflags/BG.png
Binary files differdiff --git a/data/countryflags/BH.png b/data/countryflags/BH.png
new file mode 100644
index 00000000..59c8a487
--- /dev/null
+++ b/data/countryflags/BH.png
Binary files differdiff --git a/data/countryflags/BI.png b/data/countryflags/BI.png
new file mode 100644
index 00000000..08f0ec47
--- /dev/null
+++ b/data/countryflags/BI.png
Binary files differdiff --git a/data/countryflags/BJ.png b/data/countryflags/BJ.png
new file mode 100644
index 00000000..74725ce9
--- /dev/null
+++ b/data/countryflags/BJ.png
Binary files differdiff --git a/data/countryflags/BL.png b/data/countryflags/BL.png
new file mode 100644
index 00000000..692401bd
--- /dev/null
+++ b/data/countryflags/BL.png
Binary files differdiff --git a/data/countryflags/BM.png b/data/countryflags/BM.png
new file mode 100644
index 00000000..9d7cd871
--- /dev/null
+++ b/data/countryflags/BM.png
Binary files differdiff --git a/data/countryflags/BN.png b/data/countryflags/BN.png
new file mode 100644
index 00000000..38dd8cc9
--- /dev/null
+++ b/data/countryflags/BN.png
Binary files differdiff --git a/data/countryflags/BO.png b/data/countryflags/BO.png
new file mode 100644
index 00000000..028e1fe3
--- /dev/null
+++ b/data/countryflags/BO.png
Binary files differdiff --git a/data/countryflags/BR.png b/data/countryflags/BR.png
index aada91d5..5c2ea746 100644
--- a/data/countryflags/BR.png
+++ b/data/countryflags/BR.png
Binary files differdiff --git a/data/countryflags/BS.png b/data/countryflags/BS.png
new file mode 100644
index 00000000..aae921df
--- /dev/null
+++ b/data/countryflags/BS.png
Binary files differdiff --git a/data/countryflags/BT.png b/data/countryflags/BT.png
new file mode 100644
index 00000000..130b9045
--- /dev/null
+++ b/data/countryflags/BT.png
Binary files differdiff --git a/data/countryflags/BW.png b/data/countryflags/BW.png
new file mode 100644
index 00000000..d7782c6e
--- /dev/null
+++ b/data/countryflags/BW.png
Binary files differdiff --git a/data/countryflags/BY.png b/data/countryflags/BY.png
index 6052eea4..c05a3664 100644
--- a/data/countryflags/BY.png
+++ b/data/countryflags/BY.png
Binary files differdiff --git a/data/countryflags/BZ.png b/data/countryflags/BZ.png
new file mode 100644
index 00000000..93f832e5
--- /dev/null
+++ b/data/countryflags/BZ.png
Binary files differdiff --git a/data/countryflags/CA.png b/data/countryflags/CA.png
index 9898ba57..9d9f4a1e 100644
--- a/data/countryflags/CA.png
+++ b/data/countryflags/CA.png
Binary files differdiff --git a/data/countryflags/CC.png b/data/countryflags/CC.png
new file mode 100644
index 00000000..ef1b1dd2
--- /dev/null
+++ b/data/countryflags/CC.png
Binary files differdiff --git a/data/countryflags/CD.png b/data/countryflags/CD.png
new file mode 100644
index 00000000..3c5f2f95
--- /dev/null
+++ b/data/countryflags/CD.png
Binary files differdiff --git a/data/countryflags/CF.png b/data/countryflags/CF.png
new file mode 100644
index 00000000..e42de4dc
--- /dev/null
+++ b/data/countryflags/CF.png
Binary files differdiff --git a/data/countryflags/CG.png b/data/countryflags/CG.png
new file mode 100644
index 00000000..99a97ea1
--- /dev/null
+++ b/data/countryflags/CG.png
Binary files differdiff --git a/data/countryflags/CH.png b/data/countryflags/CH.png
index fa8d6482..27881d0c 100644
--- a/data/countryflags/CH.png
+++ b/data/countryflags/CH.png
Binary files differdiff --git a/data/countryflags/CI.png b/data/countryflags/CI.png
new file mode 100644
index 00000000..fa80f60b
--- /dev/null
+++ b/data/countryflags/CI.png
Binary files differdiff --git a/data/countryflags/CK.png b/data/countryflags/CK.png
new file mode 100644
index 00000000..c7a8d325
--- /dev/null
+++ b/data/countryflags/CK.png
Binary files differdiff --git a/data/countryflags/CL.png b/data/countryflags/CL.png
index 571851c7..327b27f6 100644
--- a/data/countryflags/CL.png
+++ b/data/countryflags/CL.png
Binary files differdiff --git a/data/countryflags/CM.png b/data/countryflags/CM.png
new file mode 100644
index 00000000..9e983e2d
--- /dev/null
+++ b/data/countryflags/CM.png
Binary files differdiff --git a/data/countryflags/CN.png b/data/countryflags/CN.png
index 3251bd9a..1996131e 100644
--- a/data/countryflags/CN.png
+++ b/data/countryflags/CN.png
Binary files differdiff --git a/data/countryflags/CO.png b/data/countryflags/CO.png
index 5fdda4a5..61f8fa55 100644
--- a/data/countryflags/CO.png
+++ b/data/countryflags/CO.png
Binary files differdiff --git a/data/countryflags/CR.png b/data/countryflags/CR.png
new file mode 100644
index 00000000..f6a02efd
--- /dev/null
+++ b/data/countryflags/CR.png
Binary files differdiff --git a/data/countryflags/CU.png b/data/countryflags/CU.png
new file mode 100644
index 00000000..0dbee8c2
--- /dev/null
+++ b/data/countryflags/CU.png
Binary files differdiff --git a/data/countryflags/CV.png b/data/countryflags/CV.png
new file mode 100644
index 00000000..95afe4b5
--- /dev/null
+++ b/data/countryflags/CV.png
Binary files differdiff --git a/data/countryflags/CW.png b/data/countryflags/CW.png
new file mode 100644
index 00000000..079ec877
--- /dev/null
+++ b/data/countryflags/CW.png
Binary files differdiff --git a/data/countryflags/CX.png b/data/countryflags/CX.png
new file mode 100644
index 00000000..12051034
--- /dev/null
+++ b/data/countryflags/CX.png
Binary files differdiff --git a/data/countryflags/CY.png b/data/countryflags/CY.png
new file mode 100644
index 00000000..dd34d6c4
--- /dev/null
+++ b/data/countryflags/CY.png
Binary files differdiff --git a/data/countryflags/CZ.png b/data/countryflags/CZ.png
index cb750877..b4df10db 100644
--- a/data/countryflags/CZ.png
+++ b/data/countryflags/CZ.png
Binary files differdiff --git a/data/countryflags/DE.png b/data/countryflags/DE.png
index 699a43d8..dde74c9e 100644
--- a/data/countryflags/DE.png
+++ b/data/countryflags/DE.png
Binary files differdiff --git a/data/countryflags/DJ.png b/data/countryflags/DJ.png
new file mode 100644
index 00000000..62a8c250
--- /dev/null
+++ b/data/countryflags/DJ.png
Binary files differdiff --git a/data/countryflags/DK.png b/data/countryflags/DK.png
index df04cf93..4125f604 100644
--- a/data/countryflags/DK.png
+++ b/data/countryflags/DK.png
Binary files differdiff --git a/data/countryflags/DM.png b/data/countryflags/DM.png
new file mode 100644
index 00000000..3ea6cf59
--- /dev/null
+++ b/data/countryflags/DM.png
Binary files differdiff --git a/data/countryflags/DO.png b/data/countryflags/DO.png
new file mode 100644
index 00000000..f70848f5
--- /dev/null
+++ b/data/countryflags/DO.png
Binary files differdiff --git a/data/countryflags/DZ.png b/data/countryflags/DZ.png
new file mode 100644
index 00000000..31df99f3
--- /dev/null
+++ b/data/countryflags/DZ.png
Binary files differdiff --git a/data/countryflags/EC.png b/data/countryflags/EC.png
new file mode 100644
index 00000000..80dfbaa2
--- /dev/null
+++ b/data/countryflags/EC.png
Binary files differdiff --git a/data/countryflags/EE.png b/data/countryflags/EE.png
index 45ef482f..6dc9933a 100644
--- a/data/countryflags/EE.png
+++ b/data/countryflags/EE.png
Binary files differdiff --git a/data/countryflags/EG.png b/data/countryflags/EG.png
index 3cd437d3..b0c3bf52 100644
--- a/data/countryflags/EG.png
+++ b/data/countryflags/EG.png
Binary files differdiff --git a/data/countryflags/EH.png b/data/countryflags/EH.png
new file mode 100644
index 00000000..9af42a3b
--- /dev/null
+++ b/data/countryflags/EH.png
Binary files differdiff --git a/data/countryflags/ER.png b/data/countryflags/ER.png
new file mode 100644
index 00000000..88dd0706
--- /dev/null
+++ b/data/countryflags/ER.png
Binary files differdiff --git a/data/countryflags/ES.png b/data/countryflags/ES.png
index b507a328..412b2107 100644
--- a/data/countryflags/ES.png
+++ b/data/countryflags/ES.png
Binary files differdiff --git a/data/countryflags/ET.png b/data/countryflags/ET.png
new file mode 100644
index 00000000..9588ce9a
--- /dev/null
+++ b/data/countryflags/ET.png
Binary files differdiff --git a/data/countryflags/FI.png b/data/countryflags/FI.png
index 96b39593..0373e03e 100644
--- a/data/countryflags/FI.png
+++ b/data/countryflags/FI.png
Binary files differdiff --git a/data/countryflags/FJ.png b/data/countryflags/FJ.png
new file mode 100644
index 00000000..9d2af629
--- /dev/null
+++ b/data/countryflags/FJ.png
Binary files differdiff --git a/data/countryflags/FK.png b/data/countryflags/FK.png
new file mode 100644
index 00000000..a3f2ad34
--- /dev/null
+++ b/data/countryflags/FK.png
Binary files differdiff --git a/data/countryflags/FM.png b/data/countryflags/FM.png
new file mode 100644
index 00000000..c0cd4cf1
--- /dev/null
+++ b/data/countryflags/FM.png
Binary files differdiff --git a/data/countryflags/FO.png b/data/countryflags/FO.png
new file mode 100644
index 00000000..e46c2eb3
--- /dev/null
+++ b/data/countryflags/FO.png
Binary files differdiff --git a/data/countryflags/FR.png b/data/countryflags/FR.png
index f85cfa49..eb4ec8ab 100644
--- a/data/countryflags/FR.png
+++ b/data/countryflags/FR.png
Binary files differdiff --git a/data/countryflags/GA.png b/data/countryflags/GA.png
new file mode 100644
index 00000000..446d380d
--- /dev/null
+++ b/data/countryflags/GA.png
Binary files differdiff --git a/data/countryflags/GB.png b/data/countryflags/GB.png
index 240adcc7..4aa46067 100644
--- a/data/countryflags/GB.png
+++ b/data/countryflags/GB.png
Binary files differdiff --git a/data/countryflags/GD.png b/data/countryflags/GD.png
new file mode 100644
index 00000000..538e8e09
--- /dev/null
+++ b/data/countryflags/GD.png
Binary files differdiff --git a/data/countryflags/GE.png b/data/countryflags/GE.png
new file mode 100644
index 00000000..67953847
--- /dev/null
+++ b/data/countryflags/GE.png
Binary files differdiff --git a/data/countryflags/GF.png b/data/countryflags/GF.png
new file mode 100644
index 00000000..2b804f54
--- /dev/null
+++ b/data/countryflags/GF.png
Binary files differdiff --git a/data/countryflags/GG.png b/data/countryflags/GG.png
new file mode 100644
index 00000000..472924fb
--- /dev/null
+++ b/data/countryflags/GG.png
Binary files differdiff --git a/data/countryflags/GH.png b/data/countryflags/GH.png
new file mode 100644
index 00000000..2809250f
--- /dev/null
+++ b/data/countryflags/GH.png
Binary files differdiff --git a/data/countryflags/GI.png b/data/countryflags/GI.png
new file mode 100644
index 00000000..9c88bfd4
--- /dev/null
+++ b/data/countryflags/GI.png
Binary files differdiff --git a/data/countryflags/GL.png b/data/countryflags/GL.png
new file mode 100644
index 00000000..6191d23c
--- /dev/null
+++ b/data/countryflags/GL.png
Binary files differdiff --git a/data/countryflags/GM.png b/data/countryflags/GM.png
new file mode 100644
index 00000000..ce94adfd
--- /dev/null
+++ b/data/countryflags/GM.png
Binary files differdiff --git a/data/countryflags/GN.png b/data/countryflags/GN.png
new file mode 100644
index 00000000..3f698559
--- /dev/null
+++ b/data/countryflags/GN.png
Binary files differdiff --git a/data/countryflags/GP.png b/data/countryflags/GP.png
new file mode 100644
index 00000000..bff1c31f
--- /dev/null
+++ b/data/countryflags/GP.png
Binary files differdiff --git a/data/countryflags/GQ.png b/data/countryflags/GQ.png
new file mode 100644
index 00000000..3a7dc2c5
--- /dev/null
+++ b/data/countryflags/GQ.png
Binary files differdiff --git a/data/countryflags/GR.png b/data/countryflags/GR.png
index 18eaeb28..a9dcd902 100644
--- a/data/countryflags/GR.png
+++ b/data/countryflags/GR.png
Binary files differdiff --git a/data/countryflags/GS.png b/data/countryflags/GS.png
new file mode 100644
index 00000000..bdeda46f
--- /dev/null
+++ b/data/countryflags/GS.png
Binary files differdiff --git a/data/countryflags/GT.png b/data/countryflags/GT.png
new file mode 100644
index 00000000..9ed9c11b
--- /dev/null
+++ b/data/countryflags/GT.png
Binary files differdiff --git a/data/countryflags/GU.png b/data/countryflags/GU.png
new file mode 100644
index 00000000..1a0affcd
--- /dev/null
+++ b/data/countryflags/GU.png
Binary files differdiff --git a/data/countryflags/GW.png b/data/countryflags/GW.png
new file mode 100644
index 00000000..b22b8d0e
--- /dev/null
+++ b/data/countryflags/GW.png
Binary files differdiff --git a/data/countryflags/GY.png b/data/countryflags/GY.png
new file mode 100644
index 00000000..35fd8c8f
--- /dev/null
+++ b/data/countryflags/GY.png
Binary files differdiff --git a/data/countryflags/HK.png b/data/countryflags/HK.png
new file mode 100644
index 00000000..6e4b18ce
--- /dev/null
+++ b/data/countryflags/HK.png
Binary files differdiff --git a/data/countryflags/HN.png b/data/countryflags/HN.png
new file mode 100644
index 00000000..09504718
--- /dev/null
+++ b/data/countryflags/HN.png
Binary files differdiff --git a/data/countryflags/HR.png b/data/countryflags/HR.png
index 6097adc7..c844d4f4 100644
--- a/data/countryflags/HR.png
+++ b/data/countryflags/HR.png
Binary files differdiff --git a/data/countryflags/HT.png b/data/countryflags/HT.png
new file mode 100644
index 00000000..3ab3e79f
--- /dev/null
+++ b/data/countryflags/HT.png
Binary files differdiff --git a/data/countryflags/HU.png b/data/countryflags/HU.png
index 20c21f0e..8106da42 100644
--- a/data/countryflags/HU.png
+++ b/data/countryflags/HU.png
Binary files differdiff --git a/data/countryflags/ID.png b/data/countryflags/ID.png
index 510bac08..49b52e3c 100644
--- a/data/countryflags/ID.png
+++ b/data/countryflags/ID.png
Binary files differdiff --git a/data/countryflags/IE.png b/data/countryflags/IE.png
new file mode 100644
index 00000000..df9be88f
--- /dev/null
+++ b/data/countryflags/IE.png
Binary files differdiff --git a/data/countryflags/IL.png b/data/countryflags/IL.png
index 3852a8f9..ce4acc73 100644
--- a/data/countryflags/IL.png
+++ b/data/countryflags/IL.png
Binary files differdiff --git a/data/countryflags/IM.png b/data/countryflags/IM.png
new file mode 100644
index 00000000..efd55fe4
--- /dev/null
+++ b/data/countryflags/IM.png
Binary files differdiff --git a/data/countryflags/IN.png b/data/countryflags/IN.png
index 91d34696..c4f8dbf0 100644
--- a/data/countryflags/IN.png
+++ b/data/countryflags/IN.png
Binary files differdiff --git a/data/countryflags/IO.png b/data/countryflags/IO.png
new file mode 100644
index 00000000..e983a3f2
--- /dev/null
+++ b/data/countryflags/IO.png
Binary files differdiff --git a/data/countryflags/IQ.png b/data/countryflags/IQ.png
new file mode 100644
index 00000000..81e7a8b4
--- /dev/null
+++ b/data/countryflags/IQ.png
Binary files differdiff --git a/data/countryflags/IR.png b/data/countryflags/IR.png
index 141a0e56..dcb71c3a 100644
--- a/data/countryflags/IR.png
+++ b/data/countryflags/IR.png
Binary files differdiff --git a/data/countryflags/IS.png b/data/countryflags/IS.png
new file mode 100644
index 00000000..4307fdfd
--- /dev/null
+++ b/data/countryflags/IS.png
Binary files differdiff --git a/data/countryflags/IT.png b/data/countryflags/IT.png
index 4d4a1b3e..7372d812 100644
--- a/data/countryflags/IT.png
+++ b/data/countryflags/IT.png
Binary files differdiff --git a/data/countryflags/JE.png b/data/countryflags/JE.png
new file mode 100644
index 00000000..a6b50b0c
--- /dev/null
+++ b/data/countryflags/JE.png
Binary files differdiff --git a/data/countryflags/JM.png b/data/countryflags/JM.png
new file mode 100644
index 00000000..94cf3abf
--- /dev/null
+++ b/data/countryflags/JM.png
Binary files differdiff --git a/data/countryflags/JO.png b/data/countryflags/JO.png
new file mode 100644
index 00000000..0bb6cb65
--- /dev/null
+++ b/data/countryflags/JO.png
Binary files differdiff --git a/data/countryflags/JP.png b/data/countryflags/JP.png
new file mode 100644
index 00000000..ddfdfb5c
--- /dev/null
+++ b/data/countryflags/JP.png
Binary files differdiff --git a/data/countryflags/KE.png b/data/countryflags/KE.png
new file mode 100644
index 00000000..e44c27d6
--- /dev/null
+++ b/data/countryflags/KE.png
Binary files differdiff --git a/data/countryflags/KG.png b/data/countryflags/KG.png
new file mode 100644
index 00000000..1c57fbbc
--- /dev/null
+++ b/data/countryflags/KG.png
Binary files differdiff --git a/data/countryflags/KH.png b/data/countryflags/KH.png
new file mode 100644
index 00000000..102bc024
--- /dev/null
+++ b/data/countryflags/KH.png
Binary files differdiff --git a/data/countryflags/KI.png b/data/countryflags/KI.png
new file mode 100644
index 00000000..7f62de60
--- /dev/null
+++ b/data/countryflags/KI.png
Binary files differdiff --git a/data/countryflags/KM.png b/data/countryflags/KM.png
new file mode 100644
index 00000000..f6a77ecc
--- /dev/null
+++ b/data/countryflags/KM.png
Binary files differdiff --git a/data/countryflags/KN.png b/data/countryflags/KN.png
new file mode 100644
index 00000000..06f6a9f9
--- /dev/null
+++ b/data/countryflags/KN.png
Binary files differdiff --git a/data/countryflags/KP.png b/data/countryflags/KP.png
new file mode 100644
index 00000000..48093061
--- /dev/null
+++ b/data/countryflags/KP.png
Binary files differdiff --git a/data/countryflags/KR.png b/data/countryflags/KR.png
new file mode 100644
index 00000000..32a83733
--- /dev/null
+++ b/data/countryflags/KR.png
Binary files differdiff --git a/data/countryflags/KW.png b/data/countryflags/KW.png
new file mode 100644
index 00000000..557e38b1
--- /dev/null
+++ b/data/countryflags/KW.png
Binary files differdiff --git a/data/countryflags/KY.png b/data/countryflags/KY.png
new file mode 100644
index 00000000..020dbeef
--- /dev/null
+++ b/data/countryflags/KY.png
Binary files differdiff --git a/data/countryflags/KZ.png b/data/countryflags/KZ.png
index e3db4f93..d0b6eede 100644
--- a/data/countryflags/KZ.png
+++ b/data/countryflags/KZ.png
Binary files differdiff --git a/data/countryflags/LA.png b/data/countryflags/LA.png
new file mode 100644
index 00000000..bfe966f6
--- /dev/null
+++ b/data/countryflags/LA.png
Binary files differdiff --git a/data/countryflags/LB.png b/data/countryflags/LB.png
new file mode 100644
index 00000000..3c8ef00c
--- /dev/null
+++ b/data/countryflags/LB.png
Binary files differdiff --git a/data/countryflags/LC.png b/data/countryflags/LC.png
new file mode 100644
index 00000000..19b5f78a
--- /dev/null
+++ b/data/countryflags/LC.png
Binary files differdiff --git a/data/countryflags/LI.png b/data/countryflags/LI.png
new file mode 100644
index 00000000..06daeeb8
--- /dev/null
+++ b/data/countryflags/LI.png
Binary files differdiff --git a/data/countryflags/LK.png b/data/countryflags/LK.png
new file mode 100644
index 00000000..f7710ae9
--- /dev/null
+++ b/data/countryflags/LK.png
Binary files differdiff --git a/data/countryflags/LR.png b/data/countryflags/LR.png
new file mode 100644
index 00000000..6d361dff
--- /dev/null
+++ b/data/countryflags/LR.png
Binary files differdiff --git a/data/countryflags/LS.png b/data/countryflags/LS.png
new file mode 100644
index 00000000..02a844fe
--- /dev/null
+++ b/data/countryflags/LS.png
Binary files differdiff --git a/data/countryflags/LT.png b/data/countryflags/LT.png
index dd1d3d13..c37e857f 100644
--- a/data/countryflags/LT.png
+++ b/data/countryflags/LT.png
Binary files differdiff --git a/data/countryflags/LU.png b/data/countryflags/LU.png
index 01377c56..ed89b616 100644
--- a/data/countryflags/LU.png
+++ b/data/countryflags/LU.png
Binary files differdiff --git a/data/countryflags/LV.png b/data/countryflags/LV.png
index 8915e25d..f5f51afc 100644
--- a/data/countryflags/LV.png
+++ b/data/countryflags/LV.png
Binary files differdiff --git a/data/countryflags/LY.png b/data/countryflags/LY.png
new file mode 100644
index 00000000..1dda0198
--- /dev/null
+++ b/data/countryflags/LY.png
Binary files differdiff --git a/data/countryflags/MA.png b/data/countryflags/MA.png
new file mode 100644
index 00000000..0ae330bb
--- /dev/null
+++ b/data/countryflags/MA.png
Binary files differdiff --git a/data/countryflags/MC.png b/data/countryflags/MC.png
new file mode 100644
index 00000000..5a73c3c1
--- /dev/null
+++ b/data/countryflags/MC.png
Binary files differdiff --git a/data/countryflags/MD.png b/data/countryflags/MD.png
new file mode 100644
index 00000000..d713b079
--- /dev/null
+++ b/data/countryflags/MD.png
Binary files differdiff --git a/data/countryflags/ME.png b/data/countryflags/ME.png
new file mode 100644
index 00000000..f6348bda
--- /dev/null
+++ b/data/countryflags/ME.png
Binary files differdiff --git a/data/countryflags/MF.png b/data/countryflags/MF.png
new file mode 100644
index 00000000..0fa201c9
--- /dev/null
+++ b/data/countryflags/MF.png
Binary files differdiff --git a/data/countryflags/MG.png b/data/countryflags/MG.png
new file mode 100644
index 00000000..52030837
--- /dev/null
+++ b/data/countryflags/MG.png
Binary files differdiff --git a/data/countryflags/MH.png b/data/countryflags/MH.png
new file mode 100644
index 00000000..40d7bd3d
--- /dev/null
+++ b/data/countryflags/MH.png
Binary files differdiff --git a/data/countryflags/MK.png b/data/countryflags/MK.png
new file mode 100644
index 00000000..3743d81a
--- /dev/null
+++ b/data/countryflags/MK.png
Binary files differdiff --git a/data/countryflags/ML.png b/data/countryflags/ML.png
new file mode 100644
index 00000000..d548a1e2
--- /dev/null
+++ b/data/countryflags/ML.png
Binary files differdiff --git a/data/countryflags/MM.png b/data/countryflags/MM.png
new file mode 100644
index 00000000..58c098b8
--- /dev/null
+++ b/data/countryflags/MM.png
Binary files differdiff --git a/data/countryflags/MN.png b/data/countryflags/MN.png
new file mode 100644
index 00000000..1d9f5159
--- /dev/null
+++ b/data/countryflags/MN.png
Binary files differdiff --git a/data/countryflags/MO.png b/data/countryflags/MO.png
new file mode 100644
index 00000000..6adda298
--- /dev/null
+++ b/data/countryflags/MO.png
Binary files differdiff --git a/data/countryflags/MP.png b/data/countryflags/MP.png
new file mode 100644
index 00000000..6b308645
--- /dev/null
+++ b/data/countryflags/MP.png
Binary files differdiff --git a/data/countryflags/MQ.png b/data/countryflags/MQ.png
new file mode 100644
index 00000000..2f3c556a
--- /dev/null
+++ b/data/countryflags/MQ.png
Binary files differdiff --git a/data/countryflags/MR.png b/data/countryflags/MR.png
new file mode 100644
index 00000000..8c6462ba
--- /dev/null
+++ b/data/countryflags/MR.png
Binary files differdiff --git a/data/countryflags/MS.png b/data/countryflags/MS.png
new file mode 100644
index 00000000..27312023
--- /dev/null
+++ b/data/countryflags/MS.png
Binary files differdiff --git a/data/countryflags/MT.png b/data/countryflags/MT.png
new file mode 100644
index 00000000..043ea73e
--- /dev/null
+++ b/data/countryflags/MT.png
Binary files differdiff --git a/data/countryflags/MU.png b/data/countryflags/MU.png
new file mode 100644
index 00000000..02a5f3af
--- /dev/null
+++ b/data/countryflags/MU.png
Binary files differdiff --git a/data/countryflags/MV.png b/data/countryflags/MV.png
new file mode 100644
index 00000000..6a3fd961
--- /dev/null
+++ b/data/countryflags/MV.png
Binary files differdiff --git a/data/countryflags/MW.png b/data/countryflags/MW.png
new file mode 100644
index 00000000..5d26e5f7
--- /dev/null
+++ b/data/countryflags/MW.png
Binary files differdiff --git a/data/countryflags/MX.png b/data/countryflags/MX.png
index 9505d623..19c7ddea 100644
--- a/data/countryflags/MX.png
+++ b/data/countryflags/MX.png
Binary files differdiff --git a/data/countryflags/MY.png b/data/countryflags/MY.png
new file mode 100644
index 00000000..885ba638
--- /dev/null
+++ b/data/countryflags/MY.png
Binary files differdiff --git a/data/countryflags/MZ.png b/data/countryflags/MZ.png
new file mode 100644
index 00000000..caa5cb4d
--- /dev/null
+++ b/data/countryflags/MZ.png
Binary files differdiff --git a/data/countryflags/NA.png b/data/countryflags/NA.png
new file mode 100644
index 00000000..f6936933
--- /dev/null
+++ b/data/countryflags/NA.png
Binary files differdiff --git a/data/countryflags/NC.png b/data/countryflags/NC.png
new file mode 100644
index 00000000..5f3b0892
--- /dev/null
+++ b/data/countryflags/NC.png
Binary files differdiff --git a/data/countryflags/NE.png b/data/countryflags/NE.png
new file mode 100644
index 00000000..7d73f48e
--- /dev/null
+++ b/data/countryflags/NE.png
Binary files differdiff --git a/data/countryflags/NF.png b/data/countryflags/NF.png
new file mode 100644
index 00000000..5f5401d7
--- /dev/null
+++ b/data/countryflags/NF.png
Binary files differdiff --git a/data/countryflags/NG.png b/data/countryflags/NG.png
new file mode 100644
index 00000000..e9030716
--- /dev/null
+++ b/data/countryflags/NG.png
Binary files differdiff --git a/data/countryflags/NI.png b/data/countryflags/NI.png
new file mode 100644
index 00000000..19df0e95
--- /dev/null
+++ b/data/countryflags/NI.png
Binary files differdiff --git a/data/countryflags/NL.png b/data/countryflags/NL.png
index 5882be49..ba54b03c 100644
--- a/data/countryflags/NL.png
+++ b/data/countryflags/NL.png
Binary files differdiff --git a/data/countryflags/NO.png b/data/countryflags/NO.png
index 2e9bbec9..6f085323 100644
--- a/data/countryflags/NO.png
+++ b/data/countryflags/NO.png
Binary files differdiff --git a/data/countryflags/NP.png b/data/countryflags/NP.png
new file mode 100644
index 00000000..53c7510a
--- /dev/null
+++ b/data/countryflags/NP.png
Binary files differdiff --git a/data/countryflags/NR.png b/data/countryflags/NR.png
new file mode 100644
index 00000000..562be4fc
--- /dev/null
+++ b/data/countryflags/NR.png
Binary files differdiff --git a/data/countryflags/NU.png b/data/countryflags/NU.png
new file mode 100644
index 00000000..1928bab6
--- /dev/null
+++ b/data/countryflags/NU.png
Binary files differdiff --git a/data/countryflags/NZ.png b/data/countryflags/NZ.png
new file mode 100644
index 00000000..4e210491
--- /dev/null
+++ b/data/countryflags/NZ.png
Binary files differdiff --git a/data/countryflags/OM.png b/data/countryflags/OM.png
new file mode 100644
index 00000000..8b6aba47
--- /dev/null
+++ b/data/countryflags/OM.png
Binary files differdiff --git a/data/countryflags/PA.png b/data/countryflags/PA.png
new file mode 100644
index 00000000..ef051c61
--- /dev/null
+++ b/data/countryflags/PA.png
Binary files differdiff --git a/data/countryflags/PE.png b/data/countryflags/PE.png
new file mode 100644
index 00000000..f6188650
--- /dev/null
+++ b/data/countryflags/PE.png
Binary files differdiff --git a/data/countryflags/PF.png b/data/countryflags/PF.png
new file mode 100644
index 00000000..bf04554d
--- /dev/null
+++ b/data/countryflags/PF.png
Binary files differdiff --git a/data/countryflags/PG.png b/data/countryflags/PG.png
new file mode 100644
index 00000000..04b36a38
--- /dev/null
+++ b/data/countryflags/PG.png
Binary files differdiff --git a/data/countryflags/PH.png b/data/countryflags/PH.png
index ebcec1f2..edb506c0 100644
--- a/data/countryflags/PH.png
+++ b/data/countryflags/PH.png
Binary files differdiff --git a/data/countryflags/PK.png b/data/countryflags/PK.png
index 6e4e4950..6030b86b 100644
--- a/data/countryflags/PK.png
+++ b/data/countryflags/PK.png
Binary files differdiff --git a/data/countryflags/PL.png b/data/countryflags/PL.png
index 6e6a5c95..03b9e71f 100644
--- a/data/countryflags/PL.png
+++ b/data/countryflags/PL.png
Binary files differdiff --git a/data/countryflags/PM.png b/data/countryflags/PM.png
new file mode 100644
index 00000000..dd0dce93
--- /dev/null
+++ b/data/countryflags/PM.png
Binary files differdiff --git a/data/countryflags/PN.png b/data/countryflags/PN.png
new file mode 100644
index 00000000..a14628bb
--- /dev/null
+++ b/data/countryflags/PN.png
Binary files differdiff --git a/data/countryflags/PR.png b/data/countryflags/PR.png
new file mode 100644
index 00000000..6a3f6014
--- /dev/null
+++ b/data/countryflags/PR.png
Binary files differdiff --git a/data/countryflags/PT.png b/data/countryflags/PT.png
index b3be0973..ca90e6e9 100644
--- a/data/countryflags/PT.png
+++ b/data/countryflags/PT.png
Binary files differdiff --git a/data/countryflags/PW.png b/data/countryflags/PW.png
new file mode 100644
index 00000000..49ee4480
--- /dev/null
+++ b/data/countryflags/PW.png
Binary files differdiff --git a/data/countryflags/PY.png b/data/countryflags/PY.png
new file mode 100644
index 00000000..948f7151
--- /dev/null
+++ b/data/countryflags/PY.png
Binary files differdiff --git a/data/countryflags/QA.png b/data/countryflags/QA.png
new file mode 100644
index 00000000..cae22d2b
--- /dev/null
+++ b/data/countryflags/QA.png
Binary files differdiff --git a/data/countryflags/RE.png b/data/countryflags/RE.png
new file mode 100644
index 00000000..5efe1da4
--- /dev/null
+++ b/data/countryflags/RE.png
Binary files differdiff --git a/data/countryflags/RO.png b/data/countryflags/RO.png
index 4eae6d22..6ea76a88 100644
--- a/data/countryflags/RO.png
+++ b/data/countryflags/RO.png
Binary files differdiff --git a/data/countryflags/RS.png b/data/countryflags/RS.png
index 7cc06394..24e067b4 100644
--- a/data/countryflags/RS.png
+++ b/data/countryflags/RS.png
Binary files differdiff --git a/data/countryflags/RU.png b/data/countryflags/RU.png
index ed293810..74a8528f 100644
--- a/data/countryflags/RU.png
+++ b/data/countryflags/RU.png
Binary files differdiff --git a/data/countryflags/RW.png b/data/countryflags/RW.png
new file mode 100644
index 00000000..57d3ac5f
--- /dev/null
+++ b/data/countryflags/RW.png
Binary files differdiff --git a/data/countryflags/SA.png b/data/countryflags/SA.png
index fbfb6cf5..93598245 100644
--- a/data/countryflags/SA.png
+++ b/data/countryflags/SA.png
Binary files differdiff --git a/data/countryflags/SB.png b/data/countryflags/SB.png
new file mode 100644
index 00000000..7c4c468d
--- /dev/null
+++ b/data/countryflags/SB.png
Binary files differdiff --git a/data/countryflags/SC.png b/data/countryflags/SC.png
new file mode 100644
index 00000000..5237f2e8
--- /dev/null
+++ b/data/countryflags/SC.png
Binary files differdiff --git a/data/countryflags/SD.png b/data/countryflags/SD.png
new file mode 100644
index 00000000..6adab5ad
--- /dev/null
+++ b/data/countryflags/SD.png
Binary files differdiff --git a/data/countryflags/SE.png b/data/countryflags/SE.png
index c5c9cda9..47dcef3f 100644
--- a/data/countryflags/SE.png
+++ b/data/countryflags/SE.png
Binary files differdiff --git a/data/countryflags/SG.png b/data/countryflags/SG.png
new file mode 100644
index 00000000..c54a7608
--- /dev/null
+++ b/data/countryflags/SG.png
Binary files differdiff --git a/data/countryflags/SH.png b/data/countryflags/SH.png
new file mode 100644
index 00000000..a9b6fc9f
--- /dev/null
+++ b/data/countryflags/SH.png
Binary files differdiff --git a/data/countryflags/SI.png b/data/countryflags/SI.png
new file mode 100644
index 00000000..1e553fd5
--- /dev/null
+++ b/data/countryflags/SI.png
Binary files differdiff --git a/data/countryflags/SK.png b/data/countryflags/SK.png
index 5bd535a1..9c927e8f 100644
--- a/data/countryflags/SK.png
+++ b/data/countryflags/SK.png
Binary files differdiff --git a/data/countryflags/SL.png b/data/countryflags/SL.png
new file mode 100644
index 00000000..5d1d42fe
--- /dev/null
+++ b/data/countryflags/SL.png
Binary files differdiff --git a/data/countryflags/SM.png b/data/countryflags/SM.png
new file mode 100644
index 00000000..96726305
--- /dev/null
+++ b/data/countryflags/SM.png
Binary files differdiff --git a/data/countryflags/SN.png b/data/countryflags/SN.png
new file mode 100644
index 00000000..42a57326
--- /dev/null
+++ b/data/countryflags/SN.png
Binary files differdiff --git a/data/countryflags/SO.png b/data/countryflags/SO.png
new file mode 100644
index 00000000..93815d27
--- /dev/null
+++ b/data/countryflags/SO.png
Binary files differdiff --git a/data/countryflags/SR.png b/data/countryflags/SR.png
new file mode 100644
index 00000000..06fe40f3
--- /dev/null
+++ b/data/countryflags/SR.png
Binary files differdiff --git a/data/countryflags/SS.png b/data/countryflags/SS.png
new file mode 100644
index 00000000..203c51da
--- /dev/null
+++ b/data/countryflags/SS.png
Binary files differdiff --git a/data/countryflags/ST.png b/data/countryflags/ST.png
new file mode 100644
index 00000000..748030a0
--- /dev/null
+++ b/data/countryflags/ST.png
Binary files differdiff --git a/data/countryflags/SV.png b/data/countryflags/SV.png
index d313b0c1..d03b8be4 100644
--- a/data/countryflags/SV.png
+++ b/data/countryflags/SV.png
Binary files differdiff --git a/data/countryflags/SX.png b/data/countryflags/SX.png
new file mode 100644
index 00000000..61606935
--- /dev/null
+++ b/data/countryflags/SX.png
Binary files differdiff --git a/data/countryflags/SY.png b/data/countryflags/SY.png
new file mode 100644
index 00000000..04342871
--- /dev/null
+++ b/data/countryflags/SY.png
Binary files differdiff --git a/data/countryflags/SZ.png b/data/countryflags/SZ.png
new file mode 100644
index 00000000..bea99b2c
--- /dev/null
+++ b/data/countryflags/SZ.png
Binary files differdiff --git a/data/countryflags/TC.png b/data/countryflags/TC.png
new file mode 100644
index 00000000..0abdc845
--- /dev/null
+++ b/data/countryflags/TC.png
Binary files differdiff --git a/data/countryflags/TD.png b/data/countryflags/TD.png
new file mode 100644
index 00000000..4ffcc670
--- /dev/null
+++ b/data/countryflags/TD.png
Binary files differdiff --git a/data/countryflags/TF.png b/data/countryflags/TF.png
new file mode 100644
index 00000000..d3095b29
--- /dev/null
+++ b/data/countryflags/TF.png
Binary files differdiff --git a/data/countryflags/TG.png b/data/countryflags/TG.png
new file mode 100644
index 00000000..797f48cd
--- /dev/null
+++ b/data/countryflags/TG.png
Binary files differdiff --git a/data/countryflags/TH.png b/data/countryflags/TH.png
new file mode 100644
index 00000000..97e22df3
--- /dev/null
+++ b/data/countryflags/TH.png
Binary files differdiff --git a/data/countryflags/TJ.png b/data/countryflags/TJ.png
new file mode 100644
index 00000000..54d9830f
--- /dev/null
+++ b/data/countryflags/TJ.png
Binary files differdiff --git a/data/countryflags/TK.png b/data/countryflags/TK.png
new file mode 100644
index 00000000..623f69e0
--- /dev/null
+++ b/data/countryflags/TK.png
Binary files differdiff --git a/data/countryflags/TL.png b/data/countryflags/TL.png
new file mode 100644
index 00000000..5c6f1ff7
--- /dev/null
+++ b/data/countryflags/TL.png
Binary files differdiff --git a/data/countryflags/TM.png b/data/countryflags/TM.png
new file mode 100644
index 00000000..40767589
--- /dev/null
+++ b/data/countryflags/TM.png
Binary files differdiff --git a/data/countryflags/TN.png b/data/countryflags/TN.png
new file mode 100644
index 00000000..10f9f696
--- /dev/null
+++ b/data/countryflags/TN.png
Binary files differdiff --git a/data/countryflags/TO.png b/data/countryflags/TO.png
new file mode 100644
index 00000000..a7a703c9
--- /dev/null
+++ b/data/countryflags/TO.png
Binary files differdiff --git a/data/countryflags/TR.png b/data/countryflags/TR.png
index 8bc78a72..2df563de 100644
--- a/data/countryflags/TR.png
+++ b/data/countryflags/TR.png
Binary files differdiff --git a/data/countryflags/TT.png b/data/countryflags/TT.png
new file mode 100644
index 00000000..c3f21b2b
--- /dev/null
+++ b/data/countryflags/TT.png
Binary files differdiff --git a/data/countryflags/TV.png b/data/countryflags/TV.png
new file mode 100644
index 00000000..ede2dbca
--- /dev/null
+++ b/data/countryflags/TV.png
Binary files differdiff --git a/data/countryflags/TW.png b/data/countryflags/TW.png
new file mode 100644
index 00000000..e3c48eb2
--- /dev/null
+++ b/data/countryflags/TW.png
Binary files differdiff --git a/data/countryflags/TZ.png b/data/countryflags/TZ.png
new file mode 100644
index 00000000..38ad129d
--- /dev/null
+++ b/data/countryflags/TZ.png
Binary files differdiff --git a/data/countryflags/UA.png b/data/countryflags/UA.png
index 1356e7cb..f9fc86cd 100644
--- a/data/countryflags/UA.png
+++ b/data/countryflags/UA.png
Binary files differdiff --git a/data/countryflags/UG.png b/data/countryflags/UG.png
new file mode 100644
index 00000000..92537e9b
--- /dev/null
+++ b/data/countryflags/UG.png
Binary files differdiff --git a/data/countryflags/US.png b/data/countryflags/US.png
index 96dfca89..448930c2 100644
--- a/data/countryflags/US.png
+++ b/data/countryflags/US.png
Binary files differdiff --git a/data/countryflags/UY.png b/data/countryflags/UY.png
new file mode 100644
index 00000000..4c950068
--- /dev/null
+++ b/data/countryflags/UY.png
Binary files differdiff --git a/data/countryflags/UZ.png b/data/countryflags/UZ.png
new file mode 100644
index 00000000..0689f217
--- /dev/null
+++ b/data/countryflags/UZ.png
Binary files differdiff --git a/data/countryflags/VA.png b/data/countryflags/VA.png
new file mode 100644
index 00000000..e957e06d
--- /dev/null
+++ b/data/countryflags/VA.png
Binary files differdiff --git a/data/countryflags/VC.png b/data/countryflags/VC.png
new file mode 100644
index 00000000..60f90b55
--- /dev/null
+++ b/data/countryflags/VC.png
Binary files differdiff --git a/data/countryflags/VE.png b/data/countryflags/VE.png
new file mode 100644
index 00000000..e36f7a4b
--- /dev/null
+++ b/data/countryflags/VE.png
Binary files differdiff --git a/data/countryflags/VG.png b/data/countryflags/VG.png
new file mode 100644
index 00000000..99069e25
--- /dev/null
+++ b/data/countryflags/VG.png
Binary files differdiff --git a/data/countryflags/VI.png b/data/countryflags/VI.png
new file mode 100644
index 00000000..05fa911a
--- /dev/null
+++ b/data/countryflags/VI.png
Binary files differdiff --git a/data/countryflags/VN.png b/data/countryflags/VN.png
new file mode 100644
index 00000000..c4e19d79
--- /dev/null
+++ b/data/countryflags/VN.png
Binary files differdiff --git a/data/countryflags/VU.png b/data/countryflags/VU.png
new file mode 100644
index 00000000..228083ba
--- /dev/null
+++ b/data/countryflags/VU.png
Binary files differdiff --git a/data/countryflags/WF.png b/data/countryflags/WF.png
new file mode 100644
index 00000000..aeeba249
--- /dev/null
+++ b/data/countryflags/WF.png
Binary files differdiff --git a/data/countryflags/WS.png b/data/countryflags/WS.png
new file mode 100644
index 00000000..5d16a5d9
--- /dev/null
+++ b/data/countryflags/WS.png
Binary files differdiff --git a/data/countryflags/XEN.png b/data/countryflags/XEN.png
index 8388316c..08e3ef29 100644
--- a/data/countryflags/XEN.png
+++ b/data/countryflags/XEN.png
Binary files differdiff --git a/data/countryflags/XNI.png b/data/countryflags/XNI.png
index 410615b0..18083db0 100644
--- a/data/countryflags/XNI.png
+++ b/data/countryflags/XNI.png
Binary files differdiff --git a/data/countryflags/XSC.png b/data/countryflags/XSC.png
index f3b9ee03..3002fd50 100644
--- a/data/countryflags/XSC.png
+++ b/data/countryflags/XSC.png
Binary files differdiff --git a/data/countryflags/XWA.png b/data/countryflags/XWA.png
index bae3809e..3b6b86be 100644
--- a/data/countryflags/XWA.png
+++ b/data/countryflags/XWA.png
Binary files differdiff --git a/data/countryflags/YE.png b/data/countryflags/YE.png
new file mode 100644
index 00000000..73686007
--- /dev/null
+++ b/data/countryflags/YE.png
Binary files differdiff --git a/data/countryflags/ZA.png b/data/countryflags/ZA.png
index c7e30de0..21aec1ed 100644
--- a/data/countryflags/ZA.png
+++ b/data/countryflags/ZA.png
Binary files differdiff --git a/data/countryflags/ZM.png b/data/countryflags/ZM.png
new file mode 100644
index 00000000..2160d527
--- /dev/null
+++ b/data/countryflags/ZM.png
Binary files differdiff --git a/data/countryflags/ZW.png b/data/countryflags/ZW.png
new file mode 100644
index 00000000..b6c870ca
--- /dev/null
+++ b/data/countryflags/ZW.png
Binary files differdiff --git a/data/countryflags/index.txt b/data/countryflags/index.txt
index f841c194..3b60f1b2 100644
--- a/data/countryflags/index.txt
+++ b/data/countryflags/index.txt
@@ -1,765 +1,769 @@
-
-##### country codes #####
-
-##### custom #####
-
-default
-== -1
-
-XEN
-== 901
-
-XNI
-== 902
-
-XSC
-== 903
-
-XWA
-== 904
-
-##### ISO 3166-1 based #####
-
-#AF
-#== 4
-
-#AX
-#== 248
-
-#AL
-#== 8
-
-#DZ
-#== 12
-
-#AS
-#== 16
-
-#AD
-#== 20
-
-#AO
-#== 24
-
-#AI
-#== 660
-
-#AQ
-#== 10
-
-#AG
-#== 28
-
-AR
-== 32
-
-#AM
-#== 51
-
-#AW
-#== 533
-
-AU
-== 36
-
-AT
-== 40
-
-#AZ
-#== 31
-
-#BS
-#== 44
-
-#BH
-#== 48
-
-#BD
-#== 50
-
-#BB
-#== 52
-
-BY
-== 112
-
-BE
-== 56
-
-#BZ
-#== 84
-
-#BJ
-#== 204
-
-#BM
-#== 60
-
-#BT
-#== 64
-
-#BO
-#== 68
-
-#BQ
-#== 535
-
-BA
-== 70
-
-#BW
-#== 72
-
-#BV
-#== 74
-
-BR
-== 76
-
-#IO
-#== 86
-
-#BN
-#== 96
-
-BG
-== 100
-
-#BF
-#== 854
-
-#BI
-#== 108
-
-#KH
-#== 116
-
-#CM
-#== 120
-
-CA
-== 124
-
-#CV
-#== 132
-
-#KY
-#== 136
-
-#CF
-#== 140
-
-#TD
-#== 148
-
-CL
-== 152
-
-CN
-== 156
-
-#CX
-#== 162
-
-#CC
-#== 166
-
-CO
-== 170
-
-#KM
-#== 174
-
-#CG
-#== 178
-
-#CD
-#== 180
-
-#CK
-#== 184
-
-#CR
-#== 188
-
-#CI
-#== 384
-
-HR
-== 191
-
-#CU
-#== 192
-
-#CW
-#== 531
-
-#CY
-#== 196
-
-CZ
-== 203
-
-DK
-== 208
-
-#DJ
-#== 262
-
-#DM
-#== 212
-
-#DO
-#== 214
-
-#EC
-#== 218
-
-EG
-== 818
-
-SV
-== 222
-
-#GQ
-#== 226
-
-#ER
-#== 232
-
-EE
-== 233
-
-#ET
-#== 231
-
-#FK
-#== 238
-
-#FO
-#== 234
-
-#FJ
-#== 242
-
-FI
-== 246
-
-FR
-== 250
-
-#GF
-#== 254
-
-#PF
-#== 258
-
-#TF
-#== 260
-
-#GA
-#== 266
-
-#GM
-#== 270
-
-#GE
-#== 268
-
-DE
-== 276
-
-#GH
-#== 288
-
-#GI
-#== 292
-
-GR
-== 300
-
-#GL
-#== 304
-
-#GD
-#== 308
-
-#GP
-#== 312
-
-#GU
-#== 316
-
-#GT
-#== 320
-
-#GG
-#== 831
-
-#GN
-#== 324
-
-#GW
-#== 624
-
-#GY
-#== 328
-
-#HT
-#== 332
-
-#HM
-#== 334
-
-#VA
-#== 336
-
-#HN
-#== 340
-
-#HK
-#== 344
-
-HU
-== 348
-
-#IS
-#== 352
-
-IN
-== 356
-
-ID
-== 360
-
-IR
-== 364
-
-#IQ
-#== 368
-
-#IE
-#== 372
-
-#IM
-#== 833
-
-IL
-== 376
-
-IT
-== 380
-
-#JM
-#== 388
-
-#JP
-#== 392
-
-#JE
-#== 832
-
-#JO
-#== 400
-
-KZ
-== 398
-
-#KE
-#== 404
-
-#KI
-#== 296
-
-#KP
-#== 408
-
-#KR
-#== 410
-
-#KW
-#== 414
-
-#KG
-#== 417
-
-#LA
-#== 418
-
-LV
-== 428
-
-#LB
-#== 422
-
-#LS
-#== 426
-
-#LR
-#== 430
-
-#LY
-#== 434
-
-#LI
-#== 438
-
-LT
-== 440
-
-LU
-== 442
-
-#MO
-#== 446
-
-#MK
-#== 807
-
-#MG
-#== 450
-
-#MW
-#== 454
-
-#MY
-#== 458
-
-#MV
-#== 462
-
-#ML
-#== 466
-
-#MT
-#== 470
-
-#MH
-#== 584
-
-#MQ
-#== 474
-
-#MR
-#== 478
-
-#MU
-#== 480
-
-#YT
-#== 175
-
-MX
-== 484
-
-#FM
-#== 583
-
-#MD
-#== 498
-
-#MC
-#== 492
-
-#MN
-#== 496
-
-#ME
-#== 499
-
-#MS
-#== 500
-
-#MA
-#== 504
-
-#MZ
-#== 508
-
-#MM
-#== 104
-
-#NA
-#== 516
-
-#NR
-#== 520
-
-#NP
-#== 524
-
-NL
-== 528
-
-#NC
-#== 540
-
-#NZ
-#== 554
-
-#NI
-#== 558
-
-#NE
-#== 562
-
-#NG
-#== 566
-
-#NU
-#== 570
-
-#NF
-#== 574
-
-#MP
-#== 580
-
-NO
-== 578
-
-#OM
-#== 512
-
-PK
-== 586
-
-#PW
-#== 585
-
-#PS
-#== 275
-
-#PA
-#== 591
-
-#PG
-#== 598
-
-#PY
-#== 600
-
-#PE
-#== 604
-
-PH
-== 608
-
-#PN
-#== 612
-
-PL
-== 616
-
-PT
-== 620
-
-#PR
-#== 630
-
-#QA
-#== 634
-
-#RE
-#== 638
-
-RO
-== 642
-
-RU
-== 643
-
-#RW
-#== 646
-
-#BL
-#== 652
-
-#SH
-#== 654
-
-#KN
-#== 659
-
-#LC
-#== 662
-
-#MF
-#== 663
-
-#PM
-#== 666
-
-#VC
-#== 670
-
-#WS
-#== 882
-
-#SM
-#== 674
-
-#ST
-#== 678
-
-SA
-== 682
-
-#SN
-#== 686
-
-RS
-== 688
-
-#SC
-#== 690
-
-#SL
-#== 694
-
-#SG
-#== 702
-
-#SX
-#== 534
-
-SK
-== 703
-
-#SI
-#== 705
-
-#SB
-#== 90
-
-#SO
-#== 706
-
-ZA
-== 710
-
-#GS
-#== 239
-
-ES
-== 724
-
-#LK
-#== 144
-
-#SD
-#== 736
-
-#SR
-#== 740
-
-#SJ
-#== 744
-
-#SZ
-#== 748
-
-SE
-== 752
-
-CH
-== 756
-
-#SY
-#== 760
-
-#TW
-#== 158
-
-#TJ
-#== 762
-
-#TZ
-#== 834
-
-#TH
-#== 764
-
-#TL
-#== 626
-
-#TG
-#== 768
-
-#TK
-#== 772
-
-#TO
-#== 776
-
-#TT
-#== 780
-
-#TN
-#== 788
-
-TR
-== 792
-
-#TM
-#== 795
-
-#TC
-#== 796
-
-#TV
-#== 798
-
-#UG
-#== 800
-
-UA
-== 804
-
-#AE
-#== 784
-
-GB
-== 826
-
-US
-== 840
-
-#UM
-#== 581
-
-#UY
-#== 858
-
-#UZ
-#== 860
-
-#VU
-#== 548
-
-#VE
-#== 862
-
-#VN
-#== 704
-
-#VG
-#== 92
-
-#VI
-#== 850
-
-#WF
-#== 876
-
-#EH
-#== 732
-
-#YE
-#== 887
-
-#ZM
-#== 894
-
-#ZW
-#== 716
+

+##### country codes #####

+

+##### custom #####

+

+default

+== -1

+

+XEN

+== 901

+

+XNI

+== 902

+

+XSC

+== 903

+

+XWA

+== 904

+

+#south sudan, non official code#

+SS

+== 737

+

+##### ISO 3166-1 based #####

+

+AF

+== 4

+

+AX

+== 248

+

+AL

+== 8

+

+DZ

+== 12

+

+AS

+== 16

+

+AD

+== 20

+

+AO

+== 24

+

+AI

+== 660

+

+#AQ

+#== 10

+

+AG

+== 28

+

+AR

+== 32

+

+AM

+== 51

+

+AW

+== 533

+

+AU

+== 36

+

+AT

+== 40

+

+AZ

+== 31

+

+BS

+== 44

+

+BH

+== 48

+

+BD

+== 50

+

+BB

+== 52

+

+BY

+== 112

+

+BE

+== 56

+

+BZ

+== 84

+

+BJ

+== 204

+

+BM

+== 60

+

+BT

+== 64

+

+BO

+== 68

+

+#BQ

+#== 535

+

+BA

+== 70

+

+BW

+== 72

+

+#BV

+#== 74

+

+BR

+== 76

+

+IO

+== 86

+

+BN

+== 96

+

+BG

+== 100

+

+BF

+== 854

+

+BI

+== 108

+

+KH

+== 116

+

+CM

+== 120

+

+CA

+== 124

+

+CV

+== 132

+

+KY

+== 136

+

+CF

+== 140

+

+TD

+== 148

+

+CL

+== 152

+

+CN

+== 156

+

+CX

+== 162

+

+CC

+== 166

+

+CO

+== 170

+

+KM

+== 174

+

+CG

+== 178

+

+CD

+== 180

+

+CK

+== 184

+

+CR

+== 188

+

+CI

+== 384

+

+HR

+== 191

+

+CU

+== 192

+

+CW

+== 531

+

+CY

+== 196

+

+CZ

+== 203

+

+DK

+== 208

+

+DJ

+== 262

+

+DM

+== 212

+

+DO

+== 214

+

+EC

+== 218

+

+EG

+== 818

+

+SV

+== 222

+

+GQ

+== 226

+

+ER

+== 232

+

+EE

+== 233

+

+ET

+== 231

+

+FK

+== 238

+

+FO

+== 234

+

+FJ

+== 242

+

+FI

+== 246

+

+FR

+== 250

+

+GF

+== 254

+

+PF

+== 258

+

+TF

+== 260

+

+GA

+== 266

+

+GM

+== 270

+

+GE

+== 268

+

+DE

+== 276

+

+GH

+== 288

+

+GI

+== 292

+

+GR

+== 300

+

+GL

+== 304

+

+GD

+== 308

+

+GP

+== 312

+

+GU

+== 316

+

+GT

+== 320

+

+GG

+== 831

+

+GN

+== 324

+

+GW

+== 624

+

+GY

+== 328

+

+HT

+== 332

+

+#HM

+#== 334

+

+VA

+== 336

+

+HN

+== 340

+

+HK

+== 344

+

+HU

+== 348

+

+IS

+== 352

+

+IN

+== 356

+

+ID

+== 360

+

+IR

+== 364

+

+IQ

+== 368

+

+IE

+== 372

+

+IM

+== 833

+

+IL

+== 376

+

+IT

+== 380

+

+JM

+== 388

+

+JP

+== 392

+

+JE

+== 832

+

+JO

+== 400

+

+KZ

+== 398

+

+KE

+== 404

+

+KI

+== 296

+

+KP

+== 408

+

+KR

+== 410

+

+KW

+== 414

+

+KG

+== 417

+

+LA

+== 418

+

+LV

+== 428

+

+LB

+== 422

+

+LS

+== 426

+

+LR

+== 430

+

+LY

+== 434

+

+LI

+== 438

+

+LT

+== 440

+

+LU

+== 442

+

+MO

+== 446

+

+MK

+== 807

+

+MG

+== 450

+

+MW

+== 454

+

+MY

+== 458

+

+MV

+== 462

+

+ML

+== 466

+

+MT

+== 470

+

+MH

+== 584

+

+MQ

+== 474

+

+MR

+== 478

+

+MU

+== 480

+

+#YT

+#== 175

+

+MX

+== 484

+

+FM

+== 583

+

+MD

+== 498

+

+MC

+== 492

+

+MN

+== 496

+

+ME

+== 499

+

+MS

+== 500

+

+MA

+== 504

+

+MZ

+== 508

+

+MM

+== 104

+

+NA

+== 516

+

+NR

+== 520

+

+NP

+== 524

+

+NL

+== 528

+

+NC

+== 540

+

+NZ

+== 554

+

+NI

+== 558

+

+NE

+== 562

+

+NG

+== 566

+

+NU

+== 570

+

+NF

+== 574

+

+MP

+== 580

+

+NO

+== 578

+

+OM

+== 512

+

+PK

+== 586

+

+PW

+== 585

+

+#PS

+#== 275

+

+PA

+== 591

+

+PG

+== 598

+

+PY

+== 600

+

+PE

+== 604

+

+PH

+== 608

+

+PN

+== 612

+

+PL

+== 616

+

+PT

+== 620

+

+PR

+== 630

+

+QA

+== 634

+

+RE

+== 638

+

+RO

+== 642

+

+RU

+== 643

+

+RW

+== 646

+

+BL

+== 652

+

+SH

+== 654

+

+KN

+== 659

+

+LC

+== 662

+

+MF

+== 663

+

+PM

+== 666

+

+VC

+== 670

+

+WS

+== 882

+

+SM

+== 674

+

+ST

+== 678

+

+SA

+== 682

+

+SN

+== 686

+

+RS

+== 688

+

+SC

+== 690

+

+SL

+== 694

+

+SG

+== 702

+

+SX

+== 534

+

+SK

+== 703

+

+SI

+== 705

+

+SB

+== 90

+

+SO

+== 706

+

+ZA

+== 710

+

+GS

+== 239

+

+ES

+== 724

+

+LK

+== 144

+

+SD

+== 736

+

+SR

+== 740

+

+#SJ

+#== 744

+

+SZ

+== 748

+

+SE

+== 752

+

+CH

+== 756

+

+SY

+== 760

+

+TW

+== 158

+

+TJ

+== 762

+

+TZ

+== 834

+

+TH

+== 764

+

+TL

+== 626

+

+TG

+== 768

+

+TK

+== 772

+

+TO

+== 776

+

+TT

+== 780

+

+TN

+== 788

+

+TR

+== 792

+

+TM

+== 795

+

+TC

+== 796

+

+TV

+== 798

+

+UG

+== 800

+

+UA

+== 804

+

+AE

+== 784

+

+GB

+== 826

+

+US

+== 840

+

+#UM

+#== 581

+

+UY

+== 858

+

+UZ

+== 860

+

+VU

+== 548

+

+VE

+== 862

+

+VN

+== 704

+

+VG

+== 92

+

+VI

+== 850

+

+WF

+== 876

+

+EH

+== 732

+

+YE

+== 887

+

+ZM

+== 894

+

+ZW

+== 716

diff --git a/data/editor/desert_main.rules b/data/editor/desert_main.rules
new file mode 100644
index 00000000..0102a197
--- /dev/null
+++ b/data/editor/desert_main.rules
@@ -0,0 +1,227 @@
+[Desert]
+Index 1
+BaseTile
+
+#random
+Index 2
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 150
+
+Index 3
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 150
+
+#top
+Index 16
+Pos 0 -1 EMPTY
+
+#right 
+Index 17
+Pos 1 0 EMPTY
+
+#bottom
+Index 18
+Pos 0 1 EMPTY
+
+#left
+Index 19
+Pos -1 0 EMPTY
+
+#corner top-right
+Index 33
+Pos 0 -1 EMPTY
+Pos 1 0 EMPTY
+
+#corner top-left
+Index 32
+Pos 0 -1 EMPTY
+Pos -1 0 EMPTY
+
+#corner bottom-left
+Index 35
+Pos 0 1 EMPTY
+Pos -1 0 EMPTY
+
+#corner bottom-right
+Index 34
+Pos 0 1 EMPTY
+Pos 1 0 EMPTY
+
+#inside corner top-right
+Index 51
+Pos -1 1 EMPTY
+Pos -1 0 FULL
+Pos 0 1 FULL
+
+#inside corner top-left
+Index 50
+Pos 1 1 EMPTY
+Pos 1 0 FULL
+Pos 0 1 FULL
+
+#inside corner bottom-left
+Index 49
+Pos 1 -1 EMPTY
+Pos 1 0 FULL
+Pos 0 -1 FULL
+
+#inside corner bottom-right
+Index 48
+Pos -1 -1 EMPTY
+Pos -1 0 FULL
+Pos 0 -1 FULL
+
+[Mine]
+Index 81
+BaseTile
+
+#random
+Index 82
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 500
+
+Index 83
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 500
+
+Index 84
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 500
+
+Index 85
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 500
+
+Index 86
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 500
+
+Index 100
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 500
+
+Index 101
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 500
+
+Index 102
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 500
+
+Index 117
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 500
+
+Index 118
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 500
+
+Index 133
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 500
+
+Index 134
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 500
+
+#top
+Index 96
+Pos 0 -1 EMPTY
+
+#right 
+Index 97
+Pos 1 0 EMPTY
+
+#bottom
+Index 98
+Pos 0 1 EMPTY
+
+#left
+Index 99
+Pos -1 0 EMPTY
+
+#corner top-right
+Index 113
+Pos 0 -1 EMPTY
+Pos 1 0 EMPTY
+
+#corner top-left
+Index 112
+Pos 0 -1 EMPTY
+Pos -1 0 EMPTY
+
+#corner bottom-left
+Index 115
+Pos 0 1 EMPTY
+Pos -1 0 EMPTY
+
+#corner bottom-right
+Index 114
+Pos 0 1 EMPTY
+Pos 1 0 EMPTY
+
+#inside corner top-right
+Index 131
+Pos -1 1 EMPTY
+Pos -1 0 FULL
+Pos 0 1 FULL
+
+#inside corner top-left
+Index 130
+Pos 1 1 EMPTY
+Pos 1 0 FULL
+Pos 0 1 FULL
+
+#inside corner bottom-left
+Index 129
+Pos 1 -1 EMPTY
+Pos 1 0 FULL
+Pos 0 -1 FULL
+
+#inside corner bottom-right
+Index 128
+Pos -1 -1 EMPTY
+Pos -1 0 FULL
+Pos 0 -1 FULL
diff --git a/data/editor/grass_main.rules b/data/editor/grass_main.rules
new file mode 100644
index 00000000..b909eb0e
--- /dev/null
+++ b/data/editor/grass_main.rules
@@ -0,0 +1,225 @@
+[Grass]
+Index 1
+BaseTile
+
+#random bones
+Index 2
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 150
+
+Index 3
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 150
+
+Index 66
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 150
+
+Index 67
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 150
+
+Index 68
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 150
+#---------
+
+#top
+Index 16
+Pos 0 -1 EMPTY
+
+#right
+Index 21
+Pos 1 0 EMPTY
+
+#bottom
+Index 52
+Pos 0 1 EMPTY
+
+#left
+Index 20
+Pos -1 0 EMPTY
+
+#corner top-right
+Index 5
+Pos 0 -1 EMPTY
+Pos 1 0 EMPTY
+
+#corner top-left
+Index 4
+Pos 0 -1 EMPTY
+Pos -1 0 EMPTY
+
+#corner bottom-left
+Index 36
+Pos 0 1 EMPTY
+Pos -1 0 EMPTY
+
+#corner bottom-right
+Index 37
+Pos 0 1 EMPTY
+Pos 1 0 EMPTY
+
+#inside corner top-right
+Index 54
+Pos -1 1 EMPTY
+Pos -1 0 FULL
+Pos 0 1 FULL
+
+#inside corner top-left
+Index 53
+Pos 1 1 EMPTY
+Pos 1 0 FULL
+Pos 0 1 FULL
+
+#inside corner bottom-left
+Index 49
+Pos 1 -1 EMPTY
+Pos 1 0 FULL
+Pos 0 -1 FULL
+
+#inside corner bottom-right
+Index 48
+Pos -1 -1 EMPTY
+Pos -1 0 FULL
+Pos 0 -1 FULL
+
+#right bottom
+Index 22
+Pos -1 0 EMPTY
+Pos -1 1 FULL
+Pos 0 1 FULL
+
+#left bottom
+Index 38
+Pos 1 0 EMPTY
+Pos 1 1 FULL
+Pos 0 1 FULL
+
+#top corner right 2
+Index 33
+Pos 0 -1 EMPTY
+Pos 1 0 EMPTY
+Pos 1 1 FULL
+
+#top corner left 2
+Index 32
+Pos 0 -1 EMPTY
+Pos -1 0 EMPTY
+Pos -1 1 FULL
+
+[Cave]
+Index 13
+BaseTile
+
+#random bones
+Index 29
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 150
+
+Index 42
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 150
+
+Index 43
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 150
+
+Index 44
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 150
+
+Index 45
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 150
+#---------
+
+#top
+Index 26
+Pos 0 -1 EMPTY
+
+#right
+Index 25
+Pos 1 0 EMPTY
+
+#bottom
+Index 10
+Pos 0 1 EMPTY
+
+#left
+Index 24
+Pos -1 0 EMPTY
+
+#corner top-right
+Index 9
+Pos 0 -1 EMPTY
+Pos 1 0 EMPTY
+
+#corner top-left
+Index 8
+Pos 0 -1 EMPTY
+Pos -1 0 EMPTY
+
+#corner bottom-left
+Index 40
+Pos 0 1 EMPTY
+Pos -1 0 EMPTY
+
+#corner bottom-right
+Index 41
+Pos 0 1 EMPTY
+Pos 1 0 EMPTY
+
+#inside corner top-right
+Index 12
+Pos -1 1 EMPTY
+Pos -1 0 FULL
+Pos 0 1 FULL
+
+#inside corner top-left
+Index 11
+Pos 1 1 EMPTY
+Pos 1 0 FULL
+Pos 0 1 FULL
+
+#inside corner bottom-left
+Index 27
+Pos 1 -1 EMPTY
+Pos 1 0 FULL
+Pos 0 -1 FULL
+
+#inside corner bottom-right
+Index 28
+Pos -1 -1 EMPTY
+Pos -1 0 FULL
+Pos 0 -1 FULL
diff --git a/data/editor/jungle_main.rules b/data/editor/jungle_main.rules
new file mode 100644
index 00000000..ada1f3ac
--- /dev/null
+++ b/data/editor/jungle_main.rules
@@ -0,0 +1,266 @@
+[Jungle]
+Index 1
+BaseTile
+
+#random bricks
+Index 66
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 200
+
+Index 67
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 200
+
+#top
+Index 16
+Pos 0 -1 EMPTY
+
+Index 96
+Pos 0 -1 EMPTY
+Random 15
+
+Index 97
+Pos 0 -1 EMPTY
+Random 15
+
+Index 98
+Pos 0 -1 EMPTY
+Random 15
+
+#right 
+Index 21
+Pos 1 0 EMPTY
+
+#bottom
+Index 52
+Pos 0 1 EMPTY
+
+Index 99
+Pos 0 1 EMPTY
+Random 10
+
+Index 100
+Pos 0 1 EMPTY
+Random 10
+
+Index 101
+Pos 0 1 EMPTY
+Random 10
+
+#left
+Index 21 XFLIP
+Pos -1 0 EMPTY
+
+#corner top-right
+Index 5
+Pos 0 -1 EMPTY
+Pos 1 0 EMPTY
+
+#corner top-left
+Index 5 XFLIP
+Pos 0 -1 EMPTY
+Pos -1 0 EMPTY
+
+#corner bottom-left
+Index 37 XFLIP
+Pos 0 1 EMPTY
+Pos -1 0 EMPTY
+
+Index 39 XFLIP
+Pos 0 1 EMPTY
+Pos -1 0 EMPTY
+Random 2
+
+#corner bottom-right
+Index 37
+Pos 0 1 EMPTY
+Pos 1 0 EMPTY
+
+Index 39
+Pos 0 1 EMPTY
+Pos 1 0 EMPTY
+Random 2
+
+#inside corner top-right
+Index 54
+Pos -1 1 EMPTY
+Pos -1 0 FULL
+Pos 0 1 FULL
+
+Index 53 XFLIP
+Pos -1 1 EMPTY
+Pos -1 0 FULL
+Pos 0 1 FULL
+Random 2
+
+#inside corner top-left
+Index 54 XFLIP
+Pos 1 1 EMPTY
+Pos 1 0 FULL
+Pos 0 1 FULL
+
+Index 53
+Pos 1 1 EMPTY
+Pos 1 0 FULL
+Pos 0 1 FULL
+Random 2
+
+#inside corner bottom-left
+Index 48 XFLIP
+Pos 1 -1 EMPTY
+Pos 1 0 FULL
+Pos 0 -1 FULL
+
+Index 49
+Pos 1 -1 EMPTY
+Pos 1 0 FULL
+Pos 0 -1 FULL
+Random 3
+
+Index 50 YFLIP
+Pos 1 -1 EMPTY
+Pos 1 0 FULL
+Pos 0 -1 FULL
+Random 3
+
+#inside corner bottom-right
+Index 48
+Pos -1 -1 EMPTY
+Pos -1 0 FULL
+Pos 0 -1 FULL
+
+Index 49 XFLIP
+Pos -1 -1 EMPTY
+Pos -1 0 FULL
+Pos 0 -1 FULL
+Random 3
+
+Index 51 YFLIP
+Pos -1 -1 EMPTY
+Pos -1 0 FULL
+Pos 0 -1 FULL
+Random 3
+
+#right bottom
+Index 22
+Pos -1 0 EMPTY
+Pos -1 1 FULL
+Pos 0 1 FULL
+
+#left bottom
+Index 22 XFLIP
+Pos 1 0 EMPTY
+Pos 1 1 FULL
+Pos 0 1 FULL
+
+#top corner right 2
+Index 33
+Pos 0 -1 EMPTY
+Pos 1 0 EMPTY
+Pos 1 1 FULL
+
+#top corner left 2
+Index 32
+Pos 0 -1 EMPTY
+Pos -1 0 EMPTY
+Pos -1 1 FULL
+
+[Jungle dark]
+Index 13
+BaseTile
+
+#random bricks
+Index 42
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 150
+
+Index 43
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 150
+
+Index 44
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 150
+
+Index 45
+Pos 0 1 FULL
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos -1 0 FULL
+Random 150
+#---------
+
+#top
+Index 26
+Pos 0 -1 EMPTY
+
+#right 
+Index 25
+Pos 1 0 EMPTY
+
+#bottom
+Index 10
+Pos 0 1 EMPTY
+
+#left
+Index 24
+Pos -1 0 EMPTY
+
+#corner top-right
+Index 9
+Pos 0 -1 EMPTY
+Pos 1 0 EMPTY
+
+#corner top-left
+Index 8
+Pos 0 -1 EMPTY
+Pos -1 0 EMPTY
+
+#corner bottom-left
+Index 40
+Pos 0 1 EMPTY
+Pos -1 0 EMPTY
+
+#corner bottom-right
+Index 41
+Pos 0 1 EMPTY
+Pos 1 0 EMPTY
+
+#inside corner top-right
+Index 12
+Pos -1 1 EMPTY
+Pos -1 0 FULL
+Pos 0 1 FULL
+
+#inside corner top-left
+Index 11
+Pos 1 1 EMPTY
+Pos 1 0 FULL
+Pos 0 1 FULL
+
+#inside corner bottom-left
+Index 27
+Pos 1 -1 EMPTY
+Pos 1 0 FULL
+Pos 0 -1 FULL
+
+#inside corner bottom-right
+Index 28
+Pos -1 -1 EMPTY
+Pos -1 0 FULL
+Pos 0 -1 FULL
diff --git a/data/editor/winter_main.rules b/data/editor/winter_main.rules
new file mode 100644
index 00000000..eaeaec8e
--- /dev/null
+++ b/data/editor/winter_main.rules
@@ -0,0 +1,177 @@
+[Winter]
+Index 1
+BaseTile
+
+#top
+Index 17
+Pos 0 -1 EMPTY
+
+Index 18
+Pos 0 -1 EMPTY
+Pos -1 0 INDEX 17
+
+Index 19
+Pos 0 -1 EMPTY
+Pos -1 0 INDEX 18
+
+#bottom
+Index 97
+Pos 0 1 EMPTY
+
+Index 98
+Pos 0 1 EMPTY
+Pos -1 0 INDEX 97
+
+Index 99
+Pos 0 1 EMPTY
+Pos -1 0 INDEX 98
+
+#right
+Index 2 XFLIP
+Pos 1 0 EMPTY
+
+#left
+Index 2
+Pos -1 0 EMPTY
+
+#corner top right
+Index 20
+Pos 0 -1 EMPTY
+Pos 1 0 EMPTY
+
+Index 24
+Pos 0 -1 EMPTY
+Pos 1 0 EMPTY
+Pos -1 0 INDEX 17
+
+#corner top left
+Index 16
+Pos 0 -1 EMPTY
+Pos -1 0 EMPTY
+
+#corner bottom right
+Index 100
+Pos 0 1 EMPTY
+Pos 1 0 EMPTY
+
+#corner bottom left
+Index 96
+Pos 0 1 EMPTY
+Pos -1 0 EMPTY
+
+#inside corner top right
+Index 8
+Pos 0 1 FULL
+Pos -1 0 FULL
+Pos -1 1 EMPTY
+
+#inside corner top left
+Index 7
+Pos 0 1 FULL
+Pos 1 0 FULL
+Pos 1 1 EMPTY
+
+#inside corner bottom right
+Index 4
+Pos 0 -1 FULL
+Pos -1 0 FULL
+Pos -1 -1 EMPTY
+
+#inside corner bottom left
+Index 3
+Pos 0 -1 FULL
+Pos 1 0 FULL
+Pos 1 -1 EMPTY
+
+#one tile height
+Index 113
+Pos 0 1 EMPTY
+Pos 0 -1 EMPTY
+
+Index 114
+Pos 0 1 EMPTY
+Pos 0 -1 EMPTY
+Pos -1 0 INDEX 113
+
+Index 115
+Pos 0 1 EMPTY
+Pos 0 -1 EMPTY
+Pos -1 0 INDEX 114
+
+#one tile height right
+Index 116
+Pos 0 1 EMPTY
+Pos 0 -1 EMPTY
+Pos 1 0 EMPTY
+
+Index 120
+Pos 0 1 EMPTY
+Pos 0 -1 EMPTY
+Pos 1 0 EMPTY
+Pos -1 0 INDEX 113
+
+#one tile height left
+Index 112
+Pos 0 1 EMPTY
+Pos 0 -1 EMPTY
+Pos -1 0 EMPTY
+
+#one tile height link right
+Index 6
+Pos -1 -1 EMPTY
+Pos -1 0 FULL
+Pos 0 -1 FULL
+Pos -1 1 EMPTY
+
+#one tile height link left
+Index 5
+Pos 1 -1 EMPTY
+Pos 1 0 FULL
+Pos 0 -1 FULL
+Pos 1 1 EMPTY
+
+[Winter dark]
+Index 177
+BaseTile
+
+#bottom
+Index 194
+Pos 0 1 EMPTY
+
+Index 195
+Pos 0 1 EMPTY
+Pos -1 0 INDEX 194
+
+Index 196
+Pos 0 1 EMPTY
+Pos -1 0 INDEX 195
+
+#right
+Index 178 XFLIP
+Pos 1 0 EMPTY
+
+#left
+Index 178
+Pos -1 0 EMPTY
+
+#corner bottom right
+Index 197
+Pos 0 1 EMPTY
+Pos 1 0 EMPTY
+
+#corner bottom left
+Index 193
+Pos 0 1 EMPTY
+Pos -1 0 EMPTY
+
+#inside corner top right
+Index 180
+Pos 0 1 FULL
+Pos -1 0 FULL
+Pos -1 1 EMPTY
+
+#inside corner top left
+Index 179
+Pos 0 1 FULL
+Pos 1 0 FULL
+Pos 1 1 EMPTY
diff --git a/data/languages/belarusian.txt b/data/languages/belarusian.txt
new file mode 100644
index 00000000..672995c0
--- /dev/null
+++ b/data/languages/belarusian.txt
@@ -0,0 +1,679 @@
+
+##### translated strings #####
+
+%d Bytes
+== %d байт
+
+%d of %d servers, %d players
+== %d з %d сервераў, %d гульцоў
+
+%d%% loaded
+== %d%% загружана
+
+%ds left
+== засталося %d сек.
+
+%i minute left
+== Засталася %i хвіліна!
+
+%i minutes left
+== Засталося %i хвілін!
+
+%i second left
+== Засталася %i секунда!
+
+%i seconds left
+== Засталося %i секунд!
+
+%s wins!
+== %s перамог!
+
+-Page %d-
+== -Старонка %d-
+
+Abort
+== Адмена
+
+Add
+== Дадаць
+
+Add Friend
+== Дадаць сябра
+
+Address
+== Адрас
+
+All
+== Усё
+
+Alpha
+== Празрыст.
+
+Always show name plates
+== Заўсёды паказваць нікі гульцоў
+
+Are you sure that you want to delete the demo?
+== Вы ўпэўнены, што жадаеце выдаліць дэма?
+
+Are you sure that you want to quit?
+== Вы сапраўды жадаеце выйсці?
+
+Are you sure that you want to remove the player from your friends list?
+== Вы ўпэўнены, што жадаеце выдаліць гульца з сяброў?
+
+As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server.
+== Бо гэта ваш першы запуск гульні, калі ласка, увядзіце свой нік у поле ніжэй. Таксама рекоммендуется праверыць налады гульні і памяняць некаторыя з іх перад тым, як пачаць гуляць.
+
+Automatically record demos
+== Аўтаматычна запісваць дэма
+
+Automatically take game over screenshot
+== Рабіць здымак вынікаў гульні
+
+Blue team
+== Сінія
+
+Blue team wins!
+== Сінія перамаглі!
+
+Body
+== Цела
+
+Call vote
+== Галасаваць
+
+Change settings
+== Змяніць налады
+
+Chat
+== Чат
+
+Clan
+== Клан
+
+Client
+== Кліент
+
+Close
+== Выйсці
+
+Compatible version
+== Сумяшчальная версія
+
+Connect
+== Падлучыцца
+
+Connecting to
+== Падлучэнне да
+
+Connection Problems...
+== Праблемы з сувяззю...
+
+Console
+== Кансоль
+
+Controls
+== Кіраванне
+
+Count players only
+== Лічыць толькі гульцоў
+
+Country
+== Сцяг вашай краіны
+
+Crc:
+== Crc:
+
+Created:
+== Створаны:
+
+Current
+== Бягучы
+
+Current version: %s
+== Бягучая версія: %s
+
+Custom colors
+== Свае колеры
+
+Delete
+== Выдаліць
+
+Delete demo
+== Выдаліць дэма
+
+Demo details
+== Дэталі дэма
+
+Demofile: %s
+== Дэма: %s
+
+Demos
+== Дэма
+
+Disconnect
+== Адключыць
+
+Disconnected
+== Адключана
+
+Display Modes
+== Дазвол экрана
+
+Downloading map
+== Спампоўка карты
+
+Draw!
+== Нічыя!
+
+Dynamic Camera
+== Дынамічная камера
+
+Emoticon
+== Эмоцыі
+
+Enter
+== Уваход
+
+Error
+== Памылка
+
+Error loading demo
+== памылка пры загрузцы дэма
+
+FSAA samples
+== Сэмплаў FSAA
+
+Favorite
+== Абраны
+
+Favorites
+== Абраныя
+
+Feet
+== Ногі
+
+Filter
+== Фільтр
+
+Fire
+== Стрэл
+
+Folder
+== Тэчка
+
+Force vote
+== Фарсіраваць
+
+Free-View
+== Вольны агляд
+
+Friends
+== Сябры
+
+Fullscreen
+== Поўнаэкранны рэжым
+
+Game
+== Гульня
+
+Game info
+== Інфа пра гульню
+
+Game over
+== Гульня скончана
+
+Game type
+== Тып гульні
+
+Game types:
+== Тып гульні:
+
+General
+== Асноўныя
+
+Graphics
+== Графіка
+
+Grenade
+== Гранатамёт
+
+Hammer
+== Молат
+
+Has people playing
+== Не пусты сервер
+
+High Detail
+== Высокая дэталізацыя
+
+Hook
+== Крук
+
+Host address
+== Адрас сервера
+
+Hue
+== Адценне
+
+Info
+== Інфа
+
+Internet
+== Інтэрнэт
+
+Invalid Demo
+== Недапушчальнае дэма
+
+Join blue
+== За сініх
+
+Join game
+== Гуляць
+
+Join red
+== За чырвоных
+
+Jump
+== Скачок
+
+Kick player
+== Забаніць гульца
+
+LAN
+== LAN
+
+Language
+== Мова
+
+Length:
+== Даўжыня
+
+Lht.
+== Яркасць
+
+Loading
+== Загрузка
+
+MOTD
+== MOTD
+
+Map
+== Карта
+
+Map:
+== Карта:
+
+Max Screenshots
+== Максімальная колькасць здымкаў
+
+Max demos
+== Максімальная колькасць дэма
+
+Maximum ping:
+== Макс. пінг:
+
+Miscellaneous
+== Дадаткова
+
+Mouse sens.
+== Адчув. мышы
+
+Move left
+== Крок налева
+
+Move player to spectators
+== Зрабіць назіральнікам
+
+Move right
+== Крок направа
+
+Movement
+== Перасоўванне
+
+Mute when not active
+== Глушыць гукі, калі гульня неактыўная
+
+Name
+== Імя
+
+Name plates size
+== Памер
+
+Netversion:
+== Версія:
+
+New name:
+== Новае імя
+
+News
+== Навіны
+
+Next weapon
+== След. зброя
+
+Nickname
+== Нік
+
+No
+== Не
+
+No password
+== Без пароля
+
+No servers found
+== Сервера не знойдзены
+
+No servers match your filter criteria
+== Няма сервераў, падыходных пад ваш фільтр
+
+Ok
+== ОК
+
+Open
+== Адкрыць
+
+Parent Folder
+== Бацькоўскі каталог
+
+Password
+== Пароль
+
+Password incorrect
+== Пароль
+
+Ping
+== Пінг
+
+Pistol
+== Пісталет
+
+Play
+== Прагляд
+
+Play background music
+== Гуляць фонавую музыку
+
+Player
+== Гулец
+
+Player country:
+== Краіна:
+
+Player options
+== Опцыі гульца
+
+Players
+== Гульцы
+
+Please balance teams!
+== Збалансуйце каманды!
+
+Prev. weapon
+== Прад. зброя
+
+Quality Textures
+== Якасныя тэкстуры
+
+Quick search:
+== Хуткі пошук:
+
+Quit
+== Выйсце
+
+Quit anyway?
+== Выйсці?
+
+REC %3d:%02d
+== REC %3d:%02d
+
+Reason:
+== Чыннік:
+
+Record demo
+== Запісаць дэма
+
+Red team
+== Чырвоныя
+
+Red team wins!
+== Чырвоныя перамаглі!
+
+Refresh
+== Абнавіць
+
+Refreshing master servers
+== Абнаўленне спісу майстар-сервераў
+
+Remote console
+== Кансоль сервера
+
+Remove
+== Выдаліць
+
+Remove friend
+== Выдаліць сябра
+
+Rename
+== Пераназв.
+
+Rename demo
+== Пераназваць дэма
+
+Reset filter
+== Скінуць фільтры
+
+Reset to defaults
+== Скінуць налады
+
+Rifle
+== Бласцер
+
+Round
+== Раўнд
+
+Sample rate
+== Чашчыня
+
+Sat.
+== Кантраст
+
+Score
+== Ачкі
+
+Score board
+== Табло
+
+Score limit
+== Ліміт ачкоў
+
+Scoreboard
+== Табло
+
+Screenshot
+== Здымак
+
+Server address:
+== Адрас сервера
+
+Server details
+== Дэталі сервера
+
+Server filter
+== Фільтр сервераў
+
+Server info
+== Інфармацыя
+
+Server not full
+== Сервер не запоўнены
+
+Settings
+== Налады
+
+Shotgun
+== Драбавік
+
+Show chat
+== Паказаць чат
+
+Show friends only
+== Толькі з сябрамі
+
+Show ingame HUD
+== Паказваць нутрагульнявы HUD
+
+Show name plates
+== Паказваць нікі гульцоў
+
+Show only supported
+== Паказваць толькі падтрымоўваныя дазволы экрана
+
+Size:
+== Памер:
+
+Skins
+== Скіны
+
+Sound
+== Гук
+
+Sound error
+== Гукавая памылка
+
+Sound volume
+== Гучнасць
+
+Spectate
+== Назіраць
+
+Spectate next
+== Назіраць наст.
+
+Spectate previous
+== Назіраць папяр.
+
+Spectator mode
+== Назіральнік
+
+Spectators
+== Назіральнікі
+
+Standard gametype
+== Стандартны тып гульні
+
+Standard map
+== Стандартная карта
+
+Stop record
+== Стоп
+
+Strict gametype filter
+== Строгі фільтр рэжым.
+
+Sudden Death
+== Хуткая смерць
+
+Switch weapon on pickup
+== Перамыкаць зброю пры падборы
+
+Team
+== Каманда
+
+Team chat
+== Камандны чат
+
+Teeworlds %s is out! Download it at www.teeworlds.com!
+== Выйшла Teeworlds %s! Спампоўвайце на www.teeworlds.com!
+
+Texture Compression
+== Сціск тэкстур
+
+The audio device couldn't be initialised.
+== Аўдыё прылада не можа быць ініцыялізавана
+
+The server is running a non-standard tuning on a pure game type.
+== Сервер запушчаны з нестандартнымі наладамі на стандартным тыпе гульні.
+
+There's an unsaved map in the editor, you might want to save it before you quit the game.
+== Ёсць незахаваная карта ў рэдактары, Вы можаце захаваць яе перад тым, як выйсці.
+
+Time limit
+== Ліміт часу
+
+Time limit: %d min
+== Ліміт часу: %d
+
+Try again
+== ОК
+
+Type
+== Тып
+
+Type:
+== Тып:
+
+UI Color
+== Колер інтэрфейсу
+
+Unable to delete the demo
+== Немагчыма выдаліць дэма
+
+Unable to rename the demo
+== Немагчыма пераназваць дэма
+
+Use sounds
+== Выкарыстоўваць гукі
+
+Use team colors for name plates
+== Камандныя колеры для нікаў гульцоў
+
+V-Sync
+== Вертыкальная сінхранізацыя
+
+Version
+== Версія
+
+Version:
+== Версія:
+
+Vote command:
+== Камманда галасавання:
+
+Vote description:
+== Апісанне галасавання:
+
+Vote no
+== Супраць
+
+Vote yes
+== За
+
+Voting
+== Галасаванне
+
+Warmup
+== Размінка
+
+Weapon
+== Зброя
+
+Welcome to Teeworlds
+== Сардэчна запрашаем у Teeworlds!
+
+Yes
+== Так
+
+You must restart the game for all settings to take effect.
+== Перазапусціце гульню для ўжывання змен.
+
+Your skin
+== Ваш скін
+
+no limit
+== Без ліміту
+
+##### needs translation #####
+
+##### old translations #####
+
diff --git a/data/languages/bosnian.txt b/data/languages/bosnian.txt
index 2915511a..4888db6a 100644
--- a/data/languages/bosnian.txt
+++ b/data/languages/bosnian.txt
@@ -145,6 +145,9 @@ Delete demo
 Demo details
 == Podaci o demo-snimku
 
+Demofile: %s
+== Demo-snimak: %s
+
 Demos
 == Demo
 
@@ -388,9 +391,15 @@ Pistol
 Play
 == Pogledaj
 
+Play background music
+== Pozadinska muzika
+
 Player
 == Igrač
 
+Player country:
+== Država
+
 Player options
 == Postavke igrača
 
@@ -508,7 +517,7 @@ Shotgun
 Show chat
 == Prikaži chat
 
-Show friends
+Show friends only
 == Prikaži prijatelje
 
 Show ingame HUD
@@ -538,6 +547,12 @@ Sound volume
 Spectate
 == Posmatraj
 
+Spectate next
+== Posmatraj narednog
+
+Spectate previous
+== Posmatraj prethodnog
+
 Spectator mode
 == Posmatrački mod
 
@@ -553,6 +568,9 @@ Standard map
 Stop record
 == Prekini snimanje
 
+Strict gametype filter
+== Striktan filter tipa igre
+
 Sudden Death
 == Iznenadna smrt
 
@@ -657,14 +675,5 @@ no limit
 
 ##### needs translation #####
 
-Play background music
-== 
-
-Spectate next
-== 
-
-Spectate previous
-== 
-
 ##### old translations #####
 
diff --git a/data/languages/brazilian_portuguese.txt b/data/languages/brazilian_portuguese.txt
new file mode 100644
index 00000000..a83a0d05
--- /dev/null
+++ b/data/languages/brazilian_portuguese.txt
@@ -0,0 +1,679 @@
+
+##### translated strings #####
+
+%d Bytes
+== %d Bytes
+
+%d of %d servers, %d players
+== %d de %d servidores, %d jogadores
+
+%d%% loaded
+== %d%% carregado
+
+%ds left
+== Faltam %ds
+
+%i minute left
+== Falta %i minuto
+
+%i minutes left
+== Faltam %i minutos
+
+%i second left
+== Falta %i segundo
+
+%i seconds left
+== Faltam %i segundos
+
+%s wins!
+== %s vence!
+
+-Page %d-
+== -Página %d-
+
+Abort
+== Cancelar
+
+Add
+== Adicionar
+
+Add Friend
+== Adicionar amigo
+
+Address
+== Endereço
+
+All
+== Todos
+
+Alpha
+== Alpha
+
+Always show name plates
+== Sempre mostrar apelidos
+
+Are you sure that you want to delete the demo?
+== Tem certeza que deseja deletar o demo?
+
+Are you sure that you want to quit?
+== Você tem certeza que deseja sair?
+
+Are you sure that you want to remove the player from your friends list?
+== Tem certeza que deseja remover o jogador de sua lista de amigos?
+
+As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server.
+== Como esta é a primeira vez que você abre o jogo, por favor, coloque seu apelido abaixo. É recomendado que você verifique as configurações e então ajuste-as para suas preferências antes de entrar em um servidor.
+
+Automatically record demos
+== Gravar demos automaticamente
+
+Automatically take game over screenshot
+== Capturar tela final de jogo automaticamente
+
+Blue team
+== Time azul
+
+Blue team wins!
+== Vitória do time azul!
+
+Body
+== Corpo
+
+Call vote
+== Votar
+
+Change settings
+== Mudar configurações
+
+Chat
+== Chat
+
+Clan
+== Clã
+
+Client
+== Cliente
+
+Close
+== Fechar
+
+Compatible version
+== Versão compatível
+
+Connect
+== Conectar
+
+Connecting to
+== Conectando a
+
+Connection Problems...
+== Problemas de conexão...
+
+Console
+== Console
+
+Controls
+== Controles
+
+Count players only
+== Contar apenas jogadores
+
+Country
+== País
+
+Crc:
+== Crc:
+
+Created:
+== Criado:
+
+Current
+== Atual
+
+Current version: %s
+== Versão atual: %s
+
+Custom colors
+== Cores personalizadas
+
+Delete
+== Deletar
+
+Delete demo
+== Deletar demo
+
+Demo details
+== Detalhes do demo
+
+Demofile: %s
+== Demo: %s
+
+Demos
+== Demos
+
+Disconnect
+== Desconectar
+
+Disconnected
+== Desconectado
+
+Display Modes
+== Modos de exibição
+
+Downloading map
+== Baixando mapa
+
+Draw!
+== Empate!
+
+Dynamic Camera
+== Câmera dinâmica
+
+Emoticon
+== Emoticon
+
+Enter
+== Entrar
+
+Error
+== Erro
+
+Error loading demo
+== Erro ao carregar demo
+
+FSAA samples
+== Amostras FSAA
+
+Favorite
+== Favorito
+
+Favorites
+== Favoritos
+
+Feet
+== Pés
+
+Filter
+== Filtro
+
+Fire
+== Atirar
+
+Folder
+== Pasta
+
+Force vote
+== Forçar votação
+
+Free-View
+== Visualização livre
+
+Friends
+== Amigos
+
+Fullscreen
+== Tela cheia
+
+Game
+== Jogo
+
+Game info
+== Jogo
+
+Game over
+== Fim do jogo
+
+Game type
+== Tipo de jogo
+
+Game types:
+== Tipos de jogo:
+
+General
+== Geral
+
+Graphics
+== Gráficos
+
+Grenade
+== Granada
+
+Hammer
+== Martelo
+
+Has people playing
+== Há pessoas jogando
+
+High Detail
+== Mostrar detalhes
+
+Hook
+== Gancho
+
+Host address
+== Endereço do servidor
+
+Hue
+== Matiz
+
+Info
+== Informações
+
+Internet
+== Internet
+
+Invalid Demo
+== Demo inválido
+
+Join blue
+== Azul
+
+Join game
+== Entrar no jogo
+
+Join red
+== Vermelho
+
+Jump
+== Pular
+
+Kick player
+== Expulsar jogador
+
+LAN
+== LAN
+
+Language
+== Idioma
+
+Length:
+== Duração:
+
+Lht.
+== Luminos.
+
+Loading
+== Carregando
+
+MOTD
+== MOTD
+
+Map
+== Mapa
+
+Map:
+== Mapa:
+
+Max Screenshots
+== Máximo de capturas de tela
+
+Max demos
+== Máximo de demos
+
+Maximum ping:
+== Ping máximo:
+
+Miscellaneous
+== Diversos
+
+Mouse sens.
+== Sens. do mouse
+
+Move left
+== Esquerda
+
+Move player to spectators
+== Mover jogador para espectadores
+
+Move right
+== Direita
+
+Movement
+== Movimento
+
+Mute when not active
+== Silenciar quando inativo
+
+Name
+== Nome
+
+Name plates size
+== Tamanho dos apelidos
+
+Netversion:
+== Netversion
+
+New name:
+== Novo nome:
+
+News
+== Novidades
+
+Next weapon
+== Próxima arma
+
+Nickname
+== Apelido
+
+No
+== Não
+
+No password
+== Sem senha
+
+No servers found
+== Nenhum servidor encontrado
+
+No servers match your filter criteria
+== Nenhum servidor corresponde aos critérios do filtro
+
+Ok
+== Ok
+
+Open
+== Abrir
+
+Parent Folder
+== Diretório pai
+
+Password
+== Senha
+
+Password incorrect
+== Senha incorreta
+
+Ping
+== Ping
+
+Pistol
+== Pistola
+
+Play
+== Assistir
+
+Play background music
+== Tocar música de fundo
+
+Player
+== Jogador
+
+Player country:
+== País do jogador:
+
+Player options
+== Opções do jogador
+
+Players
+== Jogadores
+
+Please balance teams!
+== Favor balancear os times!
+
+Prev. weapon
+== Arma anterior
+
+Quality Textures
+== Texturas de qualidade
+
+Quick search:
+== Pesquisa rápida:
+
+Quit
+== Sair
+
+Quit anyway?
+== Sair mesmo assim?
+
+REC %3d:%02d
+== REC %3d:%02d
+
+Reason:
+== Motivo:
+
+Record demo
+== Gravar demo
+
+Red team
+== Time vermelho
+
+Red team wins!
+== Vitória do time vermelho!
+
+Refresh
+== Atualizar
+
+Refreshing master servers
+== Atualizando servidores mestres
+
+Remote console
+== Console remoto
+
+Remove
+== Deletar
+
+Remove friend
+== Deletar amigo
+
+Rename
+== Renomear
+
+Rename demo
+== Renomear demo
+
+Reset filter
+== Resetar filtro
+
+Reset to defaults
+== Resetar configurações
+
+Rifle
+== Laser
+
+Round
+== Rodada
+
+Sample rate
+== Frequência do som
+
+Sat.
+== Saturação
+
+Score
+== Pontos
+
+Score board
+== Placar
+
+Score limit
+== Pontuação máx.
+
+Scoreboard
+== Placar
+
+Screenshot
+== Capturar tela
+
+Server address:
+== Endereço:
+
+Server details
+== Detalhes do servidor
+
+Server filter
+== Filtro de servidores
+
+Server info
+== Servidor
+
+Server not full
+== Servidor não lotado
+
+Settings
+== Configurações
+
+Shotgun
+== Espingarda
+
+Show chat
+== Mostrar chat
+
+Show friends only
+== Mostrar apenas amigos
+
+Show ingame HUD
+== Mostrar HUD do jogo
+
+Show name plates
+== Mostrar apelidos
+
+Show only supported
+== Mostrar apenas modos suportados
+
+Size:
+== Tamanho:
+
+Skins
+== Skins
+
+Sound
+== Som
+
+Sound error
+== Erro de som
+
+Sound volume
+== Volume do som
+
+Spectate
+== Observar
+
+Spectate next
+== Observar próximo
+
+Spectate previous
+== Observar anterior
+
+Spectator mode
+== Modo espectador
+
+Spectators
+== Espectadores
+
+Standard gametype
+== Tipo de jogo padrão
+
+Standard map
+== Mapa padrão
+
+Stop record
+== Parar a gravação
+
+Strict gametype filter
+== Filtrar tipo de jogo exato
+
+Sudden Death
+== Morte Súbita
+
+Switch weapon on pickup
+== Equipar arma ao pegá-la
+
+Team
+== Time
+
+Team chat
+== Chat do time
+
+Teeworlds %s is out! Download it at www.teeworlds.com!
+== Teeworlds %s foi lançado! Baixe-o em www.teeworlds.com!
+
+Texture Compression
+== Compressão de textura
+
+The audio device couldn't be initialised.
+== O dispositivo de áudio não pôde ser inicializado.
+
+The server is running a non-standard tuning on a pure game type.
+== O servidor está rodando com modificações em um tipo de jogo oficial.
+
+There's an unsaved map in the editor, you might want to save it before you quit the game.
+== Existe um mapa não salvo no editor, você pode querer salvá-lo antes de sair do jogo.
+
+Time limit
+== Limite de tempo
+
+Time limit: %d min
+== Limite de tempo: %d minutos
+
+Try again
+== Tente de novo
+
+Type
+== Tipo
+
+Type:
+== Tipo:
+
+UI Color
+== Cor do menu
+
+Unable to delete the demo
+== Não foi possível deletar o demo
+
+Unable to rename the demo
+== Não foi possível renomear o demo
+
+Use sounds
+== Usar sons
+
+Use team colors for name plates
+== Usar cores dos times em apelidos
+
+V-Sync
+== V-Sync
+
+Version
+== Versão
+
+Version:
+== Versão
+
+Vote command:
+== Comando da votação:
+
+Vote description:
+== Descrição da votação:
+
+Vote no
+== Votar Não
+
+Vote yes
+== Votar Sim
+
+Voting
+== Votação
+
+Warmup
+== Aquecimento
+
+Weapon
+== Arma
+
+Welcome to Teeworlds
+== Bem-vindo ao Teeworlds!
+
+Yes
+== Sim
+
+You must restart the game for all settings to take effect.
+== Você deve reiniciar o jogo para que todas as alterações tenham efeito.
+
+Your skin
+== Sua skin
+
+no limit
+== sem limite
+
+##### needs translation #####
+
+##### old translations #####
+
diff --git a/data/languages/bulgarian.txt b/data/languages/bulgarian.txt
index 47af094e..38df7891 100644
--- a/data/languages/bulgarian.txt
+++ b/data/languages/bulgarian.txt
@@ -53,16 +53,16 @@ Always show name plates
 == Винаги показвай лентите с имената
 
 Are you sure that you want to delete the demo?
-== Сигурни ли сте че искате да изтриете това демо?
+== Сигуен ли си че искаш да изтриеш това демо?
 
 Are you sure that you want to quit?
-== Сигурни ли сте че искате да напуснете?
+== Сигурен ли си че искаш да напуснеш?
 
 Are you sure that you want to remove the player from your friends list?
-== Сигурни ли сте че искате да премахнете този играч от листа с приятели?
+== Сигурен ли си че искаш да премахнеш този играч от листа с приятели?
 
 As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server.
-== Тъй като стартирате играта за първи път, моля да въведете вашият ник отдолу. Препоръчително е да проверите и нагласите настройките по ваш избор преди да започнете игра.
+== Тъй като стартираш играта за първи път, моля въведи своя ник отдолу. Препоръчително е да провериш и нагласиш настройките по ваш свой избор преди да започнеш игра.
 
 Automatically record demos
 == Записвай демота автоматично
@@ -145,6 +145,9 @@ Delete demo
 Demo details
 == Детайли за демото
 
+Demofile: %s
+== Демофайл: %s
+
 Demos
 == Демота
 
@@ -347,7 +350,7 @@ News
 == Новини
 
 Next weapon
-== Следващо оръжие
+== След. оръжие
 
 Nickname
 == Ник
@@ -362,7 +365,7 @@ No servers found
 == Няма намерени сървъри
 
 No servers match your filter criteria
-== Няма сървъри съответстващи вашите настройки
+== Няма сървъри съответстващи твоите настройки
 
 Ok
 == Добре
@@ -388,9 +391,15 @@ Pistol
 Play
 == Възпроизведи
 
+Play background music
+== Пусни музика за фон
+
 Player
 == Играч
 
+Player country:
+== Страна на играча:
+
 Player options
 == Настройки на Играча
 
@@ -508,7 +517,7 @@ Shotgun
 Show chat
 == Показвай чата
 
-Show friends
+Show friends only
 == Покажи приятели
 
 Show ingame HUD
@@ -536,7 +545,10 @@ Sound volume
 == Сила на звука
 
 Spectate
-== Наблюдавайте
+== Наблюдавай
+
+Spectate next
+== Наблюдавай след.
 
 Spectator mode
 == Наблюдател
@@ -553,6 +565,9 @@ Standard map
 Stop record
 == Спри записа
 
+Strict gametype filter
+== Стриктен геймтайп филтър
+
 Sudden Death
 == Внезапна Смърт
 
@@ -566,13 +581,13 @@ Team chat
 == Отборен чат
 
 Teeworlds %s is out! Download it at www.teeworlds.com!
-== Излезна Teeworlds %s! Свалете новата версия от www.teeworlds.com!
+== Излезна Teeworlds %s! Свали новата версия от www.teeworlds.com!
 
 Texture Compression
 == Компресиране на Текстурите
 
 The audio device couldn't be initialised.
-== Аудио устройството не може да бъде стартирано
+== Аудио устройството не може да бъде стартирано.
 
 The server is running a non-standard tuning on a pure game type.
 == Този сървър използва нестандартен тунинг на стандартен тип ига.
@@ -587,7 +602,7 @@ Time limit: %d min
 == Лимит на Времето: %d мин
 
 Try again
-== Опитайте Отново
+== Опитай пак
 
 Type
 == Тип
@@ -620,10 +635,10 @@ Version:
 == Версия:
 
 Vote command:
-== Гласувай за команда:
+== Вот команда:
 
 Vote description:
-== Гласувай за обяснение:
+== Вот дефиниция:
 
 Vote no
 == Против
@@ -647,33 +662,18 @@ Yes
 == Да
 
 You must restart the game for all settings to take effect.
-== Трябва да рестартирате играта за да поемат ефект новите настройки.
+== Трябва да рестартираш играта за да поемат ефект новите настройки.
 
 Your skin
-== Вашият модел
+== Твоят модел
 
 no limit
 == Без лимит
 
 ##### needs translation #####
 
-Play background music
-== 
-
-Spectate next
-== 
-
 Spectate previous
 == 
 
 ##### old translations #####
 
-Page %d of %d
-== Страница %d/%d
-
-Next
-== Нататък
-
-Prev
-== Назад
-
diff --git a/data/languages/czech.txt b/data/languages/czech.txt
index 88a7675a..5d50f47a 100644
--- a/data/languages/czech.txt
+++ b/data/languages/czech.txt
@@ -5,7 +5,7 @@
 == %d Bajtů
 
 %d of %d servers, %d players
-== %d z %d serverů, %d hráčů
+== Serverů: %d z %d, Hráčů: %d
 
 %d%% loaded
 == %d%% načteno
@@ -26,7 +26,7 @@
 == Zbývá sekund: %i
 
 %s wins!
-== %s zvítězil!
+== Vítězí %s!
 
 -Page %d-
 == -Strana %d-
@@ -53,7 +53,7 @@ Always show name plates
 == Vždy zobrazovat
 
 Are you sure that you want to delete the demo?
-== Opravdu chcete smazat toto demo?
+== Opravdu chcete smazat tento záznam?
 
 Are you sure that you want to quit?
 == Opravdu chcete ukončit Teeworlds?
@@ -65,7 +65,7 @@ As this is the first time you launch the game, please enter your nick name below
 == Jelikož je toto Vaše první spuštění hry, zadejte prosím svou přezdívku. Než začnete hrát, doporučujeme si upravit nastavení tak, aby Vám vyhovovalo.
 
 Automatically record demos
-== Nahrávat dema automaticky
+== Automaticky nahrávat záznamy
 
 Automatically take game over screenshot
 == Na konci kola vyfotit obrazovku
@@ -116,7 +116,7 @@ Controls
 == Ovládání
 
 Count players only
-== Započítat hrajíci hráče
+== Započítat hrající hráče
 
 Country
 == Národnost
@@ -140,13 +140,16 @@ Delete
 == Smazat
 
 Delete demo
-== Smazat demo
+== Smazat záznam
 
 Demo details
 == Detaily záznamu:
 
+Demofile: %s
+== Soubor záznamu: %s
+
 Demos
-== Dema
+== Záznamy
 
 Disconnect
 == Odpojit
@@ -176,7 +179,7 @@ Error
 == Chyba
 
 Error loading demo
-== Chyba načítání dema
+== Chyba načítání záznamu
 
 FSAA samples
 == FSAA vzorkování
@@ -218,7 +221,7 @@ Game info
 == Hra
 
 Game over
-== Ukončit hru
+== Konec hry
 
 Game type
 == Herní mód
@@ -260,7 +263,7 @@ Internet
 == Internet
 
 Invalid Demo
-== Neplatné demo
+== Neplatný záznam
 
 Join blue
 == Modří
@@ -305,7 +308,7 @@ Max Screenshots
 == Maximální počet vyfocených obrazovek
 
 Max demos
-== Maximální počet dem
+== Maximální počet záznamů
 
 Maximum ping:
 == Maximální ping:
@@ -329,7 +332,7 @@ Movement
 == Pohyb
 
 Mute when not active
-== Při nečinosti ztlumit zvuk
+== Při nečinnosti ztlumit zvuk
 
 Name
 == Jméno
@@ -359,7 +362,7 @@ No password
 == Bez hesla
 
 No servers found
-== Žádny server nenalezen
+== Žádný server nenalezen
 
 No servers match your filter criteria
 == Žádný server neodpovídá požadavkům filtru
@@ -388,9 +391,15 @@ Pistol
 Play
 == Přehrát
 
+Play background music
+== Přehrávat hudbu na pozadí
+
 Player
 == Hráč
 
+Player country:
+== Národnost:
+
 Player options
 == Nastavení hráčů
 
@@ -422,7 +431,7 @@ Reason:
 == Důvod:
 
 Record demo
-== Záznam dema
+== Zaznamenat
 
 Red team
 == Červení
@@ -491,13 +500,13 @@ Server details
 == Detaily serveru
 
 Server filter
-== Filtr serverů:
+== Filtr serverů
 
 Server info
 == Informace
 
 Server not full
-== Možnost připojení
+== Možnost připojit se
 
 Settings
 == Nastavení
@@ -508,7 +517,7 @@ Shotgun
 Show chat
 == Zobrazit chat
 
-Show friends
+Show friends only
 == Zobrazit přátele
 
 Show ingame HUD
@@ -538,6 +547,12 @@ Sound volume
 Spectate
 == Pozorovat
 
+Spectate next
+== Pozorovat dalšího
+
+Spectate previous
+== Pozorovat minulého
+
 Spectator mode
 == Nastavit pozorování
 
@@ -551,13 +566,16 @@ Standard map
 == Základní mapy
 
 Stop record
-== Zastavit nahránání
+== Ukončit záznam
+
+Strict gametype filter
+== Pouze tento herní mód
 
 Sudden Death
 == Náhlá smrt
 
 Switch weapon on pickup
-== Automaticky přepínat zbraň na nově získanou
+== Přehazovat zbraň na nově sebranou
 
 Team
 == Týmu
@@ -566,7 +584,7 @@ Team chat
 == Týmový chat
 
 Teeworlds %s is out! Download it at www.teeworlds.com!
-== Nejnovější verze Teeworlds je %s! Stáhněte si ji na www.teeworlds.com!
+== Stáhněte si nové Teeworlds %s na www.teeworlds.com!
 
 Texture Compression
 == Komprimované textury
@@ -599,7 +617,7 @@ UI Color
 == Barvy uživatelského rozhraní
 
 Unable to delete the demo
-== Demo se nepodařilo smazat
+== Záznam se nepodařilo smazat
 
 Unable to rename the demo
 == Záznam nelze přejmenovat
@@ -608,7 +626,7 @@ Use sounds
 == Povolit zvuk
 
 Use team colors for name plates
-== Obarvit jména hráčů podle týmů
+== Obarvit jména hráčů podle týmu
 
 V-Sync
 == Vertikální synchronizace
@@ -641,7 +659,7 @@ Weapon
 == Zbraně
 
 Welcome to Teeworlds
-== Vítejte v Teeworls
+== Vítejte v Teeworlds
 
 Yes
 == Ano
@@ -657,14 +675,5 @@ no limit
 
 ##### needs translation #####
 
-Play background music
-== 
-
-Spectate next
-== 
-
-Spectate previous
-== 
-
 ##### old translations #####
 
diff --git a/data/languages/danish.txt b/data/languages/danish.txt
index bd2225fd..f4941582 100644
--- a/data/languages/danish.txt
+++ b/data/languages/danish.txt
@@ -508,7 +508,7 @@ Shotgun
 Show chat
 == Vis chat
 
-Show friends
+Show friends only
 == Vis venner
 
 Show ingame HUD
@@ -657,17 +657,23 @@ no limit
 
 ##### needs translation #####
 
+Demofile: %s
+== 
+
 Play background music
 == 
 
+Player country:
+== 
+
 Spectate next
 == 
 
 Spectate previous
 == 
 
-##### old translations #####
-
-##### translated strings #####
+Strict gametype filter
 == 
 
+##### old translations #####
+
diff --git a/data/languages/dutch.txt b/data/languages/dutch.txt
index e8fc7b7b..4375f784 100644
--- a/data/languages/dutch.txt
+++ b/data/languages/dutch.txt
@@ -62,7 +62,7 @@ Are you sure that you want to remove the player from your friends list?
 == Weet je zeker dat je deze speler uit je vriendenlijst wilt verwijderen?
 
 As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server.
-== Omdat dit de eerste keer is dat je het spel opstart, moet je een nicknaam kiezen. Doe dat hieronder. Het is aanbevolen om de instellingen te controleren, voordat je een spel start.
+== Omdat dit de eerste keer is dat je het spel opstart, moet je een bijnaam kiezen. Doe dat hieronder. Het is aanbevolen om de instellingen te controleren voordat je een spel start.
 
 Automatically record demos
 == Automatisch demo's opnemen
@@ -145,6 +145,9 @@ Delete demo
 Demo details
 == Demo details
 
+Demofile: %s
+== Demobestand: %s
+
 Demos
 == Demo's
 
@@ -350,7 +353,7 @@ Next weapon
 == Volgend wapen
 
 Nickname
-== Nicknaam
+== Bijnaam
 
 No
 == Nee
@@ -388,9 +391,15 @@ Pistol
 Play
 == Spelen
 
+Play background music
+== Speel achtergrondmuziek
+
 Player
 == Speler
 
+Player country:
+== Speler land:
+
 Player options
 == Speler opties
 
@@ -425,10 +434,10 @@ Record demo
 == Neem demo op
 
 Red team
-== Rood
+== Rode team
 
 Red team wins!
-== Rood wint!
+== Rode team wint!
 
 Refresh
 == Vernieuwen
@@ -508,7 +517,7 @@ Shotgun
 Show chat
 == Laat chat zien
 
-Show friends
+Show friends only
 == Laat vrienden zien
 
 Show ingame HUD
@@ -538,6 +547,12 @@ Sound volume
 Spectate
 == Toekijken
 
+Spectate next
+== Volg volgende speler
+
+Spectate previous
+== Volg vorige speler
+
 Spectator mode
 == Toeschouwer modus
 
@@ -553,6 +568,9 @@ Standard map
 Stop record
 == Stop met opnemen
 
+Strict gametype filter
+== Strikt speltype filter
+
 Sudden Death
 == Sudden Death
 
@@ -581,7 +599,7 @@ There's an unsaved map in the editor, you might want to save it before you quit
 == Er is een onopgeslagen kaart in de editor, misschien wil je deze opslaan voordat je stopt.
 
 Time limit
-== Tijdlimiet
+== Tijdslimiet
 
 Time limit: %d min
 == Tijdslimiet: %d minuten
@@ -657,14 +675,5 @@ no limit
 
 ##### needs translation #####
 
-Play background music
-== 
-
-Spectate next
-== 
-
-Spectate previous
-== 
-
 ##### old translations #####
 
diff --git a/data/languages/finnish.txt b/data/languages/finnish.txt
index 60650940..77ce6cf7 100644
--- a/data/languages/finnish.txt
+++ b/data/languages/finnish.txt
@@ -145,6 +145,9 @@ Delete demo
 Demo details
 == Demon tiedot
 
+Demofile: %s
+== Demotiedosto: %s
+
 Demos
 == Demot
 
@@ -388,9 +391,15 @@ Pistol
 Play
 == Toista
 
+Play background music
+== Soita taustamusiikki
+
 Player
 == Pelaaja
 
+Player country:
+== Pelaajan maa:
+
 Player options
 == Pelaajavalinnat
 
@@ -508,8 +517,8 @@ Shotgun
 Show chat
 == Näytä chatti
 
-Show friends
-== Näytä ystävät
+Show friends only
+== Näytä vain ystävät
 
 Show ingame HUD
 == Näytä peli-HUD
@@ -538,6 +547,12 @@ Sound volume
 Spectate
 == Katso
 
+Spectate next
+== Katso seuraavaa
+
+Spectate previous
+== Katso edellistä
+
 Spectator mode
 == Katsojatila
 
@@ -553,6 +568,9 @@ Standard map
 Stop record
 == Lopeta nauhoit.
 
+Strict gametype filter
+== Tarkka pelityyppisuodin
+
 Sudden Death
 == Äkkikuolema
 
@@ -657,14 +675,5 @@ no limit
 
 ##### needs translation #####
 
-Play background music
-== 
-
-Spectate next
-== 
-
-Spectate previous
-== 
-
 ##### old translations #####
 
diff --git a/data/languages/french.txt b/data/languages/french.txt
index 52ef4fc9..5ea3b935 100644
--- a/data/languages/french.txt
+++ b/data/languages/french.txt
@@ -145,6 +145,9 @@ Delete demo
 Demo details
 == Détails de la démo
 
+Demofile: %s
+== Démo : %s
+
 Demos
 == Démos
 
@@ -224,7 +227,7 @@ Game type
 == Type de jeu
 
 Game types:
-== Types de jeu:
+== Types de jeu :
 
 General
 == Général
@@ -308,7 +311,7 @@ Max demos
 == Nombre maximum de démos
 
 Maximum ping:
-== Ping maximum:
+== Ping maximum :
 
 Miscellaneous
 == Divers
@@ -388,9 +391,15 @@ Pistol
 Play
 == Jouer
 
+Play background music
+== Jouer la musique de fond
+
 Player
 == Joueur
 
+Player country:
+== Nation joueurs :
+
 Player options
 == Joueurs
 
@@ -508,7 +517,7 @@ Shotgun
 Show chat
 == Montrer le chat
 
-Show friends
+Show friends only
 == Montrer les amis
 
 Show ingame HUD
@@ -538,6 +547,12 @@ Sound volume
 Spectate
 == Regarder
 
+Spectate next
+== Suivre suivant
+
+Spectate previous
+== Suivre précédent
+
 Spectator mode
 == Menu spectateur
 
@@ -553,6 +568,9 @@ Standard map
 Stop record
 == Arrêter
 
+Strict gametype filter
+== Types de jeu exacts
+
 Sudden Death
 == Mort Subite
 
@@ -657,14 +675,5 @@ no limit
 
 ##### needs translation #####
 
-Play background music
-== 
-
-Spectate next
-== 
-
-Spectate previous
-== 
-
 ##### old translations #####
 
diff --git a/data/languages/german.txt b/data/languages/german.txt
index 799d2865..17720d60 100644
--- a/data/languages/german.txt
+++ b/data/languages/german.txt
@@ -145,6 +145,9 @@ Delete demo
 Demo details
 == Aufnahmendetails:
 
+Demofile: %s
+== Demodatei: %s
+
 Demos
 == Aufnahm.
 
@@ -388,9 +391,15 @@ Pistol
 Play
 == Abspielen
 
+Play background music
+== Hintergrundmusik abspielen
+
 Player
 == Spieler
 
+Player country:
+== Spielerland:
+
 Player options
 == Spieleroptionen
 
@@ -508,7 +517,7 @@ Shotgun
 Show chat
 == Chat anzeigen
 
-Show friends
+Show friends only
 == Nur Freunde zeigen
 
 Show ingame HUD
@@ -538,6 +547,12 @@ Sound volume
 Spectate
 == Zuschauen
 
+Spectate next
+== Nächstem zusehen
+
+Spectate previous
+== Vorherigem zusehen
+
 Spectator mode
 == Zuschaueroptionen
 
@@ -553,6 +568,9 @@ Standard map
 Stop record
 == Aufnahme be.
 
+Strict gametype filter
+== Genauer Spieltyp
+
 Sudden Death
 == Sudden Death
 
@@ -578,7 +596,7 @@ The server is running a non-standard tuning on a pure game type.
 == Der Server läuft nicht mit Standardeinstellungen.
 
 There's an unsaved map in the editor, you might want to save it before you quit the game.
-== Im Editor ist noch eine ungespeicherte Karte.
+== Im Editor ist noch eine ungespeicherte Karte. Vielleicht möchtest du sie speichern.
 
 Time limit
 == Zeitlimit
@@ -657,14 +675,5 @@ no limit
 
 ##### needs translation #####
 
-Play background music
-== 
-
-Spectate next
-== 
-
-Spectate previous
-== 
-
 ##### old translations #####
 
diff --git a/data/languages/hungarian.txt b/data/languages/hungarian.txt
new file mode 100644
index 00000000..49f3116a
--- /dev/null
+++ b/data/languages/hungarian.txt
@@ -0,0 +1,679 @@
+
+##### translated strings #####
+
+%d Bytes
+== %d Bit
+
+%d of %d servers, %d players
+== %d Szerver, %d Játékos
+
+%d%% loaded
+== %d%% betöltve
+
+%ds left
+==  %ds vissza
+
+%i minute left
+== %i perc vissza
+
+%i minutes left
+== %i perc vissza
+
+%i second left
+== %i másodperc vissza
+
+%i seconds left
+== %i másodperc vissza
+
+%s wins!
+== %s nyert!
+
+-Page %d-
+== -oldal %d-
+
+Abort
+== Mégse
+
+Add
+== Hozzáad
+
+Add Friend
+== Hozzáad barátot
+
+Address
+== Cím
+
+All
+== Mindenki
+
+Alpha
+== Alpha
+
+Always show name plates
+== Mindig mutassa a névtáblát
+
+Are you sure that you want to delete the demo?
+== Biztos hogy le akarod törölni a demót?
+
+Are you sure that you want to quit?
+== Biztos hogy ki akarsz lépni?
+
+Are you sure that you want to remove the player from your friends list?
+== Biztos vagy benne hogy ki akarod törölni a játékost a barátok listájáról?
+
+As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server.
+== Mivel ez az első alkalom hogy elindítottad ezt a játékot, kérlekk add meg a neved!
+
+Automatically record demos
+== Magától rögzítse a demókat
+
+Automatically take game over screenshot
+== Magától készítsen a játék végén képet
+
+Blue team
+== Kék csapat
+
+Blue team wins!
+== A kék csapat nyert!
+
+Body
+== Test
+
+Call vote
+== Szavazás
+
+Change settings
+== Beállítások átállítása
+
+Chat
+== Chat
+
+Clan
+== Klán
+
+Client
+== Kliens
+
+Close
+== Bezárás
+
+Compatible version
+== Kompatibilis Verzió
+
+Connect
+== Csatlakozás
+
+Connecting to
+== Csatlakozás a
+
+Connection Problems...
+== Csatlakozási problémák...
+
+Console
+== Konzol
+
+Controls
+== Irányítás
+
+Count players only
+== Csak számontartott játékosok
+
+Country
+== Ország
+
+Crc:
+== Crc:
+
+Created:
+== Készítette:
+
+Current
+== Aktuális
+
+Current version: %s
+== Aktuális verzió: %s
+
+Custom colors
+== Egyéni színek
+
+Delete
+== Törlés
+
+Delete demo
+== Demó törlése
+
+Demo details
+== Információk a demóról
+
+Demos
+== Demók
+
+Disconnect
+== Szerver elhagyása
+
+Disconnected
+== Kilépett
+
+Display Modes
+== Kijelző módok
+
+Downloading map
+== Pálya letöltése
+
+Draw!
+== Rajzolj!
+
+Dynamic Camera
+== Dinamikus kamera
+
+Emoticon
+== Hangulatjel
+
+Enter
+== Belépés
+
+Error
+== Hiba
+
+Error loading demo
+== Hiba a demó betöltésében
+
+FSAA samples
+== FSAA mintáku
+
+Favorite
+== Kedvenc
+
+Favorites
+== Kedvencek
+
+Feet
+== Láb
+
+Filter
+== Szűrő
+
+Fire
+== Tűz
+
+Folder
+== Mappa
+
+Force vote
+== Különleges szavazás
+
+Free-View
+== Szabad-nézet
+
+Friends
+== Barátok
+
+Fullscreen
+== Teljesképernyő
+
+Game
+== Játék
+
+Game info
+== Játék infó
+
+Game over
+== Játék vége
+
+Game type
+== Játék fajtája
+
+Game types:
+== Játék fajtái:
+
+General
+== Általános
+
+Graphics
+== Grafika
+
+Grenade
+== Gránát
+
+Hammer
+== Kalapács
+
+Has people playing
+== Játékos játszik
+
+High Detail
+== Jó részletek
+
+Hook
+== Horog
+
+Host address
+== Szerver címe
+
+Hue
+== Színárnyalat
+
+Info
+== Infó
+
+Internet
+== Internet
+
+Invalid Demo
+== Érvénytelen demó
+
+Join blue
+== Kékhez lépés
+
+Join game
+== Csatlakozás a játékhoz
+
+Join red
+== Piroshoz lépés
+
+Jump
+== Ugrás
+
+Kick player
+== Játékos kirúgása
+
+LAN
+== Helyi
+
+Language
+== Nyelv
+
+Length:
+== Hossza:
+
+Lht.
+== Lht.
+
+Loading
+== Betöltés
+
+MOTD
+== Napi üzenet
+
+Map
+== Pálya
+
+Map:
+== Pálya:
+
+Max Screenshots
+== Maximum Fotó
+
+Max demos
+== Maximum Demó
+
+Maximum ping:
+== Maximum Ping:
+
+Miscellaneous
+== Vegyes
+
+Mouse sens.
+== Egér érzékenysége
+
+Move left
+== Balra lépés
+
+Move player to spectators
+== Megfigyelő lett
+
+Move right
+== Jobbra lépés
+
+Movement
+== Mozgás
+
+Mute when not active
+== Letiltás ha nem aktív
+
+Name
+== Név
+
+Name plates size
+== Névtáblák mérete
+
+Netversion:
+== Netes verzió:
+
+New name:
+== Új Név:
+
+News
+== Hírek
+
+Next weapon
+== Következő fegyver
+
+Nickname
+== Becenév
+
+No
+== Nem
+
+No password
+== Jelszó nélküli
+
+No servers found
+== Nem talált szervereket
+
+No servers match your filter criteria
+== Nincs szerver a szűrőfeltételeidhez
+
+Ok
+== Oké
+
+Open
+== Megnyit
+
+Parent Folder
+== Szülői mappa
+
+Password
+== Jelszó
+
+Password incorrect
+== Helytelen jelszó
+
+Ping
+== Ping
+
+Pistol
+== Pisztoly
+
+Play
+== Játék
+
+Player
+== Játékos
+
+Player options
+== Játékos beállításai
+
+Players
+== Játékosok
+
+Please balance teams!
+== Kérlek egyenlítsd ki a csapatokat!
+
+Prev. weapon
+== Előző fegyver
+
+Quality Textures
+== Minőségi kidolgozás
+
+Quick search:
+== Gyors keresés:
+
+Quit
+== Kilépés
+
+Quit anyway?
+== Mindenképpen kilép?
+
+REC %3d:%02d
+== REC %3d:%02d
+
+Reason:
+== Indok:
+
+Record demo
+== Demó felvétele
+
+Red team
+== Piros csapat
+
+Red team wins!
+== A piros csapat nyert!
+
+Refresh
+== Újratöltés
+
+Refreshing master servers
+== A master szerverek frissítése
+
+Remote console
+== Távoli konzol
+
+Remove
+== Eltávolítás
+
+Remove friend
+== Barát eltávolítása
+
+Rename
+== Átnevezés
+
+Rename demo
+== Demó átnevezése
+
+Reset filter
+== Szűrő visszaállítása
+
+Reset to defaults
+== Visszaállítás az alapértelmezettre
+
+Rifle
+== Lézer
+
+Round
+== Menet
+
+Sample rate
+== Mintavételi frekvencia
+
+Sat.
+== Sat.
+
+Score
+== Pontszám
+
+Score board
+== Pontszám tábla
+
+Score limit
+== Ponthatár
+
+Scoreboard
+== Pontszámtábla
+
+Screenshot
+== Pillanatkép
+
+Server address:
+== Szerver címe:
+
+Server details
+== Szerver részletei
+
+Server filter
+== Szerver szűrő
+
+Server info
+== Szerver infó
+
+Server not full
+== Szerver nincs tele
+
+Settings
+== Beállítások
+
+Shotgun
+== Sörétes puska
+
+Show chat
+== Chat mutatása
+
+Show friends only
+== Barátok mutatása
+
+Show ingame HUD
+== Játék közbeni HUD mutatása
+
+Show name plates
+== Név táblák mutatása
+
+Show only supported
+== Csak támogatott mutatása
+
+Size:
+== Méret:
+
+Skins
+== Skinek
+
+Sound
+== Hang
+
+Sound error
+== Hang hiba
+
+Sound volume
+== Hangerő
+
+Spectate
+== Megfigyelés
+
+Spectator mode
+== Néző mód
+
+Spectators
+== Megfigyelők
+
+Standard gametype
+== Általános játékfajta
+
+Standard map
+== Általános pálya
+
+Stop record
+== Felvétel megállítása
+
+Sudden Death
+== Gyors halál
+
+Switch weapon on pickup
+== Fegyverváltás felvételnél
+
+Team
+== Csapat
+
+Team chat
+== Csapat chat
+
+Teeworlds %s is out! Download it at www.teeworlds.com!
+== Teeworlds %s kint van! Töltsd le www.teeworlds.com oldalon!
+
+Texture Compression
+== Textúra tömörítés
+
+The audio device couldn't be initialised.
+== A hangeszköz nem kezdeményezhető.
+
+The server is running a non-standard tuning on a pure game type.
+== A szerver egy nem szabványos hangolást futtat a tiszta játék típuson.
+
+There's an unsaved map in the editor, you might want to save it before you quit the game.
+== Van egy mentett térkép a szerkesztőben, talán akarod menteni, mielőtt kilépsz a játékból.
+
+Time limit
+== Időhatár
+
+Time limit: %d min
+== Időhatár: %d perc
+
+Try again
+== Próbáld újra
+
+Type
+== Típus
+
+Type:
+== Típus:
+
+UI Color
+== UI Szín
+
+Unable to delete the demo
+== Nem sikerült törölni a demó
+
+Unable to rename the demo
+== Nem sikerült átnevezni a demó
+
+Use sounds
+== Hangok használata
+
+Use team colors for name plates
+== Csapat szín használata név tábláknál
+
+V-Sync
+== V-Sync
+
+Version
+== Verzió
+
+Version:
+== Verzió:
+
+Vote command:
+== Szavazás parancsa:
+
+Vote description:
+== Szavazás leírása:
+
+Vote no
+== Nem
+
+Vote yes
+== Igen
+
+Voting
+== Szavazás
+
+Warmup
+== Kezdés
+
+Weapon
+== Fegyver
+
+Welcome to Teeworlds
+== Üdvözöljük a Teeworlds-ben
+
+Yes
+== Igen
+
+You must restart the game for all settings to take effect.
+== Újra kell indítani a játékot, a beállítások érvénybe lépéséhez.
+
+Your skin
+== Te skined
+
+no limit
+== Nincs korlát
+
+##### needs translation #####
+
+Demofile: %s
+== 
+
+Play background music
+== 
+
+Player country:
+== 
+
+Spectate next
+== 
+
+Spectate previous
+== 
+
+Strict gametype filter
+== 
+
+##### old translations #####
+
diff --git a/data/languages/index.txt b/data/languages/index.txt
index fbdae862..b7a6179b 100644
--- a/data/languages/index.txt
+++ b/data/languages/index.txt
@@ -1,59 +1,94 @@
 
 ##### language indices #####
 
+belarusian
+== Беларуская
+== 112
+
 bosnian
 == Bosanski
+== 70
+
+brazilian_portuguese
+== Português brasileiro
+== 76
 
 bulgarian
 == Български
+== 100
 
 czech
 == Česky
+== 203
 
 danish
 == Dansk
+== 208
 
 dutch
 == Nederlands
+== 528
 
 finnish
 == Suomi
+== 246
 
 french
 == Français
+== 250
 
 german
 == Deutsch
+== 276
+
+hungarian
+== Magyar
+== 348
 
 italian
 == Italiano
+== 380
 
 norwegian
 == Norsk
+== 578
 
 polish
 == Polski
+== 616
 
 portuguese
 == Português
+== 620
 
 romanian
 == Română
+== 642
 
 russian
 == Русский
+== 643
 
 serbian
 == Srpski
+== 688
 
 slovak
 == Slovensky
+== 703
 
 spanish
 == Español
+== 724
 
 swedish
 == Svenska
+== 752
+
+turkish
+== Türkçe
+== 792
 
 ukrainian
 == Українська
+== 804
diff --git a/data/languages/italian.txt b/data/languages/italian.txt
index cb136560..d566da49 100644
--- a/data/languages/italian.txt
+++ b/data/languages/italian.txt
@@ -1,30 +1,45 @@
 
 ##### translated strings #####
 
+%d Bytes
+== %d byte
+
 %d of %d servers, %d players
 == %d di %d server, %d giocatori
 
 %d%% loaded
-== %d%% caricati
+== %d%% caricato
 
 %ds left
-== %ds ancora
+== %ds sec.
 
 %i minute left
-== %i minuto ancora
+== %i minuto rimanente
 
 %i minutes left
-== %i minuti ancora
+== %i minuti rimanenti
 
 %i second left
-== %i secondo ancora
+== %i secondo rimanente
 
 %i seconds left
-== %i secondi ancora
+== %i secondi rimanenti
+
+%s wins!
+== %s ha vinto!
+
+-Page %d-
+== -Pagina %d-
 
 Abort
 == Annulla
 
+Add
+== Aggiungi
+
+Add Friend
+== Aggiungi amico
+
 Address
 == Indirizzo
 
@@ -35,25 +50,28 @@ Alpha
 == Alpha
 
 Always show name plates
-== Mostra sempre etichette dei nomi
+== Mostra sempre i nomi
 
 Are you sure that you want to delete the demo?
-== Sicuro di voler cancellare questa registrazione?
+== Sicuro di voler eliminare la demo?
 
 Are you sure that you want to quit?
-== Sicuro di voler uscire?
+== Sicuro di voler chiudere il gioco?
+
+Are you sure that you want to remove the player from your friends list?
+== Sicuro di voler rimuovere il giocatore dalla lista di amici?
 
 As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server.
-== E' la prima volta che avvii il gioco, ti preghiamo di inserire il tuo nickname. E' consigliato aggiustare le impostazioni in base alle proprie preferenze prima di entrare in un server.
+== Essendo la prima volta che avvii il gioco, ti preghiamo di inserire il tuo nickname qui sotto. Puoi cambiare le impostazioni in base alle tue preferenze prima di entrare in gioco.
 
 Automatically record demos
-== Registra demo automaticamente
+== Registra automaticamente demo
 
 Automatically take game over screenshot
-== Foto automatica a fine partita
+== Cattura schermata alla fine di ogni partita
 
 Blue team
-== Squadra Blu
+== Squadra blu
 
 Blue team wins!
 == La squadra blu ha vinto!
@@ -62,11 +80,20 @@ Body
 == Corpo
 
 Call vote
-== Inizia voto
+== Vota
+
+Change settings
+== Cambia opzioni
 
 Chat
 == Chat
 
+Clan
+== Clan
+
+Client
+== Client
+
 Close
 == Chiudi
 
@@ -80,13 +107,25 @@ Connecting to
 == Connessione a
 
 Connection Problems...
-== Connessione Persa...
+== Problemi di connessione...
 
 Console
 == Console
 
 Controls
-== Controlli
+== Comandi
+
+Count players only
+== Conta solo giocatori
+
+Country
+== Paese
+
+Crc:
+== Crc:
+
+Created:
+== Creata:
 
 Current
 == Attuale
@@ -98,10 +137,16 @@ Custom colors
 == Colori personalizzati
 
 Delete
-== Cancella
+== Elimina
 
 Delete demo
-== Cancella demo
+== Elimina demo
+
+Demo details
+== Dettagli demo
+
+Demofile: %s
+== Demo: %s
 
 Demos
 == Demo
@@ -113,19 +158,19 @@ Disconnected
 == Disconnesso
 
 Display Modes
-== Tipo di visualizzazione
+== Risoluzioni schermo
 
 Downloading map
-== Scaricando la mappa
+== Scaricamento mappa in corso
 
 Draw!
 == Pareggio!
 
 Dynamic Camera
-== Visuale dinamica
+== Camera dinamica
 
 Emoticon
-== Emozioni
+== Emoticon
 
 Enter
 == Entra
@@ -134,10 +179,10 @@ Error
 == Errore
 
 Error loading demo
-== Impossibile caricare la demo
+== Errore caricamento demo
 
 FSAA samples
-== Esempi FSAA
+== Campioni FSAA
 
 Favorite
 == Preferito
@@ -160,41 +205,47 @@ Folder
 Force vote
 == Forza voto
 
+Free-View
+== Visione libera
+
+Friends
+== Amici
+
 Fullscreen
 == Schermo intero
 
 Game
-== Gioco
+== Partita
 
 Game info
-== Dettagli gioco
+== Info partita
 
 Game over
-== Game over
+== Partita finita
 
 Game type
-== Tipo di gioco
+== Tipo
 
 Game types:
-== Tipi di gioco:
+== Tipo di gioco:
 
 General
 == Generale
 
 Graphics
-== Grafica
+== Aspetto
 
 Grenade
-== Granata
+== Lanciagranate
 
 Hammer
 == Martello
 
 Has people playing
-== Ha gente in gioco
+== Con giocatori
 
 High Detail
-== Molti dettagli
+== Alta qualità
 
 Hook
 == Rampino
@@ -203,7 +254,7 @@ Host address
 == Indirizzo host
 
 Hue
-== Col.
+== Tinta
 
 Info
 == Info
@@ -215,35 +266,50 @@ Invalid Demo
 == Demo non valida
 
 Join blue
-== Entra blu
+== Vai ai blu
 
 Join game
 == Entra
 
 Join red
-== Entra rosso
+== Vai ai rossi
 
 Jump
 == Salta
 
+Kick player
+== Caccia giocatore
+
 LAN
 == LAN
 
 Language
 == Lingua
 
+Length:
+== Durata:
+
 Lht.
-== Lum.
+== Luminosità
 
 Loading
 == Caricamento
 
 MOTD
-== MDG
+== MOTD
 
 Map
 == Mappa
 
+Map:
+== Mappa:
+
+Max Screenshots
+== Numero massimo di catture
+
+Max demos
+== Numero massimo di demo
+
 Maximum ping:
 == Ping massimo:
 
@@ -254,28 +320,37 @@ Mouse sens.
 == Sensibilità
 
 Move left
-== Muovi a sinistra
+== Sinistra
+
+Move player to spectators
+== Fai osservare il giocatore
 
 Move right
-== Muovi a destra
+== Destra
 
 Movement
 == Movimento
 
 Mute when not active
-== Muto quando inattivo
+== Silenzioso quanto inattivo
 
 Name
 == Nome
 
 Name plates size
-== Dimensione etichetta
+== Dimensione nomi
+
+Netversion:
+== Versione net
+
+New name:
+== Nuovo nome:
 
 News
-== Notizie
+== Novità
 
 Next weapon
-== Successiva
+== Arma seguente
 
 Nickname
 == Nickname
@@ -290,7 +365,7 @@ No servers found
 == Nessun server trovato
 
 No servers match your filter criteria
-== Nessun server corrisponde ai filtri di ricerca
+== Nessun server corrisponde ai tuoi criteri di ricerca
 
 Ok
 == Ok
@@ -299,7 +374,7 @@ Open
 == Apri
 
 Parent Folder
-== Indietro
+== Cartella superiore
 
 Password
 == Password
@@ -314,28 +389,43 @@ Pistol
 == Pistola
 
 Play
-== Guarda
+== Riproduci
+
+Play background music
+== Riproduci musica di sottofondo
 
 Player
 == Giocatore
 
+Player country:
+== Filtra per paese:
+
+Player options
+== Opzioni giocatore
+
 Players
 == Giocatori
 
 Please balance teams!
-== Squadre sbilanciate!
+== Equilibra le squadre!
 
 Prev. weapon
-== Precedente
+== Arma precedente
 
 Quality Textures
-== Textures di qualità
+== Texture di qualità
 
 Quick search:
 == Ricerca rapida:
 
 Quit
-== Esci
+== Chiudi
+
+Quit anyway?
+== Vuoi chiudere comunque?
+
+REC %3d:%02d
+== REC %3d:%02d
 
 Reason:
 == Motivo:
@@ -353,13 +443,25 @@ Refresh
 == Aggiorna
 
 Refreshing master servers
-== Aggiornando la lista server
+== Aggiornamento server principali
 
 Remote console
 == Console remota
 
+Remove
+== Elimina
+
+Remove friend
+== Elimina amico
+
+Rename
+== Rinomina
+
+Rename demo
+== Rinomina demo
+
 Reset filter
-== Annulla filtri
+== Azzera filtri
 
 Reset to defaults
 == Reimposta
@@ -374,73 +476,106 @@ Sample rate
 == Frequenza
 
 Sat.
-== Sat.
+== Saturazione
 
 Score
 == Punti
 
 Score board
-== Scoreboard
+== Punteggio
 
 Score limit
-== Punteggio massimo
+== Limite punti
 
 Scoreboard
-== Scoreboard
+== Punteggi
 
 Screenshot
-== Screenshot
+== Cattura schermata
+
+Server address:
+== Indirizzo server:
 
 Server details
-== Dettagli del server
+== Dettagli server
+
+Server filter
+== Filtro server
 
 Server info
-== Dettagli
+== Info server
 
 Server not full
 == Server non pieno
 
 Settings
-== Impostazioni
+== Opzioni
 
 Shotgun
 == Fucile
 
 Show chat
-== Mostra Chat
+== Mostra chat
+
+Show friends only
+== Mostra solo amici
+
+Show ingame HUD
+== Mostra HUD in gioco
 
 Show name plates
-== Mostra etichetta nomi
+== Mostra nomi
 
 Show only supported
 == Mostra solo supportati
 
+Size:
+== Dimensione:
+
 Skins
-== Skins
+== Skin
 
 Sound
 == Suono
 
+Sound error
+== Errore suono
+
 Sound volume
-== Volume
+== Volume suono
 
 Spectate
 == Osserva
 
+Spectate next
+== Osserva succ.
+
+Spectate previous
+== Osserva prec.
+
+Spectator mode
+== Menu osservazione
+
 Spectators
 == Spettatori
 
 Standard gametype
-== Tipo di gioco classico
+== Tipo di gioco normale
 
 Standard map
-== Mappa classica
+== Mappa normale
 
 Stop record
-== Termina reg.
+== Ferma reg.
+
+Strict gametype filter
+== Tipo di gioco preciso
+
+Sudden Death
+== Tempo supplementare
 
 Switch weapon on pickup
-== Cambia arma alla raccolta
+== Cambia arma ad ogni raccolta
 
 Team
 == Squadra
@@ -449,22 +584,25 @@ Team chat
 == Chat di squadra
 
 Teeworlds %s is out! Download it at www.teeworlds.com!
-== E' uscito Teeworlds %s! Scaricalo da www.teeworlds.com !
+== È uscito Teeworld %s! Scaricalo su www.teeworlds.com!
 
 Texture Compression
-== Compressione Texture
+== Compressione texture
 
 The audio device couldn't be initialised.
 == Il dispositivo audio non può essere inizializzato.
 
 The server is running a non-standard tuning on a pure game type.
-== Il server sta eseguendo un tuning non-standard su un tipo di gioco classico.
+== Il server presenta impostazioni non standard in un tipo di gioco normale.
+
+There's an unsaved map in the editor, you might want to save it before you quit the game.
+== C'è una mappa non salvata nell'editor, probabilmente vuoi salvarla prima di uscire.
 
 Time limit
 == Limite di tempo
 
 Time limit: %d min
-== Tempo limite %d min
+== Limite di tempo: %d min
 
 Try again
 == Ritenta
@@ -472,199 +610,73 @@ Try again
 Type
 == Tipo
 
+Type:
+== Tipo:
+
 UI Color
-== Colore del menu
+== Color interfaccia
 
 Unable to delete the demo
-== Impossibile eliminare la registrazione
+== Impossibile eliminare la demo
+
+Unable to rename the demo
+== Impossibile rinominare la demo
 
 Use sounds
-== Usa suoni
+== Attiva suoni
+
+Use team colors for name plates
+== Usa i colori della squadra nei nomi
 
 V-Sync
-== V-Sync
+== Sincronizzazione verticale
 
 Version
 == Versione
 
+Version:
+== Versione:
+
+Vote command:
+== Comando di voto:
+
+Vote description:
+== Descrizione voto:
+
 Vote no
 == Vota no
 
 Vote yes
-== Vota si
+== Vota sì
 
 Voting
 == Votazione
 
+Warmup
+== Riscaldamento
+
 Weapon
-== Attiva arma
+== Arma
 
 Welcome to Teeworlds
-== Benvenuto su Teeworlds!
+== Benvenuto su Teeworlds
 
 Yes
-== Si
+== Sì
 
 You must restart the game for all settings to take effect.
-== E' necessario riavviare il gioco per rendere effettive le modifiche.
+== Devi riavviare il gioco per rendere effettive le modifiche.
 
 Your skin
-== La tua Skin
+== La tua skin
 
 no limit
-== senza limite
+== senza limiti
 
 ##### needs translation #####
 
-%d Bytes
-== 
-
-%s wins!
-== 
-
--Page %d-
-== 
-
-Add
-== 
-
-Add Friend
-== 
-
-Are you sure that you want to remove the player from your friends list?
-== 
-
-Change settings
-== 
-
-Clan
-== 
-
-Client
-== 
-
-Count players only
-== 
-
-Country
-== 
-
-Crc:
-== 
-
-Created:
-== 
-
-Demo details
-== 
-
-Free-View
-== 
-
-Friends
-== 
-
-Kick player
-== 
-
-Length:
-== 
-
-Map:
-== 
-
-Max Screenshots
-== 
-
-Max demos
-== 
-
-Move player to spectators
-== 
-
-Netversion:
-== 
-
-New name:
-== 
-
-Play background music
-== 
-
-Player options
-== 
-
-Quit anyway?
-== 
-
-REC %3d:%02d
-== 
-
-Remove
-== 
-
-Remove friend
-== 
-
-Rename
-== 
-
-Rename demo
-== 
-
-Server address:
-== 
-
-Server filter
-== 
-
-Show friends
-== 
-
-Show ingame HUD
-== 
-
-Size:
-== 
-
-Sound error
-== 
-
-Spectate next
-== 
-
-Spectate previous
-== 
-
-Spectator mode
-== 
-
-Sudden Death
-== 
-
-There's an unsaved map in the editor, you might want to save it before you quit the game.
-== 
-
-Type:
-== 
-
-Unable to rename the demo
-== 
-
-Use team colors for name plates
-== 
-
-Version:
-== 
-
-Vote command:
-== 
-
-Vote description:
-== 
-
-Warmup
-== 
-
 ##### old translations #####
 
+
+== ## translated strings #####
+
diff --git a/data/languages/norwegian.txt b/data/languages/norwegian.txt
index 2e944ea5..db362e93 100644
--- a/data/languages/norwegian.txt
+++ b/data/languages/norwegian.txt
@@ -145,6 +145,9 @@ Delete demo
 Demo details
 == Demo detaljer
 
+Demofile: %s
+== Demo fil: %s
+
 Demos
 == Demoer
 
@@ -388,9 +391,15 @@ Pistol
 Play
 == Spill
 
+Play background music
+== Spill bakrunds musikk
+
 Player
 == Spiller
 
+Player country:
+== spiller landet
+
 Player options
 == Spillerinstillinger
 
@@ -508,7 +517,7 @@ Shotgun
 Show chat
 == Vis samtale
 
-Show friends
+Show friends only
 == Vis venner
 
 Show ingame HUD
@@ -538,6 +547,12 @@ Sound volume
 Spectate
 == Se på
 
+Spectate next
+== Se på neste
+
+Spectate previous
+== Se på forige
+
 Spectator mode
 == Tilhenger modus
 
@@ -545,7 +560,7 @@ Spectators
 == Tilskuere:
 
 Standard gametype
-== Standard spilltyper
+== Standard spilletyper
 
 Standard map
 == Standard bane
@@ -553,6 +568,9 @@ Standard map
 Stop record
 == Stopp inspilling
 
+Strict gametype filter
+== spilltype-filter
+
 Sudden Death
 == Brå død
 
@@ -657,14 +675,5 @@ no limit
 
 ##### needs translation #####
 
-Play background music
-== 
-
-Spectate next
-== 
-
-Spectate previous
-== 
-
 ##### old translations #####
 
diff --git a/data/languages/polish.txt b/data/languages/polish.txt
index fbc7c0da..e955e1e6 100644
--- a/data/languages/polish.txt
+++ b/data/languages/polish.txt
@@ -1,15 +1,45 @@
 
 ##### translated strings #####
 
+%d Bytes
+== %d Bajtów
+
 %d of %d servers, %d players
 == %d z %d serwerów, %d graczy
 
+%d%% loaded
+== Załadowano %d%%
+
 %ds left
-== Jeszcze %d
+== Pozostało %ds
+
+%i minute left
+== Pozostało minut: %i
+
+%i minutes left
+== Pozostało minut: %i
+
+%i second left
+== Pozostało sekund: %i
+
+%i seconds left
+== Pozostało sekund: %i
+
+%s wins!
+== Wygrał %d!
+
+-Page %d-
+== -Strona %d-
 
 Abort
 == Anuluj
 
+Add
+== Dodaj
+
+Add Friend
+== Dodaj znajomego
+
 Address
 == Adres
 
@@ -20,19 +50,31 @@ Alpha
 == Alfa
 
 Always show name plates
-== Zawsze pokazuj nicki
+== Zawsze pokazuj nicki graczy
+
+Are you sure that you want to delete the demo?
+== Czy na pewno chcesz usunąć to demo?
 
 Are you sure that you want to quit?
 == Czy na pewno chcesz opuścić grę?
 
+Are you sure that you want to remove the player from your friends list?
+== Czy na pewno chcesz usunąć tego gracza z listy znajomych?
+
 As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server.
-== To pierwsze uruchomienie gry, podaj swój nick poniżej. Zalecane jest też sprawdzenie ustawień i dopasowanie ich do siebie przed dołączeniem do gry.
+== To pierwsze uruchomienie gry, podaj swój nick poniżej. Zalecane jest też sprawdzenie ustawień i dopasowanie ich do swoich upodobań przed dołączeniem do gry.
+
+Automatically record demos
+== Automatycznie rejestruj dema
+
+Automatically take game over screenshot
+== Automatycznie zrób zrzut ekranu końca gry
 
 Blue team
-== Drużyna niebieskich
+== Niebiescy
 
 Blue team wins!
-== Drużyna niebieskich wygrała!
+== Niebiescy wygrali!
 
 Body
 == Ciało
@@ -40,9 +82,18 @@ Body
 Call vote
 == Głosowanie
 
+Change settings
+== Zmień ustawienia
+
 Chat
 == Chat
 
+Clan
+== Klan
+
+Client
+== Klient
+
 Close
 == Zamknij
 
@@ -64,6 +115,18 @@ Console
 Controls
 == Sterowanie
 
+Count players only
+== Licz tylko graczy
+
+Country
+== Kraj
+
+Crc:
+== Crc:
+
+Created:
+== Utworzono:
+
 Current
 == Aktualnie
 
@@ -71,11 +134,20 @@ Current version: %s
 == Aktualna wersja: %s
 
 Custom colors
-== Własne kolory
+== Dostosuj kolory
 
 Delete
 == Usuń
 
+Delete demo
+== Usuń demo
+
+Demo details
+== Szczegóły dema
+
+Demofile: %s
+== Plik dema: %s
+
 Demos
 == Dema
 
@@ -98,7 +170,7 @@ Dynamic Camera
 == Dynamiczna kamera
 
 Emoticon
-== Emotikonka
+== Emotikona
 
 Enter
 == Wejdź
@@ -107,10 +179,10 @@ Error
 == Błąd
 
 Error loading demo
-== Błąd przy ładowaniu demo
+== Błąd przy ładowaniu dema
 
 FSAA samples
-== próbki FSAA (anti-aliasing)
+== Próbkowanie FSAA (Antyaliasing)
 
 Favorite
 == Ulubiony
@@ -127,9 +199,18 @@ Filter
 Fire
 == Strzał
 
+Folder
+== Katalog
+
 Force vote
 == Wymuś głosowanie
 
+Free-View
+== Wolna kamera
+
+Friends
+== Znajomi
+
 Fullscreen
 == Pełny ekran
 
@@ -137,7 +218,7 @@ Game
 == Gra
 
 Game info
-== Informacje o grze
+== Info o grze
 
 Game over
 == Koniec gry
@@ -164,7 +245,7 @@ Has people playing
 == Nie pokazuj pustych
 
 High Detail
-== Wysoka jakość obrazu
+== Wysoka jakość
 
 Hook
 == Hak
@@ -181,26 +262,35 @@ Info
 Internet
 == Internet
 
+Invalid Demo
+== Nieprawidłowe demo
+
 Join blue
-== Dołącz do niebieskich
+== Do niebieskich
 
 Join game
 == Dołącz
 
 Join red
-== Dołącz do czerwonych
+== Do czerwonych
 
 Jump
 == Skok
 
+Kick player
+== Wyrzuć gracza
+
 LAN
 == LAN
 
 Language
 == Język
 
+Length:
+== Długość:
+
 Lht.
-== Jasn.
+== Jasność
 
 Loading
 == Ładowanie
@@ -211,6 +301,15 @@ MOTD
 Map
 == Mapa
 
+Map:
+== Mapa:
+
+Max Screenshots
+== Maksymalnie screenshotów
+
+Max demos
+== Maksymalnie dem
+
 Maximum ping:
 == Maksymalny ping:
 
@@ -221,22 +320,34 @@ Mouse sens.
 == Czułość myszy
 
 Move left
-== Lewo
+== W lewo
+
+Move player to spectators
+== Przesuń gracza do obserwatorów
 
 Move right
-== Prawo
+== W prawo
 
 Movement
 == Ruch
 
 Mute when not active
-== Wycisz kiedy gra nieaktywna
+== Wycisz, kiedy gra nieaktywna
 
 Name
-== Nick
+== Nazwa
+
+Name plates size
+== Wielkość wyświetlanych nicków
+
+Netversion:
+== Wersja sieciowa:
+
+New name:
+== Nowa nazwa:
 
 News
-== News
+== Wiadomości
 
 Next weapon
 == Następna broń
@@ -257,11 +368,14 @@ No servers match your filter criteria
 == Nie znaleziono serwerów spełniających twoje kryteria
 
 Ok
-== OK
+== Ok
 
 Open
 == Otwórz
 
+Parent Folder
+== Nadrzędny katalog
+
 Password
 == Hasło
 
@@ -275,16 +389,25 @@ Pistol
 == Pistolet
 
 Play
-== Start
+== Graj
+
+Play background music
+== Odtwarzaj muzykę w tle
 
 Player
 == Gracz
 
+Player country:
+== Narodowość:
+
+Player options
+== Opcje gracza
+
 Players
 == Gracze
 
 Please balance teams!
-== Konieczne wyrównanie drużyn!
+== Proszę wyrównać szanse drużyn!
 
 Prev. weapon
 == Poprzednia broń
@@ -298,11 +421,23 @@ Quick search:
 Quit
 == Wyjście
 
+Quit anyway?
+== Wyjść mimo wszystko?
+
+REC %3d:%02d
+== REC %3d:%02d
+
+Reason:
+== Powód:
+
+Record demo
+== Nagraj demo
+
 Red team
-== Drużyna czerwonych
+== Czerwoni
 
 Red team wins!
-== Drużyna czerwonych wygrała!
+== Czerwoni wygrali!
 
 Refresh
 == Odśwież
@@ -313,6 +448,18 @@ Refreshing master servers
 Remote console
 == Zdalna konsola
 
+Remove
+== Usuń
+
+Remove friend
+== Usuń znajomego
+
+Rename
+== Zmień nazwę
+
+Rename demo
+== Zmień nazwę dema
+
 Reset filter
 == Domyślne filtry
 
@@ -326,10 +473,10 @@ Round
 == Runda
 
 Sample rate
-== Próbkowanie
+== Częstotliwość próbkowania
 
 Sat.
-== Nasyc.
+== Nasycenie
 
 Score
 == Wynik
@@ -346,11 +493,17 @@ Scoreboard
 Screenshot
 == Screenshot
 
+Server address:
+== Adres serwera:
+
 Server details
 == Szczegóły serwera
 
+Server filter
+== Filtr serwerów
+
 Server info
-== Server info
+== Info serwera
 
 Server not full
 == Nie pokazuj pełnych
@@ -364,24 +517,45 @@ Shotgun
 Show chat
 == Pokaż chat
 
+Show friends only
+== Pokaż tylko znajomych
+
+Show ingame HUD
+== Pokaż wewnętrzny HUD
+
 Show name plates
 == Pokaż nicki
 
 Show only supported
 == Pokaż tylko wspierane
 
+Size:
+== Rozmiar:
+
 Skins
 == Motywy
 
 Sound
 == Dźwięk
 
+Sound error
+== Błąd dźwięku
+
 Sound volume
 == Głośność
 
 Spectate
 == Obserwuj
 
+Spectate next
+== Obserwuj następnego
+
+Spectate previous
+== Obserwuj poprzedniego
+
+Spectator mode
+== Tryb obserwatora
+
 Spectators
 == Obserwatorzy
 
@@ -391,6 +565,12 @@ Standard gametype
 Standard map
 == Standardowa mapa
 
+Stop record
+== Zakończ REC
+
+Strict gametype filter
+== Szczegółowy filtr typu gry
+
 Sudden Death
 == Nagła śmierć
 
@@ -404,35 +584,65 @@ Team chat
 == Chat drużynowy
 
 Teeworlds %s is out! Download it at www.teeworlds.com!
-== Wydano Teeworlds %s! Ściągnij je z www.teeworlds.com!
+== Nowa wersja Teeworlds %s jest dostępna! Do ściągnięcia z www.teeworlds.com!
 
 Texture Compression
 == Kompresja tekstur
 
+The audio device couldn't be initialised.
+== Urządzenie dźwiękowe nie mogło zostać zainicjowane
+
 The server is running a non-standard tuning on a pure game type.
-== Ten serwer nie korzysta ze standardowych ustawień.
+== Ten serwer korzysta z niestandardowych ustawień.
+
+There's an unsaved map in the editor, you might want to save it before you quit the game.
+== W edytorze jest niezapisana mapa! Zapisz ją, jeśli nie chcesz
 
 Time limit
 == Limit czasu
 
+Time limit: %d min
+== Limit czasu: %d min
+
 Try again
 == Ponów próbę
 
 Type
 == Typ
 
+Type:
+== Typ:
+
 UI Color
 == Kolor menu
 
+Unable to delete the demo
+== Nie można usunąć dema
+
+Unable to rename the demo
+== Nie można zmienić nazwy dema
+
 Use sounds
 == Włącz dźwięki
 
+Use team colors for name plates
+== Użyj koloru drużyn dla wyświetlania nicków
+
 V-Sync
-== V-Sync
+== Synchronizacja pionowa (V-Sync)
 
 Version
 == Wersja
 
+Version:
+== Wersja:
+
+Vote command:
+== Polecenie głosowania:
+
+Vote description:
+== Opis głosowania:
+
 Vote no
 == Nie
 
@@ -455,216 +665,18 @@ Yes
 == Tak
 
 You must restart the game for all settings to take effect.
-== Gra musi zostać uruchomiona ponownie, żeby nowe ustawienia weszły w życie.
+== Uruchom ponownie grę, aby użyć nowych ustawieńn
 
 Your skin
 == Twój wygląd
 
-##### needs translation #####
-
-%d Bytes
-== 
-
-%d%% loaded
-== 
-
-%i minute left
-== 
-
-%i minutes left
-== 
-
-%i second left
-== 
-
-%i seconds left
-== 
-
-%s wins!
-== 
-
--Page %d-
-== 
-
-Add
-== 
-
-Add Friend
-== 
-
-Are you sure that you want to delete the demo?
-== 
-
-Are you sure that you want to remove the player from your friends list?
-== 
-
-Automatically record demos
-== 
-
-Automatically take game over screenshot
-== 
-
-Change settings
-== 
-
-Clan
-== 
-
-Client
-== 
-
-Count players only
-== 
-
-Country
-== 
-
-Crc:
-== 
-
-Created:
-== 
-
-Delete demo
-== 
-
-Demo details
-== 
-
-Folder
-== 
-
-Free-View
-== 
-
-Friends
-== 
-
-Invalid Demo
-== 
-
-Kick player
-== 
-
-Length:
-== 
-
-Map:
-== 
-
-Max Screenshots
-== 
-
-Max demos
-== 
-
-Move player to spectators
-== 
-
-Name plates size
-== 
-
-Netversion:
-== 
-
-New name:
-== 
-
-Parent Folder
-== 
-
-Play background music
-== 
-
-Player options
-== 
-
-Quit anyway?
-== 
-
-REC %3d:%02d
-== 
-
-Reason:
-== 
-
-Record demo
-== 
-
-Remove
-== 
-
-Remove friend
-== 
-
-Rename
-== 
-
-Rename demo
-== 
-
-Server address:
-== 
-
-Server filter
-== 
-
-Show friends
-== 
-
-Show ingame HUD
-== 
-
-Size:
-== 
-
-Sound error
-== 
-
-Spectate next
-== 
-
-Spectate previous
-== 
-
-Spectator mode
-== 
-
-Stop record
-== 
-
-The audio device couldn't be initialised.
-== 
-
-There's an unsaved map in the editor, you might want to save it before you quit the game.
-== 
-
-Time limit: %d min
-== 
-
-Type:
-== 
-
-Unable to delete the demo
-== 
-
-Unable to rename the demo
-== 
-
-Use team colors for name plates
-== 
-
-Version:
-== 
+no limit
+== bez limitu
 
-Vote command:
-== 
+##### needs translation #####
 
-Vote description:
-== 
+##### old translations #####
 
-no limit
+utracić swojej pracy!
 == 
 
-##### old translations #####
-
diff --git a/data/languages/portuguese.txt b/data/languages/portuguese.txt
index 08e2e68c..e3c7c6cb 100644
--- a/data/languages/portuguese.txt
+++ b/data/languages/portuguese.txt
@@ -8,7 +8,7 @@
 == %d de %d servidores, %d jogadores
 
 %d%% loaded
-== %d%% carregado
+== %d%% A Carregar
 
 %ds left
 == faltam %ds
@@ -26,7 +26,7 @@
 == faltam %i segundos
 
 %s wins!
-== %s vence!
+== %s ganhou!
 
 -Page %d-
 == -Página %d-
@@ -50,28 +50,31 @@ Alpha
 == Alpha
 
 Always show name plates
-== Sempre mostrar apelidos
+== Mostrar sempre os nicks
 
 Are you sure that you want to delete the demo?
-== Tem certeza que deseja deletar o demo?
+== Tens a certeza que queres Eliminar a demo?
 
 Are you sure that you want to quit?
-== Você tem certeza que deseja sair?
+== Queres mesmo sair?
 
 Are you sure that you want to remove the player from your friends list?
-== Tem certeza que deseja remover o jogador da sua lista de amigos?
+== Queres mesmo apaga-lo da tua lista de amigos?
+
+As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server.
+== Olá! Pelos vistos é a primeira vez que inicias o jogo, por isso escolhe um Nick Name para ti!
 
 Automatically record demos
-== Gravar demos automaticamente
+== Gravar demos Automaticamente
 
 Automatically take game over screenshot
-== Capturar tela final de jogo automaticamente
+== Tirar screenshot's Automaticamente
 
 Blue team
-== Time azul
+== Equipa azul
 
 Blue team wins!
-== Time azul vence!
+== A Equipa azul ganhou!
 
 Body
 == Corpo
@@ -101,19 +104,19 @@ Connect
 == Conectar
 
 Connecting to
-== Conectando a
+== A Conectar a
 
 Connection Problems...
-== Problemas de Conexão...
+== Problemas na conecção!
 
 Console
 == Console
 
 Controls
-== Controles
+== Controlos
 
 Count players only
-== Contar apenas jogadores
+== Só contar jogadores
 
 Country
 == País
@@ -125,22 +128,25 @@ Created:
 == Criado:
 
 Current
-== Atualmente
+== Actualmente
 
 Current version: %s
-== Versão atual : %s
+== Versão actual : %s
 
 Custom colors
 == Cores personalizadas
 
 Delete
-== Deletar
+== Eliminar
 
 Delete demo
-== Deletar demo
+== Eliminar demo
 
 Demo details
-== Detalhes do demo
+== Detalhes da demo
+
+Demofile: %s
+== Demo: %s
 
 Demos
 == Demos
@@ -155,7 +161,7 @@ Display Modes
 == Modos de exibição
 
 Downloading map
-== Baixando mapa
+== A fazer download do mapa...
 
 Draw!
 == Empate!
@@ -173,7 +179,7 @@ Error
 == Erro
 
 Error loading demo
-== Erro ao carregar demo
+== Erro a carregar a demo
 
 FSAA samples
 == Amostras FSAA
@@ -191,7 +197,7 @@ Filter
 == Filtro
 
 Fire
-== Atirar
+== Disparar
 
 Folder
 == Pasta
@@ -200,13 +206,13 @@ Force vote
 == Forçar
 
 Free-View
-== Visão Livre
+== Vista Livre
 
 Friends
 == Amigos
 
 Fullscreen
-== Tela cheia
+== Fullscreen (Ecrã inteiro)
 
 Game
 == Jogo
@@ -236,7 +242,7 @@ Hammer
 == Martelo
 
 Has people playing
-== Tem gente jogando
+== Há gente a jogar
 
 High Detail
 == Mais detalhes (HD)
@@ -245,7 +251,7 @@ Hook
 == Gancho
 
 Host address
-== Endereço do host
+== Direcção do Host
 
 Hue
 == Matiz
@@ -257,37 +263,37 @@ Internet
 == Internet
 
 Invalid Demo
-== Demo inválido
+== Demo inválida
 
 Join blue
-== Azul
+== Juntar - Azuis
 
 Join game
-== Entre no jogo
+== Entrar no jogo
 
 Join red
-== Vermelho
+== J. Vermelhos
 
 Jump
-== Pular
+== Saltar
 
 Kick player
-== Kickar jogador
+== Expulsar jogador
 
 LAN
 == LAN
 
 Language
-== Idioma
+== Língua
 
 Length:
-== Duração:
+== Longitude:
 
 Lht.
 == Luz
 
 Loading
-== Carregando
+== Aguarda por favor
 
 MOTD
 == MOTD
@@ -302,7 +308,7 @@ Max Screenshots
 == Máx. de Capturas de Tela
 
 Max demos
-== Máx. de demos
+== Demos maximas
 
 Maximum ping:
 == Ping máximo:
@@ -311,13 +317,13 @@ Miscellaneous
 == Diversos
 
 Mouse sens.
-== Sens. do mouse
+== Sens. do rato
 
 Move left
 == Esquerda
 
 Move player to spectators
-== Mover jogador para observadores
+== Juntar jogador aos espectadores
 
 Move right
 == Direita
@@ -326,13 +332,13 @@ Movement
 == Movimento
 
 Mute when not active
-== Silenciar quando inativo
+== Silencíar os jogadores inactivos
 
 Name
 == Nome
 
 Name plates size
-== Tamanho dos apelidos
+== Caracteres maximos nos nicks
 
 Netversion:
 == Netversion
@@ -347,34 +353,34 @@ Next weapon
 == Próxima arma
 
 Nickname
-== Apelido
+== Nick
 
 No
 == Não
 
 No password
-== Sem senha
+== Sem password
 
 No servers found
-== Nenhum servidor encontrado
+== Nenhum servidor encontrado.
 
 No servers match your filter criteria
-== Nenhum servidor corresponde aos critérios do filtro
+== Não há servidores que correspondam às definições de procura.
 
 Ok
-== Ok
+== Aceitar
 
 Open
 == Abrir
 
 Parent Folder
-== Diretório pai
+== Pasta superior
 
 Password
-== Senha
+== Password
 
 Password incorrect
-== Senha incorreta
+== Password errada!
 
 Ping
 == Ping
@@ -383,11 +389,17 @@ Pistol
 == Pistola
 
 Play
-== Assistir
+== Ver
+
+Play background music
+== Tocar a música de fundo
 
 Player
 == Jogador
 
+Player country:
+== País do jogador
+
 Player options
 == Opções do jogador
 
@@ -395,7 +407,7 @@ Players
 == Jogadores
 
 Please balance teams!
-== Por favor, balanceie os times!
+== Equilibrem as equipas!
 
 Prev. weapon
 == Arma anterior
@@ -404,13 +416,13 @@ Quality Textures
 == Texturas de Qualidade
 
 Quick search:
-== Pesquisa rápida:
+== Busca rápida:
 
 Quit
 == Sair
 
 Quit anyway?
-== Sair mesmo assim?
+== Sair na mesma?
 
 REC %3d:%02d
 == REC %3d:%02d
@@ -419,28 +431,28 @@ Reason:
 == Motivo:
 
 Record demo
-== Gravar demo
+== Gravar uma demo
 
 Red team
-== Time vermelho
+== Equipa vermelha
 
 Red team wins!
-== Time vermelho vence!
+== Ganhou a equipa vermelha!
 
 Refresh
-== Atualizar
+== Actualizar
 
 Refreshing master servers
-== Atualizando servidores mestres
+== A Actualizar servidores
 
 Remote console
-== Console remoto
+== Remote console
 
 Remove
-== Deletar
+== Eliminar
 
 Remove friend
-== Deletar amigo
+== Deixar de ser amigo
 
 Rename
 == Renomear
@@ -449,19 +461,19 @@ Rename demo
 == Renomear demo
 
 Reset filter
-== Resetar filtro
+== Reiniciar Fitro
 
 Reset to defaults
-== Resetar para padrão
+== Por como defeito
 
 Rifle
 == Laser
 
 Round
-== Rodada
+== Ronda
 
 Sample rate
-== Taxa de som
+== Frequencia de rate
 
 Sat.
 == Sat.
@@ -470,52 +482,52 @@ Score
 == Pontos
 
 Score board
-== Placar
+== Tabela de Pontos
 
 Score limit
-== Placar máx.
+== Pontuação Máx.
 
 Scoreboard
-== Placar
+== Pontuação
 
 Screenshot
-== Captura de tela
+== Screenshot
 
 Server address:
-== Endereço do server:
+== Direção do servidor:
 
 Server details
-== Detalhes do server
+== Detalhes do servidor
 
 Server filter
-== Filtro de server
+== Filtro de servidores
 
 Server info
-== Info do server
+== Info de servidor
 
 Server not full
-== Servidor não cheio
+== Não está cheio
 
 Settings
-== Configurações
+== Config.
 
 Shotgun
 == Espingarda
 
 Show chat
-== Mostrar conversa
+== Mostrar chat
 
-Show friends
+Show friends only
 == Mostrar amigos
 
 Show ingame HUD
 == Mostrar HUD do jogo
 
 Show name plates
-== Mostrar apelidos
+== Mostrar nick's
 
 Show only supported
-== Mostrar apenas suportados
+== Mostrar apenas suportado
 
 Size:
 == Tamanho:
@@ -527,55 +539,64 @@ Sound
 == Som
 
 Sound error
-== Erro de som
+== Erro no som!
 
 Sound volume
-== Volume do som
+== Volume
 
 Spectate
 == Observar
 
+Spectate next
+== Observar próximo
+
+Spectate previous
+== Observar anterior
+
 Spectator mode
-== Modo Observador
+== Modo espectador
 
 Spectators
-== Observadores
+== Espectadores
 
 Standard gametype
-== Tipo de jogo padrão
+== Tipo de jogo normal
 
 Standard map
-== Mapa padrão
+== Mapa normal
 
 Stop record
 == Parar de gravar
 
+Strict gametype filter
+== Tipo de jogo especifico
+
 Sudden Death
-== Morte Súbita
+== Morte súbita
 
 Switch weapon on pickup
-== Trocar arma ao pegar
+== Mudar de arma ao agarrar
 
 Team
-== Time
+== Equipa
 
 Team chat
-== Conv. de equipe
+== Chat de equipa
 
 Teeworlds %s is out! Download it at www.teeworlds.com!
-== Teeworlds %s foi lançado! Baixe-o em www.teeworlds.com!
+== Teeworlds %s já saiu! Download em www.teeworlds.com!
 
 Texture Compression
 == Compressão de Textura
 
 The audio device couldn't be initialised.
-== O aparelho de áudio não pode ser inicializado.
+== O dispositivo de som não pode ser iniciado.
 
 The server is running a non-standard tuning on a pure game type.
-== O servidor está rodando uma modificação não padrão em um tipo de jogo puro.
+== O servidor está a usar um tipo de jogo não oficial.
 
 There's an unsaved map in the editor, you might want to save it before you quit the game.
-== Existe um mapa não salvo no editor, você pode querer salvá-lo antes de sair do jogo.
+== O mapa que editas-te não foi gravado. Queres gravar antes de sair?
 
 Time limit
 == Tempo máx.
@@ -584,7 +605,7 @@ Time limit: %d min
 == Limite de tempo: %d min
 
 Try again
-== Tente de novo
+== Tenta outra vez
 
 Type
 == Tipo
@@ -596,16 +617,16 @@ UI Color
 == Cor do menu
 
 Unable to delete the demo
-== Incapaz de deletar demo
+== Não podes eliminar a demo
 
 Unable to rename the demo
-== Incapaz de renomear demo
+== Impossivel de renomear a demo
 
 Use sounds
 == Usar sons
 
 Use team colors for name plates
-== Usar cores do time para apelidos
+== Usar cores dos nicks com a cor da equipa
 
 V-Sync
 == V-Sync
@@ -620,7 +641,7 @@ Vote command:
 == Comando:
 
 Vote description:
-== Descrição da votação:
+== Descrição de votação:
 
 Vote no
 == Votar não
@@ -629,7 +650,7 @@ Vote yes
 == Votar sim
 
 Voting
-== Votação
+== A votar
 
 Warmup
 == Aquecimento
@@ -644,27 +665,15 @@ Yes
 == Sim
 
 You must restart the game for all settings to take effect.
-== Você deve reiniciar o jogo para que todas as alterações tenham efeito.
+== Para que as configurações sejam efectuadas deves Reiniciar o jogo.
 
 Your skin
-== Sua skin
+== A tua skin
 
 no limit
 == sem limite
 
 ##### needs translation #####
 
-As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server.
-== 
-
-Play background music
-== 
-
-Spectate next
-== 
-
-Spectate previous
-== 
-
 ##### old translations #####
 
diff --git a/data/languages/romanian.txt b/data/languages/romanian.txt
index ca2aff9a..e38faadb 100644
--- a/data/languages/romanian.txt
+++ b/data/languages/romanian.txt
@@ -145,6 +145,9 @@ Delete demo
 Demo details
 == Detalii demo
 
+Demofile: %s
+== Fișier demo: %s
+
 Demos
 == Demo
 
@@ -329,7 +332,7 @@ Movement
 == Mișcare
 
 Mute when not active
-== Mută la inactivate
+== Opreşte sunetul la inactivate
 
 Name
 == Nume
@@ -388,9 +391,15 @@ Pistol
 Play
 == Redă
 
+Play background music
+== Redă muzică în fundal
+
 Player
 == Jucător
 
+Player country:
+== Țara jucătorului:
+
 Player options
 == Opțiuni jucător
 
@@ -508,7 +517,7 @@ Shotgun
 Show chat
 == Afișare chat
 
-Show friends
+Show friends only
 == Arată prietenii
 
 Show ingame HUD
@@ -538,6 +547,12 @@ Sound volume
 Spectate
 == Spectator
 
+Spectate next
+== Vezi următorul
+
+Spectate previous
+== Vezi anteriorul
+
 Spectator mode
 == Mod spectator
 
@@ -553,6 +568,9 @@ Standard map
 Stop record
 == Stop înreg.
 
+Strict gametype filter
+== Filtru strict tip joc
+
 Sudden Death
 == Moarte subită
 
@@ -657,14 +675,5 @@ no limit
 
 ##### needs translation #####
 
-Play background music
-== 
-
-Spectate next
-== 
-
-Spectate previous
-== 
-
 ##### old translations #####
 
diff --git a/data/languages/russian.txt b/data/languages/russian.txt
index dd8a6ec8..329f4a69 100644
--- a/data/languages/russian.txt
+++ b/data/languages/russian.txt
@@ -2,7 +2,7 @@
 ##### translated strings #####
 
 %d Bytes
-== %d Байтов
+== %d байт
 
 %d of %d servers, %d players
 == %d из %d серверов, %d игроков
@@ -14,19 +14,19 @@
 == осталось %d сек.
 
 %i minute left
-== %i минута осталась
+== Осталась %i минута!
 
 %i minutes left
-== %i минут осталось
+== Осталось %i минут!
 
 %i second left
-== %i секунда осталась
+== Осталась %i секунда!
 
 %i seconds left
-== %i секунд осталось
+== Осталось %i секунд!
 
 %s wins!
-== %s - победа!
+== %s победил!
 
 -Page %d-
 == -Страница %d-
@@ -47,34 +47,34 @@ All
 == Все
 
 Alpha
-== Прозрачность
+== Прозрачн.
 
 Always show name plates
-== Всегда показывать имена игроков
+== Всегда показывать ники игроков
 
 Are you sure that you want to delete the demo?
-== Вы действительно хотите удалить это демо?
+== Вы уверены, что хотите удалить демо?
 
 Are you sure that you want to quit?
-== Вы действительно хотите выйти?
+== Вы действительно желаете выйти?
 
 Are you sure that you want to remove the player from your friends list?
-== Вы действительно хотите удалить этого игрока из списка друзей?
+== Вы уверены, что хотите удалить игрока из друзей?
 
 As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server.
-== Вы запустили игру первый раз, пожалуйста, введите ваш никнейм. Мы рекомендуем вам настроить игру.
+== Так как это ваш первый запуск игры, пожалуйста, введите свой ник в поле ниже. Также рекоммендуется проверить настройки игры и поменять некоторые из них перед тем, как начать играть.
 
 Automatically record demos
-== Записывать демо автоматически
+== Автоматически записывать демо
 
 Automatically take game over screenshot
-== Автоматически снимать скриншоты в конце игры
+== Делать снимок результатов игры
 
 Blue team
-== Синяя команда
+== Синие
 
 Blue team wins!
-== Синяя команда победила!
+== Синие победили!
 
 Body
 == Тело
@@ -83,7 +83,7 @@ Call vote
 == Голосовать
 
 Change settings
-== Настройки
+== Изменить настройки
 
 Chat
 == Чат
@@ -95,19 +95,19 @@ Client
 == Клиент
 
 Close
-== Закрыть
+== Выход
 
 Compatible version
 == Совместимая версия
 
 Connect
-== Играть
+== Подключиться
 
 Connecting to
-== Соединяемся с
+== Подключение к
 
 Connection Problems...
-== Проблемы с соединением...
+== Проблемы со связью...
 
 Console
 == Консоль
@@ -119,10 +119,10 @@ Count players only
 == Считать только игроков
 
 Country
-== Страна
+== Флаг вашей страны
 
 Crc:
-== Crc-сумма:
+== Crc:
 
 Created:
 == Создан:
@@ -134,7 +134,7 @@ Current version: %s
 == Текущая версия: %s
 
 Custom colors
-== Произвольный цвет
+== Свои цвета
 
 Delete
 == Удалить
@@ -143,79 +143,82 @@ Delete demo
 == Удалить демо
 
 Demo details
-== Подробности
+== Детали демо
+
+Demofile: %s
+== Демо: %s
 
 Demos
 == Демо
 
 Disconnect
-== Уйти
+== Отключить
 
 Disconnected
-== Отсоединен
+== Отключено
 
 Display Modes
-== Режим отображения
+== Разрешение экрана
 
 Downloading map
-== Загрузка карты
+== Скачивание карты
 
 Draw!
 == Ничья!
 
 Dynamic Camera
-== Динамичная Камера
+== Динамическая камера
 
 Emoticon
 == Эмоции
 
 Enter
-== Войти
+== Вход
 
 Error
 == Ошибка
 
 Error loading demo
-== Ошибка при загрузке демо
+== ошибка при загрузке демо
 
 FSAA samples
-== FSAA сэмплы
+== Сэмплов FSAA
 
 Favorite
-== Избранное
+== Избранный
 
 Favorites
-== Избранное
+== Избранные
 
 Feet
-== Нога
+== Ноги
 
 Filter
 == Фильтр
 
 Fire
-== Стрельба
+== Выстрел
 
 Folder
 == Папка
 
 Force vote
-== Форсировать голосование
+== Форсировать
 
 Free-View
-== Свободный просмотр
+== Свободный обзор
 
 Friends
-== Приятели
+== Друзья
 
 Fullscreen
-== На весь экран
+== Полноэкранный режим
 
 Game
 == Игра
 
 Game info
-== Информация о игре
+== Инфо об игре
 
 Game over
 == Игра окончена
@@ -224,7 +227,7 @@ Game type
 == Тип игры
 
 Game types:
-== Типы игры:
+== Тип игры:
 
 General
 == Основные
@@ -233,19 +236,19 @@ Graphics
 == Графика
 
 Grenade
-== Граната
+== Гранатомёт
 
 Hammer
-== Молоток
+== Молот
 
 Has people playing
-== Есть игроки на сервере
+== Не пустой сервер
 
 High Detail
-== Высокая Детализация
+== Высокая детализация
 
 Hook
-== Цепь
+== Крюк
 
 Host address
 == Адрес сервера
@@ -260,22 +263,22 @@ Internet
 == Интернет
 
 Invalid Demo
-== Невалидное демо
+== Недопустимое демо
 
 Join blue
-== К синим
+== За синих
 
 Join game
-== Войти в игру
+== Играть
 
 Join red
-== К красным
+== За красных
 
 Jump
 == Прыжок
 
 Kick player
-== Кикнуть игрока
+== Забанить игрока
 
 LAN
 == LAN
@@ -284,7 +287,7 @@ Language
 == Язык
 
 Length:
-== Длина:
+== Длина
 
 Lht.
 == Яркость
@@ -302,10 +305,10 @@ Map:
 == Карта:
 
 Max Screenshots
-== Максимум Скриншотов
+== Максимальное количество снимков
 
 Max demos
-== Максимум Демо
+== Максимальное количество демо
 
 Maximum ping:
 == Макс. пинг:
@@ -314,40 +317,40 @@ Miscellaneous
 == Дополнительно
 
 Mouse sens.
-== Чувст. мыши
+== Чувств. мыши
 
 Move left
-== Влево
+== Шаг влево
 
 Move player to spectators
-== Стать наблюдателем
+== Сделать наблюдателем
 
 Move right
-== Вправо
+== Шаг вправо
 
 Movement
-== Движение
+== Перемещение
 
 Mute when not active
-== Выключить звук когда игра неактивна
+== Глушить звуки, когда игра неактивна
 
 Name
 == Имя
 
 Name plates size
-== Размер имён над игроками
+== Размер
 
 Netversion:
 == Версия:
 
 New name:
-== Новое имя:
+== Новое имя
 
 News
 == Новости
 
 Next weapon
-== Следующее оружие
+== След. оружие
 
 Nickname
 == Ник
@@ -362,22 +365,22 @@ No servers found
 == Сервера не найдены
 
 No servers match your filter criteria
-== Нет серверов, подходящих под Ваш фильтр
+== Нет серверов, подходящих под ваш фильтр
 
 Ok
-== Ok
+== ОК
 
 Open
 == Открыть
 
 Parent Folder
-== Корневая папка
+== Родительский каталог
 
 Password
 == Пароль
 
 Password incorrect
-== Неверный пароль
+== Пароль
 
 Ping
 == Пинг
@@ -386,37 +389,43 @@ Pistol
 == Пистолет
 
 Play
-== Воспроизвести
+== Просмотр
+
+Play background music
+== Играть фоновую музыку
 
 Player
 == Игрок
 
+Player country:
+== Страна:
+
 Player options
-== Настройки игрока
+== Опции игрока
 
 Players
 == Игроки
 
 Please balance teams!
-== Пожалуйста отбалансируйте команды!
+== Сбалансируйте команды!
 
 Prev. weapon
 == Пред. оружие
 
 Quality Textures
-== Качественные Текстуры
+== Качественные текстуры
 
 Quick search:
-== Быстрый поиск
+== Быстрый поиск:
 
 Quit
 == Выход
 
 Quit anyway?
-== Всё равно выйти?
+== Выйти?
 
 REC %3d:%02d
-== Записано %3d:%02d
+== REC %3d:%02d
 
 Reason:
 == Причина:
@@ -425,40 +434,40 @@ Record demo
 == Записать демо
 
 Red team
-== Красная команда
+== Красные
 
 Red team wins!
-== Красная команда победила!
+== Красные победили!
 
 Refresh
 == Обновить
 
 Refreshing master servers
-== Обновляем мастер-сервера
+== Обновление списка мастер-серверов
 
 Remote console
-== Серверная консоль
+== Консоль сервера
 
 Remove
 == Удалить
 
 Remove friend
-== Удалить из друзей
+== Удалить друга
 
 Rename
-== Переименовать
+== Переименов.
 
 Rename demo
 == Переименовать демо
 
 Reset filter
-== Сбросить фильтр
+== Сбросить фильтры
 
 Reset to defaults
-== Сбросить на стандартные настройки
+== Сбросить настройки
 
 Rifle
-== Лазер
+== Бластер
 
 Round
 == Раунд
@@ -473,28 +482,28 @@ Score
 == Очки
 
 Score board
-== Результат
+== Табло
 
 Score limit
-== Лимит на очки
+== Лимит очков
 
 Scoreboard
-== Результат
+== Табло
 
 Screenshot
-== Скриншот
+== Снимок
 
 Server address:
-== Адрес сервера:
+== Адрес сервера
 
 Server details
-== Информация о сервере
+== Детали сервера
 
 Server filter
-== Фильтр сервера
+== Фильтр серверов
 
 Server info
-== Инфо
+== Информация
 
 Server not full
 == Сервер не заполнен
@@ -508,23 +517,23 @@ Shotgun
 Show chat
 == Показать чат
 
-Show friends
-== Показывать друзей
+Show friends only
+== Только с друзьями
 
 Show ingame HUD
-== Показывать HUD
+== Показывать внутриигровой HUD
 
 Show name plates
-== Показывать имена над игроками
+== Показывать ники игроков
 
 Show only supported
-== Показывать только поддерживаемые
+== Показывать только поддерживаемые разрешения экрана
 
 Size:
-== Размер
+== Размер:
 
 Skins
-== Модели
+== Скины
 
 Sound
 == Звук
@@ -533,11 +542,17 @@ Sound error
 == Звуковая ошибка
 
 Sound volume
-== Громкость
+== Громкость звука
 
 Spectate
 == Наблюдать
 
+Spectate next
+== Наблюдать след.
+
+Spectate previous
+== Наблюдать пред.
+
 Spectator mode
 == Наблюдатель
 
@@ -551,13 +566,16 @@ Standard map
 == Стандартная карта
 
 Stop record
-== Остановить запись
+== Стоп
+
+Strict gametype filter
+== Строгий фильтр режим.
 
 Sudden Death
-== Внезапная смерть
+== Быстрая смерть
 
 Switch weapon on pickup
-== Сменить оружие на подобранное
+== Переключать оружие при подборе
 
 Team
 == Команда
@@ -566,28 +584,28 @@ Team chat
 == Командный чат
 
 Teeworlds %s is out! Download it at www.teeworlds.com!
-== Teeworlds %s в сети! Скачайте его на www.teeworlds.com!
+== Вышла Teeworlds %s! Скачивайте на www.teeworlds.com!
 
 Texture Compression
 == Сжатие текстур
 
 The audio device couldn't be initialised.
-== Аудио устройство не может быть инициализировано.
+== Аудио устройство не может быть инициализировано
 
 The server is running a non-standard tuning on a pure game type.
-== Этот сервер работает на нестандартных настройках стандартного типа игры.
+== Сервер запущен с нестандартными настройками на стандартном типе игры.
 
 There's an unsaved map in the editor, you might want to save it before you quit the game.
-== В редакторе есть несохранённая карта
+== Есть несохранённая карта в редакторе, Вы можете сохранить её перед тем, как выйти.
 
 Time limit
-== Лимит на время
+== Лимит времени
 
 Time limit: %d min
-== Лимит на время: %d мин
+== Лимит времени: %d
 
 Try again
-== Попробовать ещё раз
+== ОК
 
 Type
 == Тип
@@ -605,10 +623,10 @@ Unable to rename the demo
 == Невозможно переименовать демо
 
 Use sounds
-== Звук
+== Использовать звуки
 
 Use team colors for name plates
-== Использовать цвет команды для имён игроков
+== Командные цвета для ников игроков
 
 V-Sync
 == Вертикальная синхронизация
@@ -620,10 +638,10 @@ Version:
 == Версия:
 
 Vote command:
-== Голосование:
+== Комманда голосования:
 
 Vote description:
-== Причина голосования:
+== Описание голосования:
 
 Vote no
 == Против
@@ -635,7 +653,7 @@ Voting
 == Голосование
 
 Warmup
-== Разогрев
+== Разминка
 
 Weapon
 == Оружие
@@ -647,7 +665,7 @@ Yes
 == Да
 
 You must restart the game for all settings to take effect.
-== Вы должны перезапустить игру, чтобы настройки применились.
+== Перезапустите игру для применения изменений.
 
 Your skin
 == Ваш скин
@@ -657,14 +675,5 @@ no limit
 
 ##### needs translation #####
 
-Play background music
-== 
-
-Spectate next
-== 
-
-Spectate previous
-== 
-
 ##### old translations #####
 
diff --git a/data/languages/serbian.txt b/data/languages/serbian.txt
index 99501d77..e4ec95c2 100644
--- a/data/languages/serbian.txt
+++ b/data/languages/serbian.txt
@@ -1,15 +1,45 @@
 
 ##### translated strings #####
 
+%d Bytes
+== %d Bytes
+
 %d of %d servers, %d players
 == %d od %d server(a), %d igrač(a)
 
+%d%% loaded
+== Učitano %d%%
+
 %ds left
 == Još %ds
 
+%i minute left
+== Još %ds
+
+%i minutes left
+== Preostalo: %i min.
+
+%i second left
+== Preostalo: %i min.
+
+%i seconds left
+== Preostalo: %i sek.
+
+%s wins!
+== %s je pobijedio!
+
+-Page %d-
+== -Strana %d-
+
 Abort
 == Prekini
 
+Add
+== Dodaj
+
+Add Friend
+== Dodaj prijatelja
+
 Address
 == Adresa
 
@@ -22,12 +52,24 @@ Alpha
 Always show name plates
 == Uvek prikaži imena igrača
 
+Are you sure that you want to delete the demo?
+== Da li sigurni da želite da obrišete demo-snimak?
+
 Are you sure that you want to quit?
 == Jeste li sigurni da želite da izađete?
 
+Are you sure that you want to remove the player from your friends list?
+== Da li ste sigurni da želite da obrišete igrača iz liste prijatelja?
+
 As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server.
 == Pošto prvi put pokrećete igru, molimo da ispod unesete Vaš nadimak (nick). Preporučujemo da proverite podešavanja i podesite ih prema Vašem ukusu pre nego što se konektujete na server.
 
+Automatically record demos
+== Automatski snimi demo
+
+Automatically take game over screenshot
+== Automatski napravi screenshot
+
 Blue team
 == Plavi tim
 
@@ -40,9 +82,18 @@ Body
 Call vote
 == Glasanje
 
+Change settings
+== Izmeni podešavanja
+
 Chat
 == Chat
 
+Clan
+== Klan
+
+Client
+== Klijent
+
 Close
 == Zatvori
 
@@ -64,6 +115,18 @@ Console
 Controls
 == Kontrole
 
+Count players only
+== Izbroj samo igrače
+
+Country
+== Država
+
+Crc:
+== Crc:
+
+Created:
+== Kreirano:
+
 Current
 == Trenutno
 
@@ -76,6 +139,15 @@ Custom colors
 Delete
 == Izbriši
 
+Delete demo
+== Obriši demo-snimak
+
+Demo details
+== Detalji demo-a
+
+Demofile: %s
+== Demo-snimak: %s
+
 Demos
 == Demoi
 
@@ -127,9 +199,18 @@ Filter
 Fire
 == Pucanje
 
+Folder
+== Direktorijum
+
 Force vote
 == Obavezno glasanje
 
+Free-View
+== Slobodan pregled
+
+Friends
+== Prijatelji
+
 Fullscreen
 == Čitav ekran
 
@@ -181,6 +262,9 @@ Info
 Internet
 == Internet
 
+Invalid Demo
+== Neispravan demo-snimak
+
 Join blue
 == U plavi tim
 
@@ -193,12 +277,18 @@ Join red
 Jump
 == Skok
 
+Kick player
+== Izbaci igrača iz igre
+
 LAN
 == LAN
 
 Language
 == Jezik
 
+Length:
+== Dužina:
+
 Lht.
 == Svetl.
 
@@ -211,6 +301,15 @@ MOTD
 Map
 == Mapa
 
+Map:
+== Mapa:
+
+Max Screenshots
+== Maksimalan broj screenshot-ova
+
+Max demos
+== Maksimalan broj demo-a
+
 Maximum ping:
 == Maksimalan ping:
 
@@ -223,6 +322,9 @@ Mouse sens.
 Move left
 == Nalevo
 
+Move player to spectators
+== Prebaci igrača u posmatrače
+
 Move right
 == Nadesno
 
@@ -235,6 +337,15 @@ Mute when not active
 Name
 == Ime
 
+Name plates size
+== Veličina pozadine za ime
+
+Netversion:
+== Net-verzija:
+
+New name:
+== Novo ime:
+
 News
 == Novosti
 
@@ -259,6 +370,12 @@ No servers match your filter criteria
 Ok
 == OK
 
+Open
+== Otvori
+
+Parent Folder
+== Prethodni direktorijum
+
 Password
 == Lozinka
 
@@ -274,9 +391,18 @@ Pistol
 Play
 == Pokreni
 
+Play background music
+== Pozadinska muzika
+
 Player
 == Igrač
 
+Player country:
+== Država
+
+Player options
+== Podešavanja igrača
+
 Players
 == Igrači
 
@@ -295,6 +421,18 @@ Quick search:
 Quit
 == Izlaz
 
+Quit anyway?
+== Izlaz?
+
+REC %3d:%02d
+== REC %3d:%02d
+
+Reason:
+== Razlog:
+
+Record demo
+== Snimi demo
+
 Red team
 == Crveni tim
 
@@ -310,6 +448,18 @@ Refreshing master servers
 Remote console
 == Udaljena konzola
 
+Remove
+== Ukloni
+
+Remove friend
+== Ukloni prijatelja
+
+Rename
+== Preimenuj
+
+Rename demo
+== Preimenuj demo-snimak
+
 Reset filter
 == Poništi filter
 
@@ -343,9 +493,15 @@ Scoreboard
 Screenshot
 == Screenshot
 
+Server address:
+== Adresa servera:
+
 Server details
 == Podaci o serveru
 
+Server filter
+== Filter servera
+
 Server info
 == O Serveru
 
@@ -361,24 +517,45 @@ Shotgun
 Show chat
 == Prikaži chat
 
+Show friends only
+== Prikaži isključivo prijatelje
+
+Show ingame HUD
+== Prikaži HUD
+
 Show name plates
 == Prikaži imena igrača
 
 Show only supported
 == Prikaži samo podržane
 
+Size:
+== Veličina:
+
 Skins
 == Izgled
 
 Sound
 == Zvuk
 
+Sound error
+== Problem sa zvukom
+
 Sound volume
 == Jačina zvuka
 
 Spectate
 == Posmatraj
 
+Spectate next
+== Posmatraj narednog
+
+Spectate previous
+== Posmatraj prethodnog
+
+Spectator mode
+== Posmatrački mod
+
 Spectators
 == Posmatrači
 
@@ -388,6 +565,12 @@ Standard gametype
 Standard map
 == Standardna mapa
 
+Stop record
+== Prekini snimanje
+
+Strict gametype filter
+== Striktan filter tipa igre
+
 Sudden Death
 == Iznenadna smrt
 
@@ -406,30 +589,60 @@ Teeworlds %s is out! Download it at www.teeworlds.com!
 Texture Compression
 == Kompresija tekstura
 
+The audio device couldn't be initialised.
+== Audio-uređaj nije moguće pokrenuti.
+
 The server is running a non-standard tuning on a pure game type.
 == Server sadrži nestandardna podešavanja.
 
+There's an unsaved map in the editor, you might want to save it before you quit the game.
+== Mapa u editoru nije zapamćena, možda želite da je zapamtite pre izlaska iz igre.
+
 Time limit
 == Max. vremena
 
+Time limit: %d min
+== Vremensko ograničenje: %d min.
+
 Try again
 == Pokušaj ponovo
 
 Type
 == Tip
 
+Type:
+== Tip:
+
 UI Color
 == Boja menija
 
+Unable to delete the demo
+== Demo-snimak nije moguće obrisati
+
+Unable to rename the demo
+== Demo-snimak nije moguće preimenovati
+
 Use sounds
 == Aktiviraj zvuk
 
+Use team colors for name plates
+== Koristi timsku boju u prikazu imena
+
 V-Sync
 == V-Sync
 
 Version
 == Verzija
 
+Version:
+== Verzija:
+
+Vote command:
+== Komanda za glasanje:
+
+Vote description:
+== Opis glasanja:
+
 Vote no
 == Ne
 
@@ -457,214 +670,10 @@ You must restart the game for all settings to take effect.
 Your skin
 == Vaš izgled
 
-##### needs translation #####
-
-%d Bytes
-== 
-
-%d%% loaded
-== 
-
-%i minute left
-== 
-
-%i minutes left
-== 
-
-%i second left
-== 
-
-%i seconds left
-== 
-
-%s wins!
-== 
-
--Page %d-
-== 
-
-Add
-== 
-
-Add Friend
-== 
-
-Are you sure that you want to delete the demo?
-== 
-
-Are you sure that you want to remove the player from your friends list?
-== 
-
-Automatically record demos
-== 
-
-Automatically take game over screenshot
-== 
-
-Change settings
-== 
-
-Clan
-== 
-
-Client
-== 
-
-Count players only
-== 
-
-Country
-== 
-
-Crc:
-== 
-
-Created:
-== 
-
-Delete demo
-== 
-
-Demo details
-== 
-
-Folder
-== 
-
-Free-View
-== 
-
-Friends
-== 
-
-Invalid Demo
-== 
-
-Kick player
-== 
-
-Length:
-== 
-
-Map:
-== 
-
-Max Screenshots
-== 
-
-Max demos
-== 
-
-Move player to spectators
-== 
-
-Name plates size
-== 
-
-Netversion:
-== 
-
-New name:
-== 
-
-Open
-== 
-
-Parent Folder
-== 
-
-Play background music
-== 
-
-Player options
-== 
-
-Quit anyway?
-== 
-
-REC %3d:%02d
-== 
-
-Reason:
-== 
-
-Record demo
-== 
-
-Remove
-== 
-
-Remove friend
-== 
-
-Rename
-== 
-
-Rename demo
-== 
-
-Server address:
-== 
-
-Server filter
-== 
-
-Show friends
-== 
-
-Show ingame HUD
-== 
-
-Size:
-== 
-
-Sound error
-== 
-
-Spectate next
-== 
-
-Spectate previous
-== 
-
-Spectator mode
-== 
-
-Stop record
-== 
-
-The audio device couldn't be initialised.
-== 
-
-There's an unsaved map in the editor, you might want to save it before you quit the game.
-== 
-
-Time limit: %d min
-== 
-
-Type:
-== 
-
-Unable to delete the demo
-== 
-
-Unable to rename the demo
-== 
-
-Use team colors for name plates
-== 
-
-Version:
-== 
-
-Vote command:
-== 
-
-Vote description:
-== 
-
 no limit
-== 
+== bez ograničenja
+
+##### needs translation #####
 
 ##### old translations #####
 
diff --git a/data/languages/slovak.txt b/data/languages/slovak.txt
index 25ca442c..48816234 100644
--- a/data/languages/slovak.txt
+++ b/data/languages/slovak.txt
@@ -14,16 +14,16 @@
 == Zostáva %ds
 
 %i minute left
-== Zostáva %i minúta
+== Zostávajúce minúty: %i
 
 %i minutes left
-== Zostáva %i minút
+== Zostávajúce minúty: %i
 
 %i second left
-== Zostáva %i sekunda
+== Zostávajúce sekundy: %i
 
 %i seconds left
-== Zostáva %i sekúnd
+== Zostávajúce sekundy: %i
 
 %s wins!
 == %s vyhráva!
@@ -62,7 +62,7 @@ Are you sure that you want to remove the player from your friends list?
 == Ste si istí, že chcete tohto hráča odstrániť zo zoznamu priateľov?
 
 As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server.
-== Vitajte v hre TeeWorlds. Pred tým, ako sa pripojíte na herný server, odporúčame nastaviť si hru podľa svojich požiadavkov. Napíšte do políčka nižšie meno pre Vášho tee a pokračujte kliknutím na tlačítko.
+== Vitajte v hre TeeWorlds. Predtým, ako sa pripojíte na herný server, odporúčame nastaviť si hru podľa svojich požiadavkov. Napíšte do políčka nižšie meno pre Vášho tee a pokračujte kliknutím na tlačítko.
 
 Automatically record demos
 == Automaticky nahrávať záznamy
@@ -116,7 +116,7 @@ Controls
 == Ovládanie
 
 Count players only
-== Rátať len hráčov
+== Nepočítať divákov
 
 Country
 == Krajina
@@ -145,6 +145,9 @@ Delete demo
 Demo details
 == Detaily nahrávky
 
+Demofile: %s
+== Nahrávka: %s
+
 Demos
 == Záznamy
 
@@ -254,7 +257,7 @@ Hue
 == Hue
 
 Info
-== Informácie
+== Info
 
 Internet
 == Internet
@@ -320,7 +323,7 @@ Move left
 == Pohyb vľavo
 
 Move player to spectators
-== Presunúť hráča do skupiny divákov
+== Poslať hráča pozorovať
 
 Move right
 == Pohyb vpravo
@@ -365,7 +368,7 @@ No servers match your filter criteria
 == Žiadny server nezodpovedá zadaným kritériám
 
 Ok
-== Ok
+== OK
 
 Open
 == Otvoriť
@@ -388,9 +391,15 @@ Pistol
 Play
 == Prehrať
 
+Play background music
+== Prehrať hudbu na pozadí
+
 Player
 == Hráč
 
+Player country:
+== Filter krajín:
+
 Player options
 == Nastavenia hráča
 
@@ -407,7 +416,7 @@ Quality Textures
 == Kvalitné textúry
 
 Quick search:
-== Rýchle hľadanie:
+== Hľadanie:
 
 Quit
 == Ukončiť
@@ -473,13 +482,13 @@ Score
 == Skóre
 
 Score board
-== Prehľad skóre
+== Tabuľka výsledkov
 
 Score limit
 == Limit skóre
 
 Scoreboard
-== Prehľad skóre
+== Tabuľka výsledkov
 
 Screenshot
 == Screenshot
@@ -508,7 +517,7 @@ Shotgun
 Show chat
 == Ukázať chat
 
-Show friends
+Show friends only
 == Ukázať priateľov
 
 Show ingame HUD
@@ -527,7 +536,7 @@ Skins
 == Skiny
 
 Sound
-== Zvuky
+== Zvuk
 
 Sound error
 == Zvuková chyba
@@ -538,6 +547,12 @@ Sound volume
 Spectate
 == Pozorovať
 
+Spectate next
+== Pozorovať ďalšieho
+
+Spectate previous
+== Pozorovať predch.
+
 Spectator mode
 == Mód diváka
 
@@ -545,14 +560,17 @@ Spectators
 == Diváci
 
 Standard gametype
-== Štandartný herný typ
+== Štandardný herný typ
 
 Standard map
-== Štandartná mapa
+== Štandardná mapa
 
 Stop record
 == Nenahrávať
 
+Strict gametype filter
+== Striktný filter módov
+
 Sudden Death
 == Rýchla Smrť
 
@@ -560,7 +578,7 @@ Switch weapon on pickup
 == Nastavovať zdvíhanú zbraň ako aktuálnu
 
 Team
-== Tým
+== Týmu
 
 Team chat
 == Týmový chat
@@ -575,10 +593,10 @@ The audio device couldn't be initialised.
 == Zvukové zariadenie nemohlo byť inicializované.
 
 The server is running a non-standard tuning on a pure game type.
-== Na serveri je nastavený neštandartný tuning.
+== Server používa neštandardné nastavenia na základnom hernom móde.
 
 There's an unsaved map in the editor, you might want to save it before you quit the game.
-== V editore máte neuloženú mapu, mali by ste si ju uložiť predtým než skončíte hru.
+== V editore máte neuloženú mapu, možno si ju chcete pred skončením hry uložiť.
 
 Time limit
 == Časový limit
@@ -602,7 +620,7 @@ Unable to delete the demo
 == Nemôžem vymazať záznam
 
 Unable to rename the demo
-== Nedá sa premenovať nahrávka
+== Nahrávka sa nedá premenovať
 
 Use sounds
 == Povoliť zvuky
@@ -626,10 +644,10 @@ Vote description:
 == Popis hlasu:
 
 Vote no
-== Hlasovať proti
+== Nie
 
 Vote yes
-== Hlasovať pre
+== Áno
 
 Voting
 == Hlasovanie
@@ -638,7 +656,7 @@ Warmup
 == Rozohrávka
 
 Weapon
-== Zbraň
+== Zbrane
 
 Welcome to Teeworlds
 == Vitajte v hre Teeworlds!
@@ -657,14 +675,5 @@ no limit
 
 ##### needs translation #####
 
-Play background music
-== 
-
-Spectate next
-== 
-
-Spectate previous
-== 
-
 ##### old translations #####
 
diff --git a/data/languages/spanish.txt b/data/languages/spanish.txt
index b015fc3a..5348d16a 100644
--- a/data/languages/spanish.txt
+++ b/data/languages/spanish.txt
@@ -2,7 +2,7 @@
 ##### translated strings #####
 
 %d Bytes
-== %d Bytes
+== %d bytes
 
 %d of %d servers, %d players
 == %d de %d servidores, %d jugadores
@@ -11,22 +11,22 @@
 == %d%% cargado
 
 %ds left
-== faltan %ds
+== %ds restantes
 
 %i minute left
-== falta %i minuto
+== %i minuto restante
 
 %i minutes left
-== faltan %i minutos
+== %i minutos restantes
 
 %i second left
-== falta %i segundo
+== %i segundo restante
 
 %i seconds left
-== faltan %i segundos
+== %i segundos restantes
 
 %s wins!
-== %s gana!
+== ¡%s gana!
 
 -Page %d-
 == -Página %d-
@@ -38,7 +38,7 @@ Add
 == Añadir
 
 Add Friend
-== Añadir Amigo
+== Añadir amigo
 
 Address
 == Dirección
@@ -53,40 +53,40 @@ Always show name plates
 == Mostrar siempre los apodos
 
 Are you sure that you want to delete the demo?
-== ¿Seguro que quiere eliminar la demo?
+== ¿Estás seguro de que quieres eliminar la demo?
 
 Are you sure that you want to quit?
-== ¿Seguro que quiere salir?
+== ¿Estás seguro de que quieres salir?
 
 Are you sure that you want to remove the player from your friends list?
-== ¿Estas seguro de que quiere eliminar este jugador de su lista de amigos?
+== ¿Estás seguro de que quieres eliminar a este jugador de la lista de amigos?
 
 As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server.
-== Como es la primera vez que abre el juego, por favor, introduzca su apodo. Es recomendable que verifique su configuración y ajuste las preferencias antes de entrar en un servidor.
+== Como es la primera vez que abres el juego, por favor, introduce tu apodo. Es recomendable que verifiques tu configuración y ajustes las preferencias antes de unirte a un servidor.
 
 Automatically record demos
 == Grabar demos automáticamente
 
 Automatically take game over screenshot
-== Pantallazo 'game over' automáticamente
+== Captura de pantalla al final de la partida
 
 Blue team
 == Equipo azul
 
 Blue team wins!
-== Equipo azul gana!
+== ¡El equipo azul gana!
 
 Body
 == Cuerpo
 
 Call vote
-== Votación
+== Votar
 
 Change settings
 == Cambiar configuración
 
 Chat
-== Charla
+== Conversación
 
 Clan
 == Clan
@@ -107,7 +107,7 @@ Connecting to
 == Conectando con
 
 Connection Problems...
-== Problemas de Conexión...
+== Problemas de conexión...
 
 Console
 == Consola
@@ -119,16 +119,16 @@ Count players only
 == Solo contar jugadores
 
 Country
-== Pais
+== País
 
 Crc:
-== Crc:
+== CRC:
 
 Created:
 == Creado:
 
 Current
-== Actualmente
+== Actual
 
 Current version: %s
 == Versión actual: %s
@@ -143,7 +143,10 @@ Delete demo
 == Borrar demo
 
 Demo details
-== Detellades demo
+== Detalles de la demo
+
+Demofile: %s
+== Archivo: %s
 
 Demos
 == Demos
@@ -155,13 +158,13 @@ Disconnected
 == Desconectado
 
 Display Modes
-== Modos de exibición
+== Modos de video
 
 Downloading map
-== Bajando mapa
+== Descargando mapa
 
 Draw!
-== Empate!
+== ¡Empate!
 
 Dynamic Camera
 == Cámara dinámica
@@ -176,7 +179,7 @@ Error
 == Error
 
 Error loading demo
-== Error al cargar demo
+== Error al cargar la demo
 
 FSAA samples
 == Muestras FSAA
@@ -203,25 +206,25 @@ Force vote
 == Forzar
 
 Free-View
-== Vista Libre
+== Vista libre
 
 Friends
 == Amigos
 
 Fullscreen
-== Pantalla Completa
+== Pantalla completa
 
 Game
 == Juego
 
 Game info
-== Info. sobre el juego
+== Información del juego
 
 Game over
-== Fin del Juego
+== Fin de la partida
 
 Game type
-== Tipo de juego
+== Modo
 
 Game types:
 == Tipos de juego:
@@ -248,13 +251,13 @@ Hook
 == Gancho
 
 Host address
-== Dirección de host
+== Dirección del host
 
 Hue
 == Matiz
 
 Info
-== Info.
+== Información
 
 Internet
 == Internet
@@ -263,13 +266,13 @@ Invalid Demo
 == Demo inválida
 
 Join blue
-== Azul
+== Unirse a azul
 
 Join game
-== Entrar al juego
+== Unirse
 
 Join red
-== Rojo
+== Unirse a rojo
 
 Jump
 == Saltar
@@ -287,7 +290,7 @@ Length:
 == Longitud:
 
 Lht.
-== Luz
+== Luminosidad
 
 Loading
 == Cargando
@@ -302,10 +305,10 @@ Map:
 == Mapa:
 
 Max Screenshots
-== Max Pantallazos
+== Número máximo de capturas
 
 Max demos
-== Max demos
+== Número máximo de demos
 
 Maximum ping:
 == Ping máximo:
@@ -314,40 +317,40 @@ Miscellaneous
 == Miscelánea
 
 Mouse sens.
-== Sens. del ratón
+== Sensibilidad ratón
 
 Move left
-== Izquierda
+== Mover a la izquierda
 
 Move player to spectators
 == Mover jugador a espectadores
 
 Move right
-== Derecha
+== Mover a la derecha
 
 Movement
 == Movimiento
 
 Mute when not active
-== Silenciar en inactivo
+== Silenciar si no está activo
 
 Name
 == Nombre
 
 Name plates size
-== Máx. Caracteres para los Apodos
+== Tamaño de la fuente de los apodos
 
 Netversion:
 == Versión Net
 
 New name:
-== Nuevo Nombre:
+== Nuevo nombre:
 
 News
-== Notícia
+== Noticias
 
 Next weapon
-== Próxima arma
+== Arma siguiente
 
 Nickname
 == Apodo
@@ -362,7 +365,7 @@ No servers found
 == Ningún servidor encontrado
 
 No servers match your filter criteria
-== Ningún servidor corresponde a los critérios de filtrado
+== Ningún servidor corresponde a los criterios de filtrado
 
 Ok
 == Aceptar
@@ -371,7 +374,7 @@ Open
 == Abrir
 
 Parent Folder
-== Carpeta superior
+== Directorio padre
 
 Password
 == Contraseña
@@ -386,19 +389,25 @@ Pistol
 == Pistola
 
 Play
-== Asistir
+== Reproducir
+
+Play background music
+== Reproducir música de fondo
 
 Player
 == Jugador
 
+Player country:
+== País del jugador
+
 Player options
-== Opciones de Jugador
+== Opciones de jugador
 
 Players
 == Jugadores
 
 Please balance teams!
-== Por favor, equilibre los equipos!
+== Por favor, ¡equilibrad los equipos!
 
 Prev. weapon
 == Arma anterior
@@ -407,7 +416,7 @@ Quality Textures
 == Texturas de calidad
 
 Quick search:
-== Busqueda rápida:
+== Búsqueda rápida:
 
 Quit
 == Salir
@@ -428,7 +437,7 @@ Red team
 == Equipo rojo
 
 Red team wins!
-== Ganó el Equipo rojo!
+== ¡El equipo rojo gana!
 
 Refresh
 == Actualizar
@@ -458,7 +467,7 @@ Reset to defaults
 == Resetar por defecto
 
 Rifle
-== Laser
+== Láser
 
 Round
 == Ronda
@@ -467,40 +476,40 @@ Sample rate
 == Frecuencia de muestreo
 
 Sat.
-== Sat.
+== Saturación
 
 Score
 == Puntos
 
 Score board
-== Tabla de puntos
+== Puntuación
 
 Score limit
-== Puntuación Máx.
+== Límite puntos
 
 Scoreboard
 == Puntuación
 
 Screenshot
-== Pantallazo
+== Captura de pantalla
 
 Server address:
-== Dirección d. servidor:
+== IP del servidor:
 
 Server details
-== Detalles del server
+== Detalles del servidor
 
 Server filter
 == Filtro del servidor
 
 Server info
-== Info del server
+== Servidor
 
 Server not full
-== Servidor incompleto
+== Servidor sin llenar
 
 Settings
-== Config.
+== Configuración
 
 Shotgun
 == Escopeta
@@ -508,17 +517,17 @@ Shotgun
 Show chat
 == Mostrar chat
 
-Show friends
-== Mostrar amigos
+Show friends only
+== Solo mostrar amigos
 
 Show ingame HUD
-== Mostar HUD en juego
+== Mostar HUD durante el juego
 
 Show name plates
 == Mostrar apodos
 
 Show only supported
-== Mostrar solo soportados
+== Mostrar únicamente modos soportados
 
 Size:
 == Tamaño:
@@ -536,58 +545,67 @@ Sound volume
 == Volumen de sonido
 
 Spectate
-== Observar
+== Asistir
+
+Spectate next
+== Observar siguiente
+
+Spectate previous
+== Observar anterior
 
 Spectator mode
 == Modo espectador
 
 Spectators
-== Observadores
+== Espectadores
 
 Standard gametype
-== Tipo de juego normal
+== Tipo de juego estándar
 
 Standard map
-== Mapa normal
+== Mapa estándar
 
 Stop record
-== Parar grabación
+== Detener grabación
+
+Strict gametype filter
+== Tipo de juego del filtro
 
 Sudden Death
-== Muerte Súbita
+== Muerte súbita
 
 Switch weapon on pickup
-== Cambiar de arma
+== Cambiar al arma recogida
 
 Team
 == Equipo
 
 Team chat
-== Conv. de equipo
+== En equipo
 
 Teeworlds %s is out! Download it at www.teeworlds.com!
-== Teeworlds %s ya salió! Descárgalo desde www.teeworlds.com!
+== ¡Teeworlds %s ha salido! ¡Descárgalo desde www.teeworlds.com!
 
 Texture Compression
-== Compresión de Textura
+== Compresión de texturas
 
 The audio device couldn't be initialised.
 == El dispositivo de audio no puede ser inicializado.
 
 The server is running a non-standard tuning on a pure game type.
-== El servidor está ejecutando una afinación no estándar en un tipo de juego puro.
+== El servidor está ejecutando una configuración no estándar en un tipo de juego puro.
 
 There's an unsaved map in the editor, you might want to save it before you quit the game.
-== Tienes un mapa sin guardar en el editor, ¿quiere guardarlo antes de salir?
+== Tienes un mapa sin guardar en el editor, quizá quieras guardarlo antes de salir.
 
 Time limit
-== Tiempo máx.
+== Tiempo límite
 
 Time limit: %d min
-== Tiempo limite: %d min
+== Tiempo límite: %d minutos
 
 Try again
-== Probar de nuevo
+== Intentar de nuevo
 
 Type
 == Tipo
@@ -596,19 +614,19 @@ Type:
 == Tipo:
 
 UI Color
-== Color de menu
+== Color de menú
 
 Unable to delete the demo
-== No se puede eliminar la demo
+== No se pudo eliminar la demo
 
 Unable to rename the demo
-== Imposible renombrar la demo
+== No se pudo renombrar la demo
 
 Use sounds
 == Usar sonidos
 
 Use team colors for name plates
-== Usar el color del equipo en los apodos
+== Usar el color de equipo en los apodos
 
 V-Sync
 == V-Sync
@@ -620,7 +638,7 @@ Version:
 == Versión:
 
 Vote command:
-== Votar comando:
+== Comando de votación:
 
 Vote description:
 == Descripción de la votación:
@@ -629,10 +647,10 @@ Vote no
 == Votar no
 
 Vote yes
-== Votar si
+== Votar sí
 
 Voting
-== Votando
+== Votación
 
 Warmup
 == Calentamiento
@@ -641,10 +659,10 @@ Weapon
 == Arma
 
 Welcome to Teeworlds
-== Bienvenido a Teeworlds!
+== ¡Bienvenido/a a Teeworlds!
 
 Yes
-== Si
+== Sí
 
 You must restart the game for all settings to take effect.
 == Debes reiniciar el juego para que los cambios tengan efecto.
@@ -657,23 +675,5 @@ no limit
 
 ##### needs translation #####
 
-Play background music
-== 
-
-Spectate next
-== 
-
-Spectate previous
-== 
-
 ##### old translations #####
 
-Page %d of %d
-== Página %d de %d
-
-Next
-== Siguiente
-
-Prev
-== Anterior
-
diff --git a/data/languages/swedish.txt b/data/languages/swedish.txt
index 22176110..7f48d95d 100644
--- a/data/languages/swedish.txt
+++ b/data/languages/swedish.txt
@@ -145,6 +145,9 @@ Delete demo
 Demo details
 == Demoinformation
 
+Demofile: %s
+== Demofil: %s
+
 Demos
 == Demon
 
@@ -179,7 +182,7 @@ Error loading demo
 == Kunde inte ladda demot
 
 FSAA samples
-== FSAA samplingar
+== FSAA-samplingar
 
 Favorite
 == Favorit
@@ -239,7 +242,7 @@ Hammer
 == Hammare
 
 Has people playing
-== Har folk som spelar
+== Inte tom
 
 High Detail
 == Extra detaljer
@@ -296,7 +299,7 @@ MOTD
 == Meddelande
 
 Map
-== Karta
+== Bana
 
 Map:
 == Bana
@@ -388,9 +391,15 @@ Pistol
 Play
 == Spela
 
+Play background music
+== Aktivera bakgrundsmusik
+
 Player
 == Spelare
 
+Player country:
+== Land
+
 Player options
 == Spelaralternativ
 
@@ -497,7 +506,7 @@ Server info
 == Serverinfo
 
 Server not full
-== Inte full server
+== Lediga platser
 
 Settings
 == Inställningar
@@ -506,9 +515,9 @@ Shotgun
 == Hagelgevär
 
 Show chat
-== Visa chatt
+== Visa chatten
 
-Show friends
+Show friends only
 == Visa kompisar
 
 Show ingame HUD
@@ -533,11 +542,17 @@ Sound error
 == Ljudfel
 
 Sound volume
-== Ljud volym
+== Volym
 
 Spectate
 == Åskåda
 
+Spectate next
+== Se på nästa
+
+Spectate previous
+== Se på föregående
+
 Spectator mode
 == Åskådarläge
 
@@ -553,6 +568,9 @@ Standard map
 Stop record
 == Sluta spela in
 
+Strict gametype filter
+== Strikt speltypsfilter
+
 Sudden Death
 == Plötslig död
 
@@ -566,7 +584,7 @@ Team chat
 == Lagchatt
 
 Teeworlds %s is out! Download it at www.teeworlds.com!
-== Teeworld %s är släppt! Ladda ner det på www.teeworlds.com!
+== Teeworlds %s är ute nu! Ladda ner det från www.teeworlds.com.
 
 Texture Compression
 == Texturkompression
@@ -575,7 +593,7 @@ The audio device couldn't be initialised.
 == Ljudet kunde inte startas
 
 The server is running a non-standard tuning on a pure game type.
-== Denna server kör inte standardinställningar på standard speltyp.
+== Denna server kör inte standardinställningar på en reserverad speltyp.
 
 There's an unsaved map in the editor, you might want to save it before you quit the game.
 == Det finns en osparad bana i redigeraden, du kanske vill spara den innan du avslutar.
@@ -605,13 +623,13 @@ Unable to rename the demo
 == Kunde inte byta namn på demot
 
 Use sounds
-== Använd ljudeffekter
+== Aktivera ljudeffekter
 
 Use team colors for name plates
 == Använd lagfärger i namnskyltar
 
 V-Sync
-== V-Sync
+== V-Synk
 
 Version
 == Version
@@ -647,7 +665,7 @@ Yes
 == Ja
 
 You must restart the game for all settings to take effect.
-== Du måste starta om spelet för att ändringarna skall gälla.
+== Du måste starta om spelet för att ändringarna skall verkställas.
 
 Your skin
 == Ditt skin
@@ -657,14 +675,5 @@ no limit
 
 ##### needs translation #####
 
-Play background music
-== 
-
-Spectate next
-== 
-
-Spectate previous
-== 
-
 ##### old translations #####
 
diff --git a/data/languages/turkish.txt b/data/languages/turkish.txt
new file mode 100644
index 00000000..d91c72bf
--- /dev/null
+++ b/data/languages/turkish.txt
@@ -0,0 +1,679 @@
+
+##### translated strings #####
+
+%d Bytes
+== %d Bayt
+
+%d of %d servers, %d players
+== %d/%d sunucu, %d oyuncu
+
+%d%% loaded
+== %d%% yüklendi
+
+%ds left
+== %ds kaldı
+
+%i minute left
+== %i dakika kaldı
+
+%i minutes left
+== %i dakika kaldı
+
+%i second left
+== %i saniye kaldı
+
+%i seconds left
+== %i saniye kaldı
+
+%s wins!
+== %s kazandı!
+
+-Page %d-
+== -Page %d-
+
+Abort
+== İptal
+
+Add
+== Ekle
+
+Add Friend
+== Arkadaş Ekle
+
+Address
+== Adres
+
+All
+== Hepsi
+
+Alpha
+== Alfa
+
+Always show name plates
+== İsim etiketlerini herzaman göster
+
+Are you sure that you want to delete the demo?
+== Bu demo dosyasını silmek istediğinize eminmisiniz?
+
+Are you sure that you want to quit?
+== Çıkmak istediğinize eminmisiniz?
+
+Are you sure that you want to remove the player from your friends list?
+== Bu oyuncuyu arkadaş listenizden kaldırmak istediğinize emin misiniz?
+
+As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server.
+== Oyunu ilk açışınız olduğundan takma adınızı giriniz. Bir oyuna katılmadan evvel ayarlara bakmanız ve isteğinize göre özelleştirmeniz önerilir.
+
+Automatically record demos
+== Otomatik olarak demo kaydet
+
+Automatically take game over screenshot
+== Otomatik olarak oyun bitişinde ekran görüntüsü al
+
+Blue team
+== Mavi takım
+
+Blue team wins!
+== Mavi takım kazandı!
+
+Body
+== Gövde
+
+Call vote
+== Oylama başlat
+
+Change settings
+== Ayarları değiştir
+
+Chat
+== Chat
+
+Clan
+== Clan
+
+Client
+== İstemci
+
+Close
+== Kapa
+
+Compatible version
+== Uyumlu versiyon
+
+Connect
+== Bağlan
+
+Connecting to
+== Bağlanılıyor
+
+Connection Problems...
+== Bağlantı Hatası...
+
+Console
+== Konsol
+
+Controls
+== Kontroller
+
+Count players only
+== Sadece oyuncuları say
+
+Country
+== Ülke
+
+Crc:
+== Crc :
+
+Created:
+== Hazırlandı :
+
+Current
+== Şimdiki
+
+Current version: %s
+== Mevcut versyon : %s
+
+Custom colors
+== Özel renkler
+
+Delete
+== Sil
+
+Delete demo
+== Demoyu sil
+
+Demo details
+== Demo ayrıntıları
+
+Demos
+== Demolar
+
+Disconnect
+== Bağlantıyı kes
+
+Disconnected
+== Bağlantı kesildi
+
+Display Modes
+== Çözünürlük
+
+Downloading map
+== Harita yükleniyor
+
+Draw!
+== Berabere!
+
+Dynamic Camera
+== Dinamik kamera
+
+Emoticon
+== Yüz ifadesi
+
+Enter
+== Gir
+
+Error
+== Hata
+
+Error loading demo
+== Demo yüklenirken hata oluştu
+
+FSAA samples
+== FSAA örnekleri
+
+Favorite
+== Favori
+
+Favorites
+== Favoriler
+
+Feet
+== Ayak
+
+Filter
+== Filtre
+
+Fire
+== Ateş
+
+Folder
+== Dosya
+
+Force vote
+== Oylamayı zorla
+
+Free-View
+== Serbest Görüntü
+
+Friends
+== Arkadaşlar
+
+Fullscreen
+== Tam ekran
+
+Game
+== Oyun
+
+Game info
+== Oyun bilgisi
+
+Game over
+== Oyun sonu
+
+Game type
+== Oyun türü
+
+Game types:
+== Oyun türü:
+
+General
+== Genel
+
+Graphics
+== Grafik
+
+Grenade
+== El bombası
+
+Hammer
+== Çekiç
+
+Has people playing
+== Oynayan insan bulunduran
+
+High Detail
+== High Detail
+
+Hook
+== Kanca
+
+Host address
+== Sunucu adresi
+
+Hue
+== Renk
+
+Info
+== Bilgi
+
+Internet
+== Internet
+
+Invalid Demo
+== Geçersiz Demo
+
+Join blue
+== Maviye katıl
+
+Join game
+== Oyuna katıl
+
+Join red
+== Kırmızıya katıl
+
+Jump
+== Zıpla
+
+Kick player
+== Oyuncuyu At
+
+LAN
+== LAN
+
+Language
+== Dil
+
+Length:
+== Uzunluk:
+
+Lht.
+== Parlaklık
+
+Loading
+== Yükleniyor
+
+MOTD
+== MOTD
+
+Map
+== Harita
+
+Map:
+== Harita:
+
+Max Screenshots
+== Maksimum Ekran Alıntısı
+
+Max demos
+== Maksimum Demo
+
+Maximum ping:
+== Maximum ping
+
+Miscellaneous
+== Çeşitli
+
+Mouse sens.
+== Mouse duyarlılığı
+
+Move left
+== Sola git
+
+Move player to spectators
+== Seyircilere katıl
+
+Move right
+== Sağa git
+
+Movement
+== Hareket
+
+Mute when not active
+== Aktif değilken sessizleştir
+
+Name
+== İsim
+
+Name plates size
+== Boyut
+
+Netversion:
+== Netversion :
+
+New name:
+== Yeni isim :
+
+News
+== Haberler
+
+Next weapon
+== Sonraki silah
+
+Nickname
+== Takma ad
+
+No
+== Hayır
+
+No password
+== Şifresiz
+
+No servers found
+== Sunucu bulunamadı
+
+No servers match your filter criteria
+== Filtre kriterlerinize uygun sunucu bulunamadı
+
+Ok
+== Ok
+
+Open
+== Aç
+
+Parent Folder
+== Üst dizin
+
+Password
+== Şifre
+
+Password incorrect
+== Hatalı şifre
+
+Ping
+== Ping
+
+Pistol
+== Tabanca
+
+Play
+== Oynat
+
+Player
+== Oyuncu
+
+Player options
+== Oyuncu ayarları
+
+Players
+== Oyuncular
+
+Please balance teams!
+== Takımları dengeleyin!
+
+Prev. weapon
+== Önceki silah
+
+Quality Textures
+== Kaliteli dolgular
+
+Quick search:
+== Hızlı Arama :
+
+Quit
+== Çıkmak
+
+Quit anyway?
+== Çıkmak istediğinize emin misiniz?
+
+REC %3d:%02d
+== REC %3d:%02d
+
+Reason:
+== Neden :
+
+Record demo
+== Demo kaydet
+
+Red team
+== Kırmızı takım
+
+Red team wins!
+== Kırmızı takım kazandı!
+
+Refresh
+== Yenile
+
+Refreshing master servers
+== Ana sunucu yenileniyor
+
+Remote console
+== Uzak Konsol
+
+Remove
+== Çıkar
+
+Remove friend
+== Arkadaşı sil
+
+Rename
+== Yeniden isimlendir
+
+Rename demo
+== Demonun adını değiştir
+
+Reset filter
+== Filitreleri varsayılanlara getir
+
+Reset to defaults
+== Réinitialiser
+
+Rifle
+== Laser
+
+Round
+== Round
+
+Sample rate
+== Sample rate
+
+Sat.
+== Doygunluk
+
+Score
+== Skor
+
+Score board
+== Skor tahtası
+
+Score limit
+== Skor limiti
+
+Scoreboard
+== Skor tahtası
+
+Screenshot
+== Ekran alıntısı
+
+Server address:
+== Sunucu adresi :
+
+Server details
+== Sunucucu detayları
+
+Server filter
+== Filtreler
+
+Server info
+== Sunucu bilgisi
+
+Server not full
+== Sunucu dolu değil
+
+Settings
+== Ayarlar
+
+Shotgun
+== Tüfek
+
+Show chat
+== Sohbeti göster
+
+Show friends only
+== Sadece arkadaşları göster
+
+Show ingame HUD
+== Oyun içi HUD ı göster
+
+Show name plates
+== İsim etiketlerini göster
+
+Show only supported
+== Desteklenmeyen çözünürlükleri gizle
+
+Size:
+== Boyut:
+
+Skins
+== Skins
+
+Sound
+== Ses
+
+Sound error
+== Ses hatası
+
+Sound volume
+== Ses yüksekliği
+
+Spectate
+== İzle
+
+Spectator mode
+== İzleyici modu
+
+Spectators
+== İzleyiciler
+
+Standard gametype
+== Standart oyun türleri
+
+Standard map
+== Standart haritalar
+
+Stop record
+== Kaydı durdur
+
+Sudden Death
+== Ani ölüm
+
+Switch weapon on pickup
+== Silah alındığıda yeni silaha geç
+
+Team
+== Takım
+
+Team chat
+== Takım sohbeti
+
+Teeworlds %s is out! Download it at www.teeworlds.com!
+== Teeworlds %s çıktı! www.teeworlds.com dan indirin!
+
+Texture Compression
+== Dolgu sıkıştırması
+
+The audio device couldn't be initialised.
+== Ses donanımı başlatılamadı.
+
+The server is running a non-standard tuning on a pure game type.
+== Bu sunucu standart olmayan bir ayarı saf bir oyun türünde kullanıyor.
+
+There's an unsaved map in the editor, you might want to save it before you quit the game.
+== Editörde kaydedilmemiş bir harita var, kapatmadan evvel kaydetmek isteyebilirsiniz.
+
+Time limit
+== Süre limiti
+
+Time limit: %d min
+== Süre limiti: %d min
+
+Try again
+== Tekrar dene
+
+Type
+== Tür
+
+Type:
+== Tür:
+
+UI Color
+== Menü rengi
+
+Unable to delete the demo
+== Demo silinemiyor
+
+Unable to rename the demo
+== Demonun ismi değiştirilemiyor
+
+Use sounds
+== Sesleri kullan
+
+Use team colors for name plates
+== İsim etiketlerinde takım renklerini kullan
+
+V-Sync
+== V-Sync
+
+Version
+== Versyon
+
+Version:
+== Versyon :
+
+Vote command:
+== Komut:
+
+Vote description:
+== Açıklama :
+
+Vote no
+== Hayır oyu ver
+
+Vote yes
+== Evet oyu ver
+
+Voting
+== Oylama
+
+Warmup
+== Isınma
+
+Weapon
+== Silah
+
+Welcome to Teeworlds
+== Teeworlds'e hoşgeldin
+
+Yes
+== Evet
+
+You must restart the game for all settings to take effect.
+== Bütün ayarların aktif olması için oyunu yeniden başlatmalısınız.
+
+Your skin
+== Your skin
+
+no limit
+== limit yok
+
+##### needs translation #####
+
+Demofile: %s
+== 
+
+Play background music
+== 
+
+Player country:
+== 
+
+Spectate next
+== 
+
+Spectate previous
+== 
+
+Strict gametype filter
+== 
+
+##### old translations #####
+
diff --git a/data/languages/ukrainian.txt b/data/languages/ukrainian.txt
index c906e446..93566829 100644
--- a/data/languages/ukrainian.txt
+++ b/data/languages/ukrainian.txt
@@ -50,7 +50,7 @@ Chat
 == Чат
 
 Close
-== Закрити
+== Зачинити
 
 Compatible version
 == Сумісна версія
@@ -86,10 +86,10 @@ Demos
 == Демо
 
 Disconnect
-== # Відключитись
+== Від'єднатись
 
 Disconnected
-== Відключено
+== Від'єднанно
 
 Display Modes
 == Режими дисплея
@@ -155,7 +155,7 @@ Game type
 == Тип гри
 
 Game types:
-== Тип гри:
+== Типи гри:
 
 General
 == Основні
@@ -164,7 +164,7 @@ Graphics
 == Графіка
 
 Grenade
-== Граната
+== Ракетниця
 
 Hammer
 == Молоток
@@ -215,7 +215,7 @@ Lht.
 == Яскравість
 
 Loading
-== Завантиження
+== Завантаження
 
 MOTD
 == MOTD
@@ -245,7 +245,7 @@ Mute when not active
 == Глушити звуки, коли гра неактивна
 
 Name
-== Імя
+== Ім'я
 
 News
 == Новини
@@ -332,7 +332,7 @@ Remote console
 == Консоль сервера
 
 Reset filter
-== Сикнути фільтри
+== Скинути фільтри
 
 Reset to defaults
 == Скинути налаштування
@@ -549,6 +549,9 @@ Delete demo
 Demo details
 == 
 
+Demofile: %s
+== 
+
 Free-View
 == 
 
@@ -588,6 +591,9 @@ Parent Folder
 Play background music
 == 
 
+Player country:
+== 
+
 Player options
 == 
 
@@ -615,7 +621,7 @@ Server address:
 Server filter
 == 
 
-Show friends
+Show friends only
 == 
 
 Show ingame HUD
@@ -636,6 +642,9 @@ Spectate previous
 Spectator mode
 == 
 
+Strict gametype filter
+== 
+
 The audio device couldn't be initialised.
 == 
 
diff --git a/data/mapres/jungle_deathtiles.png b/data/mapres/jungle_deathtiles.png
index ab1de32a..24ce0a63 100644
--- a/data/mapres/jungle_deathtiles.png
+++ b/data/mapres/jungle_deathtiles.png
Binary files differdiff --git a/data/mapres/jungle_doodads.png b/data/mapres/jungle_doodads.png
index 8123ad2a..cdafe4be 100644
--- a/data/mapres/jungle_doodads.png
+++ b/data/mapres/jungle_doodads.png
Binary files differdiff --git a/datasrc/compile.py b/datasrc/compile.py
index e8901c48..088e500a 100644
--- a/datasrc/compile.py
+++ b/datasrc/compile.py
@@ -75,7 +75,7 @@ if gen_client_content_header or gen_server_content_header:
 		EmitTypeDeclaration(content.__dict__[name])
 
 	# the container pointer
-	print('extern DATACONTAINER *g_pData;')
+	print('extern CDataContainer *g_pData;')
 
 	# enums
 	EmitEnum(["IMAGE_%s"%i.name.value.upper() for i in content.container.images.items], "NUM_IMAGES")
@@ -88,7 +88,7 @@ if gen_client_content_source or gen_server_content_source:
 	if gen_server_content_source:
 		print('#include "server_data.h"')
 	EmitDefinition(content.container, "datacontainer")
-	print('DATACONTAINER *g_pData = &datacontainer;')
+	print('CDataContainer *g_pData = &datacontainer;')
 
 # NETWORK
 if gen_network_header:
diff --git a/datasrc/content.py b/datasrc/content.py
index e714e8b8..aa6554f3 100644
--- a/datasrc/content.py
+++ b/datasrc/content.py
@@ -3,13 +3,13 @@ from datatypes import *
 
 class Sound(Struct):
 	def __init__(self, filename=""):
-		Struct.__init__(self, "SOUND")
+		Struct.__init__(self, "CDataSound")
 		self.id = Int(0)
 		self.filename = String(filename)
 
 class SoundSet(Struct):
 	def __init__(self, name="", files=[]):
-		Struct.__init__(self, "SOUNDSET")
+		Struct.__init__(self, "CDataSoundset")
 		self.name = String(name)
 		self.sounds = Array(Sound())
 		self.last = Int(-1)
@@ -18,21 +18,21 @@ class SoundSet(Struct):
 
 class Image(Struct):
 	def __init__(self, name="", filename=""):
-		Struct.__init__(self, "IMAGE")
+		Struct.__init__(self, "CDataImage")
 		self.name = String(name)
 		self.filename = String(filename)
 		self.id = Int(-1)
 
 class SpriteSet(Struct):
 	def __init__(self, name="", image=None, gridx=0, gridy=0):
-		Struct.__init__(self, "SPRITESET")
+		Struct.__init__(self, "CDataSpriteset")
 		self.image = Pointer(Image, image) # TODO
 		self.gridx = Int(gridx)
 		self.gridy = Int(gridy)
 
 class Sprite(Struct):
 	def __init__(self, name="", Set=None, x=0, y=0, w=0, h=0):
-		Struct.__init__(self, "SPRITE")
+		Struct.__init__(self, "CDataSprite")
 		self.name = String(name)
 		self.set = Pointer(SpriteSet, Set) # TODO
 		self.x = Int(x)
@@ -42,14 +42,14 @@ class Sprite(Struct):
 
 class Pickup(Struct):
 	def __init__(self, name="", respawntime=15, spawndelay=0):
-		Struct.__init__(self, "PICKUPSPEC")
+		Struct.__init__(self, "CDataPickupspec")
 		self.name = String(name)
 		self.respawntime = Int(respawntime)
 		self.spawndelay = Int(spawndelay)
 
 class AnimKeyframe(Struct):
 	def __init__(self, time=0, x=0, y=0, angle=0):
-		Struct.__init__(self, "ANIM_KEYFRAME")
+		Struct.__init__(self, "CAnimKeyframe")
 		self.time = Float(time)
 		self.x = Float(x)
 		self.y = Float(y)
@@ -57,12 +57,12 @@ class AnimKeyframe(Struct):
 
 class AnimSequence(Struct):
 	def __init__(self):
-		Struct.__init__(self, "ANIM_SEQUENCE")
+		Struct.__init__(self, "CAnimSequence")
 		self.frames = Array(AnimKeyframe())
 
 class Animation(Struct):
 	def __init__(self, name=""):
-		Struct.__init__(self, "ANIMATION")
+		Struct.__init__(self, "CAnimation")
 		self.name = String(name)
 		self.body = AnimSequence()
 		self.back_foot = AnimSequence()
@@ -71,7 +71,7 @@ class Animation(Struct):
 
 class WeaponSpec(Struct):
 	def __init__(self, container=None, name=""):
-		Struct.__init__(self, "WEAPONSPEC")
+		Struct.__init__(self, "CDataWeaponspec")
 		self.name = String(name)
 		self.sprite_body = Pointer(Sprite, Sprite())
 		self.sprite_cursor = Pointer(Sprite, Sprite())
@@ -101,12 +101,12 @@ class WeaponSpec(Struct):
 
 class Weapon_Hammer(Struct):
 	def __init__(self):
-		Struct.__init__(self, "WEAPONSPEC_HAMMER")
+		Struct.__init__(self, "CDataWeaponspecHammer")
 		self.base = Pointer(WeaponSpec, WeaponSpec())
 
 class Weapon_Gun(Struct):
 	def __init__(self):
-		Struct.__init__(self, "WEAPONSPEC_GUN")
+		Struct.__init__(self, "CDataWeaponspecGun")
 		self.base = Pointer(WeaponSpec, WeaponSpec())
 		self.curvature = Float(1.25)
 		self.speed = Float(2200)
@@ -114,7 +114,7 @@ class Weapon_Gun(Struct):
 
 class Weapon_Shotgun(Struct):
 	def __init__(self):
-		Struct.__init__(self, "WEAPONSPEC_SHOTGUN")
+		Struct.__init__(self, "CDataWeaponspecShotgun")
 		self.base = Pointer(WeaponSpec, WeaponSpec())
 		self.curvature = Float(1.25)
 		self.speed = Float(2200)
@@ -123,7 +123,7 @@ class Weapon_Shotgun(Struct):
 
 class Weapon_Grenade(Struct):
 	def __init__(self):
-		Struct.__init__(self, "WEAPONSPEC_GRENADE")
+		Struct.__init__(self, "CDataWeaponspecGrenade")
 		self.base = Pointer(WeaponSpec, WeaponSpec())
 		self.curvature = Float(7.0)
 		self.speed = Float(1000)
@@ -131,7 +131,7 @@ class Weapon_Grenade(Struct):
 
 class Weapon_Rifle(Struct):
 	def __init__(self):
-		Struct.__init__(self, "WEAPONSPEC_RIFLE")
+		Struct.__init__(self, "CDataWeaponspecRifle")
 		self.base = Pointer(WeaponSpec, WeaponSpec())
 		self.reach = Float(800.0)
 		self.bounce_delay = Int(150)
@@ -140,7 +140,7 @@ class Weapon_Rifle(Struct):
 
 class Weapon_Ninja(Struct):
 	def __init__(self):
-		Struct.__init__(self, "WEAPONSPEC_NINJA")
+		Struct.__init__(self, "CDataWeaponspecNinja")
 		self.base = Pointer(WeaponSpec, WeaponSpec())
 		self.duration = Int(15000)
 		self.movetime = Int(200)
@@ -148,7 +148,7 @@ class Weapon_Ninja(Struct):
 
 class Weapons(Struct):
 	def __init__(self):
-		Struct.__init__(self, "WEAPONSPECS")
+		Struct.__init__(self, "CDataWeaponspecs")
 		self.hammer = Weapon_Hammer()
 		self.gun = Weapon_Gun()
 		self.shotgun = Weapon_Shotgun()
@@ -159,7 +159,7 @@ class Weapons(Struct):
 
 class DataContainer(Struct):
 	def __init__(self):
-		Struct.__init__(self, "DATACONTAINER")
+		Struct.__init__(self, "CDataContainer")
 		self.sounds = Array(SoundSet())
 		self.images = Array(Image())
 		self.pickups = Array(Pickup())
diff --git a/datasrc/datatypes.py b/datasrc/datatypes.py
index 73719f59..5441e378 100644
--- a/datasrc/datatypes.py
+++ b/datasrc/datatypes.py
@@ -232,8 +232,8 @@ class NetObject:
 class NetEvent(NetObject):
 	def __init__(self, name, variables):
 		NetObject.__init__(self, name, variables)
-		self.base_struct_name = "NETEVENT_%s" % self.base.upper()
-		self.struct_name = "NETEVENT_%s" % self.name.upper()
+		self.base_struct_name = "CNetEvent_%s" % self.base
+		self.struct_name = "CNetEvent_%s" % self.name
 		self.enum_name = "NETEVENTTYPE_%s" % self.name.upper()
 
 class NetMessage(NetObject):
diff --git a/datasrc/network.py b/datasrc/network.py
index 5876610a..cd5f5aee 100644
--- a/datasrc/network.py
+++ b/datasrc/network.py
@@ -204,7 +204,7 @@ Objects = [
 		NetIntRange("m_ClientID", 0, 'MAX_CLIENTS-1'),
 	]),
 
-	NetEvent("SoundGlobal:Common", [
+	NetEvent("SoundGlobal:Common", [ #TODO 0.7: remove me
 		NetIntRange("m_SoundID", 0, 'NUM_SOUNDS-1'),
 	]),
 
diff --git a/other/sdl/vc2005libs/SDL.dll b/other/sdl/vc2005libs/SDL.dll
index 628cdfcf..429ae545 100644
--- a/other/sdl/vc2005libs/SDL.dll
+++ b/other/sdl/vc2005libs/SDL.dll
Binary files differdiff --git a/other/sdl/vc2005libs/SDL.lib b/other/sdl/vc2005libs/SDL.lib
index 5b3f17c5..f4e860f8 100644
--- a/other/sdl/vc2005libs/SDL.lib
+++ b/other/sdl/vc2005libs/SDL.lib
Binary files differdiff --git a/other/sdl/vc2005libs/SDLmain.lib b/other/sdl/vc2005libs/SDLmain.lib
index 945b9ad8..825c03b9 100644
--- a/other/sdl/vc2005libs/SDLmain.lib
+++ b/other/sdl/vc2005libs/SDLmain.lib
Binary files differdiff --git a/scripts/build.py b/scripts/build.py
index d43aa6f9..17911d84 100644
--- a/scripts/build.py
+++ b/scripts/build.py
@@ -9,6 +9,7 @@ elif sys.version_info[0] == 3:
 match = re.search('(.*)/', sys.argv[0])
 if match != None:
 	os.chdir(match.group(1))
+os.chdir('../')
 
 url_bam = "http://github.com/matricks/bam/zipball/master"
 url_teeworlds = "http://github.com/oy/teeworlds/zipball/master"
@@ -76,7 +77,7 @@ else:
 print("%s-%s-%s" % (name, version_teeworlds, platform))
 
 root_dir = os.getcwd() + os.sep
-work_dir = root_dir + "work"
+work_dir = root_dir + "scripts/work"
 
 def fetch_file(url):
 	try:
@@ -128,7 +129,7 @@ def bail(reason):
 
 def clean():
 	print("*** cleaning ***")
-	try: shutil.rmtree("work")
+	try: shutil.rmtree(work_dir)
 	except: pass
 
 def file_exists(file):
@@ -150,7 +151,7 @@ if flag_clean:
 	clean()
 
 # make dir
-try: os.mkdir("work")
+try: os.mkdir(work_dir)
 except: pass
 
 # change dir
diff --git a/scripts/cmd5.py b/scripts/cmd5.py
index 69eb83b8..9b4804c7 100644
--- a/scripts/cmd5.py
+++ b/scripts/cmd5.py
@@ -30,6 +30,6 @@ for filename in sys.argv[1:]:
 
 hash = hashlib.md5(f).hexdigest().lower()[16:]
 #TODO 0.7: improve nethash creation
-if hash == "6e28a475de43adfd":
+if hash == "63d6e69c6025feff":
 	hash = "626fce9a778df4d4"
 print('#define GAME_NETVERSION_HASH "%s"' % hash)
diff --git a/scripts/copyright.py b/scripts/copyright.py
index fade0b13..269ac7a3 100755
--- a/scripts/copyright.py
+++ b/scripts/copyright.py
@@ -2,9 +2,10 @@ import os, re, sys
 match = re.search('(.*)/', sys.argv[0])
 if match != None:
 	os.chdir(match.group(1))
+os.chdir('../')
 
 notice = [b"/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */\n", b"/* If you are missing that file, acquire a complete release at teeworlds.com.                */\n"]
-exclude = ["../src%sengine%sexternal" % (os.sep, os.sep), "../src%sosxlaunch" % os.sep]
+exclude = ["src%sengine%sexternal" % (os.sep, os.sep), "src%sosxlaunch" % os.sep]
 updated_files = 0
 
 def fix_copyright_notice(filename):
@@ -42,7 +43,7 @@ def fix_copyright_notice(filename):
 	updated_files += 1
 
 skip = False
-for root, dirs, files in os.walk("../src"):
+for root, dirs, files in os.walk("src"):
 	for excluding in exclude:
 		if root[:len(excluding)] == excluding:
 			skip = True
diff --git a/scripts/tw_api.py b/scripts/tw_api.py
index 054a4fa2..36cc5f5e 100644
--- a/scripts/tw_api.py
+++ b/scripts/tw_api.py
@@ -2,18 +2,57 @@
 from socket import *
 import struct
 import sys
+import threading
+import time
 
-def get_server_info(address, port):
+
+
+NUM_MASTERSERVERS = 4
+MASTERSERVER_PORT = 8300
+
+TIMEOUT = 2
+
+SERVERTYPE_NORMAL = 0
+SERVERTYPE_LEGACY = 1
+
+PACKET_GETLIST = "\x20\x00\x00\x00\x00\x00\xff\xff\xff\xffreqt"
+PACKET_GETLIST2 = "\x20\x00\x00\x00\x00\x00\xff\xff\xff\xffreq2"
+PACKET_GETINFO = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xffgief"
+PACKET_GETINFO2 = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xffgie2" + "\x00"
+PACKET_GETINFO3 = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xffgie3" + "\x00"
+
+
+
+class Server_Info(threading.Thread):
+
+	def __init__(self, address, type):
+		self.address = address
+		self.type = type
+		self.finished = False
+		threading.Thread.__init__(self, target = self.run)
+
+	def run(self):
+		self.info = None
+		if self.type == SERVERTYPE_NORMAL:
+			self.info = get_server_info3(self.address)
+		elif self.type == SERVERTYPE_LEGACY:
+			self.info = get_server_info(self.address)
+			if self.info:
+				self.info = get_server_info2(self.address)
+		self.finished = True
+
+
+def get_server_info(address):
 	try:
-		sock = socket(AF_INET, SOCK_DGRAM) 
-		sock.settimeout(1.5); 
-		sock.sendto("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xffgief", (address, port)) 
-		data, addr = sock.recvfrom(1024) 
-		sock.close() 
-	 
+		sock = socket(AF_INET, SOCK_DGRAM)
+		sock.settimeout(TIMEOUT);
+		sock.sendto(PACKET_GETINFO, address)
+		data, addr = sock.recvfrom(1024)
+		sock.close()
+
 		data = data[14:] # skip header
-	 	
 		slots = data.split("\x00")
+
 		server_info = {}
 		server_info["version"] = slots[0]
 		server_info["name"] = slots[1]
@@ -24,108 +63,208 @@ def get_server_info(address, port):
 		server_info["num_players"] = int(slots[6])
 		server_info["max_players"] = int(slots[7])
 		server_info["players"] = []
-		
+
 		for i in xrange(0, server_info["num_players"]):
 			player = {}
-			player["name"] = slots[8+i*2+1]
-			player["score"] = slots[8+i*2]
-			server_info["players"] += [player]
-			
-		gametypes = ["dm", "tdm", "ctf"]
-		try: server_info["gametype_name"] = gametypes[server_info["gametype_id"]]
-		except: server_info["gametype_name"] = "unknown"
-		
+			player["name"] = slots[8+i*2]
+			player["score"] = int(slots[8+i*2+1])
+			server_info["players"].append(player)
+
 		return server_info
+
 	except:
+		sock.close()
 		return None
-		
-def get_server_count(address, port):
+
+
+def get_server_info2(address):
 	try:
-		sock = socket(AF_INET, SOCK_DGRAM) 
-		sock.settimeout(1.5); 
-		sock.sendto("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xffcoun", (address, port)) 
-		data, addr = sock.recvfrom(1024) 
-		sock.close() 
-	 
+		sock = socket(AF_INET, SOCK_DGRAM)
+		sock.settimeout(TIMEOUT);
+		sock.sendto(PACKET_GETINFO2, address)
+		data, addr = sock.recvfrom(1024)
+		sock.close()
+
 		data = data[14:] # skip header
-		return struct.unpack(">H", data)[0]
+		slots = data.split("\x00")
+
+		server_info = {}
+		server_info["token"] = slots[0]
+		server_info["version"] = slots[1]
+		server_info["name"] = slots[2]
+		server_info["map"] = slots[3]
+		server_info["gametype"] = slots[4]
+		server_info["flags"] = int(slots[5])
+		server_info["progression"] = int(slots[6])
+		server_info["num_players"] = int(slots[7])
+		server_info["max_players"] = int(slots[8])
+		server_info["players"] = []
+
+		for i in xrange(0, server_info["num_players"]):
+			player = {}
+			player["name"] = slots[9+i*2]
+			player["score"] = int(slots[9+i*2+1])
+			server_info["players"].append(player)
+
+		return server_info
+
 	except:
-		return -1
+		sock.close()
+		return None
 
-def get_servers(address):
-	counter = 0
-	master_port = 8300
+
+def get_server_info3(address):
+	try:
+		sock = socket(AF_INET, SOCK_DGRAM)
+		sock.settimeout(TIMEOUT);
+		sock.sendto(PACKET_GETINFO3, address)
+		data, addr = sock.recvfrom(1400)
+		sock.close()
+
+		data = data[14:] # skip header
+		slots = data.split("\x00")
+
+		server_info = {}
+		server_info["token"] = slots[0]
+		server_info["version"] = slots[1]
+		server_info["name"] = slots[2]
+		server_info["map"] = slots[3]
+		server_info["gametype"] = slots[4]
+		server_info["flags"] = int(slots[5])
+		server_info["num_players"] = int(slots[6])
+		server_info["max_players"] = int(slots[7])
+		server_info["num_clients"] = int(slots[8])
+		server_info["max_clients"] = int(slots[9])
+		server_info["players"] = []
+
+		for i in xrange(0, server_info["num_clients"]):
+			player = {}
+			player["name"] = slots[10+i*5]
+			player["clan"] = slots[10+i*5+1]
+			player["country"] = int(slots[10+i*5+2])
+			player["score"] = int(slots[10+i*5+3])
+			if int(slots[10+i*5+4]):
+				player["player"] = True
+			else:
+				player["player"] = False
+			server_info["players"].append(player)
+
+		return server_info
+
+	except:
+		sock.close()
+		return None
+
+
+
+class Master_Server_Info(threading.Thread):
+
+	def __init__(self, address):
+		self.address = address
+		self.finished = False
+		threading.Thread.__init__(self, target = self.run)
+
+	def run(self):
+		self.servers = get_list(self.address) + get_list2(self.address)
+		self.finished = True
+
+
+def get_list(address):
 	servers = []
- 
+
 	try:
-		sock = socket(AF_INET, SOCK_DGRAM) 
-		sock.settimeout(1.5)
-		sock.sendto("\x20\x00\x00\x00\x00\x00\xff\xff\xff\xffreqt", (address, master_port)) 
-	
+		sock = socket(AF_INET, SOCK_DGRAM)
+		sock.settimeout(TIMEOUT)
+		sock.sendto(PACKET_GETLIST, address)
+
 		while 1:
 			data, addr = sock.recvfrom(1024)
+
+			data = data[14:]
+			num_servers = len(data) / 6
+
+			for n in range(0, num_servers):
+				ip = ".".join(map(str, map(ord, data[n*6:n*6+4])))
+				port = ord(data[n*6+5]) * 256 + ord(data[n*6+4])
+				servers += [[(ip, port), SERVERTYPE_LEGACY]]
+
+	except:
+		sock.close()
+
+	return servers
+
+
+def get_list2(address):
+	servers = []
+
+	try:
+		sock = socket(AF_INET, SOCK_DGRAM)
+		sock.settimeout(TIMEOUT)
+		sock.sendto(PACKET_GETLIST2, address)
+
+		while 1:
+			data, addr = sock.recvfrom(1400)
 			
-			data = data[14:] 
-			num_servers = len(data) / 6 
+			data = data[14:]
+			num_servers = len(data) / 18
 
 			for n in range(0, num_servers): 
-				ip = ".".join(map(str, map(ord, data[n*6:n*6+4]))) 
-				port = ord(data[n*6+5]) * 256 + ord(data[n*6+4]) 
-				servers += [[ip, port]]
+				if data[n*18:n*18+12] == "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff":
+					ip = ".".join(map(str, map(ord, data[n*18+12:n*18+16])))
+				else:
+					ip = ":".join(map(str, map(ord, data[n*18:n*18+16])))
+				port = (ord(data[n*18+16])<<8) + ord(data[n*18+17])
+				servers += [[(ip, port), SERVERTYPE_NORMAL]]
 
-	except: # timeout
+	except:
 		sock.close()
-		return servers
 
-def get_all_servers():
-	servers = []
-	for i in range(1, 16):
-		addr = "master%d.teeworlds.com"%i
-		list = get_servers(addr)
-		if list:
-			#print addr, "had", len(list), "servers"
-			servers += list
 	return servers
 
-servers = get_all_servers()
-total_players = 0
-players_per_versions = {}
-versions = {}
-gametypes = {}
-if 1:
-	for server in servers:
-		#print "checking server", server[0], server[1]
-		info = get_server_info(server[0], server[1])
-		if info:
-			total_players += len(info["players"])
-			if info["version"] in versions:
-				versions[info["version"]] += 1
-			else:
-				versions[info["version"]] = 1
 
-			if info["version"] in players_per_versions:
-				players_per_versions[info["version"]] += len(info["players"])
-			else:
-				players_per_versions[info["version"]] = len(info["players"])
 
-			if info["gametype"] in gametypes:
-				gametypes[info["gametype"]] += 1
-			else:
-				gametypes[info["gametype"]] = 1
+master_servers = []
+
+for i in range(1, NUM_MASTERSERVERS+1):
+	m = Master_Server_Info(("master%d.teeworlds.com"%i, MASTERSERVER_PORT))
+	master_servers.append(m)
+	m.start()
+	time.sleep(0.001) # avoid issues
+
+servers = []
+
+while len(master_servers) != 0:
+	if master_servers[0].finished == True:
+		if master_servers[0].servers:
+			servers += master_servers[0].servers
+		del master_servers[0]
+	time.sleep(0.001) # be nice
+
+servers_info = []
 
-print total_players
-				
-if 0:
-	print total_players, "on", len(servers), 'servers'
-	print "versions:"
-	for v in versions:
-		print "\t",v, versions[v]
+print str(len(servers)) + " servers"
+
+for server in servers:
+	s = Server_Info(server[0], server[1])
+	servers_info.append(s)
+	s.start()
+	time.sleep(0.001) # avoid issues
+
+num_players = 0
+num_clients = 0
+
+while len(servers_info) != 0:
+	if servers_info[0].finished == True:
+
+		if servers_info[0].info:
+			num_players += servers_info[0].info["num_players"]
+			if servers_info[0].type == SERVERTYPE_NORMAL:
+				num_clients += servers_info[0].info["num_clients"]
+			else:
+				num_clients += servers_info[0].info["num_players"]
 
-	print "players per version:"
-	for v in players_per_versions:
-		print "\t",v, players_per_versions[v]
+		del servers_info[0]
 
-	print "gametypes:"
-	for v in gametypes:
-		print "\t",v, gametypes[v]
+	time.sleep(0.001) # be nice
 
+print str(num_players) + " players and " + str(num_clients-num_players) + " spectators"
diff --git a/scripts/update_localization.py b/scripts/update_localization.py
index d6848411..08c1866e 100644
--- a/scripts/update_localization.py
+++ b/scripts/update_localization.py
@@ -1,7 +1,8 @@
 import os, re, sys
-match = re.search("(.*?)/[^/]*?$", sys.argv[0])
+match = re.search('(.*)/', sys.argv[0])
 if match != None:
-	os.chdir(os.getcwd() + "/" + match.group(1))
+	os.chdir(match.group(1))
+os.chdir('../')
 
 source_exts = [".c", ".cpp", ".h"]
 
@@ -13,7 +14,7 @@ def parse_source():
 			stringtable[fields[0]] = ""
 			process_line(fields[1])
 
-	for root, dirs, files in os.walk("../src"):
+	for root, dirs, files in os.walk("src"):
 		for name in files:
 			filename = os.path.join(root, name)
 			
@@ -80,11 +81,11 @@ srctable = parse_source()
 
 print("%-40s %8s %8s %8s" % ("filename", "total", "new", "old"))
 
-for filename in os.listdir("../data/languages"):
+for filename in os.listdir("data/languages"):
 	if not ".txt" in filename:
 		continue
 	if filename == "index.txt":
 		continue
 
-	filename = "../data/languages/" + filename
+	filename = "data/languages/" + filename
 	generate_languagefile(filename, srctable, load_languagefile(filename))
diff --git a/src/base/detect.h b/src/base/detect.h
index 0b66acef..f9ca5779 100644
--- a/src/base/detect.h
+++ b/src/base/detect.h
@@ -25,7 +25,7 @@
 #endif
 
 /* unix family */
-#if defined(__FreeBSD__)
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
 	#define CONF_FAMILY_UNIX 1
 	#define CONF_FAMILY_STRING "unix"
 	#define CONF_PLATFORM_FREEBSD 1
@@ -46,6 +46,13 @@
 	#define CONF_PLATFORM_STRING "linux"
 #endif
 
+#if defined(__GNU__) || defined(__gnu__)
+	#define CONF_FAMILY_UNIX 1
+	#define CONF_FAMILY_STRING "unix"
+	#define CONF_PLATFORM_HURD 1
+	#define CONF_PLATFORM_STRING "gnu"
+#endif
+
 #if defined(MACOSX) || defined(__APPLE__) || defined(__DARWIN__)
 	#define CONF_FAMILY_UNIX 1
 	#define CONF_FAMILY_STRING "unix"
@@ -69,35 +76,61 @@
 #endif
 
 
+/* use gcc endianness definitions when available */
+#if defined(__GNUC__) && !defined(__APPLE__)
+	#if defined(__FreeBSD__) || defined(__OpenBSD__)
+		#include <sys/endian.h>
+	#else
+		#include <endian.h>
+	#endif
+
+	#if __BYTE_ORDER == __LITTLE_ENDIAN
+		#define CONF_ARCH_ENDIAN_LITTLE 1
+	#elif __BYTE_ORDER == __BIG_ENDIAN
+		#define CONF_ARCH_ENDIAN_BIG 1
+	#endif
+#endif
+
+
 /* architectures */
 #if defined(i386) || defined(__i386__) || defined(__x86__) || defined(CONF_PLATFORM_WIN32)
 	#define CONF_ARCH_IA32 1
 	#define CONF_ARCH_STRING "ia32"
-	#define CONF_ARCH_ENDIAN_LITTLE 1
+	#if !defined(CONF_ARCH_ENDIAN_LITTLE) && !defined(CONF_ARCH_ENDIAN_BIG)
+		#define CONF_ARCH_ENDIAN_LITTLE 1
+	#endif
 #endif
 
 #if defined(__ia64__)
 	#define CONF_ARCH_IA64 1
 	#define CONF_ARCH_STRING "ia64"
-	#define CONF_ARCH_ENDIAN_LITTLE 1
+	#if !defined(CONF_ARCH_ENDIAN_LITTLE) && !defined(CONF_ARCH_ENDIAN_BIG)
+		#define CONF_ARCH_ENDIAN_LITTLE 1
+	#endif
 #endif
 
 #if defined(__amd64__) || defined(__x86_64__)
 	#define CONF_ARCH_AMD64 1
 	#define CONF_ARCH_STRING "amd64"
-	#define CONF_ARCH_ENDIAN_LITTLE 1
+	#if !defined(CONF_ARCH_ENDIAN_LITTLE) && !defined(CONF_ARCH_ENDIAN_BIG)
+		#define CONF_ARCH_ENDIAN_LITTLE 1
+	#endif
 #endif
 
 #if defined(__powerpc__) || defined(__ppc__)
 	#define CONF_ARCH_PPC 1
 	#define CONF_ARCH_STRING "ppc"
-	#define CONF_ARCH_ENDIAN_BIG 1
+	#if !defined(CONF_ARCH_ENDIAN_LITTLE) && !defined(CONF_ARCH_ENDIAN_BIG)
+		#define CONF_ARCH_ENDIAN_BIG 1
+	#endif
 #endif
 
 #if defined(__sparc__)
 	#define CONF_ARCH_SPARC 1
 	#define CONF_ARCH_STRING "sparc"
-	#define CONF_ARCH_ENDIAN_BIG 1
+	#if !defined(CONF_ARCH_ENDIAN_LITTLE) && !defined(CONF_ARCH_ENDIAN_BIG)
+		#define CONF_ARCH_ENDIAN_BIG 1
+	#endif
 #endif
 
 
diff --git a/src/base/math.h b/src/base/math.h
index 1234f681..d58dbf10 100644
--- a/src/base/math.h
+++ b/src/base/math.h
@@ -39,6 +39,17 @@ inline float frandom() { return rand()/(float)(RAND_MAX); }
 inline int f2fx(float v) { return (int)(v*(float)(1<<10)); }
 inline float fx2f(int v) { return v*(1.0f/(1<<10)); }
 
+inline int gcd(int a, int b)
+{
+	while(b != 0)
+	{
+		int c = a % b;
+		a = b;
+		b = c;
+	}
+	return a;
+}
+
 class fxp
 {
 	int value;
diff --git a/src/base/system.c b/src/base/system.c
index 94481f21..466e3ca6 100644
--- a/src/base/system.c
+++ b/src/base/system.c
@@ -42,10 +42,6 @@
 	#include <fcntl.h>
 	#include <direct.h>
 	#include <errno.h>
-
-	#ifndef EWOULDBLOCK
-		#define EWOULDBLOCK WSAEWOULDBLOCK
-	#endif
 #else
 	#error NOT IMPLEMENTED
 #endif
@@ -316,9 +312,12 @@ int io_seek(IOHANDLE io, int offset, int origin)
 		break;
 	case IOSEEK_END:
 		real_origin = SEEK_END;
+		break;
+	default:
+		return -1;
 	}
 
-	return fseek((FILE*)io, offset, origin);
+	return fseek((FILE*)io, offset, real_origin);
 }
 
 long int io_tell(IOHANDLE io)
@@ -408,6 +407,17 @@ void thread_sleep(int milliseconds)
 #endif
 }
 
+void thread_detach(void *thread)
+{
+#if defined(CONF_FAMILY_UNIX)
+	pthread_detach((pthread_t)(thread));
+#elif defined(CONF_FAMILY_WINDOWS)
+	CloseHandle(thread);
+#else
+	#error not implemented
+#endif
+}
+
 
 
 
@@ -571,13 +581,23 @@ int net_addr_comp(const NETADDR *a, const NETADDR *b)
 void net_addr_str(const NETADDR *addr, char *string, int max_length)
 {
 	if(addr->type == NETTYPE_IPV4)
-		str_format(string, max_length, "%d.%d.%d.%d:%d", addr->ip[0], addr->ip[1], addr->ip[2], addr->ip[3], addr->port);
+	{
+		if(addr->port != 0)
+			str_format(string, max_length, "%d.%d.%d.%d:%d", addr->ip[0], addr->ip[1], addr->ip[2], addr->ip[3], addr->port);
+		else
+			str_format(string, max_length, "%d.%d.%d.%d", addr->ip[0], addr->ip[1], addr->ip[2], addr->ip[3]);
+	}
 	else if(addr->type == NETTYPE_IPV6)
 	{
-		str_format(string, max_length, "[%x:%x:%x:%x:%x:%x:%x:%x]:%d",
-			(addr->ip[0]<<8)|addr->ip[1], (addr->ip[2]<<8)|addr->ip[3], (addr->ip[4]<<8)|addr->ip[5], (addr->ip[6]<<8)|addr->ip[7],
-			(addr->ip[8]<<8)|addr->ip[9], (addr->ip[10]<<8)|addr->ip[11], (addr->ip[12]<<8)|addr->ip[13], (addr->ip[14]<<8)|addr->ip[15],
-			addr->port);
+		if(addr->port != 0)
+			str_format(string, max_length, "[%x:%x:%x:%x:%x:%x:%x:%x]:%d",
+				(addr->ip[0]<<8)|addr->ip[1], (addr->ip[2]<<8)|addr->ip[3], (addr->ip[4]<<8)|addr->ip[5], (addr->ip[6]<<8)|addr->ip[7],
+				(addr->ip[8]<<8)|addr->ip[9], (addr->ip[10]<<8)|addr->ip[11], (addr->ip[12]<<8)|addr->ip[13], (addr->ip[14]<<8)|addr->ip[15],
+				addr->port);
+		else
+			str_format(string, max_length, "[%x:%x:%x:%x:%x:%x:%x:%x]",
+				(addr->ip[0]<<8)|addr->ip[1], (addr->ip[2]<<8)|addr->ip[3], (addr->ip[4]<<8)|addr->ip[5], (addr->ip[6]<<8)|addr->ip[7],
+				(addr->ip[8]<<8)|addr->ip[9], (addr->ip[10]<<8)|addr->ip[11], (addr->ip[12]<<8)|addr->ip[13], (addr->ip[14]<<8)|addr->ip[15]);
 	}
 	else
 		str_format(string, max_length, "unknown type %d", addr->type);
@@ -799,8 +819,6 @@ static int priv_net_close_all_sockets(NETSOCKET sock)
 static int priv_net_create_socket(int domain, int type, struct sockaddr *addr, int sockaddrlen)
 {
 	int sock, e;
-	unsigned long mode = 1;
-	int broadcast = 1;
 
 	/* create socket */
 	sock = socket(domain, type, 0);
@@ -811,11 +829,13 @@ static int priv_net_create_socket(int domain, int type, struct sockaddr *addr, i
 	}
 
 	/* set to IPv6 only if thats what we are creating */
+#if defined(IPV6_V6ONLY)	/* windows sdk 6.1 and higher */
 	if(domain == AF_INET6)
 	{
 		int ipv6only = 1;
 		setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&ipv6only, sizeof(ipv6only));
 	}
+#endif
 
 	/* bind the socket */
 	e = bind(sock, addr, sockaddrlen);
@@ -826,16 +846,6 @@ static int priv_net_create_socket(int domain, int type, struct sockaddr *addr, i
 		return -1;
 	}
 
-	/* set non-blocking */
-#if defined(CONF_FAMILY_WINDOWS)
-	ioctlsocket(sock, FIONBIO, &mode);
-#else
-	ioctl(sock, FIONBIO, &mode);
-#endif
-
-	/* set boardcast */
-	setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcast, sizeof(broadcast));
-
 	/* return the newly created socket */
 	return sock;
 }
@@ -844,6 +854,7 @@ NETSOCKET net_udp_create(NETADDR bindaddr)
 {
 	NETSOCKET sock = invalid_socket;
 	NETADDR tmpbindaddr = bindaddr;
+	int broadcast = 1;
 
 	if(bindaddr.type&NETTYPE_IPV4)
 	{
@@ -859,6 +870,12 @@ NETSOCKET net_udp_create(NETADDR bindaddr)
 			sock.type |= NETTYPE_IPV4;
 			sock.ipv4sock = socket;
 		}
+
+		/* set non-blocking */
+		net_set_non_blocking(sock);
+
+		/* set boardcast */
+		setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcast, sizeof(broadcast));
 	}
 
 	if(bindaddr.type&NETTYPE_IPV6)
@@ -875,6 +892,12 @@ NETSOCKET net_udp_create(NETADDR bindaddr)
 			sock.type |= NETTYPE_IPV6;
 			sock.ipv6sock = socket;
 		}
+
+		/* set non-blocking */
+		net_set_non_blocking(sock);
+
+		/* set boardcast */
+		setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcast, sizeof(broadcast));
 	}
 
 	/* return */
@@ -984,32 +1007,48 @@ int net_udp_close(NETSOCKET sock)
 	return priv_net_close_all_sockets(sock);
 }
 
-// TODO: make TCP stuff work again
-NETSOCKET net_tcp_create(const NETADDR *a)
+NETSOCKET net_tcp_create(NETADDR bindaddr)
 {
-	/* TODO: IPv6 support */
 	NETSOCKET sock = invalid_socket;
+	NETADDR tmpbindaddr = bindaddr;
 
-	if(a->type&NETTYPE_IPV4)
+	if(bindaddr.type&NETTYPE_IPV4)
 	{
 		struct sockaddr_in addr;
+		int socket = -1;
+
+		/* bind, we should check for error */
+		tmpbindaddr.type = NETTYPE_IPV4;
+		netaddr_to_sockaddr_in(&tmpbindaddr, &addr);
+		socket = priv_net_create_socket(AF_INET, SOCK_STREAM, (struct sockaddr *)&addr, sizeof(addr));
+		if(socket >= 0)
+		{
+			sock.type |= NETTYPE_IPV4;
+			sock.ipv4sock = socket;
+		}
+	}
 
-		/* create socket */
-		sock.type |= NETTYPE_IPV4;
-		sock.ipv4sock = socket(AF_INET, SOCK_STREAM, 0);
-		if(sock.ipv4sock < 0)
-			return invalid_socket;
+	if(bindaddr.type&NETTYPE_IPV6)
+	{
+		struct sockaddr_in6 addr;
+		int socket = -1;
 
 		/* bind, we should check for error */
-		netaddr_to_sockaddr_in(a, &addr);
-		bind(sock.ipv4sock, (struct sockaddr *)&addr, sizeof(addr));
+		tmpbindaddr.type = NETTYPE_IPV6;
+		netaddr_to_sockaddr_in6(&tmpbindaddr, &addr);
+		socket = priv_net_create_socket(AF_INET6, SOCK_STREAM, (struct sockaddr *)&addr, sizeof(addr));
+		if(socket >= 0)
+		{
+			sock.type |= NETTYPE_IPV6;
+			sock.ipv6sock = socket;
+		}
 	}
 
 	/* return */
 	return sock;
 }
 
-int net_tcp_set_non_blocking(NETSOCKET sock)
+int net_set_non_blocking(NETSOCKET sock)
 {
 	unsigned long mode = 1;
 	if(sock.ipv4sock >= 0)
@@ -1033,7 +1072,7 @@ int net_tcp_set_non_blocking(NETSOCKET sock)
 	return 0;
 }
 
-int net_tcp_set_blocking(NETSOCKET sock)
+int net_set_blocking(NETSOCKET sock)
 {
 	unsigned long mode = 0;
 	if(sock.ipv4sock >= 0)
@@ -1059,30 +1098,31 @@ int net_tcp_set_blocking(NETSOCKET sock)
 
 int net_tcp_listen(NETSOCKET sock, int backlog)
 {
+	int err = -1;
 	if(sock.ipv4sock >= 0)
-		listen(sock.ipv4sock, backlog);
+		err = listen(sock.ipv4sock, backlog);
 	if(sock.ipv6sock >= 0)
-		listen(sock.ipv6sock, backlog);
-	return 0;
+		err = listen(sock.ipv6sock, backlog);
+	return err;
 }
 
 int net_tcp_accept(NETSOCKET sock, NETSOCKET *new_sock, NETADDR *a)
 {
 	int s;
 	socklen_t sockaddr_len;
-	struct sockaddr addr;
 
 	*new_sock = invalid_socket;
 
-	sockaddr_len = sizeof(addr);
-
 	if(sock.ipv4sock >= 0)
 	{
-		s = accept(sock.ipv4sock, &addr, &sockaddr_len);
+		struct sockaddr_in addr;
+		sockaddr_len = sizeof(addr);
 
+		s = accept(sock.ipv4sock, (struct sockaddr *)&addr, &sockaddr_len);
+		
 		if (s != -1)
 		{
-			sockaddr_to_netaddr(&addr, a);
+			sockaddr_to_netaddr((const struct sockaddr *)&addr, a);
 			new_sock->type = NETTYPE_IPV4;
 			new_sock->ipv4sock = s;
 			return s;
@@ -1091,55 +1131,74 @@ int net_tcp_accept(NETSOCKET sock, NETSOCKET *new_sock, NETADDR *a)
 
 	if(sock.ipv6sock >= 0)
 	{
-		s = accept(sock.ipv6sock, &addr, &sockaddr_len);
+		struct sockaddr_in6 addr;
+		sockaddr_len = sizeof(addr);
 
+		s = accept(sock.ipv6sock, (struct sockaddr *)&addr, &sockaddr_len);
+		
 		if (s != -1)
 		{
-			sockaddr_to_netaddr(&addr, a);
+			sockaddr_to_netaddr((const struct sockaddr *)&addr, a);
 			new_sock->type = NETTYPE_IPV6;
 			new_sock->ipv6sock = s;
 			return s;
 		}
 	}
 
-	return 0;
+	return -1;
 }
 
 int net_tcp_connect(NETSOCKET sock, const NETADDR *a)
 {
-	/*struct sockaddr addr;
-	netaddr_to_sockaddr(a, &addr);
-	return connect(sock, &addr, sizeof(addr));
-	*/
-	return 0;
+	if(a->type&NETTYPE_IPV4)
+	{
+		struct sockaddr_in addr;
+		netaddr_to_sockaddr_in(a, &addr);
+		return connect(sock.ipv4sock, (struct sockaddr *)&addr, sizeof(addr));
+	}
+
+	if(a->type&NETTYPE_IPV6)
+	{
+		struct sockaddr_in6 addr;
+		netaddr_to_sockaddr_in6(a, &addr);
+		return connect(sock.ipv6sock, (struct sockaddr *)&addr, sizeof(addr));
+	}
+
+	return -1;
 }
 
-int net_tcp_connect_non_blocking(NETSOCKET sock, const NETADDR *a)
+int net_tcp_connect_non_blocking(NETSOCKET sock, NETADDR bindaddr)
 {
-	/* struct sockaddr addr; */
 	int res = 0;
 
-	/*
-	netaddr_to_sockaddr(a, &addr);
-	net_tcp_set_non_blocking(sock);
-	res = connect(sock, &addr, sizeof(addr));
-	net_tcp_set_blocking(sock);
-	*/
+	net_set_non_blocking(sock);
+	res = net_tcp_connect(sock, &bindaddr);
+	net_set_blocking(sock);
 
 	return res;
 }
 
 int net_tcp_send(NETSOCKET sock, const void *data, int size)
 {
-	int bytes = 0;
-	/* bytes = send((int)sock, (const char*)data, size, 0); */
+	int bytes = -1;
+
+	if(sock.ipv4sock >= 0)
+		bytes = send((int)sock.ipv4sock, (const char*)data, size, 0);
+	if(sock.ipv6sock >= 0)
+		bytes = send((int)sock.ipv6sock, (const char*)data, size, 0);
+		
 	return bytes;
 }
 
 int net_tcp_recv(NETSOCKET sock, void *data, int maxsize)
 {
-	int bytes = 0;
-	/* bytes = recv((int)sock, (char*)data, maxsize, 0); */
+	int bytes = -1;
+
+	if(sock.ipv4sock >= 0)
+		bytes = recv((int)sock.ipv4sock, (char*)data, maxsize, 0);
+	if(sock.ipv6sock >= 0)
+		bytes = recv((int)sock.ipv6sock, (char*)data, maxsize, 0);
+		
 	return bytes;
 }
 
@@ -1150,12 +1209,20 @@ int net_tcp_close(NETSOCKET sock)
 
 int net_errno()
 {
+#if defined(CONF_FAMILY_WINDOWS)
+	return WSAGetLastError();
+#else
 	return errno;
+#endif
 }
 
 int net_would_block()
 {
+#if defined(CONF_FAMILY_WINDOWS)
+	return net_errno() == WSAEWOULDBLOCK;
+#else
 	return net_errno() == EWOULDBLOCK;
+#endif
 }
 
 int net_init()
@@ -1522,6 +1589,15 @@ int str_comp_nocase(const char *a, const char *b)
 #endif
 }
 
+int str_comp_nocase_num(const char *a, const char *b, const int num)
+{
+#if defined(CONF_FAMILY_WINDOWS)
+	return _strnicmp(a, b, num);
+#else
+	return strncasecmp(a, b, num);
+#endif
+}
+
 int str_comp(const char *a, const char *b)
 {
 	return strcmp(a, b);
diff --git a/src/base/system.h b/src/base/system.h
index a486b89d..aaa5b43f 100644
--- a/src/base/system.h
+++ b/src/base/system.h
@@ -367,6 +367,16 @@ void thread_destroy(void *thread);
 */
 void thread_yield();
 
+/*
+	Function: thread_detach
+		Puts the thread in the detached thread, guaranteeing that
+		resources of the thread will be freed immediately when the
+		thread terminates.
+
+	Parameters:
+		thread - Thread to detach
+*/
+void thread_detach(void *thread);
 
 /* Group: Locks */
 typedef void* LOCK;
@@ -581,7 +591,7 @@ int net_udp_close(NETSOCKET sock);
 	Returns:
 		On success it returns an handle to the socket. On failure it returns NETSOCKET_INVALID.
 */
-NETSOCKET net_tcp_create(const NETADDR *a);
+NETSOCKET net_tcp_create(NETADDR bindaddr);
 
 /*
 	Function: net_tcp_listen
@@ -815,6 +825,25 @@ char *str_skip_whitespaces(char *str);
 */
 int str_comp_nocase(const char *a, const char *b);
 
+/*
+	Function: str_comp_nocase_num
+		Compares up to num characters of two strings case insensitive.
+
+	Parameters:
+		a - String to compare.
+		b - String to compare.
+		num - Maximum characters to compare
+
+	Returns:
+		<0 - String a is lesser than string b
+		0 - String a is equal to string b
+		>0 - String a is greater than string b
+
+	Remarks:
+		- Only garanted to work with a-z/A-Z.
+		- The strings are treated as zero-termineted strings.
+*/
+int str_comp_nocase_num(const char *a, const char *b, const int num);
 
 /*
 	Function: str_comp
@@ -1065,21 +1094,21 @@ int fs_rename(const char *oldname, const char *newname);
 
 	DOCTODO: serp
 */
-int net_tcp_connect_non_blocking(NETSOCKET sock, const NETADDR *a);
+int net_tcp_connect_non_blocking(NETSOCKET sock, NETADDR bindaddr);
 
 /*
-	Function: net_tcp_set_non_blocking
+	Function: net_set_non_blocking
 
 	DOCTODO: serp
 */
-int net_tcp_set_non_blocking(NETSOCKET sock);
+int net_set_non_blocking(NETSOCKET sock);
 
 /*
-	Function: net_tcp_set_non_blocking
+	Function: net_set_non_blocking
 
 	DOCTODO: serp
 */
-int net_tcp_set_blocking(NETSOCKET sock);
+int net_set_blocking(NETSOCKET sock);
 
 /*
 	Function: net_errno
diff --git a/src/engine/client.h b/src/engine/client.h
index 78741018..966e8f61 100644
--- a/src/engine/client.h
+++ b/src/engine/client.h
@@ -95,6 +95,7 @@ public:
 	// remote console
 	virtual void RconAuth(const char *pUsername, const char *pPassword) = 0;
 	virtual bool RconAuthed() = 0;
+	virtual bool UseTempRconCommands() = 0;
 	virtual void Rcon(const char *pLine) = 0;
 
 	// server info
@@ -159,7 +160,6 @@ public:
 	virtual int OnSnapInput(int *pData) = 0;
 
 	virtual const char *GetItemName(int Type) = 0;
-	virtual int GetCountryIndex(int Code) = 0;
 	virtual const char *Version() = 0;
 	virtual const char *NetVersion() = 0;
 
diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp
index 61a96eee..bec7d4d6 100644
--- a/src/engine/client/client.cpp
+++ b/src/engine/client/client.cpp
@@ -1,5 +1,6 @@
 /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
 /* If you are missing that file, acquire a complete release at teeworlds.com.                */
+#include <new>
 
 #include <stdlib.h> // qsort
 #include <stdarg.h>
@@ -26,6 +27,7 @@
 #include <engine/shared/compression.h>
 #include <engine/shared/datafile.h>
 #include <engine/shared/demo.h>
+#include <engine/shared/filecollection.h>
 #include <engine/shared/mapchecker.h>
 #include <engine/shared/network.h>
 #include <engine/shared/packer.h>
@@ -231,185 +233,6 @@ void CSmoothTime::Update(CGraph *pGraph, int64 Target, int TimeLeft, int AdjustD
 }
 
 
-bool CFileCollection::IsFilenameValid(const char *pFilename)
-{
-	if(str_length(pFilename) != m_FileDescLength+TIMESTAMP_LENGTH+m_FileExtLength ||
-		str_comp_num(pFilename, m_aFileDesc, m_FileDescLength) ||
-		str_comp(pFilename+m_FileDescLength+TIMESTAMP_LENGTH, m_aFileExt))
-		return false;
-
-	pFilename += m_FileDescLength;
-	if(pFilename[0] == '_' &&
-		pFilename[1] >= '0' && pFilename[1] <= '9' &&
-		pFilename[2] >= '0' && pFilename[2] <= '9' &&
-		pFilename[3] >= '0' && pFilename[3] <= '9' &&
-		pFilename[4] >= '0' && pFilename[4] <= '9' &&
-		pFilename[5] == '-' &&
-		pFilename[6] >= '0' && pFilename[6] <= '9' &&
-		pFilename[7] >= '0' && pFilename[7] <= '9' &&
-		pFilename[8] == '-' &&
-		pFilename[9] >= '0' && pFilename[9] <= '9' &&
-		pFilename[10] >= '0' && pFilename[10] <= '9' &&
-		pFilename[11] == '_' &&
-		pFilename[12] >= '0' && pFilename[12] <= '9' &&
-		pFilename[13] >= '0' && pFilename[13] <= '9' &&
-		pFilename[14] == '-' &&
-		pFilename[15] >= '0' && pFilename[15] <= '9' &&
-		pFilename[16] >= '0' && pFilename[16] <= '9' &&
-		pFilename[17] == '-' &&
-		pFilename[18] >= '0' && pFilename[18] <= '9' &&
-		pFilename[19] >= '0' && pFilename[19] <= '9')
-		return true;
-
-	return false;
-}
-
-int64 CFileCollection::ExtractTimestamp(const char *pTimestring)
-{
-	int64 Timestamp = pTimestring[0]-'0'; Timestamp <<= 4;
-	Timestamp += pTimestring[1]-'0'; Timestamp <<= 4;
-	Timestamp += pTimestring[2]-'0'; Timestamp <<= 4;
-	Timestamp += pTimestring[3]-'0'; Timestamp <<= 4;
-	Timestamp += pTimestring[5]-'0'; Timestamp <<= 4;
-	Timestamp += pTimestring[6]-'0'; Timestamp <<= 4;
-	Timestamp += pTimestring[8]-'0'; Timestamp <<= 4;
-	Timestamp += pTimestring[9]-'0'; Timestamp <<= 4;
-	Timestamp += pTimestring[11]-'0'; Timestamp <<= 4;
-	Timestamp += pTimestring[12]-'0'; Timestamp <<= 4;
-	Timestamp += pTimestring[14]-'0'; Timestamp <<= 4;
-	Timestamp += pTimestring[15]-'0'; Timestamp <<= 4;
-	Timestamp += pTimestring[17]-'0'; Timestamp <<= 4;
-	Timestamp += pTimestring[18]-'0';
-
-	return Timestamp;
-}
-
-void CFileCollection::BuildTimestring(int64 Timestamp, char *pTimestring)
-{
-	pTimestring[19] = 0;
-	pTimestring[18] = (Timestamp&0xF)+'0'; Timestamp >>= 4;
-	pTimestring[17] = (Timestamp&0xF)+'0'; Timestamp >>= 4;
-	pTimestring[16] = '-';
-	pTimestring[15] = (Timestamp&0xF)+'0'; Timestamp >>= 4;
-	pTimestring[14] = (Timestamp&0xF)+'0'; Timestamp >>= 4;
-	pTimestring[13] = '-';
-	pTimestring[12] = (Timestamp&0xF)+'0'; Timestamp >>= 4;
-	pTimestring[11] = (Timestamp&0xF)+'0'; Timestamp >>= 4;
-	pTimestring[10] = '_';
-	pTimestring[9] = (Timestamp&0xF)+'0'; Timestamp >>= 4;
-	pTimestring[8] = (Timestamp&0xF)+'0'; Timestamp >>= 4;
-	pTimestring[7] = '-';
-	pTimestring[6] = (Timestamp&0xF)+'0'; Timestamp >>= 4;
-	pTimestring[5] = (Timestamp&0xF)+'0'; Timestamp >>= 4;
-	pTimestring[4] = '-';
-	pTimestring[3] = (Timestamp&0xF)+'0'; Timestamp >>= 4;
-	pTimestring[2] = (Timestamp&0xF)+'0'; Timestamp >>= 4;
-	pTimestring[1] = (Timestamp&0xF)+'0'; Timestamp >>= 4;
-	pTimestring[0] = (Timestamp&0xF)+'0';
-}
-
-void CFileCollection::Init(IStorage *pStorage, const char *pPath, const char *pFileDesc, const char *pFileExt, int MaxEntries)
-{
-	mem_zero(m_aTimestamps, sizeof(m_aTimestamps));
-	m_NumTimestamps = 0;
-	m_MaxEntries = clamp(MaxEntries, 1, static_cast<int>(MAX_ENTRIES));
-	str_copy(m_aFileDesc, pFileDesc, sizeof(m_aFileDesc));
-	m_FileDescLength = str_length(m_aFileDesc);
-	str_copy(m_aFileExt, pFileExt, sizeof(m_aFileExt));
-	m_FileExtLength = str_length(m_aFileExt);
-	str_copy(m_aPath, pPath, sizeof(m_aPath));
-	m_pStorage = pStorage;
-
-	m_pStorage->ListDirectory(IStorage::TYPE_SAVE, m_aPath, FilelistCallback, this);
-}
-
-void CFileCollection::AddEntry(int64 Timestamp)
-{
-	if(m_NumTimestamps == 0)
-	{
-		// empty list
-		m_aTimestamps[m_NumTimestamps++] = Timestamp;
-	}
-	else
-	{
-		// remove old file
-		if(m_NumTimestamps == m_MaxEntries)
-		{
-			char aBuf[512];
-			char aTimestring[TIMESTAMP_LENGTH];
-			BuildTimestring(m_aTimestamps[0], aTimestring);
-			str_format(aBuf, sizeof(aBuf), "%s/%s_%s%s", m_aPath, m_aFileDesc, aTimestring, m_aFileExt);
-			m_pStorage->RemoveFile(aBuf, IStorage::TYPE_SAVE);
-		}
-
-		// add entry to the sorted list
-		if(m_aTimestamps[0] > Timestamp)
-		{
-			// first entry
-			if(m_NumTimestamps < m_MaxEntries)
-			{
-				mem_move(m_aTimestamps+1, m_aTimestamps, m_NumTimestamps*sizeof(int64));
-				m_aTimestamps[0] = Timestamp;
-				++m_NumTimestamps;
-			}
-		}
-		else if(m_aTimestamps[m_NumTimestamps-1] <= Timestamp)
-		{
-			// last entry
-			if(m_NumTimestamps == m_MaxEntries)
-			{
-				mem_move(m_aTimestamps, m_aTimestamps+1, (m_NumTimestamps-1)*sizeof(int64));
-				m_aTimestamps[m_NumTimestamps-1] = Timestamp;
-			}
-			else
-				m_aTimestamps[m_NumTimestamps++] = Timestamp;
-		}
-		else
-		{
-			// middle entry
-			int Left = 0, Right = m_NumTimestamps-1;
-			while(Right-Left > 1)
-			{
-				int Mid = (Left+Right)/2;
-				if(m_aTimestamps[Mid] > Timestamp)
-					Right = Mid;
-				else
-					Left = Mid;
-			}
-
-			if(m_NumTimestamps == m_MaxEntries)
-			{
-				mem_move(m_aTimestamps, m_aTimestamps+1, (Right-1)*sizeof(int64));
-				m_aTimestamps[Right-1] = Timestamp;
-			}
-			else
-			{
-				mem_move(m_aTimestamps+Right+1, m_aTimestamps+Right, (m_NumTimestamps-Right)*sizeof(int64));
-				m_aTimestamps[Right] = Timestamp;
-				++m_NumTimestamps;
-			}
-		}
-	}
-}
-
-int CFileCollection::FilelistCallback(const char *pFilename, int IsDir, int StorageType, void *pUser)
-{
-	CFileCollection *pThis = static_cast<CFileCollection *>(pUser);
-
-	// check for valid file name format
-	if(IsDir || !pThis->IsFilenameValid(pFilename))
-		return 0;
-
-	// extract the timestamp
-	int64 Timestamp = pThis->ExtractTimestamp(pFilename+pThis->m_FileDescLength+1);
-
-	// add the entry
-	pThis->AddEntry(Timestamp);
-
-	return 0;
-}
-
-
 CClient::CClient() : m_DemoPlayer(&m_SnapshotDelta), m_DemoRecorder(&m_SnapshotDelta)
 {
 	m_pEditor = 0;
@@ -467,6 +290,7 @@ CClient::CClient() : m_DemoPlayer(&m_SnapshotDelta), m_DemoRecorder(&m_SnapshotD
 	m_aServerAddressStr[0] = 0;
 
 	mem_zero(m_aSnapshots, sizeof(m_aSnapshots));
+	m_SnapshotStorage.Init();
 	m_RecivedSnapshots = 0;
 
 	m_VersionInfo.m_State = CVersionInfo::STATE_INIT;
@@ -536,11 +360,6 @@ void CClient::SendReady()
 	SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH);
 }
 
-bool CClient::RconAuthed()
-{
-	return m_RconAuthed;
-}
-
 void CClient::RconAuth(const char *pName, const char *pPassword)
 {
 	if(RconAuthed())
@@ -549,6 +368,7 @@ void CClient::RconAuth(const char *pName, const char *pPassword)
 	CMsgPacker Msg(NETMSG_RCON_AUTH);
 	Msg.AddString(pName, 32);
 	Msg.AddString(pPassword, 32);
+	Msg.AddInt(1);
 	SendMsgEx(&Msg, MSGFLAG_VITAL);
 }
 
@@ -693,12 +513,12 @@ void CClient::Connect(const char *pAddress)
 
 	ServerInfoRequest();
 
-	if(net_host_lookup(m_aServerAddressStr, &m_ServerAddress, NETTYPE_ALL) != 0)
+	if(net_host_lookup(m_aServerAddressStr, &m_ServerAddress, m_NetClient.NetType()) != 0)
 	{
 		char aBufMsg[256];
 		str_format(aBufMsg, sizeof(aBufMsg), "could not find the address of %s, connecting to localhost", aBuf);
 		m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "client", aBufMsg);
-		net_host_lookup("localhost", &m_ServerAddress, NETTYPE_ALL);
+		net_host_lookup("localhost", &m_ServerAddress, m_NetClient.NetType());
 	}
 
 	m_RconAuthed = 0;
@@ -726,6 +546,7 @@ void CClient::DisconnectWithReason(const char *pReason)
 
 	//
 	m_RconAuthed = 0;
+	m_pConsole->DeregisterTempAll();
 	m_NetClient.Disconnect(pReason);
 	SetState(IClient::STATE_OFFLINE);
 	m_pMap->Unload();
@@ -1149,7 +970,7 @@ void CClient::ProcessConnlessPacket(CNetChunk *pPacket)
 		{
 			str_copy(Info.m_aClients[i].m_aName, Up.GetString(CUnpacker::SANITIZE_CC|CUnpacker::SKIP_START_WHITESPACES), sizeof(Info.m_aClients[i].m_aName));
 			str_copy(Info.m_aClients[i].m_aClan, Up.GetString(CUnpacker::SANITIZE_CC|CUnpacker::SKIP_START_WHITESPACES), sizeof(Info.m_aClients[i].m_aClan));
-			Info.m_aClients[i].m_Country = GameClient()->GetCountryIndex(str_toint(Up.GetString()));
+			Info.m_aClients[i].m_Country = str_toint(Up.GetString());
 			Info.m_aClients[i].m_Score = str_toint(Up.GetString());
 			Info.m_aClients[i].m_Player = str_toint(Up.GetString()) != 0 ? true : false;
 		}
@@ -1313,11 +1134,28 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket)
 			CMsgPacker Msg(NETMSG_PING_REPLY);
 			SendMsgEx(&Msg, 0);
 		}
+		else if(Msg == NETMSG_RCON_CMD_ADD)
+		{
+			const char *pName = Unpacker.GetString(CUnpacker::SANITIZE_CC);
+			const char *pHelp = Unpacker.GetString(CUnpacker::SANITIZE_CC);
+			const char *pParams = Unpacker.GetString(CUnpacker::SANITIZE_CC);
+			if(Unpacker.Error() == 0)
+				m_pConsole->RegisterTemp(pName, pParams, CFGFLAG_SERVER, pHelp);
+		}
+		else if(Msg == NETMSG_RCON_CMD_REM)
+		{
+			const char *pName = Unpacker.GetString(CUnpacker::SANITIZE_CC);
+			if(Unpacker.Error() == 0)
+				m_pConsole->DeregisterTemp(pName);
+		}
 		else if(Msg == NETMSG_RCON_AUTH_STATUS)
 		{
 			int Result = Unpacker.GetInt();
 			if(Unpacker.Error() == 0)
 				m_RconAuthed = Result;
+			m_UseTempRconCommands = Unpacker.GetInt();
+			if(Unpacker.Error() != 0)
+				m_UseTempRconCommands = 0;
 		}
 		else if(Msg == NETMSG_RCON_LINE)
 		{
@@ -1718,7 +1556,6 @@ void CClient::Update()
 			int64 PrevtickStart = (m_aSnapshots[SNAP_PREV]->m_Tick)*time_freq()/50;
 			int PrevPredTick = (int)(PredNow*50/time_freq());
 			int NewPredTick = PrevPredTick+1;
-			static float LastPredintra = 0;
 
 			m_GameIntraTick = (Now - PrevtickStart) / (float)(CurtickStart-PrevtickStart);
 			m_GameTickTime = (Now - PrevtickStart) / (float)Freq; //(float)SERVER_TICK_SPEED);
@@ -1735,15 +1572,12 @@ void CClient::Update()
 
 			if(NewPredTick > m_PredTick)
 			{
-				LastPredintra = m_PredIntraTick;
 				m_PredTick = NewPredTick;
 				Repredict = 1;
 
 				// send input
 				SendInput();
 			}
-
-			LastPredintra = m_PredIntraTick;
 		}
 
 		// only do sane predictions
@@ -1803,7 +1637,7 @@ void CClient::VersionUpdate()
 {
 	if(m_VersionInfo.m_State == CVersionInfo::STATE_INIT)
 	{
-		Engine()->HostLookup(&m_VersionInfo.m_VersionServeraddr, g_Config.m_ClVersionServer, m_BindAddr.type);
+		Engine()->HostLookup(&m_VersionInfo.m_VersionServeraddr, g_Config.m_ClVersionServer, m_NetClient.NetType());
 		m_VersionInfo.m_State = CVersionInfo::STATE_START;
 	}
 	else if(m_VersionInfo.m_State == CVersionInfo::STATE_START)
@@ -1885,7 +1719,7 @@ void CClient::Run()
 	Input()->Init();
 
 	// start refreshing addresses while we load
-	MasterServer()->RefreshAddresses(m_BindAddr.type);
+	MasterServer()->RefreshAddresses(m_NetClient.NetType());
 
 	// init the editor
 	m_pEditor->Init();
@@ -2332,7 +2166,12 @@ void CClient::RegisterCommands()
 	m_pConsole->Chain("br_filter_serveraddress", ConchainServerBrowserUpdate, this);
 }
 
-static CClient m_Client;
+static CClient *CreateClient()
+{
+	CClient *pClient = static_cast<CClient *>(mem_alloc(sizeof(CClient), 1));
+	mem_zero(pClient, sizeof(CClient));
+	return new(pClient) CClient;
+}
 
 /*
 	Server Time
@@ -2363,9 +2202,10 @@ int main(int argc, const char **argv) // ignore_convention
 	}
 #endif
 
+	CClient *pClient = CreateClient();
 	IKernel *pKernel = IKernel::Create();
-	pKernel->RegisterInterface(&m_Client);
-	m_Client.RegisterInterfaces();
+	pKernel->RegisterInterface(pClient);
+	pClient->RegisterInterfaces();
 
 	// create the components
 	IEngine *pEngine = CreateEngine("Teeworlds");
@@ -2418,12 +2258,12 @@ int main(int argc, const char **argv) // ignore_convention
 	pEngineMasterServer->Load();
 
 	// register all console commands
-	m_Client.RegisterCommands();
+	pClient->RegisterCommands();
 
 	pKernel->RequestInterface<IGameClient>()->OnConsoleInit();
 
 	// init client's interfaces
-	m_Client.InitInterfaces();
+	pClient->InitInterfaces();
 
 	// execute config file
 	pConsole->ExecuteFile("settings.cfg");
@@ -2438,11 +2278,11 @@ int main(int argc, const char **argv) // ignore_convention
 	// restore empty config strings to their defaults
 	pConfig->RestoreStrings();
 
-	m_Client.Engine()->InitLogfile();
+	pClient->Engine()->InitLogfile();
 
 	// run the client
 	dbg_msg("client", "starting...");
-	m_Client.Run();
+	pClient->Run();
 
 	// write down the config and quit
 	pConfig->Save();
diff --git a/src/engine/client/client.h b/src/engine/client/client.h
index ddcd1167..1504a4e4 100644
--- a/src/engine/client/client.h
+++ b/src/engine/client/client.h
@@ -51,36 +51,6 @@ public:
 };
 
 
-class CFileCollection
-{
-	enum
-	{
-		MAX_ENTRIES=1000,
-		TIMESTAMP_LENGTH=20,	// _YYYY-MM-DD_HH-MM-SS
-	};
-
-	int64 m_aTimestamps[MAX_ENTRIES];
-	int m_NumTimestamps;
-	int m_MaxEntries;
-	char m_aFileDesc[128];
-	int m_FileDescLength;
-	char m_aFileExt[32];
-	int m_FileExtLength;
-	char m_aPath[512];
-	IStorage *m_pStorage;
-
-	bool IsFilenameValid(const char *pFilename);
-	int64 ExtractTimestamp(const char *pTimestring);
-	void BuildTimestring(int64 Timestamp, char *pTimestring);
-
-public:
-	void Init(IStorage *pStorage, const char *pPath, const char *pFileDesc, const char *pFileExt, int MaxEntries);
-	void AddEntry(int64 Timestamp);
-
-	static int FilelistCallback(const char *pFilename, int IsDir, int StorageType, void *pUser);
-};
-
-
 class CClient : public IClient, public CDemoPlayer::IListner
 {
 	// needed interfaces
@@ -118,7 +88,6 @@ class CClient : public IClient, public CDemoPlayer::IListner
 	float m_FrameTimeHigh;
 	int m_Frames;
 	NETADDR m_ServerAddress;
-	NETADDR m_BindAddr;
 	int m_WindowMustRefocus;
 	int m_SnapCrcErrors;
 	bool m_AutoScreenshotRecycle;
@@ -129,6 +98,7 @@ class CClient : public IClient, public CDemoPlayer::IListner
 	int m_AckGameTick;
 	int m_CurrentRecvTick;
 	int m_RconAuthed;
+	int m_UseTempRconCommands;
 
 	// version-checking
 	char m_aVersionStr[10];
@@ -221,7 +191,8 @@ public:
 	void SendEnterGame();
 	void SendReady();
 
-	virtual bool RconAuthed();
+	virtual bool RconAuthed() { return m_RconAuthed != 0; }
+	virtual bool UseTempRconCommands() { return m_UseTempRconCommands != 0; }
 	void RconAuth(const char *pName, const char *pPassword);
 	virtual void Rcon(const char *pCmd);
 
diff --git a/src/engine/client/friends.cpp b/src/engine/client/friends.cpp
index 9ef00ed1..eca39edb 100644
--- a/src/engine/client/friends.cpp
+++ b/src/engine/client/friends.cpp
@@ -1,6 +1,7 @@
 /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
 /* If you are missing that file, acquire a complete release at teeworlds.com.                */
 #include <base/math.h>
+#include <base/system.h>
 
 #include <engine/config.h>
 #include <engine/console.h>
@@ -11,6 +12,7 @@
 CFriends::CFriends()
 {
 	mem_zero(m_aFriends, sizeof(m_aFriends));
+	m_NumFriends = 0;
 }
 
 void CFriends::ConAddFriend(IConsole::IResult *pResult, void *pUserData)
@@ -44,12 +46,35 @@ const CFriendInfo *CFriends::GetFriend(int Index) const
 	return &m_aFriends[max(0, Index%m_NumFriends)];
 }
 
+int CFriends::GetFriendState(const char *pName, const char *pClan) const
+{
+	int Result = FRIEND_NO;
+	unsigned NameHash = str_quickhash(pName);
+	unsigned ClanHash = str_quickhash(pClan);
+	for(int i = 0; i < m_NumFriends; ++i)
+	{
+		if(m_aFriends[i].m_ClanHash == ClanHash)
+		{
+			if(m_aFriends[i].m_aName[0] == 0)
+				Result = FRIEND_CLAN;
+			else if(m_aFriends[i].m_NameHash == NameHash)
+			{
+				Result = FRIEND_PLAYER;
+				break;
+			}
+		}
+	}
+	return Result;
+}
+
 bool CFriends::IsFriend(const char *pName, const char *pClan, bool PlayersOnly) const
 {
+	unsigned NameHash = str_quickhash(pName);
+	unsigned ClanHash = str_quickhash(pClan);
 	for(int i = 0; i < m_NumFriends; ++i)
 	{
-		if(!str_comp(m_aFriends[i].m_aClan, pClan) &&
-			((!PlayersOnly && m_aFriends[i].m_aName[0] == 0) || !str_comp(m_aFriends[i].m_aName, pName)))
+		if(m_aFriends[i].m_ClanHash == ClanHash &&
+			((!PlayersOnly && m_aFriends[i].m_aName[0] == 0) || m_aFriends[i].m_NameHash == NameHash))
 			return true;
 	}
 	return false;
@@ -61,22 +86,28 @@ void CFriends::AddFriend(const char *pName, const char *pClan)
 		return;
 
 	// make sure we don't have the friend already
+	unsigned NameHash = str_quickhash(pName);
+	unsigned ClanHash = str_quickhash(pClan);
 	for(int i = 0; i < m_NumFriends; ++i)
 	{
-		if(!str_comp(m_aFriends[i].m_aName, pName) && !str_comp(m_aFriends[i].m_aClan, pClan))
+		if(m_aFriends[i].m_NameHash == NameHash && m_aFriends[i].m_ClanHash == ClanHash)
 			return;
 	}
 
 	str_copy(m_aFriends[m_NumFriends].m_aName, pName, sizeof(m_aFriends[m_NumFriends].m_aName));
 	str_copy(m_aFriends[m_NumFriends].m_aClan, pClan, sizeof(m_aFriends[m_NumFriends].m_aClan));
+	m_aFriends[m_NumFriends].m_NameHash = NameHash;
+	m_aFriends[m_NumFriends].m_ClanHash = ClanHash;
 	++m_NumFriends;
 }
 
 void CFriends::RemoveFriend(const char *pName, const char *pClan)
 {
+	unsigned NameHash = str_quickhash(pName);
+	unsigned ClanHash = str_quickhash(pClan);
 	for(int i = 0; i < m_NumFriends; ++i)
 	{
-		if(!str_comp(m_aFriends[i].m_aName, pName) && !str_comp(m_aFriends[i].m_aClan, pClan))
+		if(m_aFriends[i].m_NameHash == NameHash && m_aFriends[i].m_ClanHash == ClanHash)
 		{
 			RemoveFriend(i);
 			return;
diff --git a/src/engine/client/friends.h b/src/engine/client/friends.h
index be0cfa49..d4c539e1 100644
--- a/src/engine/client/friends.h
+++ b/src/engine/client/friends.h
@@ -22,6 +22,7 @@ public:
 
 	int NumFriends() const { return m_NumFriends; }
 	const CFriendInfo *GetFriend(int Index) const;
+	int GetFriendState(const char *pName, const char *pClan) const;
 	bool IsFriend(const char *pName, const char *pClan, bool PlayersOnly) const;
 
 	void AddFriend(const char *pName, const char *pClan);
diff --git a/src/engine/client/graphics.cpp b/src/engine/client/graphics.cpp
index 641f9dfb..d1f0b8a8 100644
--- a/src/engine/client/graphics.cpp
+++ b/src/engine/client/graphics.cpp
@@ -2,6 +2,7 @@
 /* If you are missing that file, acquire a complete release at teeworlds.com.                */
 
 #include <base/detect.h>
+#include <base/math.h>
 
 #include "SDL.h"
 
@@ -119,12 +120,39 @@ void CGraphics_OpenGL::Rotate4(const CPoint &rCenter, CVertex *pPoints)
 	}
 }
 
-unsigned char CGraphics_OpenGL::Sample(int w, int h, const unsigned char *pData, int u, int v, int Offset)
+unsigned char CGraphics_OpenGL::Sample(int w, int h, const unsigned char *pData, int u, int v, int Offset, int ScaleW, int ScaleH, int Bpp)
 {
-	return (pData[(v*w+u)*4+Offset]+
-	pData[(v*w+u+1)*4+Offset]+
-	pData[((v+1)*w+u)*4+Offset]+
-	pData[((v+1)*w+u+1)*4+Offset])/4;
+	int Value = 0;
+	for(int x = 0; x < ScaleW; x++)
+		for(int y = 0; y < ScaleH; y++)
+			Value += pData[((v+y)*w+(u+x))*Bpp+Offset];
+	return Value/(ScaleW*ScaleH);
+}
+
+unsigned char *CGraphics_OpenGL::Rescale(int Width, int Height, int NewWidth, int NewHeight, int Format, const unsigned char *pData)
+{
+	unsigned char *pTmpData;
+	int ScaleW = Width/NewWidth;
+	int ScaleH = Height/NewHeight;
+
+	int Bpp = 3;
+	if(Format == CImageInfo::FORMAT_RGBA)
+		Bpp = 4;
+
+	pTmpData = (unsigned char *)mem_alloc(NewWidth*NewHeight*Bpp, 1);
+
+	int c = 0;
+	for(int y = 0; y < NewHeight; y++)
+		for(int x = 0; x < NewWidth; x++, c++)
+		{
+			pTmpData[c*Bpp] = Sample(Width, Height, pData, x*ScaleW, y*ScaleH, 0, ScaleW, ScaleH, Bpp);
+			pTmpData[c*Bpp+1] = Sample(Width, Height, pData, x*ScaleW, y*ScaleH, 1, ScaleW, ScaleH, Bpp);
+			pTmpData[c*Bpp+2] = Sample(Width, Height, pData, x*ScaleW, y*ScaleH, 2, ScaleW, ScaleH, Bpp);
+			if(Bpp == 4)
+				pTmpData[c*Bpp+3] = Sample(Width, Height, pData, x*ScaleW, y*ScaleH, 3, ScaleW, ScaleH, Bpp);
+		}
+
+	return pTmpData;
 }
 
 CGraphics_OpenGL::CGraphics_OpenGL()
@@ -151,7 +179,16 @@ CGraphics_OpenGL::CGraphics_OpenGL()
 
 void CGraphics_OpenGL::ClipEnable(int x, int y, int w, int h)
 {
-	//if(no_gfx) return;
+	if(x < 0)
+		w += x;
+	if(y < 0)
+		h += y;
+
+	x = clamp(x, 0, ScreenWidth());
+	y = clamp(y, 0, ScreenHeight());
+	w = clamp(w, 0, ScreenWidth()-x);
+	h = clamp(h, 0, ScreenHeight()-y);
+
 	glScissor(x, ScreenHeight()-(y+h), w, h);
 	glEnable(GL_SCISSOR_TEST);
 }
@@ -205,21 +242,21 @@ void CGraphics_OpenGL::GetScreen(float *pTopLeftX, float *pTopLeftY, float *pBot
 
 void CGraphics_OpenGL::LinesBegin()
 {
-	dbg_assert(m_Drawing == 0, "called begin twice");
+	dbg_assert(m_Drawing == 0, "called Graphics()->LinesBegin twice");
 	m_Drawing = DRAWING_LINES;
 	SetColor(1,1,1,1);
 }
 
 void CGraphics_OpenGL::LinesEnd()
 {
-	dbg_assert(m_Drawing == DRAWING_LINES, "called end without begin");
+	dbg_assert(m_Drawing == DRAWING_LINES, "called Graphics()->LinesEnd without begin");
 	Flush();
 	m_Drawing = 0;
 }
 
 void CGraphics_OpenGL::LinesDraw(const CLineItem *pArray, int Num)
 {
-	dbg_assert(m_Drawing == DRAWING_LINES, "called draw without begin");
+	dbg_assert(m_Drawing == DRAWING_LINES, "called Graphics()->LinesDraw without begin");
 
 	for(int i = 0; i < Num; ++i)
 	{
@@ -272,28 +309,23 @@ int CGraphics_OpenGL::LoadTextureRaw(int Width, int Height, int Format, const vo
 	m_aTextures[Tex].m_Next = -1;
 
 	// resample if needed
-	if(!(Flags&TEXLOAD_NORESAMPLE) && g_Config.m_GfxTextureQuality==0)
+	if(!(Flags&TEXLOAD_NORESAMPLE) && (Format == CImageInfo::FORMAT_RGBA || Format == CImageInfo::FORMAT_RGB))
 	{
-		if(Width > 16 && Height > 16 && Format == CImageInfo::FORMAT_RGBA)
+		if(Width > GL_MAX_TEXTURE_SIZE || Height > GL_MAX_TEXTURE_SIZE)
+		{
+			int NewWidth = min(Width, GL_MAX_TEXTURE_SIZE);
+			int NewHeight = min(Height, GL_MAX_TEXTURE_SIZE);
+			pTmpData = Rescale(Width, Height, NewWidth, NewHeight, Format, pTexData);
+			pTexData = pTmpData;
+			Width = NewWidth;
+			Height = NewHeight;
+		}
+		else if(Width > 16 && Height > 16 && g_Config.m_GfxTextureQuality == 0)
 		{
-			unsigned char *pTmpData;
-			int c = 0;
-			int x, y;
-
-			pTmpData = (unsigned char *)mem_alloc(Width*Height*4, 1);
-
-			Width/=2;
-			Height/=2;
-
-			for(y = 0; y < Height; y++)
-				for(x = 0; x < Width; x++, c++)
-				{
-					pTmpData[c*4] = Sample(Width*2, Height*2, pTexData, x*2,y*2, 0);
-					pTmpData[c*4+1] = Sample(Width*2, Height*2, pTexData, x*2,y*2, 1);
-					pTmpData[c*4+2] = Sample(Width*2, Height*2, pTexData, x*2,y*2, 2);
-					pTmpData[c*4+3] = Sample(Width*2, Height*2, pTexData, x*2,y*2, 3);
-				}
+			pTmpData = Rescale(Width, Height, Width/2, Height/2, Format, pTexData);
 			pTexData = pTmpData;
+			Width /= 2;
+			Height /= 2;
 		}
 	}
 
@@ -488,7 +520,7 @@ void CGraphics_OpenGL::Clear(float r, float g, float b)
 
 void CGraphics_OpenGL::QuadsBegin()
 {
-	dbg_assert(m_Drawing == 0, "called quads_begin twice");
+	dbg_assert(m_Drawing == 0, "called Graphics()->QuadsBegin twice");
 	m_Drawing = DRAWING_QUADS;
 
 	QuadsSetSubset(0,0,1,1);
@@ -498,7 +530,7 @@ void CGraphics_OpenGL::QuadsBegin()
 
 void CGraphics_OpenGL::QuadsEnd()
 {
-	dbg_assert(m_Drawing == DRAWING_QUADS, "called quads_end without begin");
+	dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsEnd without begin");
 	Flush();
 	m_Drawing = 0;
 }
@@ -511,7 +543,7 @@ void CGraphics_OpenGL::QuadsSetRotation(float Angle)
 
 void CGraphics_OpenGL::SetColorVertex(const CColorVertex *pArray, int Num)
 {
-	dbg_assert(m_Drawing != 0, "called gfx_quads_setcolorvertex without begin");
+	dbg_assert(m_Drawing != 0, "called Graphics()->SetColorVertex without begin");
 
 	for(int i = 0; i < Num; ++i)
 	{
@@ -524,7 +556,7 @@ void CGraphics_OpenGL::SetColorVertex(const CColorVertex *pArray, int Num)
 
 void CGraphics_OpenGL::SetColor(float r, float g, float b, float a)
 {
-	dbg_assert(m_Drawing != 0, "called gfx_quads_setcolor without begin");
+	dbg_assert(m_Drawing != 0, "called Graphics()->SetColor without begin");
 	CColorVertex Array[4] = {
 		CColorVertex(0, r, g, b, a),
 		CColorVertex(1, r, g, b, a),
@@ -570,7 +602,7 @@ void CGraphics_OpenGL::QuadsDrawTL(const CQuadItem *pArray, int Num)
 	CPoint Center;
 	Center.z = 0;
 
-	dbg_assert(m_Drawing == DRAWING_QUADS, "called quads_draw without begin");
+	dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsDrawTL without begin");
 
 	for(int i = 0; i < Num; ++i)
 	{
@@ -608,7 +640,7 @@ void CGraphics_OpenGL::QuadsDrawTL(const CQuadItem *pArray, int Num)
 
 void CGraphics_OpenGL::QuadsDrawFreeform(const CFreeformItem *pArray, int Num)
 {
-	dbg_assert(m_Drawing == DRAWING_QUADS, "called quads_draw_freeform without begin");
+	dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsDrawFreeform without begin");
 
 	for(int i = 0; i < Num; ++i)
 	{
diff --git a/src/engine/client/graphics.h b/src/engine/client/graphics.h
index 4367ccfc..95e9769a 100644
--- a/src/engine/client/graphics.h
+++ b/src/engine/client/graphics.h
@@ -66,7 +66,8 @@ protected:
 	void AddVertices(int Count);
 	void Rotate4(const CPoint &rCenter, CVertex *pPoints);
 
-	static unsigned char Sample(int w, int h, const unsigned char *pData, int u, int v, int Offset);
+	static unsigned char Sample(int w, int h, const unsigned char *pData, int u, int v, int Offset, int ScaleW, int ScaleH, int Bpp);
+	static unsigned char *Rescale(int Width, int Height, int NewWidth, int NewHeight, int Format, const unsigned char *pData);
 public:
 	CGraphics_OpenGL();
 
diff --git a/src/engine/client/serverbrowser.cpp b/src/engine/client/serverbrowser.cpp
index 64d06116..ddfc7597 100644
--- a/src/engine/client/serverbrowser.cpp
+++ b/src/engine/client/serverbrowser.cpp
@@ -144,61 +144,65 @@ void CServerBrowser::Filter()
 	{
 		int Filtered = 0;
 
-		if(g_Config.m_BrFilterFriends)
+		if(g_Config.m_BrFilterEmpty && ((g_Config.m_BrFilterSpectators && m_ppServerlist[i]->m_Info.m_NumPlayers == 0) || m_ppServerlist[i]->m_Info.m_NumClients == 0))
+			Filtered = 1;
+		else if(g_Config.m_BrFilterFull && ((g_Config.m_BrFilterSpectators && m_ppServerlist[i]->m_Info.m_NumPlayers == m_ppServerlist[i]->m_Info.m_MaxPlayers) ||
+				m_ppServerlist[i]->m_Info.m_NumClients == m_ppServerlist[i]->m_Info.m_MaxClients))
+			Filtered = 1;
+		else if(g_Config.m_BrFilterPw && m_ppServerlist[i]->m_Info.m_Flags&SERVER_FLAG_PASSWORD)
+			Filtered = 1;
+		else if(g_Config.m_BrFilterPure &&
+			(str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "DM") != 0 &&
+			str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "TDM") != 0 &&
+			str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "CTF") != 0))
+		{
+			Filtered = 1;
+		}
+		else if(g_Config.m_BrFilterPureMap &&
+			!(str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm1") == 0 ||
+			str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm2") == 0 ||
+			str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm6") == 0 ||
+			str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm7") == 0 ||
+			str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm8") == 0 ||
+			str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm9") == 0 ||
+			str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf1") == 0 ||
+			str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf2") == 0 ||
+			str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf3") == 0 ||
+			str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf4") == 0 ||
+			str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf5") == 0 ||
+			str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf6") == 0 ||
+			str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf7") == 0)
+		)
 		{
 			Filtered = 1;
-			for(p = 0; p < m_ppServerlist[i]->m_Info.m_NumClients; p++)
-			{
-				if(m_pFriends->IsFriend(m_ppServerlist[i]->m_Info.m_aClients[p].m_aName, m_ppServerlist[i]->m_Info.m_aClients[p].m_aClan, false))
-				{
-					Filtered = 0;
-					break;
-				}
-			}
 		}
+		else if(g_Config.m_BrFilterPing < m_ppServerlist[i]->m_Info.m_Latency)
+			Filtered = 1;
+		else if(g_Config.m_BrFilterCompatversion && str_comp_num(m_ppServerlist[i]->m_Info.m_aVersion, m_aNetVersion, 3) != 0)
+			Filtered = 1;
+		else if(g_Config.m_BrFilterServerAddress[0] && !str_find_nocase(m_ppServerlist[i]->m_Info.m_aAddress, g_Config.m_BrFilterServerAddress))
+			Filtered = 1;
+		else if(g_Config.m_BrFilterGametypeStrict && g_Config.m_BrFilterGametype[0] && str_comp_nocase(m_ppServerlist[i]->m_Info.m_aGameType, g_Config.m_BrFilterGametype))
+			Filtered = 1;
+		else if(!g_Config.m_BrFilterGametypeStrict && g_Config.m_BrFilterGametype[0] && !str_find_nocase(m_ppServerlist[i]->m_Info.m_aGameType, g_Config.m_BrFilterGametype))
+			Filtered = 1;
 		else
 		{
-			if(g_Config.m_BrFilterEmpty && ((g_Config.m_BrFilterSpectators && m_ppServerlist[i]->m_Info.m_NumPlayers == 0) || m_ppServerlist[i]->m_Info.m_NumClients == 0))
-				Filtered = 1;
-			else if(g_Config.m_BrFilterFull && ((g_Config.m_BrFilterSpectators && m_ppServerlist[i]->m_Info.m_NumPlayers == m_ppServerlist[i]->m_Info.m_MaxPlayers) ||
-					m_ppServerlist[i]->m_Info.m_NumClients == m_ppServerlist[i]->m_Info.m_MaxClients))
-				Filtered = 1;
-			else if(g_Config.m_BrFilterPw && m_ppServerlist[i]->m_Info.m_Flags&SERVER_FLAG_PASSWORD)
-				Filtered = 1;
-			else if(g_Config.m_BrFilterPure &&
-				(str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "DM") != 0 &&
-				str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "TDM") != 0 &&
-				str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "CTF") != 0))
-			{
-				Filtered = 1;
-			}
-			else if(g_Config.m_BrFilterPureMap &&
-				!(str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm1") == 0 ||
-				str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm2") == 0 ||
-				str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm6") == 0 ||
-				str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm7") == 0 ||
-				str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm8") == 0 ||
-				str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm9") == 0 ||
-				str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf1") == 0 ||
-				str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf2") == 0 ||
-				str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf3") == 0 ||
-				str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf4") == 0 ||
-				str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf5") == 0 ||
-				str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf6") == 0 ||
-				str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf7") == 0)
-			)
+			if(g_Config.m_BrFilterCountry)
 			{
 				Filtered = 1;
+				// match against player country
+				for(p = 0; p < m_ppServerlist[i]->m_Info.m_NumClients; p++)
+				{
+					if(m_ppServerlist[i]->m_Info.m_aClients[p].m_Country == g_Config.m_BrFilterCountryIndex)
+					{
+						Filtered = 0;
+						break;
+					}
+				}
 			}
-			else if(g_Config.m_BrFilterPing < m_ppServerlist[i]->m_Info.m_Latency)
-				Filtered = 1;
-			else if(g_Config.m_BrFilterCompatversion && str_comp_num(m_ppServerlist[i]->m_Info.m_aVersion, m_aNetVersion, 3) != 0)
-				Filtered = 1;
-			else if(g_Config.m_BrFilterServerAddress[0] && !str_find_nocase(m_ppServerlist[i]->m_Info.m_aAddress, g_Config.m_BrFilterServerAddress))
-				Filtered = 1;
-			else if(g_Config.m_BrFilterGametype[0] && !str_find_nocase(m_ppServerlist[i]->m_Info.m_aGameType, g_Config.m_BrFilterGametype))
-				Filtered = 1;
-			else if(g_Config.m_BrFilterString[0] != 0)
+
+			if(!Filtered && g_Config.m_BrFilterString[0] != 0)
 			{
 				int MatchFound = 0;
 
@@ -236,7 +240,19 @@ void CServerBrowser::Filter()
 		}
 
 		if(Filtered == 0)
-			m_pSortedServerlist[m_NumSortedServers++] = i;
+		{
+			// check for friend
+			m_ppServerlist[i]->m_Info.m_FriendState = IFriends::FRIEND_NO;
+			for(p = 0; p < m_ppServerlist[i]->m_Info.m_NumClients; p++)
+			{
+				m_ppServerlist[i]->m_Info.m_aClients[p].m_FriendState = m_pFriends->GetFriendState(m_ppServerlist[i]->m_Info.m_aClients[p].m_aName,
+					m_ppServerlist[i]->m_Info.m_aClients[p].m_aClan);
+				m_ppServerlist[i]->m_Info.m_FriendState = max(m_ppServerlist[i]->m_Info.m_FriendState, m_ppServerlist[i]->m_Info.m_aClients[p].m_FriendState);
+			}
+
+			if(!g_Config.m_BrFilterFriends || m_ppServerlist[i]->m_Info.m_FriendState != IFriends::FRIEND_NO)
+				m_pSortedServerlist[m_NumSortedServers++] = i;
+		}
 	}
 }
 
@@ -252,7 +268,9 @@ int CServerBrowser::SortHash() const
 	i |= g_Config.m_BrFilterCompatversion<<10;
 	i |= g_Config.m_BrFilterPure<<11;
 	i |= g_Config.m_BrFilterPureMap<<12;
-	i |= g_Config.m_BrFilterPing<<18;
+	i |= g_Config.m_BrFilterGametypeStrict<<13;
+	i |= g_Config.m_BrFilterCountry<<14;
+	i |= g_Config.m_BrFilterPing<<15;
 	return i;
 }
 
diff --git a/src/engine/client/sound.cpp b/src/engine/client/sound.cpp
index 4678bb8a..c2ca91f7 100644
--- a/src/engine/client/sound.cpp
+++ b/src/engine/client/sound.cpp
@@ -31,6 +31,7 @@ struct CSample
 	int m_Channels;
 	int m_LoopStart;
 	int m_LoopEnd;
+	int m_PausedAt;
 };
 
 struct CChannel
@@ -54,7 +55,6 @@ static CVoice m_aVoices[NUM_VOICES] = { {0} };
 static CChannel m_aChannels[NUM_CHANNELS] = { {255, 0} };
 
 static LOCK m_SoundLock = 0;
-static int m_SoundEnabled = 0;
 
 static int m_CenterX = 0;
 static int m_CenterY = 0;
@@ -198,6 +198,7 @@ static void SdlCallback(void *pUnused, Uint8 *pStream, int Len)
 
 int CSound::Init()
 {
+	m_SoundEnabled = 0;
 	m_pGraphics = Kernel()->RequestInterface<IEngineGraphics>();
 	m_pStorage = Kernel()->RequestInterface<IStorage>();
 	
@@ -393,6 +394,7 @@ int CSound::LoadWV(const char *pFilename)
 		pSample->m_NumFrames = m_aSamples;
 		pSample->m_LoopStart = -1;
 		pSample->m_LoopEnd = -1;
+		pSample->m_PausedAt = 0;
 	}
 	else
 	{
@@ -446,7 +448,10 @@ int CSound::Play(int ChannelID, int SampleID, int Flags, float x, float y)
 	{
 		m_aVoices[VoiceID].m_pSample = &m_aSamples[SampleID];
 		m_aVoices[VoiceID].m_pChannel = &m_aChannels[ChannelID];
-		m_aVoices[VoiceID].m_Tick = 0;
+		if(Flags & FLAG_LOOP)
+			m_aVoices[VoiceID].m_Tick = m_aSamples[SampleID].m_PausedAt;
+		else
+			m_aVoices[VoiceID].m_Tick = 0;
 		m_aVoices[VoiceID].m_Vol = 255;
 		m_aVoices[VoiceID].m_Flags = Flags;
 		m_aVoices[VoiceID].m_X = (int)x;
@@ -475,7 +480,13 @@ void CSound::Stop(int SampleID)
 	for(int i = 0; i < NUM_VOICES; i++)
 	{
 		if(m_aVoices[i].m_pSample == pSample)
+		{
+			if(m_aVoices[i].m_Flags & FLAG_LOOP)
+				m_aVoices[i].m_pSample->m_PausedAt = m_aVoices[i].m_Tick;
+			else
+				m_aVoices[i].m_pSample->m_PausedAt = 0;
 			m_aVoices[i].m_pSample = 0;
+		}
 	}
 	lock_release(m_SoundLock);
 }
@@ -486,6 +497,13 @@ void CSound::StopAll()
 	lock_wait(m_SoundLock);
 	for(int i = 0; i < NUM_VOICES; i++)
 	{
+		if(m_aVoices[i].m_pSample)
+		{
+			if(m_aVoices[i].m_Flags & FLAG_LOOP)
+				m_aVoices[i].m_pSample->m_PausedAt = m_aVoices[i].m_Tick;
+			else
+				m_aVoices[i].m_pSample->m_PausedAt = 0;
+		}
 		m_aVoices[i].m_pSample = 0;
 	}
 	lock_release(m_SoundLock);
diff --git a/src/engine/client/sound.h b/src/engine/client/sound.h
index 3cc84d4d..8112427c 100644
--- a/src/engine/client/sound.h
+++ b/src/engine/client/sound.h
@@ -7,6 +7,8 @@
 
 class CSound : public IEngineSound
 {
+	int m_SoundEnabled;
+
 public:
 	IEngineGraphics *m_pGraphics;
 	IStorage *m_pStorage;
@@ -23,6 +25,8 @@ public:
 	static IOHANDLE ms_File;
 	static int ReadData(void *pBuffer, int Size);
 
+	virtual bool IsSoundEnabled() { return m_SoundEnabled != 0; }
+
 	virtual int LoadWV(const char *pFilename);
 
 	virtual void SetListenerPos(float x, float y);
diff --git a/src/engine/client/text.cpp b/src/engine/client/text.cpp
index b0896b92..4a3a2eb3 100644
--- a/src/engine/client/text.cpp
+++ b/src/engine/client/text.cpp
@@ -570,7 +570,6 @@ public:
 		float DrawX = 0.0f, DrawY = 0.0f;
 		int LineCount = 0;
 		float CursorX, CursorY;
-		const char *pEnd;
 
 		float Size = pCursor->m_FontSize;
 
@@ -605,8 +604,6 @@ public:
 		if(Length < 0)
 			Length = str_length(pText);
 
-		pEnd = pText + Length;
-
 		// if we don't want to render, we can just skip the first outline pass
 		i = 1;
 		if(pCursor->m_Flags&TEXTFLAG_RENDER)
diff --git a/src/engine/console.h b/src/engine/console.h
index f8ec67b0..0abf4ad2 100644
--- a/src/engine/console.h
+++ b/src/engine/console.h
@@ -10,11 +10,21 @@ class IConsole : public IInterface
 	MACRO_INTERFACE("console", 0)
 public:
 
+	//	TODO: rework/cleanup
 	enum
 	{
 		OUTPUT_LEVEL_STANDARD=0,
 		OUTPUT_LEVEL_ADDINFO,
-		OUTPUT_LEVEL_DEBUG
+		OUTPUT_LEVEL_DEBUG,
+
+		ACCESS_LEVEL_ADMIN=0,
+		ACCESS_LEVEL_MOD,
+
+		TEMPCMD_NAME_LENGTH=32,
+		TEMPCMD_HELP_LENGTH=96,
+		TEMPCMD_PARAMS_LENGTH=16,
+
+		MAX_PRINT_CB=4,
 	};
 
 	// TODO: rework this interface to reduce the amount of virtual calls
@@ -35,10 +45,18 @@ public:
 
 	class CCommandInfo
 	{
+	protected:
+		int m_AccessLevel;
 	public:
+		CCommandInfo() { m_AccessLevel = ACCESS_LEVEL_ADMIN; }
+		virtual ~CCommandInfo() {}
 		const char *m_pName;
 		const char *m_pHelp;
 		const char *m_pParams;
+
+		virtual const CCommandInfo *NextCommandInfo(int AccessLevel, int FlagMask) const = 0;
+
+		int GetAccessLevel() const { return m_AccessLevel; }
 	};
 
 	typedef void (*FPrintCallback)(const char *pStr, void *pUser);
@@ -46,12 +64,15 @@ public:
 	typedef void (*FCommandCallback)(IResult *pResult, void *pUserData);
 	typedef void (*FChainCommandCallback)(IResult *pResult, void *pUserData, FCommandCallback pfnCallback, void *pCallbackUserData);
 
-	virtual CCommandInfo *GetCommandInfo(const char *pName, int FlagMask) = 0;
-	virtual void PossibleCommands(const char *pStr, int FlagMask, FPossibleCallback pfnCallback, void *pUser) = 0;
+	virtual const CCommandInfo *FirstCommandInfo(int AccessLevel, int Flagmask) const = 0;
+	virtual const CCommandInfo *GetCommandInfo(const char *pName, int FlagMask, bool Temp) = 0;
+	virtual void PossibleCommands(const char *pStr, int FlagMask, bool Temp, FPossibleCallback pfnCallback, void *pUser) = 0;
 	virtual void ParseArguments(int NumArgs, const char **ppArguments) = 0;
 
-	virtual void Register(const char *pName, const char *pParams,
-		int Flags, FCommandCallback pfnFunc, void *pUser, const char *pHelp) = 0;
+	virtual void Register(const char *pName, const char *pParams, int Flags, FCommandCallback pfnFunc, void *pUser, const char *pHelp) = 0;
+	virtual void RegisterTemp(const char *pName, const char *pParams, int Flags, const char *pHelp) = 0;
+	virtual void DeregisterTemp(const char *pName) = 0;
+	virtual void DeregisterTempAll() = 0;
 	virtual void Chain(const char *pName, FChainCommandCallback pfnChainFunc, void *pUser) = 0;
 	virtual void StoreCommands(bool Store) = 0;
 
@@ -60,8 +81,11 @@ public:
 	virtual void ExecuteLineStroked(int Stroke, const char *pStr) = 0;
 	virtual void ExecuteFile(const char *pFilename) = 0;
 
-	virtual void RegisterPrintCallback(FPrintCallback pfnPrintCallback, void *pUserData) = 0;
+	virtual int RegisterPrintCallback(int OutputLevel, FPrintCallback pfnPrintCallback, void *pUserData) = 0;
+	virtual void SetPrintOutputLevel(int Index, int OutputLevel) = 0;
 	virtual void Print(int Level, const char *pFrom, const char *pStr) = 0;
+
+	virtual void SetAccessLevel(int AccessLevel) = 0;
 };
 
 extern IConsole *CreateConsole(int FlagMask);
diff --git a/src/engine/demo.h b/src/engine/demo.h
index 891c78d6..a9e4f700 100644
--- a/src/engine/demo.h
+++ b/src/engine/demo.h
@@ -46,7 +46,7 @@ public:
 	virtual void Pause() = 0;
 	virtual void Unpause() = 0;
 	virtual const CInfo *BaseInfo() const = 0;
-	virtual char *GetDemoName() = 0;
+	virtual void GetDemoName(char *pBuffer, int BufferSize) const = 0;
 	virtual bool GetDemoInfo(class IStorage *pStorage, const char *pFilename, int StorageType, CDemoHeader *pDemoHeader) const = 0;
 	virtual int GetDemoType() const = 0;
 };
diff --git a/src/engine/external/pnglite/pnglite.c b/src/engine/external/pnglite/pnglite.c
index b33f60ba..a036b4c2 100644
--- a/src/engine/external/pnglite/pnglite.c
+++ b/src/engine/external/pnglite/pnglite.c
@@ -113,7 +113,7 @@ int png_init(png_alloc_t pngalloc, png_free_t pngfree)
 	if(pngalloc)

 		png_alloc = pngalloc;

 	else

-		png_alloc = &malloc;

+		png_alloc = (png_alloc_t)&malloc;

 

 	if(pngfree)

 		png_free = pngfree;

@@ -492,7 +492,7 @@ static int png_write_idats(png_t* png, unsigned char* data)
 	(void)png_end_deflate;

 	(void)png_deflate;

 

-	chunk = png_alloc(size);

+	chunk = png_alloc(size+8);

 	memcpy(chunk, "IDAT", 4);

 	

 	written = size;

diff --git a/src/engine/external/pnglite/pnglite.h b/src/engine/external/pnglite/pnglite.h
index eae3d4ce..72ff1c52 100644
--- a/src/engine/external/pnglite/pnglite.h
+++ b/src/engine/external/pnglite/pnglite.h
@@ -73,7 +73,7 @@ enum
 typedef unsigned (*png_write_callback_t)(void* input, unsigned long size, unsigned long numel, void* user_pointer);

 typedef unsigned (*png_read_callback_t)(void* output, unsigned long size, unsigned long numel, void* user_pointer);

 typedef void (*png_free_t)(void* p);

-typedef void * (*png_alloc_t)(size_t s);

+typedef void * (*png_alloc_t)(unsigned long s);

 

 typedef struct

 {

diff --git a/src/engine/friends.h b/src/engine/friends.h
index bf9df904..164e3461 100644
--- a/src/engine/friends.h
+++ b/src/engine/friends.h
@@ -11,6 +11,8 @@ struct CFriendInfo
 {
 	char m_aName[MAX_NAME_LENGTH];
 	char m_aClan[MAX_CLAN_LENGTH];
+	unsigned m_NameHash;
+	unsigned m_ClanHash;
 };
 
 class IFriends : public IInterface
@@ -19,6 +21,10 @@ class IFriends : public IInterface
 public:
 	enum
 	{
+		FRIEND_NO=0,
+		FRIEND_CLAN,
+		FRIEND_PLAYER,
+
 		MAX_FRIENDS=128,
 	};
 
@@ -26,11 +32,11 @@ public:
 
 	virtual int NumFriends() const = 0;
 	virtual const CFriendInfo *GetFriend(int Index) const = 0;
+	virtual int GetFriendState(const char *pName, const char *pClan) const = 0;
 	virtual bool IsFriend(const char *pName, const char *pClan, bool PlayersOnly) const = 0;
 
 	virtual void AddFriend(const char *pName, const char *pClan) = 0;
 	virtual void RemoveFriend(const char *pName, const char *pClan) = 0;
-	virtual void RemoveFriend(int Index) = 0;
 };
 
 #endif
diff --git a/src/engine/masterserver.h b/src/engine/masterserver.h
index 74a394dc..57433993 100644
--- a/src/engine/masterserver.h
+++ b/src/engine/masterserver.h
@@ -23,7 +23,6 @@ public:
 	virtual int RefreshAddresses(int Nettype) = 0;
 	virtual void Update() = 0;
 	virtual int IsRefreshing() = 0;
-	virtual void DumpServers() = 0;
 	virtual NETADDR GetAddr(int Index) = 0;
 	virtual const char *GetName(int Index) = 0;
 	virtual bool IsValid(int Index) = 0;
diff --git a/src/engine/server.h b/src/engine/server.h
index 28dc3d11..d3eae861 100644
--- a/src/engine/server.h
+++ b/src/engine/server.h
@@ -31,7 +31,6 @@ public:
 	virtual bool ClientIngame(int ClientID) = 0;
 	virtual int GetClientInfo(int ClientID, CClientInfo *pInfo) = 0;
 	virtual void GetClientAddr(int ClientID, char *pAddrStr, int Size) = 0;
-	virtual int *LatestInput(int ClientID, int *pSize) = 0;
 
 	virtual int SendMsg(CMsgPacker *pMsg, int Flags, int ClientID) = 0;
 
@@ -57,6 +56,11 @@ public:
 
 	virtual bool IsAuthed(int ClientID) = 0;
 	virtual void Kick(int ClientID, const char *pReason) = 0;
+
+	virtual void DemoRecorder_HandleAutoStart() = 0;
+	
+	//zCatch
+	virtual void MapReload() = 0;
 };
 
 class IGameServer : public IInterface
diff --git a/src/engine/server/register.h b/src/engine/server/register.h
index 44e6dd14..c0392380 100644
--- a/src/engine/server/register.h
+++ b/src/engine/server/register.h
@@ -32,20 +32,20 @@ class CRegister
 	int m_RegisterFirst;
 	int m_RegisterCount;
 
-	class CMasterserverInfo m_aMasterserverInfo[IMasterServer::MAX_MASTERSERVERS];
+	CMasterserverInfo m_aMasterserverInfo[IMasterServer::MAX_MASTERSERVERS];
 	int m_RegisterRegisteredServer;
 
 	void RegisterNewState(int State);
 	void RegisterSendFwcheckresponse(NETADDR *pAddr);
 	void RegisterSendHeartbeat(NETADDR Addr);
 	void RegisterSendCountRequest(NETADDR Addr);
-	void RegisterGotCount(class CNetChunk *pChunk);
+	void RegisterGotCount(struct CNetChunk *pChunk);
 
 public:
 	CRegister();
 	void Init(class CNetServer *pNetServer, class IEngineMasterServer *pMasterServer, class IConsole *pConsole);
 	void RegisterUpdate(int Nettype);
-	int RegisterProcessPacket(class CNetChunk *pPacket);
+	int RegisterProcessPacket(struct CNetChunk *pPacket);
 };
 
 #endif
diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp
index f7898f26..05d31f18 100644
--- a/src/engine/server/server.cpp
+++ b/src/engine/server/server.cpp
@@ -1,6 +1,7 @@
 /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
 /* If you are missing that file, acquire a complete release at teeworlds.com.                */
 
+#include <base/math.h>
 #include <base/system.h>
 
 #include <engine/config.h>
@@ -15,6 +16,8 @@
 #include <engine/shared/config.h>
 #include <engine/shared/datafile.h>
 #include <engine/shared/demo.h>
+#include <engine/shared/econ.h>
+#include <engine/shared/filecollection.h>
 #include <engine/shared/mapchecker.h>
 #include <engine/shared/network.h>
 #include <engine/shared/packer.h>
@@ -185,6 +188,7 @@ CServer::CServer() : m_DemoRecorder(&m_SnapshotDelta)
 	m_MapReload = 0;
 
 	m_RconClientID = -1;
+	m_RconAuthLevel = AUTHED_ADMIN;
 
 	Init();
 }
@@ -284,6 +288,11 @@ void CServer::Kick(int ClientID, const char *pReason)
 		Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "you can't kick yourself");
  		return;
 	}
+	else if(m_aClients[ClientID].m_Authed > m_RconAuthLevel)
+	{
+		Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "kick command denied");
+ 		return;
+	}
 
 	m_NetServer.Drop(ClientID, pReason);
 }
@@ -349,21 +358,14 @@ void CServer::GetClientAddr(int ClientID, char *pAddrStr, int Size)
 }
 
 
-int *CServer::LatestInput(int ClientID, int *size)
-{
-	if(ClientID < 0 || ClientID >= MAX_CLIENTS || m_aClients[ClientID].m_State < CServer::CClient::STATE_READY)
-		return 0;
-	return m_aClients[ClientID].m_LatestInput.m_aData;
-}
-
 const char *CServer::ClientName(int ClientID)
 {
 	if(ClientID < 0 || ClientID >= MAX_CLIENTS || m_aClients[ClientID].m_State == CServer::CClient::STATE_EMPTY)
-		return "(invalid client)";
+		return "(invalid)";
 	if(m_aClients[ClientID].m_State == CServer::CClient::STATE_INGAME)
 		return m_aClients[ClientID].m_aName;
 	else
-		return "(connecting client)";
+		return "(connecting)";
 
 }
 
@@ -582,8 +584,9 @@ int CServer::NewClientCallback(int ClientID, void *pUser)
 	pThis->m_aClients[ClientID].m_aName[0] = 0;
 	pThis->m_aClients[ClientID].m_aClan[0] = 0;
 	pThis->m_aClients[ClientID].m_Country = -1;
-	pThis->m_aClients[ClientID].m_Authed = 0;
+	pThis->m_aClients[ClientID].m_Authed = AUTHED_NO;
 	pThis->m_aClients[ClientID].m_AuthTries = 0;
+	pThis->m_aClients[ClientID].m_pRconCmdToSend = 0;
 	pThis->m_aClients[ClientID].Reset();
 	return 0;
 }
@@ -607,8 +610,9 @@ int CServer::DelClientCallback(int ClientID, const char *pReason, void *pUser)
 	pThis->m_aClients[ClientID].m_aName[0] = 0;
 	pThis->m_aClients[ClientID].m_aClan[0] = 0;
 	pThis->m_aClients[ClientID].m_Country = -1;
-	pThis->m_aClients[ClientID].m_Authed = 0;
+	pThis->m_aClients[ClientID].m_Authed = AUTHED_NO;
 	pThis->m_aClients[ClientID].m_AuthTries = 0;
+	pThis->m_aClients[ClientID].m_pRconCmdToSend = 0;
 	pThis->m_aClients[ClientID].m_Snapshots.PurgeAll();
 	return 0;
 }
@@ -646,13 +650,44 @@ void CServer::SendRconLineAuthed(const char *pLine, void *pUser)
 
 	for(i = 0; i < MAX_CLIENTS; i++)
 	{
-		if(pThis->m_aClients[i].m_State != CClient::STATE_EMPTY && pThis->m_aClients[i].m_Authed)
+		if(pThis->m_aClients[i].m_State != CClient::STATE_EMPTY && pThis->m_aClients[i].m_Authed >= pThis->m_RconAuthLevel)
 			pThis->SendRconLine(i, pLine);
 	}
 
 	ReentryGuard--;
 }
 
+void CServer::SendRconCmdAdd(const IConsole::CCommandInfo *pCommandInfo, int ClientID)
+{
+	CMsgPacker Msg(NETMSG_RCON_CMD_ADD);
+	Msg.AddString(pCommandInfo->m_pName, IConsole::TEMPCMD_NAME_LENGTH);
+	Msg.AddString(pCommandInfo->m_pHelp, IConsole::TEMPCMD_HELP_LENGTH);
+	Msg.AddString(pCommandInfo->m_pParams, IConsole::TEMPCMD_PARAMS_LENGTH);
+	SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true);
+}
+
+void CServer::SendRconCmdRem(const IConsole::CCommandInfo *pCommandInfo, int ClientID)
+{
+	CMsgPacker Msg(NETMSG_RCON_CMD_REM);
+	Msg.AddString(pCommandInfo->m_pName, 256);
+	SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true);
+}
+
+void CServer::UpdateClientRconCommands()
+{
+	int ClientID = Tick() % MAX_CLIENTS;
+		
+	if(m_aClients[ClientID].m_State != CClient::STATE_EMPTY && m_aClients[ClientID].m_Authed)
+	{
+		int ConsoleAccessLevel = m_aClients[ClientID].m_Authed == AUTHED_ADMIN ? IConsole::ACCESS_LEVEL_ADMIN : IConsole::ACCESS_LEVEL_MOD;
+		for(int i = 0; i < MAX_RCONCMD_SEND && m_aClients[ClientID].m_pRconCmdToSend; ++i)
+		{
+			SendRconCmdAdd(m_aClients[ClientID].m_pRconCmdToSend, ClientID);
+			m_aClients[ClientID].m_pRconCmdToSend = m_aClients[ClientID].m_pRconCmdToSend->NextCommandInfo(ConsoleAccessLevel, CFGFLAG_SERVER);
+		}
+	}
+}
+
 void CServer::ProcessClientPacket(CNetChunk *pPacket)
 {
 	int ClientID = pPacket->m_ClientID;
@@ -824,8 +859,12 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket)
 				str_format(aBuf, sizeof(aBuf), "ClientID=%d rcon='%s'", ClientID, pCmd);
 				Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBuf);
 				m_RconClientID = ClientID;
+				m_RconAuthLevel = m_aClients[ClientID].m_Authed;
+				Console()->SetAccessLevel(m_aClients[ClientID].m_Authed == AUTHED_ADMIN ? IConsole::ACCESS_LEVEL_ADMIN : IConsole::ACCESS_LEVEL_MOD);
 				Console()->ExecuteLine(pCmd);
+				Console()->SetAccessLevel(IConsole::ACCESS_LEVEL_ADMIN);
 				m_RconClientID = -1;
+				m_RconAuthLevel = AUTHED_ADMIN;
 			}
 		}
 		else if(Msg == NETMSG_RCON_AUTH)
@@ -836,20 +875,40 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket)
 
 			if(Unpacker.Error() == 0)
 			{
-				if(g_Config.m_SvRconPassword[0] == 0)
+				if(g_Config.m_SvRconPassword[0] == 0 && g_Config.m_SvRconModPassword[0] == 0)
 				{
-					SendRconLine(ClientID, "No rcon password set on server. Set sv_rcon_password to enable the remote console.");
+					SendRconLine(ClientID, "No rcon password set on server. Set sv_rcon_password and/or sv_rcon_mod_password to enable the remote console.");
 				}
-				else if(str_comp(pPw, g_Config.m_SvRconPassword) == 0)
+				else if(g_Config.m_SvRconPassword[0] && str_comp(pPw, g_Config.m_SvRconPassword) == 0)
 				{
 					CMsgPacker Msg(NETMSG_RCON_AUTH_STATUS);
-					Msg.AddInt(1);
+					Msg.AddInt(1);	//authed
+					Msg.AddInt(1);	//cmdlist
 					SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true);
 
-					m_aClients[ClientID].m_Authed = 1;
-					SendRconLine(ClientID, "Authentication successful. Remote console access granted.");
+					m_aClients[ClientID].m_Authed = AUTHED_ADMIN;
+					int SendRconCmds = Unpacker.GetInt();
+					if(Unpacker.Error() == 0 && SendRconCmds)
+						m_aClients[ClientID].m_pRconCmdToSend = Console()->FirstCommandInfo(IConsole::ACCESS_LEVEL_ADMIN, CFGFLAG_SERVER);
+					SendRconLine(ClientID, "Admin authentication successful. Full remote console access granted.");
 					char aBuf[256];
-					str_format(aBuf, sizeof(aBuf), "ClientID=%d authed", ClientID);
+					str_format(aBuf, sizeof(aBuf), "ClientID=%d authed (admin)", ClientID);
+					Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
+				}
+				else if(g_Config.m_SvRconModPassword[0] && str_comp(pPw, g_Config.m_SvRconModPassword) == 0)
+				{
+					CMsgPacker Msg(NETMSG_RCON_AUTH_STATUS);
+					Msg.AddInt(1);	//authed
+					Msg.AddInt(1);	//cmdlist
+					SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true);
+
+					m_aClients[ClientID].m_Authed = AUTHED_MOD;
+					int SendRconCmds = Unpacker.GetInt();
+					if(Unpacker.Error() == 0 && SendRconCmds)
+						m_aClients[ClientID].m_pRconCmdToSend = Console()->FirstCommandInfo(IConsole::ACCESS_LEVEL_MOD, CFGFLAG_SERVER);
+					SendRconLine(ClientID, "Moderator authentication successful. Limited remote console access granted.");
+					char aBuf[256];
+					str_format(aBuf, sizeof(aBuf), "ClientID=%d authed (moderator)", ClientID);
 					Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
 				}
 				else if(g_Config.m_SvRconMaxTries)
@@ -1063,6 +1122,8 @@ void CServer::PumpNetwork()
 		else
 			ProcessClientPacket(&Packet);
 	}
+
+	m_Econ.Update();
 }
 
 char *CServer::GetMapName()
@@ -1137,7 +1198,7 @@ int CServer::Run()
 	m_pStorage = Kernel()->RequestInterface<IStorage>();
 
 	//
-	Console()->RegisterPrintCallback(SendRconLineAuthed, this);
+	m_PrintCBIndex = Console()->RegisterPrintCallback(g_Config.m_ConsoleOutputLevel, SendRconLineAuthed, this);
 
 	// load map
 	if(!LoadMap(g_Config.m_SvMap))
@@ -1160,7 +1221,6 @@ int CServer::Run()
 		BindAddr.port = g_Config.m_SvPort;
 	}
 
-
 	if(!m_NetServer.Open(BindAddr, g_Config.m_SvMaxClients, g_Config.m_SvMaxClientsPerIP, 0))
 	{
 		dbg_msg("server", "couldn't open socket. port might already be in use");
@@ -1169,8 +1229,13 @@ int CServer::Run()
 
 	m_NetServer.SetCallbacks(NewClientCallback, DelClientCallback, this);
 
+<<<<<<< HEAD
+	m_Econ.Init(Console());
+
+=======
 	Console()->ExecuteFile(SERVER_BANMASTERFILE);
 		
+>>>>>>> heinrich5991/banmaster
 	char aBuf[256];
 	str_format(aBuf, sizeof(aBuf), "server name is '%s'", g_Config.m_SvName);
 	Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
@@ -1265,10 +1330,12 @@ int CServer::Run()
 			{
 				if(g_Config.m_SvHighBandwidth || (m_CurrentGameTick%2) == 0)
 					DoSnapshot();
+
+				UpdateClientRconCommands();
 			}
 
 			// master server stuff
-			m_Register.RegisterUpdate(BindAddr.type);
+			m_Register.RegisterUpdate(m_NetServer.NetType());
 
 			PumpNetwork();
 
@@ -1306,6 +1373,8 @@ int CServer::Run()
 	{
 		if(m_aClients[i].m_State != CClient::STATE_EMPTY)
 			m_NetServer.Drop(i, "Server shutdown");
+
+		m_Econ.Shutdown();
 	}
 
 	GameServer()->OnShutdown();
@@ -1337,7 +1406,7 @@ void CServer::ConBan(IConsole::IResult *pResult, void *pUser)
 	const char *pReason = "No reason given";
 
 	if(pResult->NumArguments() > 1)
-		Minutes = pResult->GetInteger(1);
+		Minutes = max(0, pResult->GetInteger(1));
 
 	if(pResult->NumArguments() > 2)
 		pReason = pResult->GetString(2);
@@ -1353,6 +1422,20 @@ void CServer::ConBan(IConsole::IResult *pResult, void *pUser)
 				pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "you can't ban yourself");
 				return;
 			}
+
+			for(int i = 0; i < MAX_CLIENTS; ++i)
+			{
+				if(i == pServer->m_RconClientID)
+					continue;
+
+				AddrCheck = pServer->m_NetServer.ClientAddr(i);
+				AddrCheck.port = 0;
+				if(net_addr_comp(&Addr, &AddrCheck) == 0 && pServer->m_aClients[i].m_Authed > pServer->m_RconAuthLevel)
+				{
+					pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "ban command denied");
+					return;
+				}
+			}
 		}
 		pServer->BanAdd(Addr, Minutes*60, pReason);
 	}
@@ -1370,6 +1453,11 @@ void CServer::ConBan(IConsole::IResult *pResult, void *pUser)
 			pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "you can't ban yourself");
 			return;
 		}
+		else if(pServer->m_aClients[ClientID].m_Authed > pServer->m_RconAuthLevel)
+		{
+			pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "ban command denied");
+			return;
+		}
 
 		Addr = pServer->m_NetServer.ClientAddr(ClientID);
 		pServer->BanAdd(Addr, Minutes*60, pReason);
@@ -1475,6 +1563,30 @@ void CServer::ConShutdown(IConsole::IResult *pResult, void *pUser)
 	((CServer *)pUser)->m_RunServer = 0;
 }
 
+void CServer::DemoRecorder_HandleAutoStart()
+{
+	if(g_Config.m_SvAutoDemoRecord)
+	{
+		m_DemoRecorder.Stop();
+		char aFilename[128];
+		char aDate[20];
+		str_timestamp(aDate, sizeof(aDate));
+		str_format(aFilename, sizeof(aFilename), "demos/%s_%s.demo", "auto/autorecord", aDate);
+		m_DemoRecorder.Start(Storage(), m_pConsole, aFilename, GameServer()->NetVersion(), m_aCurrentMap, m_CurrentMapCrc, "server");
+		if(g_Config.m_SvAutoDemoMax)
+		{
+			// clean up auto recorded demos
+			CFileCollection AutoDemos;
+			AutoDemos.Init(Storage(), "demos/server", "autorecord", ".demo", g_Config.m_SvAutoDemoMax);
+		}
+	}
+}
+
+void CServer::MapReload()
+{
+	m_MapReload = 1;
+}
+
 void CServer::ConRecord(IConsole::IResult *pResult, void *pUser)
 {
 	CServer* pServer = (CServer *)pUser;
@@ -1554,30 +1666,75 @@ void CServer::ConchainMaxclientsperipUpdate(IConsole::IResult *pResult, void *pU
 		((CServer *)pUserData)->m_NetServer.SetMaxClientsPerIP(pResult->GetInteger(0));
 }
 
+void CServer::ConchainModCommandUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
+{
+	if(pResult->NumArguments() == 2)
+	{
+		CServer *pThis = static_cast<CServer *>(pUserData);
+		const IConsole::CCommandInfo *pInfo = pThis->Console()->GetCommandInfo(pResult->GetString(0), CFGFLAG_SERVER, false);
+		int OldAccessLevel;
+		if(pInfo)
+			OldAccessLevel = pInfo->GetAccessLevel();
+		pfnCallback(pResult, pCallbackUserData);
+		if(pInfo && OldAccessLevel != pInfo->GetAccessLevel())
+		{
+			for(int i = 0; i < MAX_CLIENTS; ++i)
+			{
+				if(pThis->m_aClients[i].m_State == CServer::CClient::STATE_EMPTY || pThis->m_aClients[i].m_Authed != CServer::AUTHED_MOD ||
+					(pThis->m_aClients[i].m_pRconCmdToSend && str_comp(pResult->GetString(0), pThis->m_aClients[i].m_pRconCmdToSend->m_pName) >= 0))
+					continue;
+
+				if(OldAccessLevel == IConsole::ACCESS_LEVEL_ADMIN)
+					pThis->SendRconCmdAdd(pInfo, i);
+				else
+					pThis->SendRconCmdRem(pInfo, i);
+			}
+		}
+	}
+	else
+		pfnCallback(pResult, pCallbackUserData);
+}
+
+void CServer::ConchainConsoleOutputLevelUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
+{
+	pfnCallback(pResult, pCallbackUserData);
+	if(pResult->NumArguments() == 1)
+	{
+		CServer *pThis = static_cast<CServer *>(pUserData);
+		pThis->Console()->SetPrintOutputLevel(pThis->m_PrintCBIndex, pResult->GetInteger(0));
+	}
+}
+
 void CServer::RegisterCommands()
 {
 	m_pConsole = Kernel()->RequestInterface<IConsole>();
 
-	Console()->Register("kick", "i?r", CFGFLAG_SERVER, ConKick, this, "");
-	Console()->Register("ban", "s?ir", CFGFLAG_SERVER|CFGFLAG_STORE, ConBan, this, "");
-	Console()->Register("unban", "s", CFGFLAG_SERVER|CFGFLAG_STORE, ConUnban, this, "");
-	Console()->Register("bans", "", CFGFLAG_SERVER|CFGFLAG_STORE, ConBans, this, "");
-	Console()->Register("status", "", CFGFLAG_SERVER, ConStatus, this, "");
-	Console()->Register("shutdown", "", CFGFLAG_SERVER, ConShutdown, this, "");
+	Console()->Register("kick", "i?r", CFGFLAG_SERVER, ConKick, this, "Kick player with specified id for any reason");
+	Console()->Register("ban", "s?ir", CFGFLAG_SERVER|CFGFLAG_STORE, ConBan, this, "Ban player with ip/id for x minutes for any reason");
+	Console()->Register("unban", "s", CFGFLAG_SERVER|CFGFLAG_STORE, ConUnban, this, "Unban ip");
+	Console()->Register("bans", "", CFGFLAG_SERVER|CFGFLAG_STORE, ConBans, this, "Show banlist");
+	Console()->Register("status", "", CFGFLAG_SERVER, ConStatus, this, "List players");
+	Console()->Register("shutdown", "", CFGFLAG_SERVER, ConShutdown, this, "Shut down");
 
-	Console()->Register("record", "?s", CFGFLAG_SERVER|CFGFLAG_STORE, ConRecord, this, "");
-	Console()->Register("stoprecord", "", CFGFLAG_SERVER, ConStopRecord, this, "");
+	Console()->Register("record", "?s", CFGFLAG_SERVER|CFGFLAG_STORE, ConRecord, this, "Record to a file");
+	Console()->Register("stoprecord", "", CFGFLAG_SERVER, ConStopRecord, this, "Stop recording");
 
+<<<<<<< HEAD
+	Console()->Register("reload", "", CFGFLAG_SERVER, ConMapReload, this, "Reload the map");
+=======
 	Console()->Register("add_banmaster", "s", CFGFLAG_SERVER, ConAddBanmaster, this, "");
 	Console()->Register("banmasters", "", CFGFLAG_SERVER, ConBanmasters, this, "");
 	Console()->Register("clear_banmasters",	"", CFGFLAG_SERVER, ConClearBanmasters, this, "");
 
 	Console()->Register("reload", "", CFGFLAG_SERVER, ConMapReload, this, "");
+>>>>>>> heinrich5991/banmaster
 
 	Console()->Chain("sv_name", ConchainSpecialInfoupdate, this);
 	Console()->Chain("password", ConchainSpecialInfoupdate, this);
 
 	Console()->Chain("sv_max_clients_per_ip", ConchainMaxclientsperipUpdate, this);
+	Console()->Chain("mod_command", ConchainModCommandUpdate, this);
+	Console()->Chain("console_output_level", ConchainConsoleOutputLevelUpdate, this);
 }
 
 
diff --git a/src/engine/server/server.h b/src/engine/server/server.h
index ba4dff0b..40301aa9 100644
--- a/src/engine/server/server.h
+++ b/src/engine/server/server.h
@@ -49,6 +49,15 @@ public:
 	class IConsole *Console() { return m_pConsole; }
 	class IStorage *Storage() { return m_pStorage; }
 
+	enum
+	{
+		AUTHED_NO=0,
+		AUTHED_MOD,
+		AUTHED_ADMIN,
+
+		MAX_RCONCMD_SEND=16,
+	};
+
 	class CClient
 	{
 	public:
@@ -93,6 +102,8 @@ public:
 		int m_Authed;
 		int m_AuthTries;
 
+		const IConsole::CCommandInfo *m_pRconCmdToSend;
+
 		void Reset();
 	};
 
@@ -102,6 +113,7 @@ public:
 	CSnapshotBuilder m_SnapshotBuilder;
 	CSnapIDPool m_IDPool;
 	CNetServer m_NetServer;
+	CEcon m_Econ;
 
 	IEngineMap *m_pMap;
 
@@ -110,6 +122,8 @@ public:
 	int m_RunServer;
 	int m_MapReload;
 	int m_RconClientID;
+	int m_RconAuthLevel;
+	int m_PrintCBIndex;
 
 	int64 m_Lastheartbeat;
 	//static NETADDR4 master_server;
@@ -134,6 +148,8 @@ public:
 
 	void Kick(int ClientID, const char *pReason);
 
+	void DemoRecorder_HandleAutoStart();
+
 	//int Tick()
 	int64 TickStartTime(int Tick);
 	//int TickSpeed()
@@ -148,8 +164,6 @@ public:
 	int ClientCountry(int ClientID);
 	bool ClientIngame(int ClientID);
 
-	int *LatestInput(int ClientID, int *size);
-
 	virtual int SendMsg(CMsgPacker *pMsg, int Flags, int ClientID);
 	int SendMsgEx(CMsgPacker *pMsg, int Flags, int ClientID, bool System);
 
@@ -163,6 +177,10 @@ public:
 	void SendRconLine(int ClientID, const char *pLine);
 	static void SendRconLineAuthed(const char *pLine, void *pUser);
 
+	void SendRconCmdAdd(const IConsole::CCommandInfo *pCommandInfo, int ClientID);
+	void SendRconCmdRem(const IConsole::CCommandInfo *pCommandInfo, int ClientID);
+	void UpdateClientRconCommands();
+
 	void ProcessClientPacket(CNetChunk *pPacket);
 
 	void SendServerInfo(NETADDR *pAddr, int Token);
@@ -171,7 +189,6 @@ public:
 	int BanAdd(NETADDR Addr, int Seconds, const char *pReason);
 	int BanRemove(NETADDR Addr);
 
-
 	void PumpNetwork();
 
 	char *GetMapName();
@@ -194,6 +211,8 @@ public:
 	static void ConClearBanmasters(IConsole::IResult *pResult, void *pUser);
 	static void ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
 	static void ConchainMaxclientsperipUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
+	static void ConchainModCommandUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
+	static void ConchainConsoleOutputLevelUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
 
 	void RegisterCommands();
 
@@ -202,6 +221,9 @@ public:
 	virtual void SnapFreeID(int ID);
 	virtual void *SnapNewItem(int Type, int ID, int Size);
 	void SnapSetStaticsize(int ItemType, int Size);
+	
+	//zCatch
+	virtual void MapReload();
 };
 
 #endif
diff --git a/src/engine/serverbrowser.h b/src/engine/serverbrowser.h
index 3ca59f9c..1a49eaf0 100644
--- a/src/engine/serverbrowser.h
+++ b/src/engine/serverbrowser.h
@@ -24,7 +24,9 @@ public:
 		int m_Country;
 		int m_Score;
 		bool m_Player;
-	} ;
+		
+		int m_FriendState;
+	};
 
 	int m_SortedIndex;
 	int m_ServerIndex;
@@ -32,6 +34,7 @@ public:
 	NETADDR m_NetAddr;
 
 	int m_QuickSearchHit;
+	int m_FriendState;
 
 	int m_MaxClients;
 	int m_NumClients;
diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h
index a540bc8a..27199356 100644
--- a/src/engine/shared/config_variables.h
+++ b/src/engine/shared/config_variables.h
@@ -32,9 +32,12 @@ MACRO_CONFIG_INT(BrFilterFull, br_filter_full, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLI
 MACRO_CONFIG_INT(BrFilterEmpty, br_filter_empty, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out empty server in browser")
 MACRO_CONFIG_INT(BrFilterSpectators, br_filter_spectators, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out spectators from player numbers")
 MACRO_CONFIG_INT(BrFilterFriends, br_filter_friends, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out servers with no friends")
+MACRO_CONFIG_INT(BrFilterCountry, br_filter_country, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out servers with non-matching player country")
+MACRO_CONFIG_INT(BrFilterCountryIndex, br_filter_country_index, -1, -1, 999, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Player country to filter by in the server browser")
 MACRO_CONFIG_INT(BrFilterPw, br_filter_pw, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out password protected servers in browser")
 MACRO_CONFIG_INT(BrFilterPing, br_filter_ping, 999, 0, 999, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Ping to filter by in the server browser")
 MACRO_CONFIG_STR(BrFilterGametype, br_filter_gametype, 128, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Game types to filter")
+MACRO_CONFIG_INT(BrFilterGametypeStrict, br_filter_gametype_strict, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Strict gametype filter")
 MACRO_CONFIG_STR(BrFilterServerAddress, br_filter_serveraddress, 128, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Server address to filter")
 MACRO_CONFIG_INT(BrFilterPure, br_filter_pure, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out non-standard servers in browser")
 MACRO_CONFIG_INT(BrFilterPureMap, br_filter_pure_map, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out non-standard maps in browser")
@@ -79,10 +82,23 @@ MACRO_CONFIG_INT(SvMaxClients, sv_max_clients, 8, 1, MAX_CLIENTS, CFGFLAG_SERVER
 MACRO_CONFIG_INT(SvMaxClientsPerIP, sv_max_clients_per_ip, 4, 1, MAX_CLIENTS, CFGFLAG_SERVER, "Maximum number of clients with the same IP that can connect to the server")
 MACRO_CONFIG_INT(SvHighBandwidth, sv_high_bandwidth, 0, 0, 1, CFGFLAG_SERVER, "Use high bandwidth mode. Doubles the bandwidth required for the server. LAN use only")
 MACRO_CONFIG_INT(SvRegister, sv_register, 1, 0, 1, CFGFLAG_SERVER, "Register server with master server for public listing")
-MACRO_CONFIG_STR(SvRconPassword, sv_rcon_password, 32, "", CFGFLAG_SERVER, "Remote console password")
+MACRO_CONFIG_STR(SvRconPassword, sv_rcon_password, 32, "", CFGFLAG_SERVER, "Remote console password (full access)")
+MACRO_CONFIG_STR(SvRconModPassword, sv_rcon_mod_password, 32, "", CFGFLAG_SERVER, "Remote console password for moderators (limited access)")
 MACRO_CONFIG_INT(SvRconMaxTries, sv_rcon_max_tries, 3, 0, 100, CFGFLAG_SERVER, "Maximum number of tries for remote console authentication")
 MACRO_CONFIG_INT(SvRconBantime, sv_rcon_bantime, 5, 0, 1440, CFGFLAG_SERVER, "The time a client gets banned if remote console authentication fails. 0 makes it just use kick")
+<<<<<<< HEAD
+MACRO_CONFIG_INT(SvAutoDemoRecord, sv_auto_demo_record, 0, 0, 1, CFGFLAG_SERVER, "Automatically record demos")
+MACRO_CONFIG_INT(SvAutoDemoMax, sv_auto_demo_max, 10, 0, 1000, CFGFLAG_SERVER, "Maximum number of automatically recorded demos (0 = no limit)")
+
+MACRO_CONFIG_STR(EcBindaddr, ec_bindaddr, 128, "localhost", CFGFLAG_SERVER, "Address to bind the external console to. Anything but 'localhost' is dangerous")
+MACRO_CONFIG_INT(EcPort, ec_port, 0, 0, 0, CFGFLAG_SERVER, "Port to use for the external console")
+MACRO_CONFIG_STR(EcPassword, ec_password, 32, "", CFGFLAG_SERVER, "External console password")
+MACRO_CONFIG_INT(EcBantime, ec_bantime, 0, 0, 1440, CFGFLAG_SERVER, "The time a client gets banned if econ authentication fails. 0 just closes the connection")
+MACRO_CONFIG_INT(EcAuthTimeout, ec_auth_timeout, 30, 1, 120, CFGFLAG_SERVER, "Time in seconds before the the econ authentification times out")
+MACRO_CONFIG_INT(EcOutputLevel, ec_output_level, 1, 0, 2, CFGFLAG_SERVER, "Adjusts the amount of information in the external console")
+=======
 MACRO_CONFIG_INT(SvGlobalBantime, sv_global_bantime, 60, 0, 1440, CFGFLAG_SERVER, "The time a client gets banned if the ban server reports it. 0 to disable")
+>>>>>>> heinrich5991/banmaster
 
 MACRO_CONFIG_INT(Debug, debug, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SERVER, "Debug mode")
 MACRO_CONFIG_INT(DbgStress, dbg_stress, 0, 0, 0, CFGFLAG_CLIENT|CFGFLAG_SERVER, "Stress systems")
diff --git a/src/engine/shared/console.cpp b/src/engine/shared/console.cpp
index 032ff2ae..e4cb1991 100644
--- a/src/engine/shared/console.cpp
+++ b/src/engine/shared/console.cpp
@@ -1,11 +1,15 @@
 /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
 /* If you are missing that file, acquire a complete release at teeworlds.com.                */
 #include <new>
+
+#include <base/math.h>
 #include <base/system.h>
-#include <engine/shared/protocol.h>
+
 #include <engine/storage.h>
-#include "console.h"
+#include <engine/shared/protocol.h>
+
 #include "config.h"
+#include "console.h"
 #include "linereader.h"
 
 const char *CConsole::CResult::GetString(unsigned Index)
@@ -29,6 +33,29 @@ float CConsole::CResult::GetFloat(unsigned Index)
 	return str_tofloat(m_apArgs[Index]);
 }
 
+const IConsole::CCommandInfo *CConsole::CCommand::NextCommandInfo(int AccessLevel, int FlagMask) const
+{
+	const CCommand *pInfo = m_pNext;
+	while(pInfo)
+	{
+		if(pInfo->m_Flags&FlagMask && pInfo->m_AccessLevel >= AccessLevel)
+			break;
+		pInfo = pInfo->m_pNext;
+	}
+	return pInfo;
+}
+
+const IConsole::CCommandInfo *CConsole::FirstCommandInfo(int AccessLevel, int FlagMask) const
+{
+	for(const CCommand *pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext)
+	{
+		if(pCommand->m_Flags&FlagMask && pCommand->GetAccessLevel() >= AccessLevel)
+			return pCommand;
+	}
+
+	return 0;
+}
+
 // the maximum number of tokens occurs in a string of length CONSOLE_MAX_STR_LENGTH with tokens size 1 separated by single spaces
 
 
@@ -146,20 +173,34 @@ int CConsole::ParseArgs(CResult *pResult, const char *pFormat)
 	return Error;
 }
 
-void CConsole::RegisterPrintCallback(FPrintCallback pfnPrintCallback, void *pUserData)
+int CConsole::RegisterPrintCallback(int OutputLevel, FPrintCallback pfnPrintCallback, void *pUserData)
+{
+	if(m_NumPrintCB == MAX_PRINT_CB)
+		return -1;
+
+	m_aPrintCB[m_NumPrintCB].m_OutputLevel = clamp(OutputLevel, (int)(OUTPUT_LEVEL_STANDARD), (int)(OUTPUT_LEVEL_DEBUG));
+	m_aPrintCB[m_NumPrintCB].m_pfnPrintCallback = pfnPrintCallback;
+	m_aPrintCB[m_NumPrintCB].m_pPrintCallbackUserdata = pUserData;
+	return m_NumPrintCB++;
+}
+
+void CConsole::SetPrintOutputLevel(int Index, int OutputLevel)
 {
-	m_pfnPrintCallback = pfnPrintCallback;
-	m_pPrintCallbackUserdata = pUserData;
+	if(Index >= 0 && Index < MAX_PRINT_CB)
+		m_aPrintCB[Index].m_OutputLevel = clamp(OutputLevel, (int)(OUTPUT_LEVEL_STANDARD), (int)(OUTPUT_LEVEL_DEBUG));
 }
 
 void CConsole::Print(int Level, const char *pFrom, const char *pStr)
 {
 	dbg_msg(pFrom ,"%s", pStr);
-	if(Level <= g_Config.m_ConsoleOutputLevel && m_pfnPrintCallback)
+	for(int i = 0; i < m_NumPrintCB; ++i)
 	{
-		char aBuf[1024];
-		str_format(aBuf, sizeof(aBuf), "[%s]: %s", pFrom, pStr);
-		m_pfnPrintCallback(aBuf, m_pPrintCallbackUserdata);
+		if(Level <= m_aPrintCB[i].m_OutputLevel && m_aPrintCB[i].m_pfnPrintCallback)
+		{
+			char aBuf[1024];
+			str_format(aBuf, sizeof(aBuf), "[%s]: %s", pFrom, pStr);
+			m_aPrintCB[i].m_pfnPrintCallback(aBuf, m_aPrintCB[i].m_pPrintCallbackUserdata);
+		}
 	}
 }
 
@@ -247,35 +288,47 @@ void CConsole::ExecuteLineStroked(int Stroke, const char *pStr)
 		if(ParseStart(&Result, pStr, (pEnd-pStr) + 1) != 0)
 			return;
 
+		if(!*Result.m_pCommand)
+			return;
+
 		CCommand *pCommand = FindCommand(Result.m_pCommand, m_FlagMask);
 
 		if(pCommand)
 		{
-			int IsStrokeCommand = 0;
-			if(Result.m_pCommand[0] == '+')
+			if(pCommand->GetAccessLevel() >= m_AccessLevel)
 			{
-				// insert the stroke direction token
-				Result.AddArgument(m_paStrokeStr[Stroke]);
-				IsStrokeCommand = 1;
-			}
-
-			if(Stroke || IsStrokeCommand)
-			{
-				if(ParseArgs(&Result, pCommand->m_pParams))
+				int IsStrokeCommand = 0;
+				if(Result.m_pCommand[0] == '+')
 				{
-					char aBuf[256];
-					str_format(aBuf, sizeof(aBuf), "Invalid arguments... Usage: %s %s", pCommand->m_pName, pCommand->m_pParams);
-					Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf);
+					// insert the stroke direction token
+					Result.AddArgument(m_paStrokeStr[Stroke]);
+					IsStrokeCommand = 1;
 				}
-				else if(m_StoreCommands && pCommand->m_Flags&CFGFLAG_STORE)
+
+				if(Stroke || IsStrokeCommand)
 				{
-					m_ExecutionQueue.AddEntry();
-					m_ExecutionQueue.m_pLast->m_pfnCommandCallback = pCommand->m_pfnCallback;
-					m_ExecutionQueue.m_pLast->m_pCommandUserData = pCommand->m_pUserData;
-					m_ExecutionQueue.m_pLast->m_Result = Result;
+					if(ParseArgs(&Result, pCommand->m_pParams))
+					{
+						char aBuf[256];
+						str_format(aBuf, sizeof(aBuf), "Invalid arguments... Usage: %s %s", pCommand->m_pName, pCommand->m_pParams);
+						Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf);
+					}
+					else if(m_StoreCommands && pCommand->m_Flags&CFGFLAG_STORE)
+					{
+						m_ExecutionQueue.AddEntry();
+						m_ExecutionQueue.m_pLast->m_pfnCommandCallback = pCommand->m_pfnCallback;
+						m_ExecutionQueue.m_pLast->m_pCommandUserData = pCommand->m_pUserData;
+						m_ExecutionQueue.m_pLast->m_Result = Result;
+					}
+					else
+						pCommand->m_pfnCallback(&Result, pCommand->m_pUserData);
 				}
-				else
-					pCommand->m_pfnCallback(&Result, pCommand->m_pUserData);
+			}
+			else if(Stroke)
+			{
+				char aBuf[256];
+				str_format(aBuf, sizeof(aBuf), "Access for command %s denied.", Result.m_pCommand);
+				Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf);
 			}
 		}
 		else if(Stroke)
@@ -289,12 +342,11 @@ void CConsole::ExecuteLineStroked(int Stroke, const char *pStr)
 	}
 }
 
-void CConsole::PossibleCommands(const char *pStr, int FlagMask, FPossibleCallback pfnCallback, void *pUser)
+void CConsole::PossibleCommands(const char *pStr, int FlagMask, bool Temp, FPossibleCallback pfnCallback, void *pUser)
 {
-	CCommand *pCommand;
-	for(pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext)
+	for(CCommand *pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext)
 	{
-		if(pCommand->m_Flags&FlagMask)
+		if(pCommand->m_Flags&FlagMask && pCommand->m_Temp == Temp)
 		{
 			if(str_find_nocase(pCommand->m_pName, pStr))
 				pfnCallback(pCommand->m_pName, pUser);
@@ -304,8 +356,7 @@ void CConsole::PossibleCommands(const char *pStr, int FlagMask, FPossibleCallbac
 
 CConsole::CCommand *CConsole::FindCommand(const char *pName, int FlagMask)
 {
-	CCommand *pCommand;
-	for (pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext)
+	for(CCommand *pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext)
 	{
 		if(pCommand->m_Flags&FlagMask)
 		{
@@ -380,6 +431,62 @@ void CConsole::Con_Exec(IResult *pResult, void *pUserData)
 	((CConsole*)pUserData)->ExecuteFile(pResult->GetString(0));
 }
 
+void CConsole::ConModCommandAccess(IResult *pResult, void *pUser)
+{
+	CConsole* pConsole = static_cast<CConsole *>(pUser);
+	char aBuf[128];
+	CCommand *pCommand = pConsole->FindCommand(pResult->GetString(0), CFGFLAG_SERVER);
+	if(pCommand)
+	{
+		if(pResult->NumArguments() == 2)
+		{
+			pCommand->SetAccessLevel(pResult->GetInteger(1));
+			str_format(aBuf, sizeof(aBuf), "moderator access for '%s' is now %s", pResult->GetString(0), pCommand->GetAccessLevel() ? "enabled" : "disabled");
+		}
+		else
+			str_format(aBuf, sizeof(aBuf), "moderator access for '%s' is %s", pResult->GetString(0), pCommand->GetAccessLevel() ? "enabled" : "disabled");
+	}
+	else
+		str_format(aBuf, sizeof(aBuf), "No such command: '%s'.", pResult->GetString(0));
+
+	pConsole->Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf);
+}
+
+void CConsole::ConModCommandStatus(IResult *pResult, void *pUser)
+{
+	CConsole* pConsole = static_cast<CConsole *>(pUser);
+	char aBuf[240];
+	mem_zero(aBuf, sizeof(aBuf));
+	int Used = 0;
+
+	for(CCommand *pCommand = pConsole->m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext)
+	{
+		if(pCommand->m_Flags&pConsole->m_FlagMask && pCommand->GetAccessLevel() == ACCESS_LEVEL_MOD)
+		{
+			int Length = str_length(pCommand->m_pName);
+			if(Used + Length + 2 < (int)(sizeof(aBuf)))
+			{
+				if(Used > 0)
+				{
+					Used += 2;
+					str_append(aBuf, ", ", sizeof(aBuf));
+				}
+				str_append(aBuf, pCommand->m_pName, sizeof(aBuf));
+				Used += Length;
+			}
+			else
+			{
+				pConsole->Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf);
+				mem_zero(aBuf, sizeof(aBuf));
+				str_copy(aBuf, pCommand->m_pName, sizeof(aBuf));
+				Used = Length;
+			}
+		}
+	}
+	if(Used > 0)
+		pConsole->Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf);
+}
+
 struct CIntVariableData
 {
 	IConsole *m_pConsole;
@@ -460,14 +567,17 @@ static void StrVariableCommand(IConsole::IResult *pResult, void *pUserData)
 CConsole::CConsole(int FlagMask)
 {
 	m_FlagMask = FlagMask;
+	m_AccessLevel = ACCESS_LEVEL_ADMIN;
+	m_pRecycleList = 0;
+	m_TempCommands.Reset();
 	m_StoreCommands = true;
 	m_paStrokeStr[0] = "0";
 	m_paStrokeStr[1] = "1";
 	m_ExecutionQueue.Reset();
 	m_pFirstCommand = 0;
 	m_pFirstExec = 0;
-	m_pPrintCallbackUserdata = 0;
-	m_pfnPrintCallback = 0;
+	mem_zero(m_aPrintCB, sizeof(m_aPrintCB));
+	m_NumPrintCB = 0;
 
 	m_pStorage = 0;
 
@@ -475,6 +585,9 @@ CConsole::CConsole(int FlagMask)
 	Register("echo", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, Con_Echo, this, "Echo the text");
 	Register("exec", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, Con_Exec, this, "Execute the specified file");
 
+	Register("mod_command", "s?i", CFGFLAG_SERVER, ConModCommandAccess, this, "Specify command accessibility for moderators");
+	Register("mod_status", "", CFGFLAG_SERVER, ConModCommandStatus, this, "List all commands which are accessible for moderators");
+
 	// TODO: this should disappear
 	#define MACRO_CONFIG_INT(Name,ScriptName,Def,Min,Max,Flags,Desc) \
 	{ \
@@ -518,20 +631,131 @@ void CConsole::ParseArguments(int NumArgs, const char **ppArguments)
 	}
 }
 
+void CConsole::AddCommandSorted(CCommand *pCommand)
+{
+	if(!m_pFirstCommand || str_comp(pCommand->m_pName, m_pFirstCommand->m_pName) < 0)
+	{
+		if(m_pFirstCommand && m_pFirstCommand->m_pNext)
+			pCommand->m_pNext = m_pFirstCommand;
+		else
+			pCommand->m_pNext = 0;
+		m_pFirstCommand = pCommand;
+	}
+	else
+	{
+		for(CCommand *p = m_pFirstCommand; p; p = p->m_pNext)
+		{
+			if(!p->m_pNext || str_comp(pCommand->m_pName, p->m_pNext->m_pName) < 0)
+			{
+				pCommand->m_pNext = p->m_pNext;
+				p->m_pNext = pCommand;
+				break;
+			}
+		}
+	}
+}
+
 void CConsole::Register(const char *pName, const char *pParams,
 	int Flags, FCommandCallback pfnFunc, void *pUser, const char *pHelp)
 {
-	CCommand *pCommand = (CCommand *)mem_alloc(sizeof(CCommand), sizeof(void*));
+	CCommand *pCommand = new(mem_alloc(sizeof(CCommand), sizeof(void*))) CCommand;
 	pCommand->m_pfnCallback = pfnFunc;
 	pCommand->m_pUserData = pUser;
-	pCommand->m_pHelp = pHelp;
+
 	pCommand->m_pName = pName;
+	pCommand->m_pHelp = pHelp;
 	pCommand->m_pParams = pParams;
+	
 	pCommand->m_Flags = Flags;
+	pCommand->m_Temp = false;
+
+	AddCommandSorted(pCommand);
+}
+
+void CConsole::RegisterTemp(const char *pName, const char *pParams,	int Flags, const char *pHelp)
+{
+	CCommand *pCommand;
+	if(m_pRecycleList)
+	{
+		pCommand = m_pRecycleList;
+		str_copy(const_cast<char *>(pCommand->m_pName), pName, TEMPCMD_NAME_LENGTH);
+		str_copy(const_cast<char *>(pCommand->m_pHelp), pHelp, TEMPCMD_HELP_LENGTH);
+		str_copy(const_cast<char *>(pCommand->m_pParams), pParams, TEMPCMD_PARAMS_LENGTH);
+
+		m_pRecycleList = m_pRecycleList->m_pNext;
+	}
+	else
+	{
+		pCommand = new(m_TempCommands.Allocate(sizeof(CCommand))) CCommand;
+		char *pMem = static_cast<char *>(m_TempCommands.Allocate(TEMPCMD_NAME_LENGTH));
+		str_copy(pMem, pName, TEMPCMD_NAME_LENGTH);
+		pCommand->m_pName = pMem;
+		pMem = static_cast<char *>(m_TempCommands.Allocate(TEMPCMD_HELP_LENGTH));
+		str_copy(pMem, pHelp, TEMPCMD_HELP_LENGTH);
+		pCommand->m_pHelp = pMem;
+		pMem = static_cast<char *>(m_TempCommands.Allocate(TEMPCMD_PARAMS_LENGTH));
+		str_copy(pMem, pParams, TEMPCMD_PARAMS_LENGTH);
+		pCommand->m_pParams = pMem;
+	}
 
+	pCommand->m_pfnCallback = 0;
+	pCommand->m_pUserData = 0;	
+	pCommand->m_Flags = Flags;
+	pCommand->m_Temp = true;
 
-	pCommand->m_pNext = m_pFirstCommand;
-	m_pFirstCommand = pCommand;
+	AddCommandSorted(pCommand);
+}
+
+void CConsole::DeregisterTemp(const char *pName)
+{
+	if(!m_pFirstCommand)
+		return;
+
+	CCommand *pRemoved = 0;
+
+	// remove temp entry from command list
+	if(m_pFirstCommand->m_Temp && str_comp(m_pFirstCommand->m_pName, pName) == 0)
+	{
+		pRemoved = m_pFirstCommand;
+		m_pFirstCommand = m_pFirstCommand->m_pNext;
+	}
+	else
+	{
+		for(CCommand *pCommand = m_pFirstCommand; pCommand->m_pNext; pCommand = pCommand->m_pNext)
+			if(pCommand->m_pNext->m_Temp && str_comp(pCommand->m_pNext->m_pName, pName) == 0)
+			{
+				pRemoved = pCommand->m_pNext;
+				pCommand->m_pNext = pCommand->m_pNext->m_pNext;
+				break;
+			}
+	}
+	
+	// add to recycle list
+	if(pRemoved)
+	{
+		pRemoved->m_pNext = m_pRecycleList;
+		m_pRecycleList = pRemoved;
+	}
+}
+
+void CConsole::DeregisterTempAll()
+{
+	// set non temp as first one
+	for(; m_pFirstCommand && m_pFirstCommand->m_Temp; m_pFirstCommand = m_pFirstCommand->m_pNext);
+	
+	// remove temp entries from command list
+	for(CCommand *pCommand = m_pFirstCommand; pCommand && pCommand->m_pNext; pCommand = pCommand->m_pNext)
+	{
+		CCommand *pNext = pCommand->m_pNext;
+		if(pNext->m_Temp)
+		{
+			for(; pNext && pNext->m_Temp; pNext = pNext->m_pNext);
+			pCommand->m_pNext = pNext;
+		}
+	}
+
+	m_TempCommands.Reset();
+	m_pRecycleList = 0;
 }
 
 void CConsole::Con_Chain(IResult *pResult, void *pUserData)
@@ -577,9 +801,18 @@ void CConsole::StoreCommands(bool Store)
 }
 
 
-IConsole::CCommandInfo *CConsole::GetCommandInfo(const char *pName, int FlagMask)
+const IConsole::CCommandInfo *CConsole::GetCommandInfo(const char *pName, int FlagMask, bool Temp)
 {
-	return FindCommand(pName, FlagMask);
+	for(CCommand *pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext)
+	{
+		if(pCommand->m_Flags&FlagMask && pCommand->m_Temp == Temp)
+		{
+			if(str_comp_nocase(pCommand->m_pName, pName) == 0)
+				return pCommand;
+		}
+	}
+
+	return 0;
 }
 
 
diff --git a/src/engine/shared/console.h b/src/engine/shared/console.h
index 77c46fb0..6989c696 100644
--- a/src/engine/shared/console.h
+++ b/src/engine/shared/console.h
@@ -13,8 +13,13 @@ class CConsole : public IConsole
 	public:
 		CCommand *m_pNext;
 		int m_Flags;
+		bool m_Temp;
 		FCommandCallback m_pfnCallback;
 		void *m_pUserData;
+
+		virtual const CCommandInfo *NextCommandInfo(int AccessLevel, int FlagMask) const;
+
+		void SetAccessLevel(int AccessLevel) { m_AccessLevel = clamp(AccessLevel, (int)(ACCESS_LEVEL_ADMIN), (int)(ACCESS_LEVEL_MOD)); }
 	};
 
 
@@ -36,21 +41,32 @@ class CConsole : public IConsole
 	{
 	public:
 		const char *m_pFilename;
-		struct CExecFile *m_pPrev;
+		CExecFile *m_pPrev;
 	};
 
 	CExecFile *m_pFirstExec;
 	class IStorage *m_pStorage;
+	int m_AccessLevel;
+
+	CCommand *m_pRecycleList;
+	CHeap m_TempCommands;
 
 	static void Con_Chain(IResult *pResult, void *pUserData);
 	static void Con_Echo(IResult *pResult, void *pUserData);
 	static void Con_Exec(IResult *pResult, void *pUserData);
+	static void ConModCommandAccess(IResult *pResult, void *pUser);
+	static void ConModCommandStatus(IConsole::IResult *pResult, void *pUser);
 
 	void ExecuteFileRecurse(const char *pFilename);
 	void ExecuteLineStroked(int Stroke, const char *pStr);
 
-	FPrintCallback m_pfnPrintCallback;
-	void *m_pPrintCallbackUserdata;
+	struct
+	{
+		int m_OutputLevel;
+		FPrintCallback m_pfnPrintCallback;
+		void *m_pPrintCallbackUserdata;
+	} m_aPrintCB[MAX_PRINT_CB];
+	int m_NumPrintCB;
 
 	enum
 	{
@@ -134,16 +150,21 @@ class CConsole : public IConsole
 		}
 	} m_ExecutionQueue;
 
+	void AddCommandSorted(CCommand *pCommand);
 	CCommand *FindCommand(const char *pName, int FlagMask);
 
 public:
 	CConsole(int FlagMask);
 
-	virtual CCommandInfo *GetCommandInfo(const char *pName, int FlagMask);
-	virtual void PossibleCommands(const char *pStr, int FlagMask, FPossibleCallback pfnCallback, void *pUser) ;
+	virtual const CCommandInfo *FirstCommandInfo(int AccessLevel, int Flagmask) const;
+	virtual const CCommandInfo *GetCommandInfo(const char *pName, int FlagMask, bool Temp);
+	virtual void PossibleCommands(const char *pStr, int FlagMask, bool Temp, FPossibleCallback pfnCallback, void *pUser);
 
 	virtual void ParseArguments(int NumArgs, const char **ppArguments);
 	virtual void Register(const char *pName, const char *pParams, int Flags, FCommandCallback pfnFunc, void *pUser, const char *pHelp);
+	virtual void RegisterTemp(const char *pName, const char *pParams, int Flags, const char *pHelp);
+	virtual void DeregisterTemp(const char *pName);
+	virtual void DeregisterTempAll();
 	virtual void Chain(const char *pName, FChainCommandCallback pfnChainFunc, void *pUser);
 	virtual void StoreCommands(bool Store);
 
@@ -151,8 +172,11 @@ public:
 	virtual void ExecuteLine(const char *pStr);
 	virtual void ExecuteFile(const char *pFilename);
 
-	virtual void RegisterPrintCallback(FPrintCallback pfnPrintCallback, void *pUserData);
+	virtual int RegisterPrintCallback(int OutputLevel, FPrintCallback pfnPrintCallback, void *pUserData);
+	virtual void SetPrintOutputLevel(int Index, int OutputLevel);
 	virtual void Print(int Level, const char *pFrom, const char *pStr);
+
+	void SetAccessLevel(int AccessLevel) { m_AccessLevel = clamp(AccessLevel, (int)(ACCESS_LEVEL_ADMIN), (int)(ACCESS_LEVEL_MOD)); }
 };
 
 #endif
diff --git a/src/engine/shared/datafile.cpp b/src/engine/shared/datafile.cpp
index 3a5a4b1d..e2215635 100644
--- a/src/engine/shared/datafile.cpp
+++ b/src/engine/shared/datafile.cpp
@@ -273,7 +273,9 @@ void *CDataFileReader::GetDataImpl(int Index, int Swap)
 	{
 		// fetch the data size
 		int DataSize = GetDataSize(Index);
+#if defined(CONF_ARCH_ENDIAN_BIG)
 		int SwapSize = DataSize;
+#endif
 
 		if(m_pDataFile->m_Header.m_Version == 4)
 		{
@@ -292,7 +294,9 @@ void *CDataFileReader::GetDataImpl(int Index, int Swap)
 			// decompress the data, TODO: check for errors
 			s = UncompressedSize;
 			uncompress((Bytef*)m_pDataFile->m_ppDataPtrs[Index], &s, (Bytef*)pTemp, DataSize); // ignore_convention
+#if defined(CONF_ARCH_ENDIAN_BIG)
 			SwapSize = s;
+#endif
 
 			// clean up the temporary buffers
 			mem_free(pTemp);
@@ -418,6 +422,25 @@ unsigned CDataFileReader::Crc()
 	return m_pDataFile->m_Crc;
 }
 
+
+CDataFileWriter::CDataFileWriter()
+{
+	m_File = 0;
+	m_pItemTypes = static_cast<CItemTypeInfo *>(mem_alloc(sizeof(CItemTypeInfo) * MAX_ITEM_TYPES, 1));
+	m_pItems = static_cast<CItemInfo *>(mem_alloc(sizeof(CItemInfo) * MAX_ITEMS, 1));
+	m_pDatas = static_cast<CDataInfo *>(mem_alloc(sizeof(CDataInfo) * MAX_DATAS, 1));
+}
+
+CDataFileWriter::~CDataFileWriter()
+{
+	mem_free(m_pItemTypes);
+	m_pItemTypes = 0;
+	mem_free(m_pItems);
+	m_pItems = 0;
+	mem_free(m_pDatas);
+	m_pDatas = 0;
+}
+
 bool CDataFileWriter::Open(class IStorage *pStorage, const char *pFilename)
 {
 	dbg_assert(!m_File, "a file already exists");
@@ -428,12 +451,12 @@ bool CDataFileWriter::Open(class IStorage *pStorage, const char *pFilename)
 	m_NumItems = 0;
 	m_NumDatas = 0;
 	m_NumItemTypes = 0;
-	mem_zero(&m_aItemTypes, sizeof(m_aItemTypes));
+	mem_zero(m_pItemTypes, sizeof(CItemTypeInfo) * MAX_ITEM_TYPES);
 
-	for(int i = 0; i < 0xffff; i++)
+	for(int i = 0; i < MAX_ITEM_TYPES; i++)
 	{
-		m_aItemTypes[i].m_First = -1;
-		m_aItemTypes[i].m_Last = -1;
+		m_pItemTypes[i].m_First = -1;
+		m_pItemTypes[i].m_Last = -1;
 	}
 
 	return true;
@@ -447,29 +470,29 @@ int CDataFileWriter::AddItem(int Type, int ID, int Size, void *pData)
 	dbg_assert(m_NumItems < 1024, "too many items");
 	dbg_assert(Size%sizeof(int) == 0, "incorrect boundary");
 
-	m_aItems[m_NumItems].m_Type = Type;
-	m_aItems[m_NumItems].m_ID = ID;
-	m_aItems[m_NumItems].m_Size = Size;
+	m_pItems[m_NumItems].m_Type = Type;
+	m_pItems[m_NumItems].m_ID = ID;
+	m_pItems[m_NumItems].m_Size = Size;
 
 	// copy data
-	m_aItems[m_NumItems].m_pData = mem_alloc(Size, 1);
-	mem_copy(m_aItems[m_NumItems].m_pData, pData, Size);
+	m_pItems[m_NumItems].m_pData = mem_alloc(Size, 1);
+	mem_copy(m_pItems[m_NumItems].m_pData, pData, Size);
 
-	if(!m_aItemTypes[Type].m_Num) // count item types
+	if(!m_pItemTypes[Type].m_Num) // count item types
 		m_NumItemTypes++;
 
 	// link
-	m_aItems[m_NumItems].m_Prev = m_aItemTypes[Type].m_Last;
-	m_aItems[m_NumItems].m_Next = -1;
+	m_pItems[m_NumItems].m_Prev = m_pItemTypes[Type].m_Last;
+	m_pItems[m_NumItems].m_Next = -1;
 
-	if(m_aItemTypes[Type].m_Last != -1)
-		m_aItems[m_aItemTypes[Type].m_Last].m_Next = m_NumItems;
-	m_aItemTypes[Type].m_Last = m_NumItems;
+	if(m_pItemTypes[Type].m_Last != -1)
+		m_pItems[m_pItemTypes[Type].m_Last].m_Next = m_NumItems;
+	m_pItemTypes[Type].m_Last = m_NumItems;
 
-	if(m_aItemTypes[Type].m_First == -1)
-		m_aItemTypes[Type].m_First = m_NumItems;
+	if(m_pItemTypes[Type].m_First == -1)
+		m_pItemTypes[Type].m_First = m_NumItems;
 
-	m_aItemTypes[Type].m_Num++;
+	m_pItemTypes[Type].m_Num++;
 
 	m_NumItems++;
 	return m_NumItems-1;
@@ -481,7 +504,7 @@ int CDataFileWriter::AddData(int Size, void *pData)
 
 	dbg_assert(m_NumDatas < 1024, "too much data");
 
-	CDataInfo *pInfo = &m_aDatas[m_NumDatas];
+	CDataInfo *pInfo = &m_pDatas[m_NumDatas];
 	unsigned long s = compressBound(Size);
 	void *pCompData = mem_alloc(s, 1); // temporary buffer that we use during compression
 
@@ -536,13 +559,13 @@ int CDataFileWriter::Finish()
 	for(int i = 0; i < m_NumItems; i++)
 	{
 		if(DEBUG)
-			dbg_msg("datafile", "item=%d size=%d (%d)", i, m_aItems[i].m_Size, m_aItems[i].m_Size+sizeof(CDatafileItem));
-		ItemSize += m_aItems[i].m_Size + sizeof(CDatafileItem);
+			dbg_msg("datafile", "item=%d size=%d (%d)", i, m_pItems[i].m_Size, m_pItems[i].m_Size+sizeof(CDatafileItem));
+		ItemSize += m_pItems[i].m_Size + sizeof(CDatafileItem);
 	}
 
 
 	for(int i = 0; i < m_NumDatas; i++)
-		DataSize += m_aDatas[i].m_CompressedSize;
+		DataSize += m_pDatas[i].m_CompressedSize;
 
 	// calculate the complete size
 	TypesSize = m_NumItemTypes*sizeof(CDatafileItemType);
@@ -583,30 +606,30 @@ int CDataFileWriter::Finish()
 	// write types
 	for(int i = 0, Count = 0; i < 0xffff; i++)
 	{
-		if(m_aItemTypes[i].m_Num)
+		if(m_pItemTypes[i].m_Num)
 		{
 			// write info
 			CDatafileItemType Info;
 			Info.m_Type = i;
 			Info.m_Start = Count;
-			Info.m_Num = m_aItemTypes[i].m_Num;
+			Info.m_Num = m_pItemTypes[i].m_Num;
 			if(DEBUG)
 				dbg_msg("datafile", "writing type=%x start=%d num=%d", Info.m_Type, Info.m_Start, Info.m_Num);
 #if defined(CONF_ARCH_ENDIAN_BIG)
 			swap_endian(&Info, sizeof(int), sizeof(CDatafileItemType)/sizeof(int));
 #endif
 			io_write(m_File, &Info, sizeof(Info));
-			Count += m_aItemTypes[i].m_Num;
+			Count += m_pItemTypes[i].m_Num;
 		}
 	}
 
 	// write item offsets
 	for(int i = 0, Offset = 0; i < 0xffff; i++)
 	{
-		if(m_aItemTypes[i].m_Num)
+		if(m_pItemTypes[i].m_Num)
 		{
-			// write all m_aItems in of this type
-			int k = m_aItemTypes[i].m_First;
+			// write all m_pItems in of this type
+			int k = m_pItemTypes[i].m_First;
 			while(k != -1)
 			{
 				if(DEBUG)
@@ -616,10 +639,10 @@ int CDataFileWriter::Finish()
 				swap_endian(&Temp, sizeof(int), sizeof(Temp)/sizeof(int));
 #endif
 				io_write(m_File, &Temp, sizeof(Temp));
-				Offset += m_aItems[k].m_Size + sizeof(CDatafileItem);
+				Offset += m_pItems[k].m_Size + sizeof(CDatafileItem);
 
 				// next
-				k = m_aItems[k].m_Next;
+				k = m_pItems[k].m_Next;
 			}
 		}
 	}
@@ -634,45 +657,45 @@ int CDataFileWriter::Finish()
 		swap_endian(&Temp, sizeof(int), sizeof(Temp)/sizeof(int));
 #endif
 		io_write(m_File, &Temp, sizeof(Temp));
-		Offset += m_aDatas[i].m_CompressedSize;
+		Offset += m_pDatas[i].m_CompressedSize;
 	}
 
 	// write data uncompressed sizes
 	for(int i = 0; i < m_NumDatas; i++)
 	{
 		if(DEBUG)
-			dbg_msg("datafile", "writing data uncompressed size num=%d size=%d", i, m_aDatas[i].m_UncompressedSize);
-		int UncompressedSize = m_aDatas[i].m_UncompressedSize;
+			dbg_msg("datafile", "writing data uncompressed size num=%d size=%d", i, m_pDatas[i].m_UncompressedSize);
+		int UncompressedSize = m_pDatas[i].m_UncompressedSize;
 #if defined(CONF_ARCH_ENDIAN_BIG)
 		swap_endian(&UncompressedSize, sizeof(int), sizeof(UncompressedSize)/sizeof(int));
 #endif
 		io_write(m_File, &UncompressedSize, sizeof(UncompressedSize));
 	}
 
-	// write m_aItems
+	// write m_pItems
 	for(int i = 0; i < 0xffff; i++)
 	{
-		if(m_aItemTypes[i].m_Num)
+		if(m_pItemTypes[i].m_Num)
 		{
-			// write all m_aItems in of this type
-			int k = m_aItemTypes[i].m_First;
+			// write all m_pItems in of this type
+			int k = m_pItemTypes[i].m_First;
 			while(k != -1)
 			{
 				CDatafileItem Item;
-				Item.m_TypeAndID = (i<<16)|m_aItems[k].m_ID;
-				Item.m_Size = m_aItems[k].m_Size;
+				Item.m_TypeAndID = (i<<16)|m_pItems[k].m_ID;
+				Item.m_Size = m_pItems[k].m_Size;
 				if(DEBUG)
-					dbg_msg("datafile", "writing item type=%x idx=%d id=%d size=%d", i, k, m_aItems[k].m_ID, m_aItems[k].m_Size);
+					dbg_msg("datafile", "writing item type=%x idx=%d id=%d size=%d", i, k, m_pItems[k].m_ID, m_pItems[k].m_Size);
 
 #if defined(CONF_ARCH_ENDIAN_BIG)
 				swap_endian(&Item, sizeof(int), sizeof(Item)/sizeof(int));
-				swap_endian(m_aItems[k].m_pData, sizeof(int), m_aItems[k].m_Size/sizeof(int));
+				swap_endian(m_pItems[k].m_pData, sizeof(int), m_pItems[k].m_Size/sizeof(int));
 #endif
 				io_write(m_File, &Item, sizeof(Item));
-				io_write(m_File, m_aItems[k].m_pData, m_aItems[k].m_Size);
+				io_write(m_File, m_pItems[k].m_pData, m_pItems[k].m_Size);
 
 				// next
-				k = m_aItems[k].m_Next;
+				k = m_pItems[k].m_Next;
 			}
 		}
 	}
@@ -681,15 +704,15 @@ int CDataFileWriter::Finish()
 	for(int i = 0; i < m_NumDatas; i++)
 	{
 		if(DEBUG)
-			dbg_msg("datafile", "writing data id=%d size=%d", i, m_aDatas[i].m_CompressedSize);
-		io_write(m_File, m_aDatas[i].m_pCompressedData, m_aDatas[i].m_CompressedSize);
+			dbg_msg("datafile", "writing data id=%d size=%d", i, m_pDatas[i].m_CompressedSize);
+		io_write(m_File, m_pDatas[i].m_pCompressedData, m_pDatas[i].m_CompressedSize);
 	}
 
 	// free data
 	for(int i = 0; i < m_NumItems; i++)
-		mem_free(m_aItems[i].m_pData);
+		mem_free(m_pItems[i].m_pData);
 	for(int i = 0; i < m_NumDatas; ++i)
-		mem_free(m_aDatas[i].m_pCompressedData);
+		mem_free(m_pDatas[i].m_pCompressedData);
 
 	io_close(m_File);
 	m_File = 0;
diff --git a/src/engine/shared/datafile.h b/src/engine/shared/datafile.h
index 46e8ada8..cafce20e 100644
--- a/src/engine/shared/datafile.h
+++ b/src/engine/shared/datafile.h
@@ -6,7 +6,7 @@
 // raw datafile access
 class CDataFileReader
 {
-	class CDatafile *m_pDataFile;
+	struct CDatafile *m_pDataFile;
 	void *GetDataImpl(int Index, int Swap);
 public:
 	CDataFileReader() : m_pDataFile(0) {}
@@ -61,16 +61,24 @@ class CDataFileWriter
 		int m_Last;
 	};
 
+	enum
+	{
+		MAX_ITEM_TYPES=0xffff,
+		MAX_ITEMS=1024,
+		MAX_DATAS=1024,
+	};
+
 	IOHANDLE m_File;
 	int m_NumItems;
 	int m_NumDatas;
 	int m_NumItemTypes;
-	CItemTypeInfo m_aItemTypes[0xffff];
-	CItemInfo m_aItems[1024];
-	CDataInfo m_aDatas[1024];
+	CItemTypeInfo *m_pItemTypes;
+	CItemInfo *m_pItems;
+	CDataInfo *m_pDatas;
 
 public:
-	CDataFileWriter() : m_File(0) {}
+	CDataFileWriter();
+	~CDataFileWriter();
 	bool Open(class IStorage *pStorage, const char *Filename);
 	int AddData(int Size, void *pData);
 	int AddDataSwapped(int Size, void *pData);
diff --git a/src/engine/shared/demo.cpp b/src/engine/shared/demo.cpp
index e48c2f1e..51dd51d9 100644
--- a/src/engine/shared/demo.cpp
+++ b/src/engine/shared/demo.cpp
@@ -1,13 +1,16 @@
 /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
 /* If you are missing that file, acquire a complete release at teeworlds.com.                */
+#include <base/math.h>
 #include <base/system.h>
+
 #include <engine/console.h>
 #include <engine/storage.h>
+
+#include "compression.h"
 #include "demo.h"
 #include "memheap.h"
-#include "snapshot.h"
-#include "compression.h"
 #include "network.h"
+#include "snapshot.h"
 
 static const unsigned char gs_aHeaderMarker[7] = {'T', 'W', 'D', 'E', 'M', 'O', 0};
 static const unsigned char gs_ActVersion = 3;
@@ -760,16 +763,21 @@ int CDemoPlayer::Stop()
 	return 0;
 }
 
-char *CDemoPlayer::GetDemoName()
+void CDemoPlayer::GetDemoName(char *pBuffer, int BufferSize) const
 {
-	// get the name of the demo without its path
-	char *pDemoShortName = &m_aFilename[0];
-	for(int i = 0; i < str_length(m_aFilename)-1; i++)
+	const char *pFileName = m_aFilename;
+	const char *pExtractedName = pFileName;
+	const char *pEnd = 0;
+	for(; *pFileName; ++pFileName)
 	{
-		if(m_aFilename[i] == '/' || m_aFilename[i] == '\\')
-			pDemoShortName = &m_aFilename[i+1];
+		if(*pFileName == '/' || *pFileName == '\\')
+			pExtractedName = pFileName+1;
+		else if(*pFileName == '.')
+			pEnd = pFileName;
 	}
-	return pDemoShortName;
+	
+	int Length = pEnd > pExtractedName ? min(BufferSize, (int)(pEnd-pExtractedName+1)) : BufferSize;
+	str_copy(pBuffer, pExtractedName, Length);
 }
 
 bool CDemoPlayer::GetDemoInfo(class IStorage *pStorage, const char *pFilename, int StorageType, CDemoHeader *pDemoHeader) const
diff --git a/src/engine/shared/demo.h b/src/engine/shared/demo.h
index f1897f21..f4ac5685 100644
--- a/src/engine/shared/demo.h
+++ b/src/engine/shared/demo.h
@@ -110,7 +110,7 @@ public:
 	void SetSpeed(float Speed);
 	int SetPos(float Precent);
 	const CInfo *BaseInfo() const { return &m_Info.m_Info; }
-	char *GetDemoName();
+	void GetDemoName(char *pBuffer, int BufferSize) const;
 	bool GetDemoInfo(class IStorage *pStorage, const char *pFilename, int StorageType, CDemoHeader *pDemoHeader) const;
 	int GetDemoType() const;
 
diff --git a/src/engine/shared/econ.cpp b/src/engine/shared/econ.cpp
new file mode 100644
index 00000000..617cdbd6
--- /dev/null
+++ b/src/engine/shared/econ.cpp
@@ -0,0 +1,173 @@
+#include <engine/console.h>
+#include <engine/shared/config.h>
+
+#include "econ.h"
+
+int CEcon::NewClientCallback(int ClientID, void *pUser)
+{
+	CEcon *pThis = (CEcon *)pUser;
+
+	NETADDR Addr = pThis->m_NetConsole.ClientAddr(ClientID);
+	char aAddrStr[NETADDR_MAXSTRSIZE];
+	net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr));
+	char aBuf[128];
+	str_format(aBuf, sizeof(aBuf), "client accepted. cid=%d addr=%s'", ClientID, aAddrStr);
+	pThis->Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "econ", aBuf);
+
+	pThis->m_aClients[ClientID].m_State = CClient::STATE_CONNECTED;
+	pThis->m_aClients[ClientID].m_TimeConnected = time_get();
+	pThis->m_aClients[ClientID].m_AuthTries = 0;
+
+	pThis->m_NetConsole.Send(ClientID, "Enter password:");
+	return 0;
+}
+
+int CEcon::DelClientCallback(int ClientID, const char *pReason, void *pUser)
+{
+	CEcon *pThis = (CEcon *)pUser;
+
+	NETADDR Addr = pThis->m_NetConsole.ClientAddr(ClientID);
+	char aAddrStr[NETADDR_MAXSTRSIZE];
+	net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr));
+	char aBuf[256];
+	str_format(aBuf, sizeof(aBuf), "client dropped. cid=%d addr=%s reason='%s'", ClientID, aAddrStr, pReason);
+	pThis->Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "econ", aBuf);
+
+	pThis->m_aClients[ClientID].m_State = CClient::STATE_EMPTY;
+	return 0;
+}
+
+void CEcon::SendLineCB(const char *pLine, void *pUserData)
+{
+	static_cast<CEcon *>(pUserData)->Send(-1, pLine);
+}
+
+void CEcon::ConchainEconOutputLevelUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
+{
+	pfnCallback(pResult, pCallbackUserData);
+	if(pResult->NumArguments() == 1)
+	{
+		CEcon *pThis = static_cast<CEcon *>(pUserData);
+		pThis->Console()->SetPrintOutputLevel(pThis->m_PrintCBIndex, pResult->GetInteger(0));
+	}
+}
+
+void CEcon::Init(IConsole *pConsole)
+{
+	m_pConsole = pConsole;
+
+	for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++)
+		m_aClients[i].m_State = CClient::STATE_EMPTY;
+
+	m_Ready = false;
+
+	if(g_Config.m_EcPort == 0 || g_Config.m_EcPassword[0] == 0)
+		return;
+
+	NETADDR BindAddr;
+	if(g_Config.m_EcBindaddr[0] && net_host_lookup(g_Config.m_EcBindaddr, &BindAddr, NETTYPE_ALL) == 0)
+		BindAddr.port = g_Config.m_EcPort;
+	else
+	{
+		mem_zero(&BindAddr, sizeof(BindAddr));
+		BindAddr.type = NETTYPE_ALL;
+		BindAddr.port = g_Config.m_EcPort;
+	}
+
+	if(m_NetConsole.Open(BindAddr, 0))
+	{
+		m_NetConsole.SetCallbacks(NewClientCallback, DelClientCallback, this);
+		m_Ready = true;
+		char aBuf[128];
+		str_format(aBuf, sizeof(aBuf), "bound to %s:%d", g_Config.m_EcBindaddr, g_Config.m_EcPort);
+		Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD,"econ", aBuf);
+
+		Console()->Chain("ec_output_level", ConchainEconOutputLevelUpdate, this);
+		m_PrintCBIndex = Console()->RegisterPrintCallback(g_Config.m_EcOutputLevel, SendLineCB, this);
+	}
+	else
+		Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD,"econ", "couldn't open socket. port might already be in use");
+}
+
+void CEcon::Update()
+{
+	if(!m_Ready)
+		return;
+
+	m_NetConsole.Update();
+
+	char aBuf[NET_MAX_PACKETSIZE];
+	int ClientID;
+
+	while(m_NetConsole.Recv(aBuf, (int)(sizeof(aBuf))-1, &ClientID))
+	{
+		dbg_assert(m_aClients[ClientID].m_State != CClient::STATE_EMPTY, "got message from empty slot");
+		if(m_aClients[ClientID].m_State == CClient::STATE_CONNECTED)
+		{
+			if(str_comp(aBuf, g_Config.m_EcPassword) == 0)
+			{
+				m_aClients[ClientID].m_State = CClient::STATE_AUTHED;
+				m_NetConsole.Send(ClientID, "Authentication successful. External console access granted.");
+
+				str_format(aBuf, sizeof(aBuf), "cid=%d authed", ClientID);
+				Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "econ", aBuf);
+			}
+			else
+			{
+				m_aClients[ClientID].m_AuthTries++;
+				char aBuf[128];
+				str_format(aBuf, sizeof(aBuf), "Wrong password %d/%d.", m_aClients[ClientID].m_AuthTries, MAX_AUTH_TRIES);
+				m_NetConsole.Send(ClientID, aBuf);
+				if(m_aClients[ClientID].m_AuthTries >= MAX_AUTH_TRIES)
+				{
+					if(!g_Config.m_EcBantime)
+						m_NetConsole.Drop(ClientID, "Too many authentication tries");
+					else
+					{
+						NETADDR Addr = m_NetConsole.ClientAddr(ClientID);
+						m_NetConsole.AddBan(Addr, g_Config.m_EcBantime*60);
+					}
+				}
+			}
+		}
+		else if(m_aClients[ClientID].m_State == CClient::STATE_AUTHED)
+		{
+			char aFormatted[256];
+			str_format(aFormatted, sizeof(aBuf), "cid=%d cmd='%s'", ClientID, aBuf);
+			Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aFormatted);
+			Console()->ExecuteLine(aBuf);
+		}
+	}
+
+	for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; ++i)
+	{
+		if(m_aClients[i].m_State == CClient::STATE_CONNECTED &&
+			time_get() > m_aClients[i].m_TimeConnected + g_Config.m_EcAuthTimeout * time_freq())
+			m_NetConsole.Drop(i, "authentication timeout");
+	}
+}
+
+void CEcon::Send(int ClientID, const char *pLine)
+{
+	if(!m_Ready)
+		return;
+
+	if(ClientID == -1)
+	{
+		for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++)
+		{
+			if(m_aClients[i].m_State == CClient::STATE_AUTHED)
+				m_NetConsole.Send(i, pLine);
+		}
+	}
+	else if(ClientID >= 0 && ClientID < NET_MAX_CONSOLE_CLIENTS && m_aClients[ClientID].m_State == CClient::STATE_AUTHED)
+		m_NetConsole.Send(ClientID, pLine);
+}
+
+void CEcon::Shutdown()
+{
+	if(!m_Ready)
+		return;
+
+	m_NetConsole.Close();
+}
diff --git a/src/engine/shared/econ.h b/src/engine/shared/econ.h
new file mode 100644
index 00000000..daec34c4
--- /dev/null
+++ b/src/engine/shared/econ.h
@@ -0,0 +1,50 @@
+#ifndef ENGINE_SHARED_ECON_H
+#define ENGINE_SHARED_ECON_H
+
+#include "network.h"
+
+class CEcon
+{
+	enum
+	{
+		MAX_AUTH_TRIES=3,
+	};
+
+	class CClient
+	{
+	public:
+		enum
+		{
+			STATE_EMPTY=0,
+			STATE_CONNECTED,
+			STATE_AUTHED,
+		};
+
+		int m_State;
+		int64 m_TimeConnected;
+		int m_AuthTries;
+	};
+	CClient m_aClients[NET_MAX_CONSOLE_CLIENTS];
+
+	IConsole *m_pConsole;
+	CNetConsole m_NetConsole;
+
+	bool m_Ready;
+	int m_PrintCBIndex;
+
+	static void SendLineCB(const char *pLine, void *pUserData);
+	static void ConchainEconOutputLevelUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
+
+	static int NewClientCallback(int ClientID, void *pUser);
+	static int DelClientCallback(int ClientID, const char *pReason, void *pUser);
+
+public:
+	IConsole *Console() { return m_pConsole; }
+
+	void Init(IConsole *pConsole);
+	void Update();
+	void Send(int ClientID, const char *pLine);
+	void Shutdown();
+};
+
+#endif
diff --git a/src/engine/shared/filecollection.cpp b/src/engine/shared/filecollection.cpp
new file mode 100644
index 00000000..622534f2
--- /dev/null
+++ b/src/engine/shared/filecollection.cpp
@@ -0,0 +1,186 @@
+/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
+/* If you are missing that file, acquire a complete release at teeworlds.com.                */
+
+#include <base/math.h>
+
+#include <engine/storage.h>
+
+#include "filecollection.h"
+
+bool CFileCollection::IsFilenameValid(const char *pFilename)
+{
+	if(str_length(pFilename) != m_FileDescLength+TIMESTAMP_LENGTH+m_FileExtLength ||
+		str_comp_num(pFilename, m_aFileDesc, m_FileDescLength) ||
+		str_comp(pFilename+m_FileDescLength+TIMESTAMP_LENGTH, m_aFileExt))
+		return false;
+
+	pFilename += m_FileDescLength;
+	if(pFilename[0] == '_' &&
+		pFilename[1] >= '0' && pFilename[1] <= '9' &&
+		pFilename[2] >= '0' && pFilename[2] <= '9' &&
+		pFilename[3] >= '0' && pFilename[3] <= '9' &&
+		pFilename[4] >= '0' && pFilename[4] <= '9' &&
+		pFilename[5] == '-' &&
+		pFilename[6] >= '0' && pFilename[6] <= '9' &&
+		pFilename[7] >= '0' && pFilename[7] <= '9' &&
+		pFilename[8] == '-' &&
+		pFilename[9] >= '0' && pFilename[9] <= '9' &&
+		pFilename[10] >= '0' && pFilename[10] <= '9' &&
+		pFilename[11] == '_' &&
+		pFilename[12] >= '0' && pFilename[12] <= '9' &&
+		pFilename[13] >= '0' && pFilename[13] <= '9' &&
+		pFilename[14] == '-' &&
+		pFilename[15] >= '0' && pFilename[15] <= '9' &&
+		pFilename[16] >= '0' && pFilename[16] <= '9' &&
+		pFilename[17] == '-' &&
+		pFilename[18] >= '0' && pFilename[18] <= '9' &&
+		pFilename[19] >= '0' && pFilename[19] <= '9')
+		return true;
+
+	return false;
+}
+
+int64 CFileCollection::ExtractTimestamp(const char *pTimestring)
+{
+	int64 Timestamp = pTimestring[0]-'0'; Timestamp <<= 4;
+	Timestamp += pTimestring[1]-'0'; Timestamp <<= 4;
+	Timestamp += pTimestring[2]-'0'; Timestamp <<= 4;
+	Timestamp += pTimestring[3]-'0'; Timestamp <<= 4;
+	Timestamp += pTimestring[5]-'0'; Timestamp <<= 4;
+	Timestamp += pTimestring[6]-'0'; Timestamp <<= 4;
+	Timestamp += pTimestring[8]-'0'; Timestamp <<= 4;
+	Timestamp += pTimestring[9]-'0'; Timestamp <<= 4;
+	Timestamp += pTimestring[11]-'0'; Timestamp <<= 4;
+	Timestamp += pTimestring[12]-'0'; Timestamp <<= 4;
+	Timestamp += pTimestring[14]-'0'; Timestamp <<= 4;
+	Timestamp += pTimestring[15]-'0'; Timestamp <<= 4;
+	Timestamp += pTimestring[17]-'0'; Timestamp <<= 4;
+	Timestamp += pTimestring[18]-'0';
+
+	return Timestamp;
+}
+
+void CFileCollection::BuildTimestring(int64 Timestamp, char *pTimestring)
+{
+	pTimestring[19] = 0;
+	pTimestring[18] = (Timestamp&0xF)+'0'; Timestamp >>= 4;
+	pTimestring[17] = (Timestamp&0xF)+'0'; Timestamp >>= 4;
+	pTimestring[16] = '-';
+	pTimestring[15] = (Timestamp&0xF)+'0'; Timestamp >>= 4;
+	pTimestring[14] = (Timestamp&0xF)+'0'; Timestamp >>= 4;
+	pTimestring[13] = '-';
+	pTimestring[12] = (Timestamp&0xF)+'0'; Timestamp >>= 4;
+	pTimestring[11] = (Timestamp&0xF)+'0'; Timestamp >>= 4;
+	pTimestring[10] = '_';
+	pTimestring[9] = (Timestamp&0xF)+'0'; Timestamp >>= 4;
+	pTimestring[8] = (Timestamp&0xF)+'0'; Timestamp >>= 4;
+	pTimestring[7] = '-';
+	pTimestring[6] = (Timestamp&0xF)+'0'; Timestamp >>= 4;
+	pTimestring[5] = (Timestamp&0xF)+'0'; Timestamp >>= 4;
+	pTimestring[4] = '-';
+	pTimestring[3] = (Timestamp&0xF)+'0'; Timestamp >>= 4;
+	pTimestring[2] = (Timestamp&0xF)+'0'; Timestamp >>= 4;
+	pTimestring[1] = (Timestamp&0xF)+'0'; Timestamp >>= 4;
+	pTimestring[0] = (Timestamp&0xF)+'0';
+}
+
+void CFileCollection::Init(IStorage *pStorage, const char *pPath, const char *pFileDesc, const char *pFileExt, int MaxEntries)
+{
+	mem_zero(m_aTimestamps, sizeof(m_aTimestamps));
+	m_NumTimestamps = 0;
+	m_MaxEntries = clamp(MaxEntries, 1, static_cast<int>(MAX_ENTRIES));
+	str_copy(m_aFileDesc, pFileDesc, sizeof(m_aFileDesc));
+	m_FileDescLength = str_length(m_aFileDesc);
+	str_copy(m_aFileExt, pFileExt, sizeof(m_aFileExt));
+	m_FileExtLength = str_length(m_aFileExt);
+	str_copy(m_aPath, pPath, sizeof(m_aPath));
+	m_pStorage = pStorage;
+
+	m_pStorage->ListDirectory(IStorage::TYPE_SAVE, m_aPath, FilelistCallback, this);
+}
+
+void CFileCollection::AddEntry(int64 Timestamp)
+{
+	if(m_NumTimestamps == 0)
+	{
+		// empty list
+		m_aTimestamps[m_NumTimestamps++] = Timestamp;
+	}
+	else
+	{
+		// remove old file
+		if(m_NumTimestamps == m_MaxEntries)
+		{
+			char aBuf[512];
+			char aTimestring[TIMESTAMP_LENGTH];
+			BuildTimestring(m_aTimestamps[0], aTimestring);
+			str_format(aBuf, sizeof(aBuf), "%s/%s_%s%s", m_aPath, m_aFileDesc, aTimestring, m_aFileExt);
+			m_pStorage->RemoveFile(aBuf, IStorage::TYPE_SAVE);
+		}
+
+		// add entry to the sorted list
+		if(m_aTimestamps[0] > Timestamp)
+		{
+			// first entry
+			if(m_NumTimestamps < m_MaxEntries)
+			{
+				mem_move(m_aTimestamps+1, m_aTimestamps, m_NumTimestamps*sizeof(int64));
+				m_aTimestamps[0] = Timestamp;
+				++m_NumTimestamps;
+			}
+		}
+		else if(m_aTimestamps[m_NumTimestamps-1] <= Timestamp)
+		{
+			// last entry
+			if(m_NumTimestamps == m_MaxEntries)
+			{
+				mem_move(m_aTimestamps, m_aTimestamps+1, (m_NumTimestamps-1)*sizeof(int64));
+				m_aTimestamps[m_NumTimestamps-1] = Timestamp;
+			}
+			else
+				m_aTimestamps[m_NumTimestamps++] = Timestamp;
+		}
+		else
+		{
+			// middle entry
+			int Left = 0, Right = m_NumTimestamps-1;
+			while(Right-Left > 1)
+			{
+				int Mid = (Left+Right)/2;
+				if(m_aTimestamps[Mid] > Timestamp)
+					Right = Mid;
+				else
+					Left = Mid;
+			}
+
+			if(m_NumTimestamps == m_MaxEntries)
+			{
+				mem_move(m_aTimestamps, m_aTimestamps+1, (Right-1)*sizeof(int64));
+				m_aTimestamps[Right-1] = Timestamp;
+			}
+			else
+			{
+				mem_move(m_aTimestamps+Right+1, m_aTimestamps+Right, (m_NumTimestamps-Right)*sizeof(int64));
+				m_aTimestamps[Right] = Timestamp;
+				++m_NumTimestamps;
+			}
+		}
+	}
+}
+
+int CFileCollection::FilelistCallback(const char *pFilename, int IsDir, int StorageType, void *pUser)
+{
+	CFileCollection *pThis = static_cast<CFileCollection *>(pUser);
+
+	// check for valid file name format
+	if(IsDir || !pThis->IsFilenameValid(pFilename))
+		return 0;
+
+	// extract the timestamp
+	int64 Timestamp = pThis->ExtractTimestamp(pFilename+pThis->m_FileDescLength+1);
+
+	// add the entry
+	pThis->AddEntry(Timestamp);
+
+	return 0;
+}
diff --git a/src/engine/shared/filecollection.h b/src/engine/shared/filecollection.h
new file mode 100644
index 00000000..ac633892
--- /dev/null
+++ b/src/engine/shared/filecollection.h
@@ -0,0 +1,35 @@
+/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
+/* If you are missing that file, acquire a complete release at teeworlds.com.                */
+#ifndef ENGINE_SHARED_FILECOLLECTION_H
+#define ENGINE_SHARED_FILECOLLECTION_H
+
+class CFileCollection
+{
+	enum
+	{
+		MAX_ENTRIES=1000,
+		TIMESTAMP_LENGTH=20,	// _YYYY-MM-DD_HH-MM-SS
+	};
+
+	int64 m_aTimestamps[MAX_ENTRIES];
+	int m_NumTimestamps;
+	int m_MaxEntries;
+	char m_aFileDesc[128];
+	int m_FileDescLength;
+	char m_aFileExt[32];
+	int m_FileExtLength;
+	char m_aPath[512];
+	IStorage *m_pStorage;
+
+	bool IsFilenameValid(const char *pFilename);
+	int64 ExtractTimestamp(const char *pTimestring);
+	void BuildTimestring(int64 Timestamp, char *pTimestring);
+
+public:
+	void Init(IStorage *pStorage, const char *pPath, const char *pFileDesc, const char *pFileExt, int MaxEntries);
+	void AddEntry(int64 Timestamp);
+
+	static int FilelistCallback(const char *pFilename, int IsDir, int StorageType, void *pUser);
+};
+
+#endif
diff --git a/src/engine/shared/huffman.cpp b/src/engine/shared/huffman.cpp
index c88502b5..d08c39c1 100644
--- a/src/engine/shared/huffman.cpp
+++ b/src/engine/shared/huffman.cpp
@@ -57,8 +57,8 @@ void CHuffman::ConstructTree(const unsigned *pFrequencies)
 	{
 		m_aNodes[i].m_NumBits = 0xFFFFFFFF;
 		m_aNodes[i].m_Symbol = i;
-		m_aNodes[i].m_aLeafs[0] = -1;
-		m_aNodes[i].m_aLeafs[1] = -1;
+		m_aNodes[i].m_aLeafs[0] = 0xffff;
+		m_aNodes[i].m_aLeafs[1] = 0xffff;
 
 		if(i == HUFFMAN_EOF_SYMBOL)
 			aNodesLeftStorage[i].m_Frequency = 1;
diff --git a/src/engine/shared/mapchecker.h b/src/engine/shared/mapchecker.h
index 6d9568d0..8f6d24e8 100644
--- a/src/engine/shared/mapchecker.h
+++ b/src/engine/shared/mapchecker.h
@@ -30,7 +30,7 @@ class CMapChecker
 
 public:
 	CMapChecker();
-	void AddMaplist(class CMapVersion *pMaplist, int Num);
+	void AddMaplist(struct CMapVersion *pMaplist, int Num);
 	bool IsMapValid(const char *pMapName, unsigned MapCrc, unsigned MapSize);
 	bool ReadAndValidateMap(class IStorage *pStorage, const char *pFilename, int StorageType);
 };
diff --git a/src/engine/shared/masterserver.cpp b/src/engine/shared/masterserver.cpp
index 1bf402ca..1271eeaf 100644
--- a/src/engine/shared/masterserver.cpp
+++ b/src/engine/shared/masterserver.cpp
@@ -21,51 +21,57 @@ public:
 		bool m_Valid;
 
 		CHostLookup m_Lookup;
-	} ;
+	};
+
+	enum
+	{
+		STATE_INIT,
+		STATE_UPDATE,
+		STATE_READY,
+	};
 
 	CMasterInfo m_aMasterServers[MAX_MASTERSERVERS];
-	int m_NeedsUpdate;
+	int m_State;
 	IEngine *m_pEngine;
 	IStorage *m_pStorage;
 
 	CMasterServer()
 	{
 		SetDefault();
-		m_NeedsUpdate = -1;
+		m_State = STATE_INIT;
 		m_pEngine = 0;
+		m_pStorage = 0;
 	}
 
 	virtual int RefreshAddresses(int Nettype)
 	{
-		int i;
-
-		if(m_NeedsUpdate != -1)
-			return 0;
+		if(m_State != STATE_INIT)
+			return -1;
 
 		dbg_msg("engine/mastersrv", "refreshing master server addresses");
 
 		// add lookup jobs
-		for(i = 0; i < MAX_MASTERSERVERS; i++)
+		for(int i = 0; i < MAX_MASTERSERVERS; i++)
 		{
 			m_pEngine->HostLookup(&m_aMasterServers[i].m_Lookup, m_aMasterServers[i].m_aHostname, Nettype);
 			m_aMasterServers[i].m_Valid = false;
 		}
 
-		m_NeedsUpdate = 1;
+		m_State = STATE_UPDATE;
 		return 0;
 	}
 
 	virtual void Update()
 	{
 		// check if we need to update
-		if(m_NeedsUpdate != 1)
+		if(m_State != STATE_UPDATE)
 			return;
-		m_NeedsUpdate = 0;
+		m_State = STATE_READY;
 
 		for(int i = 0; i < MAX_MASTERSERVERS; i++)
 		{
 			if(m_aMasterServers[i].m_Lookup.m_Job.Status() != CJob::STATE_DONE)
-				m_NeedsUpdate = 1;
+				m_State = STATE_UPDATE;
 			else
 			{
 				if(m_aMasterServers[i].m_Lookup.m_Job.Result() == 0)
@@ -79,7 +85,7 @@ public:
 			}
 		}
 
-		if(!m_NeedsUpdate)
+		if(m_State == STATE_READY)
 		{
 			dbg_msg("engine/mastersrv", "saving addresses");
 			Save();
@@ -88,7 +94,7 @@ public:
 
 	virtual int IsRefreshing()
 	{
-		return m_NeedsUpdate;
+		return m_State != STATE_READY;
 	}
 
 	virtual NETADDR GetAddr(int Index)
@@ -106,16 +112,6 @@ public:
 		return m_aMasterServers[Index].m_Valid;
 	}
 
-	virtual void DumpServers()
-	{
-		for(int i = 0; i < MAX_MASTERSERVERS; i++)
-		{
-			char aAddrStr[NETADDR_MAXSTRSIZE];
-			net_addr_str(&m_aMasterServers[i].m_Addr, aAddrStr, sizeof(aAddrStr));
-			dbg_msg("mastersrv", "#%d = %s", i, aAddrStr);
-		}
-	}
-
 	virtual void Init()
 	{
 		m_pEngine = Kernel()->RequestInterface<IEngine>();
@@ -131,17 +127,15 @@ public:
 
 	virtual int Load()
 	{
-		CLineReader LineReader;
-		IOHANDLE File;
-		int Count = 0;
 		if(!m_pStorage)
 			return -1;
 
 		// try to open file
-		File = m_pStorage->OpenFile("masters.cfg", IOFLAG_READ, IStorage::TYPE_SAVE);
+		IOHANDLE File = m_pStorage->OpenFile("masters.cfg", IOFLAG_READ, IStorage::TYPE_SAVE);
 		if(!File)
 			return -1;
 
+		CLineReader LineReader;
 		LineReader.Init(File);
 		while(1)
 		{
@@ -152,19 +146,32 @@ public:
 
 			// parse line
 			char aAddrStr[NETADDR_MAXSTRSIZE];
-			if(sscanf(pLine, "%s %s", Info.m_aHostname, aAddrStr) == 2 && net_addr_from_str(&Info.m_Addr, aAddrStr) == 0)
+			if(sscanf(pLine, "%127s %47s", Info.m_aHostname, aAddrStr) == 2 && net_addr_from_str(&Info.m_Addr, aAddrStr) == 0)
 			{
 				Info.m_Addr.port = 8300;
-				if(Count != MAX_MASTERSERVERS)
+				bool Added = false;
+				for(int i = 0; i < MAX_MASTERSERVERS; ++i)
+					if(str_comp(m_aMasterServers[i].m_aHostname, Info.m_aHostname) == 0)
+					{
+						m_aMasterServers[i] = Info;
+						Added = true;
+						break;
+					}
+				
+				if(!Added)
 				{
-					m_aMasterServers[Count] = Info;
-					Count++;
+					for(int i = 0; i < MAX_MASTERSERVERS; ++i)
+						if(m_aMasterServers[i].m_Addr.type == NETTYPE_INVALID)
+						{
+							m_aMasterServers[i] = Info;
+							Added = true;
+							break;
+						}
 				}
-				//else
-				//	dbg_msg("engine/mastersrv", "warning: skipped master server '%s' due to limit of %d", pLine, MAX_MASTERSERVERS);
+
+				if(!Added)
+					break;
 			}
-			//else
-			//	dbg_msg("engine/mastersrv", "warning: couldn't parse master server '%s'", pLine);
 		}
 
 		io_close(File);
@@ -173,21 +180,22 @@ public:
 
 	virtual int Save()
 	{
-		IOHANDLE File;
-
 		if(!m_pStorage)
 			return -1;
 
 		// try to open file
-		File = m_pStorage->OpenFile("masters.cfg", IOFLAG_WRITE, IStorage::TYPE_SAVE);
+		IOHANDLE File = m_pStorage->OpenFile("masters.cfg", IOFLAG_WRITE, IStorage::TYPE_SAVE);
 		if(!File)
 			return -1;
 
 		for(int i = 0; i < MAX_MASTERSERVERS; i++)
 		{
 			char aAddrStr[NETADDR_MAXSTRSIZE];
-			net_addr_str(&m_aMasterServers[i].m_Addr, aAddrStr, sizeof(aAddrStr));
-			char aBuf[1024];
+			if(m_aMasterServers[i].m_Addr.type != NETTYPE_INVALID)
+				net_addr_str(&m_aMasterServers[i].m_Addr, aAddrStr, sizeof(aAddrStr));
+			else
+				aAddrStr[0] = 0;
+			char aBuf[256];
 			str_format(aBuf, sizeof(aBuf), "%s %s\n", m_aMasterServers[i].m_aHostname, aAddrStr);
 
 			io_write(File, aBuf, str_length(aBuf));
diff --git a/src/engine/shared/network.h b/src/engine/shared/network.h
index 075fc8e5..ca6ca02a 100644
--- a/src/engine/shared/network.h
+++ b/src/engine/shared/network.h
@@ -49,6 +49,7 @@ enum
 	NET_MAX_CHUNKHEADERSIZE = 5,
 	NET_PACKETHEADERSIZE = 3,
 	NET_MAX_CLIENTS = 16,
+	NET_MAX_CONSOLE_CLIENTS = 4,
 	NET_MAX_SEQUENCE = 1<<10,
 	NET_SEQUENCE_MASK = NET_MAX_SEQUENCE-1,
 
@@ -192,7 +193,37 @@ public:
 	int AckSequence() const { return m_Ack; }
 };
 
-struct CNetRecvUnpacker
+class CConsoleNetConnection
+{
+private:
+	int m_State;
+
+	NETADDR m_PeerAddr;
+	NETSOCKET m_Socket;
+
+	char m_aBuffer[NET_MAX_PACKETSIZE];
+	int m_BufferOffset;
+
+	char m_aErrorString[256];
+
+	bool m_LineEndingDetected;
+	char m_aLineEnding[3];
+
+public:
+	void Init(NETSOCKET Socket, const NETADDR *pAddr);
+	void Disconnect(const char *pReason);
+
+	int State() const { return m_State; }
+	NETADDR PeerAddress() const { return m_PeerAddr; }
+	const char *ErrorString() const { return m_aErrorString; }
+
+	void Reset();
+	int Update();
+	int Send(const char *pLine);
+	int Recv(char *pLine, int MaxLength);
+};
+
+class CNetRecvUnpacker
 {
 public:
 	bool m_Valid;
@@ -226,13 +257,13 @@ public:
 	};
 
 private:
-	class CSlot
+	struct CSlot
 	{
 	public:
 		CNetConnection m_Connection;
 	};
 
-	class CBan
+	struct CBan
 	{
 	public:
 		CBanInfo m_Info;
@@ -292,6 +323,7 @@ public:
 	// status requests
 	NETADDR ClientAddr(int ClientID) const { return m_aSlots[ClientID].m_Connection.PeerAddress(); }
 	NETSOCKET Socket() const { return m_Socket; }
+	int NetType() { return m_Socket.type; }
 	int MaxClients() const { return m_MaxClients; }
 
 	//
@@ -303,6 +335,59 @@ public:
 	void BanmastersClear();
 };
 
+class CNetConsole
+{
+	enum
+	{
+		MAX_BANS=128,
+	};
+
+	int FindBan(NETADDR Addr);
+	void UpdateBans();
+
+	struct CBanEntry
+	{
+		NETADDR m_Addr;
+		int m_Expires;
+	} m_aBans[MAX_BANS];
+	int m_NumBans;
+
+	struct CSlot
+	{
+		CConsoleNetConnection m_Connection;
+	};
+
+	NETSOCKET m_Socket;
+	CSlot m_aSlots[NET_MAX_CONSOLE_CLIENTS];
+
+	NETFUNC_NEWCLIENT m_pfnNewClient;
+	NETFUNC_DELCLIENT m_pfnDelClient;
+	void *m_UserPtr;
+
+	CNetRecvUnpacker m_RecvUnpacker;
+
+public:
+	void SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser);
+
+	//
+	bool Open(NETADDR BindAddr, int Flags);
+	int Close();
+
+	//
+	int Recv(char *pLine, int MaxLength, int *pClientID = 0);
+	int Send(int ClientID, const char *pLine);
+	int Update();
+
+	//
+	int AcceptClient(NETSOCKET Socket, const NETADDR *pAddr);
+	int Drop(int ClientID, const char *pReason);
+
+	bool AddBan(NETADDR Addr, int Seconds);
+
+	// status requests
+	NETADDR ClientAddr(int ClientID) const { return m_aSlots[ClientID].m_Connection.PeerAddress(); }
+};
+
 
 
 // client side
@@ -332,6 +417,7 @@ public:
 	int ResetErrorString();
 
 	// error and state
+	int NetType() { return m_Socket.type; }
 	int State();
 	int GotProblems();
 	const char *ErrorString();
diff --git a/src/engine/shared/network_console.cpp b/src/engine/shared/network_console.cpp
new file mode 100644
index 00000000..13ed3751
--- /dev/null
+++ b/src/engine/shared/network_console.cpp
@@ -0,0 +1,219 @@
+/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
+/* If you are missing that file, acquire a complete release at teeworlds.com.                */
+#include <base/system.h>
+#include "network.h"
+
+bool CNetConsole::Open(NETADDR BindAddr, int Flags)
+{
+	// zero out the whole structure
+	mem_zero(this, sizeof(*this));
+	m_Socket.type = NETTYPE_INVALID;
+	m_Socket.ipv4sock = -1;
+	m_Socket.ipv6sock = -1;
+
+	// open socket
+	m_Socket = net_tcp_create(BindAddr);
+	if(!m_Socket.type)
+		return false;
+	if(net_tcp_listen(m_Socket, NET_MAX_CONSOLE_CLIENTS))
+		return false;
+	net_set_non_blocking(m_Socket);
+
+	for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++)
+		m_aSlots[i].m_Connection.Reset();
+
+	return true;
+}
+
+void CNetConsole::SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser)
+{
+	m_pfnNewClient = pfnNewClient;
+	m_pfnDelClient = pfnDelClient;
+	m_UserPtr = pUser;
+}
+
+int CNetConsole::Close()
+{
+	for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++)
+		m_aSlots[i].m_Connection.Disconnect("closing console");
+
+	net_tcp_close(m_Socket);
+
+	return 0;
+}
+
+int CNetConsole::Drop(int ClientID, const char *pReason)
+{
+	if(m_pfnDelClient)
+		m_pfnDelClient(ClientID, pReason, m_UserPtr);
+
+	m_aSlots[ClientID].m_Connection.Disconnect(pReason);
+
+	return 0;
+}
+
+int CNetConsole::AcceptClient(NETSOCKET Socket, const NETADDR *pAddr)
+{
+	char aError[256] = { 0 };
+	int FreeSlot = -1;
+	
+	// look for free slot or multiple client
+	for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++)
+	{
+		if(FreeSlot == -1 && m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE)
+			FreeSlot = i;
+		if(m_aSlots[i].m_Connection.State() != NET_CONNSTATE_OFFLINE)
+		{
+			NETADDR PeerAddr = m_aSlots[i].m_Connection.PeerAddress();
+			if(net_addr_comp(pAddr, &PeerAddr) == 0)
+			{
+				str_copy(aError, "only one client per IP allowed", sizeof(aError));
+				break;
+			}
+		}
+	}
+
+	// accept client
+	if(!aError[0] && FreeSlot != -1)
+	{
+		m_aSlots[FreeSlot].m_Connection.Init(Socket, pAddr);
+		if(m_pfnNewClient)
+			m_pfnNewClient(FreeSlot, m_UserPtr);
+		return 0;
+	}
+
+	// reject client
+	if(!aError[0])
+		str_copy(aError, "no free slot available", sizeof(aError));
+
+	net_tcp_send(Socket, aError, str_length(aError));
+	net_tcp_close(Socket);
+
+	return -1;
+}
+
+int CNetConsole::Update()
+{
+	NETSOCKET Socket;
+	NETADDR Addr;
+
+	if(net_tcp_accept(m_Socket, &Socket, &Addr) > 0)
+	{
+		int Index = FindBan(Addr);
+		if(Index == -1)
+			AcceptClient(Socket, &Addr);
+		else
+		{
+			char aBuf[128];
+			if(m_aBans[Index].m_Expires > -1)
+			{
+				int Mins = (m_aBans[Index].m_Expires-time_timestamp()+ 59) / 60;
+				if(Mins <= 1)
+					str_format(aBuf, sizeof(aBuf), "You have been banned for 1 minute");
+				else
+					str_format(aBuf, sizeof(aBuf), "You have been banned for %d minutes", Mins);
+			}
+			else
+				str_format(aBuf, sizeof(aBuf), "You have been banned for life");
+			
+			net_tcp_send(Socket, aBuf, str_length(aBuf));
+			net_tcp_close(Socket);
+		}
+	}
+
+	for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++)
+	{
+		if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ONLINE)
+			m_aSlots[i].m_Connection.Update();
+		if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ERROR)
+			Drop(i, m_aSlots[i].m_Connection.ErrorString());
+	}
+
+	UpdateBans();
+
+	return 0;
+}
+
+int CNetConsole::Recv(char *pLine, int MaxLength, int *pClientID)
+{
+	for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++)
+	{
+		if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ONLINE && m_aSlots[i].m_Connection.Recv(pLine, MaxLength))
+		{
+			if(pClientID)
+				*pClientID = i;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+int CNetConsole::Send(int ClientID, const char *pLine)
+{
+	if(m_aSlots[ClientID].m_Connection.State() == NET_CONNSTATE_ONLINE)
+		return m_aSlots[ClientID].m_Connection.Send(pLine);
+	else
+		return -1;
+}
+
+int CNetConsole::FindBan(NETADDR Addr)
+{
+	Addr.port = 0;
+	for(int i = 0; i < m_NumBans; i++)
+		if(net_addr_comp(&m_aBans[i].m_Addr, &Addr) == 0)
+			return i;
+
+	return -1;
+}
+
+bool CNetConsole::AddBan(NETADDR Addr, int Seconds)
+{
+	if(m_NumBans == MAX_BANS)
+		return false;
+	
+	Addr.port = 0;
+	int Index = FindBan(Addr);
+	if(Index == -1)
+	{
+		Index = m_NumBans++;
+		m_aBans[Index].m_Addr = Addr;
+	}
+	m_aBans[Index].m_Expires = Seconds>0 ? time_timestamp()+Seconds : -1;
+
+	for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++)
+	{
+		if(m_aSlots[i].m_Connection.State() != NET_CONNSTATE_OFFLINE)
+		{
+			NETADDR PeerAddr = m_aSlots[i].m_Connection.PeerAddress();
+			PeerAddr.port = 0;
+			if(net_addr_comp(&Addr, &PeerAddr) == 0)
+			{
+				char aBuf[128];
+				if(Seconds>0)
+				{
+					int Mins = (Seconds + 59) / 60;
+					if(Mins <= 1)
+						str_format(aBuf, sizeof(aBuf), "You have been banned for 1 minute");
+					else
+						str_format(aBuf, sizeof(aBuf), "You have been banned for %d minutes", Mins);
+				}
+				else
+					str_format(aBuf, sizeof(aBuf), "You have been banned for life");
+				Drop(i, aBuf);
+			}
+		}
+	}
+	return true;
+}
+
+void CNetConsole::UpdateBans()
+{
+	int Now = time_timestamp();
+	for(int i = 0; i < m_NumBans; ++i)
+		if(m_aBans[i].m_Expires > 0 && m_aBans[i].m_Expires < Now)
+		{
+			m_aBans[i] = m_aBans[m_NumBans-1];
+			--m_NumBans;
+			break;
+		}
+}
diff --git a/src/engine/shared/network_console_conn.cpp b/src/engine/shared/network_console_conn.cpp
new file mode 100644
index 00000000..75b581fa
--- /dev/null
+++ b/src/engine/shared/network_console_conn.cpp
@@ -0,0 +1,186 @@
+/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
+/* If you are missing that file, acquire a complete release at teeworlds.com.                */
+#include <base/system.h>
+#include "network.h"
+
+void CConsoleNetConnection::Reset()
+{
+	m_State = NET_CONNSTATE_OFFLINE;
+	mem_zero(&m_PeerAddr, sizeof(m_PeerAddr));
+	m_aErrorString[0] = 0;
+
+	m_Socket.type = NETTYPE_INVALID;
+	m_Socket.ipv4sock = -1;
+	m_Socket.ipv6sock = -1;
+	m_aBuffer[0] = 0;
+	m_BufferOffset = 0;
+
+	m_LineEndingDetected = false;
+	#if defined(CONF_FAMILY_WINDOWS)
+		m_aLineEnding[0] = '\r';
+		m_aLineEnding[1] = '\n';
+		m_aLineEnding[2] = 0;
+	#else
+		m_aLineEnding[0] = '\n';
+		m_aLineEnding[1] = 0;
+		m_aLineEnding[2] = 0;
+	#endif
+}
+
+void CConsoleNetConnection::Init(NETSOCKET Socket, const NETADDR *pAddr)
+{
+	Reset();
+
+	m_Socket = Socket;
+	net_set_non_blocking(m_Socket);
+
+	m_PeerAddr = *pAddr;
+	m_State = NET_CONNSTATE_ONLINE;
+}
+
+void CConsoleNetConnection::Disconnect(const char *pReason)
+{
+	if(State() == NET_CONNSTATE_OFFLINE)
+		return;
+
+	if(pReason && pReason[0])
+		Send(pReason);
+
+	net_tcp_close(m_Socket);
+
+	Reset();
+}
+
+int CConsoleNetConnection::Update()
+{
+	if(State() == NET_CONNSTATE_ONLINE)
+	{
+		if((int)(sizeof(m_aBuffer)) <= m_BufferOffset)
+		{
+			m_State = NET_CONNSTATE_ERROR;
+			str_copy(m_aErrorString, "too weak connection (out of buffer)", sizeof(m_aErrorString));
+			return -1;
+		}
+
+		int Bytes = net_tcp_recv(m_Socket, m_aBuffer+m_BufferOffset, (int)(sizeof(m_aBuffer))-m_BufferOffset);
+
+		if(Bytes > 0)
+		{
+			m_BufferOffset += Bytes;
+		}
+		else if(Bytes < 0)
+		{
+			if(net_would_block()) // no data received
+				return 0;
+
+			m_State = NET_CONNSTATE_ERROR; // error
+			str_copy(m_aErrorString, "connection failure", sizeof(m_aErrorString));
+			return -1;
+		}
+		else
+		{
+			m_State = NET_CONNSTATE_ERROR;
+			str_copy(m_aErrorString, "remote end closed the connection", sizeof(m_aErrorString));
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+int CConsoleNetConnection::Recv(char *pLine, int MaxLength)
+{
+	if(State() == NET_CONNSTATE_ONLINE)
+	{
+		if(m_BufferOffset)
+		{
+			// find message start
+			int StartOffset = 0;
+			while(m_aBuffer[StartOffset] == '\r' || m_aBuffer[StartOffset] == '\n')
+			{
+				// detect clients line ending format
+				if(!m_LineEndingDetected)
+				{
+					m_aLineEnding[0] = m_aBuffer[StartOffset];
+					if(StartOffset+1 < m_BufferOffset && (m_aBuffer[StartOffset+1] == '\r' || m_aBuffer[StartOffset+1] == '\n') &&
+						m_aBuffer[StartOffset] != m_aBuffer[StartOffset+1])
+						m_aLineEnding[1] = m_aBuffer[StartOffset+1];
+					m_LineEndingDetected = true;
+				}
+
+				if(++StartOffset >= m_BufferOffset)
+				{
+					m_BufferOffset = 0;
+					return 0;
+				}
+			}
+
+			// find message end
+			int EndOffset = StartOffset;
+			while(m_aBuffer[EndOffset] != '\r' && m_aBuffer[EndOffset] != '\n')
+			{
+				if(++EndOffset >= m_BufferOffset)
+				{
+					if(StartOffset > 0)
+					{
+						mem_move(m_aBuffer, m_aBuffer+StartOffset, m_BufferOffset-StartOffset);
+						m_BufferOffset -= StartOffset;
+					}
+					return 0;
+				}
+			}
+
+			// extract message and update buffer
+			if(MaxLength-1 < EndOffset-StartOffset)
+			{
+				if(StartOffset > 0)
+				{
+					mem_move(m_aBuffer, m_aBuffer+StartOffset, m_BufferOffset-StartOffset);
+					m_BufferOffset -= StartOffset;
+				}
+				return 0;
+			}
+			mem_copy(pLine, m_aBuffer+StartOffset, EndOffset-StartOffset);
+			pLine[EndOffset-StartOffset] = 0;
+			str_sanitize_cc(pLine);
+			mem_move(m_aBuffer, m_aBuffer+EndOffset, m_BufferOffset-EndOffset);
+			m_BufferOffset -= EndOffset;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+int CConsoleNetConnection::Send(const char *pLine)
+{
+	if(State() != NET_CONNSTATE_ONLINE)
+		return -1;
+
+	char aBuf[1024];
+	str_copy(aBuf, pLine, (int)(sizeof(aBuf))-2);
+	int Length = str_length(aBuf);
+	aBuf[Length] = m_aLineEnding[0];
+	aBuf[Length+1] = m_aLineEnding[1];
+	aBuf[Length+2] = m_aLineEnding[2];
+	Length += 3;
+	const char *pData = aBuf;
+
+	while(true)
+	{
+		int Send = net_tcp_send(m_Socket, pData, Length);
+		if(Send < 0)
+		{
+			m_State = NET_CONNSTATE_ERROR;
+			str_copy(m_aErrorString, "failed to send packet", sizeof(m_aErrorString));
+			return -1;
+		}
+
+		if(Send >= Length)
+			break;
+
+		pData += Send;
+		Length -= Send;
+	}
+	
+	return 0;
+}
diff --git a/src/engine/shared/network_server.cpp b/src/engine/shared/network_server.cpp
index 21da1e36..73e3cf17 100644
--- a/src/engine/shared/network_server.cpp
+++ b/src/engine/shared/network_server.cpp
@@ -80,9 +80,9 @@ int CNetServer::Close()
 int CNetServer::Drop(int ClientID, const char *pReason)
 {
 	// TODO: insert lots of checks here
-	NETADDR Addr = ClientAddr(ClientID);
+	/*NETADDR Addr = ClientAddr(ClientID);
 
-	/*dbg_msg("net_server", "client dropped. cid=%d ip=%d.%d.%d.%d reason=\"%s\"",
+	dbg_msg("net_server", "client dropped. cid=%d ip=%d.%d.%d.%d reason=\"%s\"",
 		ClientID,
 		Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3],
 		pReason
@@ -222,10 +222,10 @@ int CNetServer::BanAdd(NETADDR Addr, int Seconds, const char *pReason)
 		char Buf[128];
 		NETADDR BanAddr;
 
-		int Mins = (Seconds + 59) / 60;
-		if(Mins)
+		if(Stamp > -1)
 		{
-			if(Mins == 1)
+			int Mins = (Seconds + 59) / 60;
+			if(Mins <= 1)
 				str_format(Buf, sizeof(Buf), "You have been banned for 1 minute (%s)", pReason);
 			else
 				str_format(Buf, sizeof(Buf), "You have been banned for %d minutes (%s)", Mins, pReason);
@@ -256,7 +256,7 @@ int CNetServer::Update()
 	}
 
 	// remove expired bans
-	while(m_BanPool_FirstUsed && m_BanPool_FirstUsed->m_Info.m_Expires < Now)
+	while(m_BanPool_FirstUsed && m_BanPool_FirstUsed->m_Info.m_Expires > -1 && m_BanPool_FirstUsed->m_Info.m_Expires < Now)
 	{
 		CBan *pBan = m_BanPool_FirstUsed;
 		BanRemoveByObject(pBan);
@@ -308,10 +308,10 @@ int CNetServer::Recv(CNetChunk *pChunk)
 			{
 				// banned, reply with a message
 				char BanStr[128];
-				if(pBan->m_Info.m_Expires)
+				if(pBan->m_Info.m_Expires > -1)
 				{
 					int Mins = ((pBan->m_Info.m_Expires - Now)+59)/60;
-					if(Mins == 1)
+					if(Mins <= 1)
 						str_format(BanStr, sizeof(BanStr), "Banned for 1 minute (%s)", pBan->m_Info.m_Reason);
 					else
 						str_format(BanStr, sizeof(BanStr), "Banned for %d minutes (%s)", Mins, pBan->m_Info.m_Reason);
diff --git a/src/engine/shared/protocol.h b/src/engine/shared/protocol.h
index 4a4895ad..ba04da8a 100644
--- a/src/engine/shared/protocol.h
+++ b/src/engine/shared/protocol.h
@@ -65,7 +65,11 @@ enum
 	// sent by both
 	NETMSG_PING,
 	NETMSG_PING_REPLY,
-	NETMSG_ERROR
+	NETMSG_ERROR,
+
+	// sent by server (todo: move it up)
+	NETMSG_RCON_CMD_ADD,
+	NETMSG_RCON_CMD_REM,
 };
 
 // this should be revised
diff --git a/src/engine/sound.h b/src/engine/sound.h
index ac4aeaf5..f55a978d 100644
--- a/src/engine/sound.h
+++ b/src/engine/sound.h
@@ -16,6 +16,8 @@ public:
 		FLAG_ALL=3
 	};
 
+	virtual bool IsSoundEnabled() = 0;
+
 	virtual int LoadWV(const char *pFilename) = 0;
 	
 	virtual void SetChannel(int ChannelID, float Volume, float Panning) = 0;
diff --git a/src/game/client/animstate.cpp b/src/game/client/animstate.cpp
index 1289126b..ce595359 100644
--- a/src/game/client/animstate.cpp
+++ b/src/game/client/animstate.cpp
@@ -7,7 +7,7 @@
 
 #include "animstate.h"
 
-static void AnimSeqEval(ANIM_SEQUENCE *pSeq, float Time, ANIM_KEYFRAME *pFrame)
+static void AnimSeqEval(CAnimSequence *pSeq, float Time, CAnimKeyframe *pFrame)
 {
 	if(pSeq->m_NumFrames == 0)
 	{
@@ -23,8 +23,8 @@ static void AnimSeqEval(ANIM_SEQUENCE *pSeq, float Time, ANIM_KEYFRAME *pFrame)
 	else
 	{
 		//time = max(0.0f, min(1.0f, time / duration)); // TODO: use clamp
-		ANIM_KEYFRAME *pFrame1 = 0;
-		ANIM_KEYFRAME *pFrame2 = 0;
+		CAnimKeyframe *pFrame1 = 0;
+		CAnimKeyframe *pFrame2 = 0;
 		float Blend = 0.0f;
 
 		// TODO: make this smarter.. binary search
@@ -49,7 +49,7 @@ static void AnimSeqEval(ANIM_SEQUENCE *pSeq, float Time, ANIM_KEYFRAME *pFrame)
 	}
 }
 
-static void AnimAddKeyframe(ANIM_KEYFRAME *pSeq, ANIM_KEYFRAME *pAdded, float Amount)
+static void AnimAddKeyframe(CAnimKeyframe *pSeq, CAnimKeyframe *pAdded, float Amount)
 {
 	pSeq->m_X += pAdded->m_X*Amount;
 	pSeq->m_Y += pAdded->m_Y*Amount;
@@ -65,7 +65,7 @@ static void AnimAdd(CAnimState *pState, CAnimState *pAdded, float Amount)
 }
 
 
-void CAnimState::Set(ANIMATION *pAnim, float Time)
+void CAnimState::Set(CAnimation *pAnim, float Time)
 {
 	AnimSeqEval(&pAnim->m_Body, Time, &m_Body);
 	AnimSeqEval(&pAnim->m_BackFoot, Time, &m_BackFoot);
@@ -73,7 +73,7 @@ void CAnimState::Set(ANIMATION *pAnim, float Time)
 	AnimSeqEval(&pAnim->m_Attach, Time, &m_Attach);
 }
 
-void CAnimState::Add(ANIMATION *pAnim, float Time, float Amount)
+void CAnimState::Add(CAnimation *pAnim, float Time, float Amount)
 {
 	CAnimState Add;
 	Add.Set(pAnim, Time);
diff --git a/src/game/client/animstate.h b/src/game/client/animstate.h
index 63b6a80a..fbc0a2f8 100644
--- a/src/game/client/animstate.h
+++ b/src/game/client/animstate.h
@@ -5,18 +5,18 @@
 
 class CAnimState
 {
-	ANIM_KEYFRAME m_Body;
-	ANIM_KEYFRAME m_BackFoot;
-	ANIM_KEYFRAME m_FrontFoot;
-	ANIM_KEYFRAME m_Attach;
+	CAnimKeyframe m_Body;
+	CAnimKeyframe m_BackFoot;
+	CAnimKeyframe m_FrontFoot;
+	CAnimKeyframe m_Attach;
 
 public:
-	ANIM_KEYFRAME *GetBody() { return &m_Body; };
-	ANIM_KEYFRAME *GetBackFoot() { return &m_BackFoot; };
-	ANIM_KEYFRAME *GetFrontFoot() { return &m_FrontFoot; };
-	ANIM_KEYFRAME *GetAttach() { return &m_Attach; };
-	void Set(ANIMATION *pAnim, float Time);
-	void Add(ANIMATION *pAdded, float Time, float Amount);
+	CAnimKeyframe *GetBody() { return &m_Body; };
+	CAnimKeyframe *GetBackFoot() { return &m_BackFoot; };
+	CAnimKeyframe *GetFrontFoot() { return &m_FrontFoot; };
+	CAnimKeyframe *GetAttach() { return &m_Attach; };
+	void Set(CAnimation *pAnim, float Time);
+	void Add(CAnimation *pAdded, float Time, float Amount);
 
 	static CAnimState *GetIdle();
 };
diff --git a/src/game/client/components/chat.cpp b/src/game/client/components/chat.cpp
index ee294dc4..aba38bf6 100644
--- a/src/game/client/components/chat.cpp
+++ b/src/game/client/components/chat.cpp
@@ -132,17 +132,28 @@ bool CChat::OnInput(IInput::CEvent Event)
 
 		// find next possible name
 		const char *pCompletionString = 0;
-		m_CompletionChosen = (m_CompletionChosen+1)%MAX_CLIENTS;
-		for(int i = 0; i < MAX_CLIENTS; ++i)
+		m_CompletionChosen = (m_CompletionChosen+1)%(2*MAX_CLIENTS);
+		for(int i = 0; i < 2*MAX_CLIENTS; ++i)
 		{
+			int SearchType = ((m_CompletionChosen+i)%(2*MAX_CLIENTS))/MAX_CLIENTS;
 			int Index = (m_CompletionChosen+i)%MAX_CLIENTS;
 			if(!m_pClient->m_Snap.m_paPlayerInfos[Index])
 				continue;
 
-			if(str_find_nocase(m_pClient->m_aClients[Index].m_aName, m_aCompletionBuffer))
+			bool Found = false;
+			if(SearchType == 1)
+			{
+				if(str_comp_nocase_num(m_pClient->m_aClients[Index].m_aName, m_aCompletionBuffer, str_length(m_aCompletionBuffer)) &&
+					str_find_nocase(m_pClient->m_aClients[Index].m_aName, m_aCompletionBuffer))
+					Found = true;
+			}
+			else if(!str_comp_nocase_num(m_pClient->m_aClients[Index].m_aName, m_aCompletionBuffer, str_length(m_aCompletionBuffer)))
+				Found = true;
+
+			if(Found)
 			{
 				pCompletionString = m_pClient->m_aClients[Index].m_aName;
-				m_CompletionChosen = Index;
+				m_CompletionChosen = Index+SearchType*MAX_CLIENTS;
 				break;
 			}
 		}
@@ -151,10 +162,25 @@ bool CChat::OnInput(IInput::CEvent Event)
 		if(pCompletionString)
 		{
 			char aBuf[256];
+			// add part before the name
 			str_copy(aBuf, m_Input.GetString(), min(static_cast<int>(sizeof(aBuf)), m_PlaceholderOffset+1));
+
+			// add the name
 			str_append(aBuf, pCompletionString, sizeof(aBuf));
+
+			// add seperator
+			const char *pSeparator = "";
+			if(*(m_Input.GetString()+m_PlaceholderOffset+m_PlaceholderLength) != ' ')
+				pSeparator = m_PlaceholderOffset == 0 ? ": " : " ";
+			else if(m_PlaceholderOffset == 0)
+				pSeparator = ":";
+			if(*pSeparator)
+				str_append(aBuf, pSeparator, sizeof(aBuf));
+
+			// add part after the name
 			str_append(aBuf, m_Input.GetString()+m_PlaceholderOffset+m_PlaceholderLength, sizeof(aBuf));
-			m_PlaceholderLength = str_length(pCompletionString);
+
+			m_PlaceholderLength = str_length(pSeparator)+str_length(pCompletionString);
 			m_OldChatStringLength = m_Input.GetLength();
 			m_Input.Set(aBuf);
 			m_Input.SetCursorOffset(m_PlaceholderOffset+m_PlaceholderLength);
@@ -184,11 +210,7 @@ bool CChat::OnInput(IInput::CEvent Event)
 			m_pHistoryEntry = m_History.Last();
 
 		if (m_pHistoryEntry)
-		{
-			unsigned int Len = str_length(m_pHistoryEntry);
-			if (Len < sizeof(m_Input) - 1) // TODO: WTF?
-				m_Input.Set(m_pHistoryEntry);
-		}
+			m_Input.Set(m_pHistoryEntry);
 	}
 	else if (Event.m_Flags&IInput::FLAG_PRESS && Event.m_Key == KEY_DOWN)
 	{
@@ -196,11 +218,7 @@ bool CChat::OnInput(IInput::CEvent Event)
 			m_pHistoryEntry = m_History.Next(m_pHistoryEntry);
 
 		if (m_pHistoryEntry)
-		{
-			unsigned int Len = str_length(m_pHistoryEntry);
-			if (Len < sizeof(m_Input) - 1) // TODO: WTF?
-				m_Input.Set(m_pHistoryEntry);
-		}
+			m_Input.Set(m_pHistoryEntry);
 		else
 			m_Input.Clear();
 	}
@@ -246,6 +264,7 @@ void CChat::AddLine(int ClientID, int Team, const char *pLine)
 	char *p = const_cast<char*>(pLine);
 	while(*p)
 	{
+		Highlighted = false;
 		pLine = p;
 		// find line seperator and strip multiline
 		while(*p)
@@ -264,9 +283,16 @@ void CChat::AddLine(int ClientID, int Team, const char *pLine)
 		m_aLines[m_CurrentLine].m_ClientID = ClientID;
 		m_aLines[m_CurrentLine].m_Team = Team;
 		m_aLines[m_CurrentLine].m_NameColor = -2;
-		m_aLines[m_CurrentLine].m_Highlighted = str_find_nocase(pLine, m_pClient->m_aClients[m_pClient->m_Snap.m_LocalClientID].m_aName) != 0;
-		if(m_aLines[m_CurrentLine].m_Highlighted)
-			Highlighted = true;
+		
+		// check for highlighted name
+		const char *pHL = str_find_nocase(pLine, m_pClient->m_aClients[m_pClient->m_Snap.m_LocalClientID].m_aName);
+		if(pHL)
+		{
+			int Length = str_length(m_pClient->m_aClients[m_pClient->m_Snap.m_LocalClientID].m_aName);
+			if((pLine == pHL || pHL[-1] == ' ') && (pHL[Length] == 0 || pHL[Length] == ' ' || (pHL[Length] == ':' && pHL[Length+1] == ' ')))
+				Highlighted = true;
+		}
+		m_aLines[m_CurrentLine].m_Highlighted =  Highlighted;
 
 		if(ClientID == -1) // server message
 		{
@@ -292,7 +318,7 @@ void CChat::AddLine(int ClientID, int Team, const char *pLine)
 
 		char aBuf[1024];
 		str_format(aBuf, sizeof(aBuf), "%s%s", m_aLines[m_CurrentLine].m_aName, m_aLines[m_CurrentLine].m_aText);
-		Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "chat", aBuf);
+		Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, m_aLines[m_CurrentLine].m_Team?"teamchat":"chat", aBuf);
 	}
 
 	// play sound
@@ -354,7 +380,9 @@ void CChat::OnRender()
 		}
 
 		TextRender()->TextEx(&Cursor, m_Input.GetString()+m_ChatStringOffset, m_Input.GetCursorOffset()-m_ChatStringOffset);
+		static float MarkerOffset = TextRender()->TextWidth(0, 8.0f, "|", -1)/3;
 		CTextCursor Marker = Cursor;
+		Marker.m_X -= MarkerOffset;
 		TextRender()->TextEx(&Marker, "|", -1);
 		TextRender()->TextEx(&Cursor, m_Input.GetString()+m_Input.GetCursorOffset(), -1);
 	}
diff --git a/src/game/client/components/console.cpp b/src/game/client/components/console.cpp
index 33c6db43..f2e9e65d 100644
--- a/src/game/client/components/console.cpp
+++ b/src/game/client/components/console.cpp
@@ -51,7 +51,7 @@ CGameConsole::CInstance::CInstance(int Type)
 	m_CompletionChosen = -1;
 	m_CompletionRenderOffset = 0.0f;
 
-	m_pCommand = 0x0;
+	m_IsCommand = false;
 }
 
 void CGameConsole::CInstance::Init(CGameConsole *pGameConsole)
@@ -127,11 +127,7 @@ void CGameConsole::CInstance::OnInput(IInput::CEvent Event)
 				m_pHistoryEntry = m_History.Last();
 
 			if (m_pHistoryEntry)
-			{
-				unsigned int Len = str_length(m_pHistoryEntry);
-				if (Len < sizeof(m_Input) - 1) // TODO: WTF?
-					m_Input.Set(m_pHistoryEntry);
-			}
+				m_Input.Set(m_pHistoryEntry);
 			Handled = true;
 		}
 		else if (Event.m_Key == KEY_DOWN)
@@ -140,11 +136,7 @@ void CGameConsole::CInstance::OnInput(IInput::CEvent Event)
 				m_pHistoryEntry = m_History.Next(m_pHistoryEntry);
 
 			if (m_pHistoryEntry)
-			{
-				unsigned int Len = str_length(m_pHistoryEntry);
-				if (Len < sizeof(m_Input) - 1) // TODO: WTF?
-					m_Input.Set(m_pHistoryEntry);
-			}
+				m_Input.Set(m_pHistoryEntry);
 			else
 				m_Input.Clear();
 			Handled = true;
@@ -155,14 +147,16 @@ void CGameConsole::CInstance::OnInput(IInput::CEvent Event)
 			{
 				m_CompletionChosen++;
 				m_CompletionEnumerationCount = 0;
-				m_pGameConsole->m_pConsole->PossibleCommands(m_aCompletionBuffer, m_CompletionFlagmask, PossibleCommandsCompleteCallback, this);
+				m_pGameConsole->m_pConsole->PossibleCommands(m_aCompletionBuffer, m_CompletionFlagmask, m_Type != CGameConsole::CONSOLETYPE_LOCAL &&
+					m_pGameConsole->Client()->RconAuthed() && m_pGameConsole->Client()->UseTempRconCommands(),	PossibleCommandsCompleteCallback, this);
 
 				// handle wrapping
 				if(m_CompletionEnumerationCount && m_CompletionChosen >= m_CompletionEnumerationCount)
 				{
 					m_CompletionChosen %= m_CompletionEnumerationCount;
 					m_CompletionEnumerationCount = 0;
-					m_pGameConsole->m_pConsole->PossibleCommands(m_aCompletionBuffer, m_CompletionFlagmask, PossibleCommandsCompleteCallback, this);
+					m_pGameConsole->m_pConsole->PossibleCommands(m_aCompletionBuffer, m_CompletionFlagmask, m_Type != CGameConsole::CONSOLETYPE_LOCAL &&
+						m_pGameConsole->Client()->RconAuthed() && m_pGameConsole->Client()->UseTempRconCommands(),	PossibleCommandsCompleteCallback, this);
 				}
 			}
 		}
@@ -198,7 +192,17 @@ void CGameConsole::CInstance::OnInput(IInput::CEvent Event)
 				aBuf[i] = *pSrc;
 			aBuf[i] = 0;
 
-			m_pCommand = m_pGameConsole->m_pConsole->GetCommandInfo(aBuf, m_CompletionFlagmask);
+			const IConsole::CCommandInfo *pCommand = m_pGameConsole->m_pConsole->GetCommandInfo(aBuf, m_CompletionFlagmask,
+				m_Type != CGameConsole::CONSOLETYPE_LOCAL && m_pGameConsole->Client()->RconAuthed() && m_pGameConsole->Client()->UseTempRconCommands());
+			if(pCommand)
+			{
+				m_IsCommand = true;
+				str_copy(m_aCommandName, pCommand->m_pName, IConsole::TEMPCMD_NAME_LENGTH);
+				str_copy(m_aCommandHelp, pCommand->m_pHelp, IConsole::TEMPCMD_HELP_LENGTH);
+				str_copy(m_aCommandParams, pCommand->m_pParams, IConsole::TEMPCMD_PARAMS_LENGTH);
+			}
+			else
+				m_IsCommand = false;
 		}
 	}
 }
@@ -446,7 +450,9 @@ void CGameConsole::OnRender()
 		}
 
 		TextRender()->TextEx(&Cursor, aInputString, pConsole->m_Input.GetCursorOffset());
+		static float MarkerOffset = TextRender()->TextWidth(0, FontSize, "|", -1)/3;
 		CTextCursor Marker = Cursor;
+		Marker.m_X -= MarkerOffset;
 		TextRender()->TextEx(&Marker, "|", -1);
 		TextRender()->TextEx(&Cursor, aInputString+pConsole->m_Input.GetCursorOffset(), -1);
 
@@ -455,19 +461,19 @@ void CGameConsole::OnRender()
 		{
 			if(pConsole->m_Input.GetString()[0] != 0)
 			{
-				m_pConsole->PossibleCommands(pConsole->m_aCompletionBuffer, pConsole->m_CompletionFlagmask, PossibleCommandsRenderCallback, &Info);
+				m_pConsole->PossibleCommands(pConsole->m_aCompletionBuffer, pConsole->m_CompletionFlagmask, m_ConsoleType != CGameConsole::CONSOLETYPE_LOCAL &&
+					Client()->RconAuthed() && Client()->UseTempRconCommands(), PossibleCommandsRenderCallback, &Info);
 				pConsole->m_CompletionRenderOffset = Info.m_Offset;
 
 				if(Info.m_EnumCount <= 0)
 				{
-					if(pConsole->m_pCommand)
+					if(pConsole->m_IsCommand)
 					{
-
 						char aBuf[512];
-						str_format(aBuf, sizeof(aBuf), "Help: %s ", pConsole->m_pCommand->m_pHelp);
+						str_format(aBuf, sizeof(aBuf), "Help: %s ", pConsole->m_aCommandHelp);
 						TextRender()->TextEx(&Info.m_Cursor, aBuf, -1);
 						TextRender()->TextColor(0.75f, 0.75f, 0.75f, 1);
-						str_format(aBuf, sizeof(aBuf), "Syntax: %s %s", pConsole->m_pCommand->m_pName, pConsole->m_pCommand->m_pParams);
+						str_format(aBuf, sizeof(aBuf), "Syntax: %s %s", pConsole->m_aCommandName, pConsole->m_aCommandParams);
 						TextRender()->TextEx(&Info.m_Cursor, aBuf, -1);
 					}
 				}
@@ -656,6 +662,16 @@ void CGameConsole::ClientConsolePrintCallback(const char *pStr, void *pUserData)
 	((CGameConsole *)pUserData)->m_LocalConsole.PrintLine(pStr);
 }
 
+void CGameConsole::ConchainConsoleOutputLevelUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
+{
+	pfnCallback(pResult, pCallbackUserData);
+	if(pResult->NumArguments() == 1)
+	{
+		CGameConsole *pThis = static_cast<CGameConsole *>(pUserData);
+		pThis->Console()->SetPrintOutputLevel(pThis->m_PrintCBIndex, pResult->GetInteger(0));
+	}
+}
+
 void CGameConsole::PrintLine(int Type, const char *pLine)
 {
 	if(Type == CONSOLETYPE_LOCAL)
@@ -673,7 +689,7 @@ void CGameConsole::OnConsoleInit()
 	m_pConsole = Kernel()->RequestInterface<IConsole>();
 
 	//
-	Console()->RegisterPrintCallback(ClientConsolePrintCallback, this);
+	m_PrintCBIndex = Console()->RegisterPrintCallback(g_Config.m_ConsoleOutputLevel, ClientConsolePrintCallback, this);
 
 	Console()->Register("toggle_local_console", "", CFGFLAG_CLIENT, ConToggleLocalConsole, this, "Toggle local console");
 	Console()->Register("toggle_remote_console", "", CFGFLAG_CLIENT, ConToggleRemoteConsole, this, "Toggle remote console");
@@ -681,6 +697,8 @@ void CGameConsole::OnConsoleInit()
 	Console()->Register("clear_remote_console", "", CFGFLAG_CLIENT, ConClearRemoteConsole, this, "Clear remote console");
 	Console()->Register("dump_local_console", "", CFGFLAG_CLIENT, ConDumpLocalConsole, this, "Dump local console");
 	Console()->Register("dump_remote_console", "", CFGFLAG_CLIENT, ConDumpRemoteConsole, this, "Dump remote console");
+
+	Console()->Chain("console_output_level", ConchainConsoleOutputLevelUpdate, this);
 }
 
 void CGameConsole::OnStateChange(int NewState, int OldState)
diff --git a/src/game/client/components/console.h b/src/game/client/components/console.h
index 003a9423..6bcc75a6 100644
--- a/src/game/client/components/console.h
+++ b/src/game/client/components/console.h
@@ -33,7 +33,10 @@ class CGameConsole : public CComponent
 		int m_CompletionFlagmask;
 		float m_CompletionRenderOffset;
 
-		IConsole::CCommandInfo *m_pCommand;
+		bool m_IsCommand;
+		char m_aCommandName[IConsole::TEMPCMD_NAME_LENGTH];
+		char m_aCommandHelp[IConsole::TEMPCMD_HELP_LENGTH];
+		char m_aCommandParams[IConsole::TEMPCMD_PARAMS_LENGTH];
 
 		CInstance(int t);
 		void Init(CGameConsole *pGameConsole);
@@ -57,6 +60,7 @@ class CGameConsole : public CComponent
 
 	CInstance *CurrentConsole();
 	float TimeNow();
+	int m_PrintCBIndex;
 
 	int m_ConsoleType;
 	int m_ConsoleState;
@@ -74,6 +78,7 @@ class CGameConsole : public CComponent
 	static void ConClearRemoteConsole(IConsole::IResult *pResult, void *pUserData);
 	static void ConDumpLocalConsole(IConsole::IResult *pResult, void *pUserData);
 	static void ConDumpRemoteConsole(IConsole::IResult *pResult, void *pUserData);
+	static void ConchainConsoleOutputLevelUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
 
 public:
 	enum
diff --git a/src/game/client/components/countryflags.cpp b/src/game/client/components/countryflags.cpp
index 2429ad3f..ef350cd7 100644
--- a/src/game/client/components/countryflags.cpp
+++ b/src/game/client/components/countryflags.cpp
@@ -45,6 +45,15 @@ void CCountryFlags::LoadCountryflagsIndexfile()
 			continue;
 		}
 
+		int CountryCode = str_toint(pReplacement+3);
+		if(CountryCode < CODE_LB || CountryCode > CODE_UB)
+		{
+			char aBuf[128];
+			str_format(aBuf, sizeof(aBuf), "country code '%i' not within valid code range [%i..%i]", CountryCode, CODE_LB, CODE_UB);
+			Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "countryflags", aBuf);
+			continue;
+		}
+
 		// load the graphic file
 		char aBuf[128];
 		str_format(aBuf, sizeof(aBuf), "countryflags/%s.png", aOrigin);
@@ -59,7 +68,8 @@ void CCountryFlags::LoadCountryflagsIndexfile()
 
 		// add entry
 		CCountryFlag CountryFlag;
-		CountryFlag.m_CountryCode = str_toint(pReplacement+3);
+		CountryFlag.m_CountryCode = CountryCode;
+		str_copy(CountryFlag.m_aCountryCodeString, aOrigin, sizeof(CountryFlag.m_aCountryCodeString));
 		CountryFlag.m_Texture = Graphics()->LoadTextureRaw(Info.m_Width, Info.m_Height, Info.m_Format, Info.m_pData, Info.m_Format, 0);
 		mem_free(Info.m_pData);
 		str_format(aBuf, sizeof(aBuf), "loaded country flag '%s'", aOrigin);
@@ -67,6 +77,10 @@ void CCountryFlags::LoadCountryflagsIndexfile()
 		m_aCountryFlags.add(CountryFlag);
 	}
 	io_close(File);
+
+	mem_zero(m_CodeIndexLUT, sizeof(m_CodeIndexLUT));
+	for(int i = 0; i < m_aCountryFlags.size(); ++i)
+		m_CodeIndexLUT[max(0, (m_aCountryFlags[i].m_CountryCode-CODE_LB)%CODE_RANGE)] = i+1;
 }
 
 void CCountryFlags::OnInit()
@@ -89,17 +103,12 @@ int CCountryFlags::Num() const
 	return m_aCountryFlags.size();
 }
 
-const CCountryFlags::CCountryFlag *CCountryFlags::Get(int Index) const
+const CCountryFlags::CCountryFlag *CCountryFlags::GetByCountryCode(int CountryCode) const
 {
-	return &m_aCountryFlags[max(0, Index%m_aCountryFlags.size())];
+	return GetByIndex(m_CodeIndexLUT[max(0, (CountryCode-CODE_LB)%CODE_RANGE)]-1);
 }
 
-int CCountryFlags::Find(int CountryCode) const
+const CCountryFlags::CCountryFlag *CCountryFlags::GetByIndex(int Index) const
 {
-	for(int i = 0; i < m_aCountryFlags.size(); ++i)
-	{
-		if(m_aCountryFlags[i].m_CountryCode == CountryCode)
-			return i;
-	}
-	return -1;
+	return &m_aCountryFlags[max(0, Index%m_aCountryFlags.size())];
 }
diff --git a/src/game/client/components/countryflags.h b/src/game/client/components/countryflags.h
index cd629094..ad24a762 100644
--- a/src/game/client/components/countryflags.h
+++ b/src/game/client/components/countryflags.h
@@ -12,19 +12,28 @@ public:
 	struct CCountryFlag
 	{
 		int m_CountryCode;
+		char m_aCountryCodeString[8];
 		int m_Texture;
 
-		bool operator<(const CCountryFlag &Other) { return m_CountryCode < Other.m_CountryCode; }
+		bool operator<(const CCountryFlag &Other) { return str_comp(m_aCountryCodeString, Other.m_aCountryCodeString) < 0; }
 	};
 
 	void OnInit();
 
 	int Num() const;
-	const CCountryFlag *Get(int Index) const;
-	int Find(int CountryCode) const;
+	const CCountryFlag *GetByCountryCode(int CountryCode) const;
+	const CCountryFlag *GetByIndex(int Index) const;
+	//int Find(int CountryCode) const;
 
 private:
+	enum
+	{
+		CODE_LB=-1,
+		CODE_UB=999,
+		CODE_RANGE=CODE_UB-CODE_LB+1,
+	};
 	sorted_array<CCountryFlag> m_aCountryFlags;
+	int m_CodeIndexLUT[CODE_RANGE];
 
 	void LoadCountryflagsIndexfile();
 };
diff --git a/src/game/client/components/debughud.cpp b/src/game/client/components/debughud.cpp
index 7145705c..6adc61b2 100644
--- a/src/game/client/components/debughud.cpp
+++ b/src/game/client/components/debughud.cpp
@@ -42,11 +42,11 @@ void CDebugHud::RenderNetCorrections()
 
 	x = Width-10.0f;
 	char aBuf[128];
-	str_format(aBuf, sizeof(aBuf), "%.0f", Velspeed);
+	str_format(aBuf, sizeof(aBuf), "%.0f", Velspeed/32);
 	float w = TextRender()->TextWidth(0, Fontsize, aBuf, -1);
 	TextRender()->Text(0, x-w, y, Fontsize, aBuf, -1);
 	y += LineHeight;
-	str_format(aBuf, sizeof(aBuf), "%.0f", Velspeed*Ramp);
+	str_format(aBuf, sizeof(aBuf), "%.0f", Velspeed/32*Ramp);
 	w = TextRender()->TextWidth(0, Fontsize, aBuf, -1);
 	TextRender()->Text(0, x-w, y, Fontsize, aBuf, -1);
 	y += LineHeight;
@@ -54,11 +54,11 @@ void CDebugHud::RenderNetCorrections()
 	w = TextRender()->TextWidth(0, Fontsize, aBuf, -1);
 	TextRender()->Text(0, x-w, y, Fontsize, aBuf, -1);
 	y += 2*LineHeight;
-	str_format(aBuf, sizeof(aBuf), "%d", m_pClient->m_Snap.m_pLocalCharacter->m_X);
+	str_format(aBuf, sizeof(aBuf), "%d", m_pClient->m_Snap.m_pLocalCharacter->m_X/32);
 	w = TextRender()->TextWidth(0, Fontsize, aBuf, -1);
 	TextRender()->Text(0, x-w, y, Fontsize, aBuf, -1);
 	y += LineHeight;
-	str_format(aBuf, sizeof(aBuf), "%d", m_pClient->m_Snap.m_pLocalCharacter->m_Y);
+	str_format(aBuf, sizeof(aBuf), "%d", m_pClient->m_Snap.m_pLocalCharacter->m_Y/32);
 	w = TextRender()->TextWidth(0, Fontsize, aBuf, -1);
 	TextRender()->Text(0, x-w, y, Fontsize, aBuf, -1);
 	y += 2*LineHeight;
diff --git a/src/game/client/components/effects.cpp b/src/game/client/components/effects.cpp
index 573ac410..8554b7ba 100644
--- a/src/game/client/components/effects.cpp
+++ b/src/game/client/components/effects.cpp
@@ -86,7 +86,7 @@ void CEffects::SmokeTrail(vec2 Pos, vec2 Vel)
 	p.m_LifeSpan = 0.5f + frandom()*0.5f;
 	p.m_StartSize = 12.0f + frandom()*8;
 	p.m_EndSize = 0;
-	p.m_Friction = 0.7;
+	p.m_Friction = 0.7f;
 	p.m_Gravity = frandom()*-500.0f;
 	m_pClient->m_pParticles->Add(CParticles::GROUP_PROJECTILE_TRAIL, &p);
 }
diff --git a/src/game/client/components/emoticon.cpp b/src/game/client/components/emoticon.cpp
index 741a604f..b2f48b80 100644
--- a/src/game/client/components/emoticon.cpp
+++ b/src/game/client/components/emoticon.cpp
@@ -54,6 +54,7 @@ bool CEmoticon::OnMouseMove(float x, float y)
 	if(!m_Active)
 		return false;
 
+	UI()->ConvertMouseMove(&x, &y);
 	m_SelectorMouse += vec2(x,y);
 	return true;
 }
@@ -101,6 +102,13 @@ void CEmoticon::OnRender()
 		return;
 	}
 
+	if(m_pClient->m_Snap.m_SpecInfo.m_Active)
+	{
+		m_Active = false;
+		m_WasActive = false;
+		return;
+	}
+
 	m_WasActive = true;
 
 	if (length(m_SelectorMouse) > 140)
diff --git a/src/game/client/components/hud.cpp b/src/game/client/components/hud.cpp
index 188691a1..11343912 100644
--- a/src/game/client/components/hud.cpp
+++ b/src/game/client/components/hud.cpp
@@ -35,7 +35,7 @@ void CHud::RenderGameTimer()
 	{
 		char Buf[32];
 		int Time = 0;
-		if(m_pClient->m_Snap.m_pGameInfoObj->m_TimeLimit)
+		if(m_pClient->m_Snap.m_pGameInfoObj->m_TimeLimit && !m_pClient->m_Snap.m_pGameInfoObj->m_WarmupTimer)
 		{
 			Time = m_pClient->m_Snap.m_pGameInfoObj->m_TimeLimit*60 - ((Client()->GameTick()-m_pClient->m_Snap.m_pGameInfoObj->m_RoundStartTick)/Client()->GameTickSpeed());
 
@@ -49,7 +49,7 @@ void CHud::RenderGameTimer()
 		float FontSize = 10.0f;
 		float w = TextRender()->TextWidth(0, FontSize, Buf, -1);
 		// last 60 sec red, last 10 sec blink
-		if(m_pClient->m_Snap.m_pGameInfoObj->m_TimeLimit && Time <= 60)
+		if(m_pClient->m_Snap.m_pGameInfoObj->m_TimeLimit && Time <= 60 && !m_pClient->m_Snap.m_pGameInfoObj->m_WarmupTimer)
 		{
 			float Alpha = Time <= 10 && (2*time_get()/time_freq()) % 2 ? 0.5f : 1.0f;
 			TextRender()->TextColor(1.0f, 0.25f, 0.25f, Alpha);
@@ -108,7 +108,9 @@ void CHud::RenderScoreHud()
 
 				if(GameFlags&GAMEFLAG_FLAGS)
 				{
-					if(FlagCarrier[t] == FLAG_ATSTAND || (FlagCarrier[t] == FLAG_TAKEN && ((Client()->GameTick()/10)&1)))
+					int BlinkTimer = (m_pClient->m_FlagDropTick[t] != 0 && 
+										(Client()->GameTick()-m_pClient->m_FlagDropTick[t])/Client()->GameTickSpeed() >= 25) ? 10 : 20;
+					if(FlagCarrier[t] == FLAG_ATSTAND || (FlagCarrier[t] == FLAG_TAKEN && ((Client()->GameTick()/BlinkTimer)&1)))
 					{
 						// draw flag
 						Graphics()->BlendNormal();
diff --git a/src/game/client/components/maplayers.cpp b/src/game/client/components/maplayers.cpp
index 848651ca..096f9cc5 100644
--- a/src/game/client/components/maplayers.cpp
+++ b/src/game/client/components/maplayers.cpp
@@ -63,21 +63,25 @@ void CMapLayers::EnvelopeEval(float TimeOffset, int Env, float *pChannels, void
 
 	CMapItemEnvelope *pItem = (CMapItemEnvelope *)pThis->m_pLayers->Map()->GetItem(Start+Env, 0, 0);
 
+	static float Time = 0;
 	if(pThis->Client()->State() == IClient::STATE_DEMOPLAYBACK)
 	{
 		const IDemoPlayer::CInfo *pInfo = pThis->DemoPlayer()->BaseInfo();
-		static float Time = 0;
-		static float LastLocalTime = pThis->Client()->LocalTime();
+		static int LastLocalTick = pInfo->m_CurrentTick;
 
 		if(!pInfo->m_Paused)
-			Time += (pThis->Client()->LocalTime()-LastLocalTime)*pInfo->m_Speed;
+			Time += (pInfo->m_CurrentTick-LastLocalTick) / (float)pThis->Client()->GameTickSpeed() * pInfo->m_Speed;
 
 		pThis->RenderTools()->RenderEvalEnvelope(pPoints+pItem->m_StartPoint, pItem->m_NumPoints, 4, Time+TimeOffset, pChannels);
 
-		LastLocalTime = pThis->Client()->LocalTime();
+		LastLocalTick = pInfo->m_CurrentTick;
 	}
 	else
-		pThis->RenderTools()->RenderEvalEnvelope(pPoints+pItem->m_StartPoint, pItem->m_NumPoints, 4, pThis->Client()->LocalTime()+TimeOffset, pChannels);
+	{
+		if(pThis->m_pClient->m_Snap.m_pGameInfoObj)
+			Time = (pThis->Client()->GameTick()-pThis->m_pClient->m_Snap.m_pGameInfoObj->m_RoundStartTick) / (float)pThis->Client()->GameTickSpeed();
+		pThis->RenderTools()->RenderEvalEnvelope(pPoints+pItem->m_StartPoint, pItem->m_NumPoints, 4, Time+TimeOffset, pChannels);
+	}
 }
 
 void CMapLayers::OnRender()
@@ -186,9 +190,11 @@ void CMapLayers::OnRender()
 					CTile *pTiles = (CTile *)m_pLayers->Map()->GetData(pTMap->m_Data);
 					Graphics()->BlendNone();
 					vec4 Color = vec4(pTMap->m_Color.r/255.0f, pTMap->m_Color.g/255.0f, pTMap->m_Color.b/255.0f, pTMap->m_Color.a/255.0f);
-					RenderTools()->RenderTilemap(pTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_OPAQUE);
+					RenderTools()->RenderTilemap(pTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_OPAQUE,
+													EnvelopeEval, this, pTMap->m_ColorEnv, pTMap->m_ColorEnvOffset);
 					Graphics()->BlendNormal();
-					RenderTools()->RenderTilemap(pTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_TRANSPARENT);
+					RenderTools()->RenderTilemap(pTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_TRANSPARENT,
+													EnvelopeEval, this, pTMap->m_ColorEnv, pTMap->m_ColorEnvOffset);
 				}
 				else if(pLayer->m_Type == LAYERTYPE_QUADS)
 				{
diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp
index 53192714..8f330f78 100644
--- a/src/game/client/components/menus.cpp
+++ b/src/game/client/components/menus.cpp
@@ -27,6 +27,7 @@
 #include <game/localization.h>
 #include <mastersrv/mastersrv.h>
 
+#include "countryflags.h"
 #include "menus.h"
 #include "skins.h"
 
@@ -209,7 +210,7 @@ int CMenus::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrS
 
 			for(int i = 1; i <= Len; i++)
 			{
-				if(TextRender()->TextWidth(0, FontSize, pStr, i) - *Offset + 10 > MxRel)
+				if(TextRender()->TextWidth(0, FontSize, pStr, i) - *Offset > MxRel)
 				{
 					s_AtIndex = i - 1;
 					break;
@@ -251,6 +252,7 @@ int CMenus::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrS
 	{
 		if(!UI()->MouseButton(0))
 		{
+			s_AtIndex = min(s_AtIndex, str_length(pStr));
 			s_DoScroll = false;
 			UI()->SetActiveItem(0);
 		}
@@ -645,8 +647,6 @@ void CMenus::RenderLoading()
 
 	RenderBackground();
 
-	float tw;
-
 	float w = 700;
 	float h = 200;
 	float x = Screen.w/2-w/2;
@@ -663,7 +663,6 @@ void CMenus::RenderLoading()
 
 	const char *pCaption = Localize("Loading");
 
-	tw = TextRender()->TextWidth(0, 48.0f, pCaption, -1);
 	CUIRect r;
 	r.x = x;
 	r.y = y+20;
@@ -737,6 +736,8 @@ void CMenus::OnInit()
 
 	Console()->Chain("add_favorite", ConchainServerbrowserUpdate, this);
 	Console()->Chain("remove_favorite", ConchainServerbrowserUpdate, this);
+	Console()->Chain("add_friend", ConchainFriendlistUpdate, this);
+	Console()->Chain("remove_friend", ConchainFriendlistUpdate, this);
 
 	// setup load amount
 	m_LoadCurrent = 0;
@@ -960,7 +961,7 @@ int CMenus::Render()
 			Box.VMargin(20.f/UI()->Scale(), &Box);
 			if(m_pClient->Editor()->HasUnsavedData())
 			{
-				char aBuf[128];
+				char aBuf[256];
 				str_format(aBuf, sizeof(aBuf), "%s\n%s", Localize("There's an unsaved map in the editor, you might want to save it before you quit the game."), Localize("Quit anyway?"));
 				UI()->DoLabelScaled(&Box, aBuf, 20.f, -1, Part.w-20.0f);
 			}
@@ -1058,7 +1059,7 @@ int CMenus::Render()
 
 				// time left
 				const char *pTimeLeftString;
-				int TimeLeft = m_DownloadSpeed > 0.0f ? (Client()->MapDownloadTotalsize()-Client()->MapDownloadAmount())/m_DownloadSpeed : 0.0f;
+				int TimeLeft = max(1, m_DownloadSpeed > 0.0f ? static_cast<int>((Client()->MapDownloadTotalsize()-Client()->MapDownloadAmount())/m_DownloadSpeed) : 1);
 				if(TimeLeft >= 60)
 				{
 					TimeLeft /= 60;
@@ -1097,6 +1098,69 @@ int CMenus::Render()
 			if(DoButton_Menu(&s_Button, Localize("Ok"), 0, &Part) || m_EscapePressed || m_EnterPressed)
 				m_Popup = POPUP_FIRST_LAUNCH;
 		}
+		else if(m_Popup == POPUP_COUNTRY)
+		{
+			Box = Screen;
+			Box.VMargin(150.0f, &Box);
+			Box.HMargin(150.0f, &Box);
+			Box.HSplitTop(20.f, &Part, &Box);
+			Box.HSplitBottom(20.f, &Box, &Part);
+			Box.HSplitBottom(24.f, &Box, &Part);
+			Box.HSplitBottom(20.f, &Box, 0);
+			Box.VMargin(20.0f, &Box);
+			
+			static int ActSelection = -2;
+			if(ActSelection == -2)
+				ActSelection = g_Config.m_BrFilterCountryIndex;
+			static float s_ScrollValue = 0.0f;
+			int OldSelected = -1;
+			UiDoListboxStart(&s_ScrollValue, &Box, 50.0f, Localize("Country"), "", m_pClient->m_pCountryFlags->Num(), 6, OldSelected, s_ScrollValue);
+
+			for(int i = 0; i < m_pClient->m_pCountryFlags->Num(); ++i)
+			{
+				const CCountryFlags::CCountryFlag *pEntry = m_pClient->m_pCountryFlags->GetByIndex(i);
+				if(pEntry->m_CountryCode == ActSelection)
+					OldSelected = i;
+
+				CListboxItem Item = UiDoListboxNextItem(&pEntry->m_CountryCode, OldSelected == i);
+				if(Item.m_Visible)
+				{
+					CUIRect Label;
+					Item.m_Rect.Margin(5.0f, &Item.m_Rect);
+					Item.m_Rect.HSplitBottom(10.0f, &Item.m_Rect, &Label);
+					float OldWidth = Item.m_Rect.w;
+					Item.m_Rect.w = Item.m_Rect.h*2;
+					Item.m_Rect.x += (OldWidth-Item.m_Rect.w)/ 2.0f;
+					Graphics()->TextureSet(pEntry->m_Texture);
+					Graphics()->QuadsBegin();
+					Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
+					IGraphics::CQuadItem QuadItem(Item.m_Rect.x, Item.m_Rect.y, Item.m_Rect.w, Item.m_Rect.h);
+					Graphics()->QuadsDrawTL(&QuadItem, 1);
+					Graphics()->QuadsEnd();
+					UI()->DoLabel(&Label, pEntry->m_aCountryCodeString, 10.0f, 0);
+				}
+			}
+
+			const int NewSelected = UiDoListboxEnd(&s_ScrollValue, 0);
+			if(OldSelected != NewSelected)
+				ActSelection = m_pClient->m_pCountryFlags->GetByIndex(NewSelected)->m_CountryCode;
+
+			Part.VMargin(120.0f, &Part);
+
+			static int s_Button = 0;
+			if(DoButton_Menu(&s_Button, Localize("Ok"), 0, &Part) || m_EnterPressed)
+			{
+				g_Config.m_BrFilterCountryIndex = ActSelection;
+				Client()->ServerBrowserUpdate();
+				m_Popup = POPUP_NONE;
+			}
+
+			if(m_EscapePressed)
+			{
+				ActSelection = g_Config.m_BrFilterCountryIndex;
+				m_Popup = POPUP_NONE;
+			}
+		}
 		else if(m_Popup == POPUP_DELETE_DEMO)
 		{
 			CUIRect Yes, No;
@@ -1208,7 +1272,9 @@ int CMenus::Render()
 				// remove friend
 				if(m_FriendlistSelectedIndex >= 0)
 				{
-					m_pClient->Friends()->RemoveFriend(m_FriendlistSelectedIndex);
+					m_pClient->Friends()->RemoveFriend(m_lFriends[m_FriendlistSelectedIndex].m_pFriendInfo->m_aName,
+						m_lFriends[m_FriendlistSelectedIndex].m_pFriendInfo->m_aClan);
+					FriendlistOnUpdate();
 					Client()->ServerBrowserUpdate();
 				}
 			}
@@ -1285,6 +1351,7 @@ bool CMenus::OnMouseMove(float x, float y)
 	if(!m_MenuActive)
 		return false;
 
+	UI()->ConvertMouseMove(&x, &y);
 	m_MousePos.x += x;
 	m_MousePos.y += y;
 	if(m_MousePos.x < 0) m_MousePos.x = 0;
@@ -1335,7 +1402,8 @@ void CMenus::OnStateChange(int NewState, int OldState)
 
 	if(NewState == IClient::STATE_OFFLINE)
 	{
-		m_pClient->m_pSounds->Play(CSounds::CHN_MUSIC, SOUND_MENU, 1.0f, vec2(0, 0));
+		if(OldState >= IClient::STATE_ONLINE)
+			m_pClient->m_pSounds->Play(CSounds::CHN_MUSIC, SOUND_MENU, 1.0f, vec2(0, 0));
 		m_Popup = POPUP_NONE;
 		if(Client()->ErrorString() && Client()->ErrorString()[0] != 0)
 		{
diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h
index 51b8a1f8..585fb91f 100644
--- a/src/game/client/components/menus.h
+++ b/src/game/client/components/menus.h
@@ -7,6 +7,7 @@
 #include <base/tl/sorted_array.h>
 
 #include <engine/demo.h>
+#include <engine/friends.h>
 
 #include <game/voting.h>
 #include <game/client/component.h>
@@ -100,6 +101,7 @@ class CMenus : public CComponent
 		POPUP_DISCONNECTED,
 		POPUP_PURE,
 		POPUP_LANGUAGE,
+		POPUP_COUNTRY,
 		POPUP_DELETE_DEMO,
 		POPUP_RENAME_DEMO,
 		POPUP_REMOVE_FRIEND,
@@ -202,8 +204,34 @@ class CMenus : public CComponent
 	void DemolistPopulate();
 	static int DemolistFetchCallback(const char *pName, int IsDir, int StorageType, void *pUser);
 
+	// friends
+	struct CFriendItem
+	{
+		const CFriendInfo *m_pFriendInfo;
+		int m_NumFound;
+
+		bool operator<(const CFriendItem &Other)
+		{
+			if(m_NumFound && !Other.m_NumFound)
+				return true;
+			else if(!m_NumFound && Other.m_NumFound)
+				return false;
+			else
+			{
+				int Result = str_comp(m_pFriendInfo->m_aName, Other.m_pFriendInfo->m_aName);
+				if(Result)
+					return Result < 0;
+				else
+					return str_comp(m_pFriendInfo->m_aClan, Other.m_pFriendInfo->m_aClan) < 0;
+			}
+		}
+	};
+
+	sorted_array<CFriendItem> m_lFriends;
 	int m_FriendlistSelectedIndex;
 
+	void FriendlistOnUpdate();
+
 	// found in menus.cpp
 	int Render();
 	//void render_background();
@@ -225,11 +253,13 @@ class CMenus : public CComponent
 
 	// found in menus_browser.cpp
 	int m_SelectedIndex;
+	int m_ScrollOffset;
 	void RenderServerbrowserServerList(CUIRect View);
 	void RenderServerbrowserServerDetail(CUIRect View);
 	void RenderServerbrowserFilters(CUIRect View);
 	void RenderServerbrowserFriends(CUIRect View);
 	void RenderServerbrowser(CUIRect MainView);
+	static void ConchainFriendlistUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
 	static void ConchainServerbrowserUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
 
 	// found in menus_settings.cpp
diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp
index 3ab02db8..8501c67d 100644
--- a/src/game/client/components/menus_browser.cpp
+++ b/src/game/client/components/menus_browser.cpp
@@ -154,9 +154,14 @@ void CMenus::RenderServerbrowserServerList(CUIRect View)
 	int ScrollNum = NumServers-Num+1;
 	if(ScrollNum > 0)
 	{
-		if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP))
+		if(m_ScrollOffset)
+		{
+			s_ScrollValue = (float)(m_ScrollOffset)/ScrollNum;
+			m_ScrollOffset = 0;
+		}
+		if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP) && UI()->MouseInside(&View))
 			s_ScrollValue -= 3.0f/ScrollNum;
-		if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN))
+		if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN) && UI()->MouseInside(&View))
 			s_ScrollValue += 3.0f/ScrollNum;
 	}
 	else
@@ -213,16 +218,14 @@ void CMenus::RenderServerbrowserServerList(CUIRect View)
 
 	m_SelectedIndex = -1;
 
-	for (int i = 0; i < NumServers; i++)
-	{
-		const CServerInfo *pItem = ServerBrowser()->SortedGet(i);
-		NumPlayers += pItem->m_NumPlayers;
-	}
+	// reset friend counter
+	for(int i = 0; i < m_lFriends.size(); m_lFriends[i++].m_NumFound = 0);
 
 	for (int i = 0; i < NumServers; i++)
 	{
 		int ItemIndex = i;
 		const CServerInfo *pItem = ServerBrowser()->SortedGet(ItemIndex);
+		NumPlayers += pItem->m_NumPlayers;
 		CUIRect Row;
 		CUIRect SelectHitBox;
 
@@ -234,6 +237,29 @@ void CMenus::RenderServerbrowserServerList(CUIRect View)
 		if(Selected)
 			m_SelectedIndex = i;
 
+		// update friend counter
+		if(pItem->m_FriendState != IFriends::FRIEND_NO)
+		{
+			for(int j = 0; j < pItem->m_NumClients; ++j)
+			{
+				if(pItem->m_aClients[j].m_FriendState != IFriends::FRIEND_NO)
+				{
+					unsigned NameHash = str_quickhash(pItem->m_aClients[j].m_aName);
+					unsigned ClanHash = str_quickhash(pItem->m_aClients[j].m_aClan);
+					for(int f = 0; f < m_lFriends.size(); ++f)
+					{
+						if(ClanHash == m_lFriends[f].m_pFriendInfo->m_ClanHash &&
+							(!m_lFriends[f].m_pFriendInfo->m_aName[0] || NameHash == m_lFriends[f].m_pFriendInfo->m_NameHash))
+						{
+							m_lFriends[f].m_NumFound++;
+							if(m_lFriends[f].m_pFriendInfo->m_aName[0])
+								break;
+						}
+					}
+				}
+			}
+		}
+
 		// make sure that only those in view can be selected
 		if(Row.y+Row.h > OriginalView.y && Row.y < OriginalView.y+OriginalView.h)
 		{
@@ -353,6 +379,15 @@ void CMenus::RenderServerbrowserServerList(CUIRect View)
 			}
 			else if(ID == COL_PLAYERS)
 			{
+				CUIRect Icon;
+				Button.VMargin(4.0f, &Button);
+				if(pItem->m_FriendState != IFriends::FRIEND_NO)
+				{
+					Button.VSplitLeft(Button.h, &Icon, &Button);
+					Icon.Margin(2.0f, &Icon);
+					DoButton_Icon(IMAGE_BROWSEICONS, SPRITE_BROWSE_HEART, &Icon);
+				}
+
 				if(g_Config.m_BrFilterSpectators)
 					str_format(aTemp, sizeof(aTemp), "%i/%i", pItem->m_NumPlayers, pItem->m_MaxPlayers);
 				else
@@ -463,7 +498,7 @@ void CMenus::RenderServerbrowserFilters(CUIRect View)
 		g_Config.m_BrFilterFull ^= 1;
 
 	ServerFilter.HSplitTop(20.0f, &Button, &ServerFilter);
-	if (DoButton_CheckBox(&g_Config.m_BrFilterFriends, Localize("Show friends"), g_Config.m_BrFilterFriends, &Button))
+	if (DoButton_CheckBox(&g_Config.m_BrFilterFriends, Localize("Show friends only"), g_Config.m_BrFilterFriends, &Button))
 		g_Config.m_BrFilterFriends ^= 1;
 
 	ServerFilter.HSplitTop(20.0f, &Button, &ServerFilter);
@@ -481,6 +516,10 @@ void CMenus::RenderServerbrowserFilters(CUIRect View)
 	ServerFilter.HSplitTop(20.0f, &Button, &ServerFilter);
 	if (DoButton_CheckBox((char *)&g_Config.m_BrFilterPureMap, Localize("Standard map"), g_Config.m_BrFilterPureMap, &Button))
 		g_Config.m_BrFilterPureMap ^= 1;
+	
+	ServerFilter.HSplitTop(20.0f, &Button, &ServerFilter);
+	if (DoButton_CheckBox((char *)&g_Config.m_BrFilterGametypeStrict, Localize("Strict gametype filter"), g_Config.m_BrFilterGametypeStrict, &Button))
+		g_Config.m_BrFilterGametypeStrict ^= 1;
 
 	ServerFilter.HSplitTop(5.0f, 0, &ServerFilter);
 
@@ -515,21 +554,50 @@ void CMenus::RenderServerbrowserFilters(CUIRect View)
 	if(DoEditBox(&g_Config.m_BrFilterServerAddress, &Button, g_Config.m_BrFilterServerAddress, sizeof(g_Config.m_BrFilterServerAddress), FontSize, &OffsetAddr))
 		Client()->ServerBrowserUpdate();
 
+	// player country
+	{
+		CUIRect Rect;
+		ServerFilter.HSplitTop(3.0f, 0, &ServerFilter);
+		ServerFilter.HSplitTop(26.0f, &Button, &ServerFilter);
+		Button.VSplitRight(60.0f, &Button, &Rect);
+		Button.HMargin(3.0f, &Button);
+		if(DoButton_CheckBox(&g_Config.m_BrFilterCountry, Localize("Player country:"), g_Config.m_BrFilterCountry, &Button))
+			g_Config.m_BrFilterCountry ^= 1;
+		
+		float OldWidth = Rect.w;
+		Rect.w = Rect.h*2;
+		Rect.x += (OldWidth-Rect.w)/2.0f;
+		Graphics()->TextureSet(m_pClient->m_pCountryFlags->GetByCountryCode(g_Config.m_BrFilterCountryIndex)->m_Texture);
+		Graphics()->QuadsBegin();
+		Graphics()->SetColor(1.0f, 1.0f, 1.0f, g_Config.m_BrFilterCountry?1.0f: 0.5f);
+		IGraphics::CQuadItem QuadItem(Rect.x, Rect.y, Rect.w, Rect.h);
+		Graphics()->QuadsDrawTL(&QuadItem, 1);
+		Graphics()->QuadsEnd();
+
+		if(g_Config.m_BrFilterCountry && UI()->DoButtonLogic(&g_Config.m_BrFilterCountryIndex, "", 0, &Rect))
+			m_Popup = POPUP_COUNTRY;
+	}
+
 	ServerFilter.HSplitBottom(5.0f, &ServerFilter, 0);
 	ServerFilter.HSplitBottom(ms_ButtonHeight-2.0f, &ServerFilter, &Button);
 	static int s_ClearButton = 0;
 	if(DoButton_Menu(&s_ClearButton, Localize("Reset filter"), 0, &Button))
 	{
+		g_Config.m_BrFilterString[0] = 0;
 		g_Config.m_BrFilterFull = 0;
 		g_Config.m_BrFilterEmpty = 0;
+		g_Config.m_BrFilterSpectators = 0;
+		g_Config.m_BrFilterFriends = 0;
+		g_Config.m_BrFilterCountry = 0;
+		g_Config.m_BrFilterCountryIndex = -1;
 		g_Config.m_BrFilterPw = 0;
 		g_Config.m_BrFilterPing = 999;
 		g_Config.m_BrFilterGametype[0] = 0;
+		g_Config.m_BrFilterGametypeStrict = 0;
 		g_Config.m_BrFilterServerAddress[0] = 0;
-		g_Config.m_BrFilterCompatversion = 1;
-		g_Config.m_BrFilterString[0] = 0;
 		g_Config.m_BrFilterPure = 1;
 		g_Config.m_BrFilterPureMap = 1;
+		g_Config.m_BrFilterCompatversion = 1;
 		Client()->ServerBrowserUpdate();
 	}
 }
@@ -617,25 +685,38 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View)
 	RenderTools()->DrawUIRect(&ServerScoreBoard, vec4(0,0,0,0.15f), CUI::CORNER_B, 4.0f);
 	UI()->DoLabelScaled(&ServerHeader, Localize("Scoreboard"), FontSize+2.0f, 0);
 
-	if (pSelectedServer)
+	if(pSelectedServer)
 	{
-		ServerScoreBoard.VSplitLeft(5.0f, 0, &ServerScoreBoard);
 		ServerScoreBoard.Margin(3.0f, &ServerScoreBoard);
 		for (int i = 0; i < pSelectedServer->m_NumClients; i++)
 		{
 			CUIRect Name, Clan, Score, Flag;
 			ServerScoreBoard.HSplitTop(25.0f, &Name, &ServerScoreBoard);
+			if(UI()->DoButtonLogic(&pSelectedServer->m_aClients[i], "", 0, &Name))
+			{
+				if(pSelectedServer->m_aClients[i].m_FriendState == IFriends::FRIEND_PLAYER)
+					m_pClient->Friends()->RemoveFriend(pSelectedServer->m_aClients[i].m_aName, pSelectedServer->m_aClients[i].m_aClan);
+				else
+					m_pClient->Friends()->AddFriend(pSelectedServer->m_aClients[i].m_aName, pSelectedServer->m_aClients[i].m_aClan);
+				FriendlistOnUpdate();
+				Client()->ServerBrowserUpdate();
+			}
+
+			vec4 Colour = pSelectedServer->m_aClients[i].m_FriendState == IFriends::FRIEND_NO ? vec4(1.0f, 1.0f, 1.0f, (i%2+1)*0.05f) :
+																								vec4(0.5f, 1.0f, 0.5f, 0.15f+(i%2+1)*0.05f);
+			RenderTools()->DrawUIRect(&Name, Colour, CUI::CORNER_ALL, 4.0f);
+			Name.VSplitLeft(5.0f, 0, &Name);
 			Name.VSplitLeft(30.0f, &Score, &Name);
 			Name.VSplitRight(34.0f, &Name, &Flag);
 			Flag.HMargin(4.0f, &Flag);
-			Name.HSplitTop(12.0f, &Name, &Clan);
+			Name.HSplitTop(11.0f, &Name, &Clan);
 
 			// score
 			if(pSelectedServer->m_aClients[i].m_Player)
 			{
 				char aTemp[16];
 				str_format(aTemp, sizeof(aTemp), "%d", pSelectedServer->m_aClients[i].m_Score);
-				TextRender()->SetCursor(&Cursor, Score.x, Score.y+(Score.h-FontSize)/2.0f, FontSize, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END);
+				TextRender()->SetCursor(&Cursor, Score.x, Score.y+(Score.h-FontSize)/4.0f, FontSize, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END);
 				Cursor.m_LineWidth = Score.w;
 				TextRender()->TextEx(&Cursor, aTemp, -1);
 			}
@@ -685,7 +766,7 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View)
 				TextRender()->TextEx(&Cursor, pClan, -1);
 
 			// flag
-			Graphics()->TextureSet(m_pClient->m_pCountryFlags->Get(pSelectedServer->m_aClients[i].m_Country)->m_Texture);
+			Graphics()->TextureSet(m_pClient->m_pCountryFlags->GetByCountryCode(pSelectedServer->m_aClients[i].m_Country)->m_Texture);
 			Graphics()->QuadsBegin();
 			Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.5f);
 			IGraphics::CQuadItem QuadItem(Flag.x, Flag.y, Flag.w, Flag.h);
@@ -695,45 +776,102 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View)
 	}
 }
 
+void CMenus::FriendlistOnUpdate()
+{
+	m_lFriends.clear();
+	for(int i = 0; i < m_pClient->Friends()->NumFriends(); ++i)
+	{
+		CFriendItem Item;
+		Item.m_pFriendInfo = m_pClient->Friends()->GetFriend(i);
+		Item.m_NumFound = 0;
+		m_lFriends.add_unsorted(Item);
+	}
+	m_lFriends.sort_range();
+}
+
 void CMenus::RenderServerbrowserFriends(CUIRect View)
 {
+	static int s_Inited = 0;
+	if(!s_Inited)
+	{
+		FriendlistOnUpdate();
+		s_Inited = 1;
+	}
+
 	CUIRect ServerFriends = View, FilterHeader;
-	const float FontSize = 12.0f;
+	const float FontSize = 10.0f;
 
 	// header
 	ServerFriends.HSplitTop(ms_ListheaderHeight, &FilterHeader, &ServerFriends);
 	RenderTools()->DrawUIRect(&FilterHeader, vec4(1,1,1,0.25f), CUI::CORNER_T, 4.0f);
 	RenderTools()->DrawUIRect(&ServerFriends, vec4(0,0,0,0.15f), 0, 4.0f);
-	UI()->DoLabelScaled(&FilterHeader, Localize("Friends"), FontSize+2.0f, 0);
+	UI()->DoLabelScaled(&FilterHeader, Localize("Friends"), FontSize+4.0f, 0);
 	CUIRect Button, List;
 
-	ServerFriends.VSplitLeft(5.0f, 0, &ServerFriends);
 	ServerFriends.Margin(3.0f, &ServerFriends);
-	ServerFriends.VMargin(5.0f, &ServerFriends);
+	ServerFriends.VMargin(3.0f, &ServerFriends);
 	ServerFriends.HSplitBottom(100.0f, &List, &ServerFriends);
 
 	// friends list(remove friend)
-	static int s_FriendList = 0;
 	static float s_ScrollValue = 0;
-	UiDoListboxStart(&s_FriendList, &List, 40.0f, "", "", m_pClient->Friends()->NumFriends(), 1, m_FriendlistSelectedIndex, s_ScrollValue);
+	UiDoListboxStart(&m_lFriends, &List, 30.0f, "", "", m_lFriends.size(), 1, m_FriendlistSelectedIndex, s_ScrollValue);
 
-	for(int i = 0; i < m_pClient->Friends()->NumFriends(); ++i)
+	m_lFriends.sort_range();
+	for(int i = 0; i < m_lFriends.size(); ++i)
 	{
-		const CFriendInfo *pFriend = m_pClient->Friends()->GetFriend(i);
-		CListboxItem Item = UiDoListboxNextItem(pFriend);
+		CListboxItem Item = UiDoListboxNextItem(&m_lFriends[i]);
 
 		if(Item.m_Visible)
 		{
-			Item.m_Rect.Margin(2.5f, &Item.m_Rect);
-			RenderTools()->DrawUIRect(&Item.m_Rect, vec4(1.0f, 1.0f, 1.0f, 0.1f), CUI::CORNER_ALL, 4.0f);
-			Item.m_Rect.Margin(2.5f, &Item.m_Rect);
-			Item.m_Rect.HSplitTop(14.0f, &Item.m_Rect, &Button);
-			UI()->DoLabelScaled(&Item.m_Rect, pFriend->m_aName, FontSize, -1);
-			UI()->DoLabelScaled(&Button, pFriend->m_aClan, FontSize, -1);
+			Item.m_Rect.Margin(1.5f, &Item.m_Rect);
+			CUIRect OnState;
+			Item.m_Rect.VSplitRight(30.0f, &Item.m_Rect, &OnState);
+			RenderTools()->DrawUIRect(&Item.m_Rect, vec4(1.0f, 1.0f, 1.0f, 0.1f), CUI::CORNER_L, 4.0f);
+
+			Item.m_Rect.VMargin(2.5f, &Item.m_Rect);
+			Item.m_Rect.HSplitTop(12.0f, &Item.m_Rect, &Button);
+			UI()->DoLabelScaled(&Item.m_Rect, m_lFriends[i].m_pFriendInfo->m_aName, FontSize, -1);
+			UI()->DoLabelScaled(&Button, m_lFriends[i].m_pFriendInfo->m_aClan, FontSize, -1);
+
+			RenderTools()->DrawUIRect(&OnState, m_lFriends[i].m_NumFound ? vec4(0.0f, 1.0f, 0.0f, 0.25f) : vec4(1.0f, 0.0f, 0.0f, 0.25f), CUI::CORNER_R, 4.0f);
+			OnState.HMargin((OnState.h-FontSize)/3, &OnState);
+			OnState.VMargin(5.0f, &OnState);
+			char aBuf[64];
+			str_format(aBuf, sizeof(aBuf), "%i", m_lFriends[i].m_NumFound);
+			UI()->DoLabelScaled(&OnState, aBuf, FontSize+2, 1);
 		}
 	}
 
-	m_FriendlistSelectedIndex = UiDoListboxEnd(&s_ScrollValue, 0);
+	bool Activated = false;
+	m_FriendlistSelectedIndex = UiDoListboxEnd(&s_ScrollValue, &Activated);
+
+	// activate found server with friend
+	if(Activated && !m_EnterPressed && m_lFriends[m_FriendlistSelectedIndex].m_NumFound)
+	{
+		bool Found = false;
+		int NumServers = ServerBrowser()->NumSortedServers();
+		for (int i = 0; i < NumServers && !Found; i++)
+		{
+			int ItemIndex = m_SelectedIndex != -1 ? (m_SelectedIndex+i+1)%NumServers : i;
+			const CServerInfo *pItem = ServerBrowser()->SortedGet(ItemIndex);
+			if(pItem->m_FriendState != IFriends::FRIEND_NO)
+			{
+				for(int j = 0; j < pItem->m_NumClients && !Found; ++j)
+				{
+					if(pItem->m_aClients[j].m_FriendState != IFriends::FRIEND_NO &&
+						str_quickhash(pItem->m_aClients[j].m_aClan) == m_lFriends[m_FriendlistSelectedIndex].m_pFriendInfo->m_ClanHash &&
+						(!m_lFriends[m_FriendlistSelectedIndex].m_pFriendInfo->m_aName[0] ||
+						str_quickhash(pItem->m_aClients[j].m_aName) == m_lFriends[m_FriendlistSelectedIndex].m_pFriendInfo->m_NameHash))
+					{
+						str_copy(g_Config.m_UiServerAddress, pItem->m_aAddress, sizeof(g_Config.m_UiServerAddress));
+						m_ScrollOffset = ItemIndex;
+						m_SelectedIndex = ItemIndex;
+						Found = true;
+					}
+				}
+			}
+		}
+	}
 
 	ServerFriends.HSplitTop(2.5f, 0, &ServerFriends);
 	ServerFriends.HSplitTop(20.0f, &Button, &ServerFriends);
@@ -768,10 +906,11 @@ void CMenus::RenderServerbrowserFriends(CUIRect View)
 
 		ServerFriends.HSplitTop(3.0f, 0, &ServerFriends);
 		ServerFriends.HSplitTop(20.0f, &Button, &ServerFriends);
-		static int s_RemoveButton = 0;
-		if(DoButton_Menu(&s_RemoveButton, Localize("Add Friend"), 0, &Button))
+		static int s_AddButton = 0;
+		if(DoButton_Menu(&s_AddButton, Localize("Add Friend"), 0, &Button))
 		{
 			m_pClient->Friends()->AddFriend(s_aName, s_aClan);
+			FriendlistOnUpdate();
 			Client()->ServerBrowserUpdate();
 		}
 	}
@@ -910,6 +1049,16 @@ void CMenus::RenderServerbrowser(CUIRect MainView)
 	}
 }
 
+void CMenus::ConchainFriendlistUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
+{
+	pfnCallback(pResult, pCallbackUserData);
+	if(pResult->NumArguments() == 2 && ((CMenus *)pUserData)->Client()->State() == IClient::STATE_OFFLINE)
+	{
+		((CMenus *)pUserData)->FriendlistOnUpdate();
+		((CMenus *)pUserData)->Client()->ServerBrowserUpdate();
+	}
+}
+
 void CMenus::ConchainServerbrowserUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
 {
 	pfnCallback(pResult, pCallbackUserData);
diff --git a/src/game/client/components/menus_demo.cpp b/src/game/client/components/menus_demo.cpp
index 7fcfab99..f3a75f0c 100644
--- a/src/game/client/components/menus_demo.cpp
+++ b/src/game/client/components/menus_demo.cpp
@@ -200,8 +200,10 @@ void CMenus::RenderDemoPlayer(CUIRect MainView)
 			Client()->Disconnect();
 
 		// demo name
+		char aDemoName[64] = {0};
+		DemoPlayer()->GetDemoName(aDemoName, sizeof(aDemoName));
 		char aBuf[128];
-		str_format(aBuf, sizeof(aBuf), "Demofile: %s", DemoPlayer()->GetDemoName());
+		str_format(aBuf, sizeof(aBuf), Localize("Demofile: %s"), aDemoName);
 		CTextCursor Cursor;
 		TextRender()->SetCursor(&Cursor, NameBar.x, NameBar.y, Button.h*0.5f, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END);
 		Cursor.m_LineWidth = MainView.w;
@@ -289,9 +291,9 @@ void CMenus::UiDoListboxStart(const void *pID, const CUIRect *pRect, float RowHe
 		Num = 0;
 	if(Num > 0)
 	{
-		if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP))
+		if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP) && UI()->MouseInside(&View))
 			gs_ListBoxScrollValue -= 3.0f/Num;
-		if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN))
+		if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN) && UI()->MouseInside(&View))
 			gs_ListBoxScrollValue += 3.0f/Num;
 
 		if(gs_ListBoxScrollValue < 0.0f) gs_ListBoxScrollValue = 0.0f;
@@ -371,7 +373,7 @@ CMenus::CListboxItem CMenus::UiDoListboxNextItem(const void *pId, bool Selected)
 		{
 			gs_ListBoxDoneEvents = 1;
 
-			if(m_EnterPressed || (Input()->MouseDoubleClick() && UI()->ActiveItem() == pId))
+			if(m_EnterPressed || (UI()->ActiveItem() == pId && Input()->MouseDoubleClick()))
 			{
 				gs_ListBoxItemActivated = true;
 				UI()->SetActiveItem(0);
@@ -553,8 +555,8 @@ void CMenus::RenderDemoList(CUIRect MainView)
 		Labels.HSplitTop(20.0f, &Left, &Labels);
 		Left.VSplitLeft(150.0f, &Left, &Right);
 		UI()->DoLabelScaled(&Left, Localize("Length:"), 14.0f, -1);
-		int Length = (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aLength[0]<<24) | (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aLength[1]<<16) |
-					(m_lDemos[m_DemolistSelectedIndex].m_Info.m_aLength[2]<<8) | (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aLength[3]);
+		int Length = ((m_lDemos[m_DemolistSelectedIndex].m_Info.m_aLength[0]<<24)&0xFF000000) | ((m_lDemos[m_DemolistSelectedIndex].m_Info.m_aLength[1]<<16)&0xFF0000) |
+					((m_lDemos[m_DemolistSelectedIndex].m_Info.m_aLength[2]<<8)&0xFF00) | (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aLength[3]&0xFF);
 		char aBuf[64];
 		str_format(aBuf, sizeof(aBuf), "%d:%02d", Length/60, Length%60);
 		UI()->DoLabelScaled(&Right, aBuf, 14.0f, -1);
@@ -576,9 +578,9 @@ void CMenus::RenderDemoList(CUIRect MainView)
 		Left.VSplitLeft(20.0f, 0, &Left);
 		Left.VSplitLeft(130.0f, &Left, &Right);
 		UI()->DoLabelScaled(&Left, Localize("Size:"), 14.0f, -1);
-		Length = (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapSize[0]<<24) | (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapSize[1]<<16) |
+		unsigned Size = (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapSize[0]<<24) | (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapSize[1]<<16) |
 					(m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapSize[2]<<8) | (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapSize[3]);
-		str_format(aBuf, sizeof(aBuf), Localize("%d Bytes"), Length);
+		str_format(aBuf, sizeof(aBuf), Localize("%d Bytes"), Size);
 		UI()->DoLabelScaled(&Right, aBuf, 14.0f, -1);
 		Labels.HSplitTop(5.0f, 0, &Labels);
 		Labels.HSplitTop(20.0f, &Left, &Labels);
diff --git a/src/game/client/components/menus_ingame.cpp b/src/game/client/components/menus_ingame.cpp
index 33aaa14f..1afef922 100644
--- a/src/game/client/components/menus_ingame.cpp
+++ b/src/game/client/components/menus_ingame.cpp
@@ -188,10 +188,12 @@ void CMenus::RenderPlayers(CUIRect MainView)
 		Button.VSplitLeft((Width-Button.h)/4.0f, 0, &Button);
 		Button.VSplitLeft(Button.h, &Button, 0);
 		if(DoButton_Toggle(&s_aPlayerIDs[i][1], m_pClient->m_aClients[i].m_Friend, &Button))
+		{
 			if(m_pClient->m_aClients[i].m_Friend)
 				m_pClient->Friends()->RemoveFriend(m_pClient->m_aClients[i].m_aName, m_pClient->m_aClients[i].m_aClan);
 			else
 				m_pClient->Friends()->AddFriend(m_pClient->m_aClients[i].m_aName, m_pClient->m_aClients[i].m_aClan);
+		}
 	}
 
 	/*
diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp
index 28f3559d..51fdbd29 100644
--- a/src/game/client/components/menus_settings.cpp
+++ b/src/game/client/components/menus_settings.cpp
@@ -208,17 +208,16 @@ void CMenus::RenderSettingsPlayer(CUIRect MainView)
 
 	for(int i = 0; i < m_pClient->m_pCountryFlags->Num(); ++i)
 	{
-		const CCountryFlags::CCountryFlag *pEntry = m_pClient->m_pCountryFlags->Get(i);
-		if(pEntry == 0)
-			continue;
-
+		const CCountryFlags::CCountryFlag *pEntry = m_pClient->m_pCountryFlags->GetByIndex(i);
 		if(pEntry->m_CountryCode == g_Config.m_PlayerCountry)
 			OldSelected = i;
 
 		CListboxItem Item = UiDoListboxNextItem(&pEntry->m_CountryCode, OldSelected == i);
 		if(Item.m_Visible)
 		{
-			Item.m_Rect.Margin(10.0f, &Item.m_Rect);
+			CUIRect Label;
+			Item.m_Rect.Margin(5.0f, &Item.m_Rect);
+			Item.m_Rect.HSplitBottom(10.0f, &Item.m_Rect, &Label);
 			float OldWidth = Item.m_Rect.w;
 			Item.m_Rect.w = Item.m_Rect.h*2;
 			Item.m_Rect.x += (OldWidth-Item.m_Rect.w)/ 2.0f;
@@ -228,13 +227,14 @@ void CMenus::RenderSettingsPlayer(CUIRect MainView)
 			IGraphics::CQuadItem QuadItem(Item.m_Rect.x, Item.m_Rect.y, Item.m_Rect.w, Item.m_Rect.h);
 			Graphics()->QuadsDrawTL(&QuadItem, 1);
 			Graphics()->QuadsEnd();
+			UI()->DoLabel(&Label, pEntry->m_aCountryCodeString, 10.0f, 0);
 		}
 	}
 
 	const int NewSelected = UiDoListboxEnd(&s_ScrollValue, 0);
 	if(OldSelected != NewSelected)
 	{
-		g_Config.m_PlayerCountry = m_pClient->m_pCountryFlags->Get(NewSelected)->m_CountryCode;
+		g_Config.m_PlayerCountry = m_pClient->m_pCountryFlags->GetByIndex(NewSelected)->m_CountryCode;
 		m_NeedSendinfo = true;
 	}
 }
@@ -627,7 +627,8 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView)
 	// display mode list
 	static float s_ScrollValue = 0;
 	int OldSelected = -1;
-	str_format(aBuf, sizeof(aBuf), "%s: %dx%d %d bit", Localize("Current"), s_GfxScreenWidth, s_GfxScreenHeight, s_GfxColorDepth);
+	int G = gcd(s_GfxScreenWidth, s_GfxScreenHeight);
+	str_format(aBuf, sizeof(aBuf), "%s: %dx%d %d bit (%d:%d)", Localize("Current"), s_GfxScreenWidth, s_GfxScreenHeight, s_GfxColorDepth, s_GfxScreenWidth/G, s_GfxScreenHeight/G);
 	UiDoListboxStart(&s_NumNodes , &ModeList, 24.0f, Localize("Display Modes"), aBuf, s_NumNodes, 1, OldSelected, s_ScrollValue);
 
 	for(int i = 0; i < s_NumNodes; ++i)
@@ -643,7 +644,8 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView)
 		CListboxItem Item = UiDoListboxNextItem(&s_aModes[i], OldSelected == i);
 		if(Item.m_Visible)
 		{
-			str_format(aBuf, sizeof(aBuf), " %dx%d %d bit", s_aModes[i].m_Width, s_aModes[i].m_Height, Depth);
+			int G = gcd(s_aModes[i].m_Width, s_aModes[i].m_Height);
+			str_format(aBuf, sizeof(aBuf), " %dx%d %d bit (%d:%d)", s_aModes[i].m_Width, s_aModes[i].m_Height, Depth, s_aModes[i].m_Width/G, s_aModes[i].m_Height/G);
 			UI()->DoLabelScaled(&Item.m_Rect, aBuf, 16.0f, -1);
 		}
 	}
@@ -757,7 +759,10 @@ void CMenus::RenderSettingsSound(CUIRect MainView)
 	{
 		g_Config.m_SndEnable ^= 1;
 		if(g_Config.m_SndEnable)
-			m_pClient->m_pSounds->Play(CSounds::CHN_MUSIC, SOUND_MENU, 1.0f, vec2(0, 0));
+		{
+			if(g_Config.m_SndMusic)
+				m_pClient->m_pSounds->Play(CSounds::CHN_MUSIC, SOUND_MENU, 1.0f, vec2(0, 0));
+		}
 		else
 			m_pClient->m_pSounds->Stop(SOUND_MENU);
 		m_NeedRestartSound = g_Config.m_SndEnable && (!s_SndEnable || s_SndRate != g_Config.m_SndRate);
@@ -810,10 +815,11 @@ class CLanguage
 {
 public:
 	CLanguage() {}
-	CLanguage(const char *n, const char *f) : m_Name(n), m_FileName(f) {}
+	CLanguage(const char *n, const char *f, int Code) : m_Name(n), m_FileName(f), m_CountryCode(Code) {}
 
 	string m_Name;
 	string m_FileName;
+	int m_CountryCode;
 
 	bool operator<(const CLanguage &Other) { return m_Name < Other.m_Name; }
 };
@@ -828,6 +834,7 @@ void LoadLanguageIndexfile(IStorage *pStorage, IConsole *pConsole, sorted_array<
 	}
 
 	char aOrigin[128];
+	char aReplacement[128];
 	CLineReader LineReader;
 	LineReader.Init(File);
 	char *pLine;
@@ -837,14 +844,32 @@ void LoadLanguageIndexfile(IStorage *pStorage, IConsole *pConsole, sorted_array<
 			continue;
 
 		str_copy(aOrigin, pLine, sizeof(aOrigin));
-		char *pReplacement = LineReader.Get();
-		if(!pReplacement)
+
+		pLine = LineReader.Get();
+		if(!pLine)
 		{
 			pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "localization", "unexpected end of index file");
 			break;
 		}
 
-		if(pReplacement[0] != '=' || pReplacement[1] != '=' || pReplacement[2] != ' ')
+		if(pLine[0] != '=' || pLine[1] != '=' || pLine[2] != ' ')
+		{
+			char aBuf[128];
+			str_format(aBuf, sizeof(aBuf), "malform replacement for index '%s'", aOrigin);
+			pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "localization", aBuf);
+			(void)LineReader.Get();
+			continue;
+		}
+		str_copy(aReplacement, pLine+3, sizeof(aReplacement));
+
+		pLine = LineReader.Get();
+		if(!pLine)
+		{
+			pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "localization", "unexpected end of index file");
+			break;
+		}
+
+		if(pLine[0] != '=' || pLine[1] != '=' || pLine[2] != ' ')
 		{
 			char aBuf[128];
 			str_format(aBuf, sizeof(aBuf), "malform replacement for index '%s'", aOrigin);
@@ -854,7 +879,7 @@ void LoadLanguageIndexfile(IStorage *pStorage, IConsole *pConsole, sorted_array<
 
 		char aFileName[128];
 		str_format(aFileName, sizeof(aFileName), "languages/%s.txt", aOrigin);
-		pLanguages->add(CLanguage(pReplacement+3, aFileName));
+		pLanguages->add(CLanguage(aReplacement, aFileName, str_toint(pLine+3)));
 	}
 	io_close(File);
 }
@@ -868,7 +893,7 @@ void CMenus::RenderLanguageSelection(CUIRect MainView)
 
 	if(s_Languages.size() == 0)
 	{
-		s_Languages.add(CLanguage("English", ""));
+		s_Languages.add(CLanguage("English", "", 826));
 		LoadLanguageIndexfile(Storage(), Console(), &s_Languages);
 		for(int i = 0; i < s_Languages.size(); i++)
 			if(str_comp(s_Languages[i].m_FileName, g_Config.m_ClLanguagefile) == 0)
@@ -887,7 +912,19 @@ void CMenus::RenderLanguageSelection(CUIRect MainView)
 		CListboxItem Item = UiDoListboxNextItem(&r.front());
 
 		if(Item.m_Visible)
-			UI()->DoLabelScaled(&Item.m_Rect, r.front().m_Name, 16.0f, -1);
+		{
+			CUIRect Rect;
+			Item.m_Rect.VSplitLeft(Item.m_Rect.h*2.0f, &Rect, &Item.m_Rect);
+			Rect.VMargin(6.0f, &Rect);
+			Rect.HMargin(3.0f, &Rect);
+			Graphics()->TextureSet(m_pClient->m_pCountryFlags->GetByCountryCode(r.front().m_CountryCode)->m_Texture);
+			Graphics()->QuadsBegin();
+			IGraphics::CQuadItem QuadItem(Rect.x, Rect.y, Rect.w, Rect.h);
+			Graphics()->QuadsDrawTL(&QuadItem, 1);
+			Graphics()->QuadsEnd();
+			Item.m_Rect.HSplitTop(2.0f, 0, &Item.m_Rect);
+ 			UI()->DoLabelScaled(&Item.m_Rect, r.front().m_Name, 16.0f, -1);
+		}
 	}
 
 	s_SelectedLanguage = UiDoListboxEnd(&s_ScrollValue, 0);
diff --git a/src/game/client/components/scoreboard.cpp b/src/game/client/components/scoreboard.cpp
index 2cec5e62..ae11c7ea 100644
--- a/src/game/client/components/scoreboard.cpp
+++ b/src/game/client/components/scoreboard.cpp
@@ -273,7 +273,7 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch
 		TextRender()->TextEx(&Cursor, m_pClient->m_aClients[pInfo->m_ClientID].m_aClan, -1);
 
 		// country flag
-		Graphics()->TextureSet(m_pClient->m_pCountryFlags->Get(m_pClient->m_aClients[pInfo->m_ClientID].m_Country)->m_Texture);
+		Graphics()->TextureSet(m_pClient->m_pCountryFlags->GetByCountryCode(m_pClient->m_aClients[pInfo->m_ClientID].m_Country)->m_Texture);
 		Graphics()->QuadsBegin();
 		Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.5f);
 		IGraphics::CQuadItem QuadItem(CountryOffset, y+(Spacing+TeeSizeMod*5.0f)/2.0f, CountryLength, LineHeight-Spacing-TeeSizeMod*5.0f);
diff --git a/src/game/client/components/sounds.cpp b/src/game/client/components/sounds.cpp
index ffafa128..c4ade00e 100644
--- a/src/game/client/components/sounds.cpp
+++ b/src/game/client/components/sounds.cpp
@@ -65,8 +65,17 @@ void CSounds::OnInit()
 
 void CSounds::OnReset()
 {
-	Sound()->StopAll();
-	ClearQueue();
+	if(Client()->State() >= IClient::STATE_ONLINE)
+	{
+		Sound()->StopAll();
+		ClearQueue();
+	}
+}
+
+void CSounds::OnStateChange(int NewState, int OldState)
+{
+	if(NewState == IClient::STATE_ONLINE || NewState == IClient::STATE_DEMOPLAYBACK)
+		OnReset();
 }
 
 void CSounds::OnRender()
@@ -128,10 +137,10 @@ void CSounds::PlayAndRecord(int Chn, int SetId, float Vol, vec2 Pos)
 
 void CSounds::Play(int Chn, int SetId, float Vol, vec2 Pos)
 {
-	if(!g_Config.m_SndEnable || (Chn == CHN_MUSIC && !g_Config.m_SndMusic) || m_WaitForSoundJob || SetId < 0 || SetId >= g_pData->m_NumSounds)
+	if(!g_Config.m_SndEnable || !Sound()->IsSoundEnabled() || (Chn == CHN_MUSIC && !g_Config.m_SndMusic) || m_WaitForSoundJob || SetId < 0 || SetId >= g_pData->m_NumSounds)
 		return;
 
-	SOUNDSET *pSet = &g_pData->m_aSounds[SetId];
+	CDataSoundset *pSet = &g_pData->m_aSounds[SetId];
 
 	if(!pSet->m_NumSounds)
 		return;
@@ -162,7 +171,7 @@ void CSounds::Stop(int SetId)
 	if(m_WaitForSoundJob || SetId < 0 || SetId >= g_pData->m_NumSounds)
 		return;
 	
-	SOUNDSET *pSet = &g_pData->m_aSounds[SetId];
+	CDataSoundset *pSet = &g_pData->m_aSounds[SetId];
 	
 	for(int i = 0; i < pSet->m_NumSounds; i++)
 		Sound()->Stop(pSet->m_aSounds[i].m_Id);
diff --git a/src/game/client/components/sounds.h b/src/game/client/components/sounds.h
index 2670f793..ab9cc8e6 100644
--- a/src/game/client/components/sounds.h
+++ b/src/game/client/components/sounds.h
@@ -32,6 +32,7 @@ public:
 
 	virtual void OnInit();
 	virtual void OnReset();
+	virtual void OnStateChange(int NewState, int OldState);
 	virtual void OnRender();
 
 	void ClearQueue();
diff --git a/src/game/client/components/spectator.cpp b/src/game/client/components/spectator.cpp
index 41c7b48f..c09b2ee2 100644
--- a/src/game/client/components/spectator.cpp
+++ b/src/game/client/components/spectator.cpp
@@ -64,8 +64,8 @@ void CSpectator::ConSpectateNext(IConsole::IResult *pResult, void *pUserData)
 				if(!pSelf->m_pClient->m_Snap.m_paPlayerInfos[i] || pSelf->m_pClient->m_Snap.m_paPlayerInfos[i]->m_Team == TEAM_SPECTATORS)
 					continue;
 
-			NewSpectatorID = i;
-			GotNewSpectatorID = true;
+				NewSpectatorID = i;
+				GotNewSpectatorID = true;
 				break;
 			}
 		}
@@ -139,6 +139,7 @@ bool CSpectator::OnMouseMove(float x, float y)
 	if(!m_Active)
 		return false;
 
+	UI()->ConvertMouseMove(&x, &y);
 	m_SelectorMouse += vec2(x,y);
 	return true;
 }
@@ -161,6 +162,13 @@ void CSpectator::OnRender()
 		return;
 	}
 
+	if(!m_pClient->m_Snap.m_SpecInfo.m_Active)
+	{
+		m_Active = false;
+		m_WasActive = false;
+		return;
+	}
+
 	m_WasActive = true;
 	m_SelectedSpectatorID = NO_SELECTION;
 
@@ -236,6 +244,23 @@ void CSpectator::OnRender()
 		TextRender()->TextColor(1.0f, 1.0f, 1.0f, Selected?1.0f:0.5f);
 		TextRender()->Text(0, Width/2.0f+x+50.0f, Height/2.0f+y+5.0f, FontSize, m_pClient->m_aClients[i].m_aName, 220.0f);
 
+		// flag
+		if(m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_FLAGS &&
+			m_pClient->m_Snap.m_pGameDataObj && (m_pClient->m_Snap.m_pGameDataObj->m_FlagCarrierRed == m_pClient->m_Snap.m_paPlayerInfos[i]->m_ClientID ||
+			m_pClient->m_Snap.m_pGameDataObj->m_FlagCarrierBlue == m_pClient->m_Snap.m_paPlayerInfos[i]->m_ClientID))
+		{
+			Graphics()->BlendNormal();
+			Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id);
+			Graphics()->QuadsBegin();
+
+			RenderTools()->SelectSprite(m_pClient->m_Snap.m_paPlayerInfos[i]->m_Team==TEAM_RED ? SPRITE_FLAG_BLUE : SPRITE_FLAG_RED, SPRITE_FLAG_FLIP_X);
+
+			float Size = LineHeight;
+			IGraphics::CQuadItem QuadItem(Width/2.0f+x-LineHeight/5.0f, Height/2.0f+y-LineHeight/3.0f, Size/2.0f, Size);
+			Graphics()->QuadsDrawTL(&QuadItem, 1);
+			Graphics()->QuadsEnd();
+		}
+
 		CTeeRenderInfo TeeInfo = m_pClient->m_aClients[i].m_RenderInfo;
 		RenderTools()->RenderTee(CAnimState::GetIdle(), &TeeInfo, EMOTE_NORMAL, vec2(1.0f, 0.0f), vec2(Width/2.0f+x+20.0f, Height/2.0f+y+20.0f));
 
diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp
index 511cf894..7b6b1192 100644
--- a/src/game/client/gameclient.cpp
+++ b/src/game/client/gameclient.cpp
@@ -90,17 +90,6 @@ void CGameClient::CStack::Add(class CComponent *pComponent) { m_paComponents[m_N
 const char *CGameClient::Version() { return GAME_VERSION; }
 const char *CGameClient::NetVersion() { return GAME_NETVERSION; }
 const char *CGameClient::GetItemName(int Type) { return m_NetObjHandler.GetObjName(Type); }
-int CGameClient::GetCountryIndex(int Code)
-{
-	int Index = g_GameClient.m_pCountryFlags->Find(Code);
-	if(Index < 0)
-	{
-		Index = g_GameClient.m_pCountryFlags->Find(-1);
-		if(Index < 0)
-			Index = 0;
-	}
-	return Index;
-}
 
 void CGameClient::OnConsoleInit()
 {
@@ -196,7 +185,7 @@ void CGameClient::OnConsoleInit()
 	Console()->Register("restart", "?i", CFGFLAG_SERVER, 0, 0, "Restart in x seconds");
 	Console()->Register("broadcast", "r", CFGFLAG_SERVER, 0, 0, "Broadcast message");
 	Console()->Register("say", "r", CFGFLAG_SERVER, 0, 0, "Say in chat");
-	Console()->Register("set_team", "ii", CFGFLAG_SERVER, 0, 0, "Set team of player to team");
+	Console()->Register("set_team", "ii?i", CFGFLAG_SERVER, 0, 0, "Set team of player to team");
 	Console()->Register("set_team_all", "i", CFGFLAG_SERVER, 0, 0, "Set team of all players to team");
 	Console()->Register("add_vote", "sr", CFGFLAG_SERVER, 0, 0, "Add a voting option");
 	Console()->Register("remove_vote", "s", CFGFLAG_SERVER, 0, 0, "remove a voting option");
@@ -352,6 +341,9 @@ void CGameClient::OnReset()
 		m_All.m_paComponents[i]->OnReset();
 
 	m_DemoSpecID = SPEC_FREEVIEW;
+	m_FlagDropTick[TEAM_RED] = 0;
+	m_FlagDropTick[TEAM_BLUE] = 0;
+	m_Tuning = CTuningParams();
 }
 
 
@@ -602,32 +594,32 @@ void CGameClient::ProcessEvents()
 
 		if(Item.m_Type == NETEVENTTYPE_DAMAGEIND)
 		{
-			NETEVENT_DAMAGEIND *ev = (NETEVENT_DAMAGEIND *)pData;
+			CNetEvent_DamageInd *ev = (CNetEvent_DamageInd *)pData;
 			g_GameClient.m_pEffects->DamageIndicator(vec2(ev->m_X, ev->m_Y), GetDirection(ev->m_Angle));
 		}
 		else if(Item.m_Type == NETEVENTTYPE_EXPLOSION)
 		{
-			NETEVENT_EXPLOSION *ev = (NETEVENT_EXPLOSION *)pData;
+			CNetEvent_Explosion *ev = (CNetEvent_Explosion *)pData;
 			g_GameClient.m_pEffects->Explosion(vec2(ev->m_X, ev->m_Y));
 		}
 		else if(Item.m_Type == NETEVENTTYPE_HAMMERHIT)
 		{
-			NETEVENT_HAMMERHIT *ev = (NETEVENT_HAMMERHIT *)pData;
+			CNetEvent_HammerHit *ev = (CNetEvent_HammerHit *)pData;
 			g_GameClient.m_pEffects->HammerHit(vec2(ev->m_X, ev->m_Y));
 		}
 		else if(Item.m_Type == NETEVENTTYPE_SPAWN)
 		{
-			NETEVENT_SPAWN *ev = (NETEVENT_SPAWN *)pData;
+			CNetEvent_Spawn *ev = (CNetEvent_Spawn *)pData;
 			g_GameClient.m_pEffects->PlayerSpawn(vec2(ev->m_X, ev->m_Y));
 		}
 		else if(Item.m_Type == NETEVENTTYPE_DEATH)
 		{
-			NETEVENT_DEATH *ev = (NETEVENT_DEATH *)pData;
+			CNetEvent_Death *ev = (CNetEvent_Death *)pData;
 			g_GameClient.m_pEffects->PlayerDeath(vec2(ev->m_X, ev->m_Y), ev->m_ClientID);
 		}
 		else if(Item.m_Type == NETEVENTTYPE_SOUNDWORLD)
 		{
-			NETEVENT_SOUNDWORLD *ev = (NETEVENT_SOUNDWORLD *)pData;
+			CNetEvent_SoundWorld *ev = (CNetEvent_SoundWorld *)pData;
 			g_GameClient.m_pSounds->Play(CSounds::CHN_WORLD, ev->m_SoundID, 1.0f, vec2(ev->m_X, ev->m_Y));
 		}
 	}
@@ -696,7 +688,7 @@ void CGameClient::OnNewSnapshot()
 				int ClientID = Item.m_ID;
 				IntsToStr(&pInfo->m_Name0, 4, m_aClients[ClientID].m_aName);
 				IntsToStr(&pInfo->m_Clan0, 3, m_aClients[ClientID].m_aClan);
-				m_aClients[ClientID].m_Country = GetCountryIndex(pInfo->m_Country);
+				m_aClients[ClientID].m_Country = pInfo->m_Country;
 				IntsToStr(&pInfo->m_Skin0, 6, m_aClients[ClientID].m_aSkinName);
 
 				m_aClients[ClientID].m_UseCustomColor = pInfo->m_UseCustomColor;
@@ -794,6 +786,20 @@ void CGameClient::OnNewSnapshot()
 			{
 				m_Snap.m_pGameDataObj = (const CNetObj_GameData *)pData;
 				m_Snap.m_GameDataSnapID = Item.m_ID;
+				if(m_Snap.m_pGameDataObj->m_FlagCarrierRed == FLAG_TAKEN)
+				{
+					if(m_FlagDropTick[TEAM_RED] == 0)
+						m_FlagDropTick[TEAM_RED] = Client()->GameTick();
+				}
+				else if(m_FlagDropTick[TEAM_RED] != 0)
+						m_FlagDropTick[TEAM_RED] = 0;
+				if(m_Snap.m_pGameDataObj->m_FlagCarrierBlue == FLAG_TAKEN)
+				{
+					if(m_FlagDropTick[TEAM_BLUE] == 0)
+						m_FlagDropTick[TEAM_BLUE] = Client()->GameTick();
+				}
+				else if(m_FlagDropTick[TEAM_BLUE] != 0)
+						m_FlagDropTick[TEAM_BLUE] = 0;
 			}
 			else if(Item.m_Type == NETOBJTYPE_FLAG)
 				m_Snap.m_paFlags[Item.m_ID%2] = (const CNetObj_Flag *)pData;
diff --git a/src/game/client/gameclient.h b/src/game/client/gameclient.h
index a89f4e86..4783f8b4 100644
--- a/src/game/client/gameclient.h
+++ b/src/game/client/gameclient.h
@@ -89,6 +89,7 @@ public:
 	bool m_SuppressEvents;
 	bool m_NewTick;
 	bool m_NewPredictedTick;
+	int m_FlagDropTick[2];
 
 	// TODO: move this
 	CTuningParams m_Tuning;
@@ -211,7 +212,6 @@ public:
 	virtual void OnStartGame();
 
 	virtual const char *GetItemName(int Type);
-	virtual int GetCountryIndex(int Code);
 	virtual const char *Version();
 	virtual const char *NetVersion();
 
diff --git a/src/game/client/lineinput.cpp b/src/game/client/lineinput.cpp
index 29b891c2..2de85d66 100644
--- a/src/game/client/lineinput.cpp
+++ b/src/game/client/lineinput.cpp
@@ -42,7 +42,7 @@ bool CLineInput::Manipulate(IInput::CEvent e, char *pStr, int StrMaxSize, int *p
 
 		if (Len < StrMaxSize - CharSize && CursorPos < StrMaxSize - CharSize)
 		{
-			mem_move(pStr + CursorPos + CharSize, pStr + CursorPos, Len - CursorPos + CharSize);
+			mem_move(pStr + CursorPos + CharSize, pStr + CursorPos, Len-CursorPos+1); // +1 == null term
 			for(int i = 0; i < CharSize; i++)
 				pStr[CursorPos+i] = Tmp[i];
 			CursorPos += CharSize;
diff --git a/src/game/client/render.cpp b/src/game/client/render.cpp
index 5dbc3842..93909d0c 100644
--- a/src/game/client/render.cpp
+++ b/src/game/client/render.cpp
@@ -37,7 +37,7 @@ static void layershot_end()
 	config.cl_layershot++;
 }*/
 
-void CRenderTools::SelectSprite(SPRITE *pSpr, int Flags, int sx, int sy)
+void CRenderTools::SelectSprite(CDataSprite *pSpr, int Flags, int sx, int sy)
 {
 	int x = pSpr->m_X+sx;
 	int y = pSpr->m_Y+sy;
@@ -232,7 +232,7 @@ void CRenderTools::RenderTee(CAnimState *pAnim, CTeeRenderInfo *pInfo, int Emote
 			}
 
 			// draw feet
-			ANIM_KEYFRAME *pFoot = f ? pAnim->GetFrontFoot() : pAnim->GetBackFoot();
+			CAnimKeyframe *pFoot = f ? pAnim->GetFrontFoot() : pAnim->GetBackFoot();
 
 			float w = BaseSize;
 			float h = BaseSize/2;
diff --git a/src/game/client/render.h b/src/game/client/render.h
index 10705e56..d3d7fc40 100644
--- a/src/game/client/render.h
+++ b/src/game/client/render.h
@@ -39,6 +39,7 @@ enum
 	TILERENDERFLAG_EXTEND=4,
 };
 
+typedef void (*ENVELOPE_EVAL)(float TimeOffset, int Env, float *pChannels, void *pUser);
 
 class CRenderTools
 {
@@ -51,7 +52,7 @@ public:
 
 	//typedef struct SPRITE;
 
-	void SelectSprite(struct SPRITE *pSprite, int Flags=0, int sx=0, int sy=0);
+	void SelectSprite(struct CDataSprite *pSprite, int Flags=0, int sx=0, int sy=0);
 	void SelectSprite(int id, int Flags=0, int sx=0, int sy=0);
 
 	void DrawSprite(float x, float y, float size);
@@ -70,8 +71,8 @@ public:
 
 	// map render methods (gc_render_map.cpp)
 	static void RenderEvalEnvelope(CEnvPoint *pPoints, int NumPoints, int Channels, float Time, float *pResult);
-	void RenderQuads(CQuad *pQuads, int NumQuads, int Flags, void (*pfnEval)(float TimeOffset, int Env, float *pChannels, void *pUser), void *pUser);
-	void RenderTilemap(CTile *pTiles, int w, int h, float Scale, vec4 Color, int Flags);
+	void RenderQuads(CQuad *pQuads, int NumQuads, int Flags, ENVELOPE_EVAL pfnEval, void *pUser);
+	void RenderTilemap(CTile *pTiles, int w, int h, float Scale, vec4 Color, int RenderFlags, ENVELOPE_EVAL pfnEval, void *pUser, int ColorEnv, int ColorEnvOffset);
 
 	// helpers
 	void MapscreenToWorld(float CenterX, float CenterY, float ParallaxX, float ParallaxY,
diff --git a/src/game/client/render_map.cpp b/src/game/client/render_map.cpp
index 33cc1c7d..23fa42e0 100644
--- a/src/game/client/render_map.cpp
+++ b/src/game/client/render_map.cpp
@@ -78,7 +78,7 @@ static void Rotate(CPoint *pCenter, CPoint *pPoint, float Rotation)
 	pPoint->y = (int)(x * sinf(Rotation) + y * cosf(Rotation) + pCenter->y);
 }
 
-void CRenderTools::RenderQuads(CQuad *pQuads, int NumQuads, int RenderFlags, void (*pfnEval)(float TimeOffset, int Env, float *pChannels, void *pUser), void *pUser)
+void CRenderTools::RenderQuads(CQuad *pQuads, int NumQuads, int RenderFlags, ENVELOPE_EVAL pfnEval, void *pUser)
 {
 	Graphics()->QuadsBegin();
 	float Conv = 1/255.0f;
@@ -162,7 +162,8 @@ void CRenderTools::RenderQuads(CQuad *pQuads, int NumQuads, int RenderFlags, voi
 	Graphics()->QuadsEnd();
 }
 
-void CRenderTools::RenderTilemap(CTile *pTiles, int w, int h, float Scale, vec4 Color, int RenderFlags)
+void CRenderTools::RenderTilemap(CTile *pTiles, int w, int h, float Scale, vec4 Color, int RenderFlags,
+									ENVELOPE_EVAL pfnEval, void *pUser, int ColorEnv, int ColorEnvOffset)
 {
 	//Graphics()->TextureSet(img_get(tmap->image));
 	float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
@@ -174,8 +175,19 @@ void CRenderTools::RenderTilemap(CTile *pTiles, int w, int h, float Scale, vec4
 	float FinalTileSize = Scale/(ScreenX1-ScreenX0) * Graphics()->ScreenWidth();
 	float FinalTilesetScale = FinalTileSize/TilePixelSize;
 
+	float r=1, g=1, b=1, a=1;
+	if(ColorEnv >= 0)
+	{
+		float aChannels[4];
+		pfnEval(ColorEnvOffset/1000.0f, ColorEnv, aChannels, pUser);
+		r = aChannels[0];
+		g = aChannels[1];
+		b = aChannels[2];
+		a = aChannels[3];
+	}
+
 	Graphics()->QuadsBegin();
-	Graphics()->SetColor(Color.r, Color.g, Color.b, Color.a);
+	Graphics()->SetColor(Color.r*r, Color.g*g, Color.b*b, Color.a*a);
 
 	int StartY = (int)(ScreenY0/Scale)-1;
 	int StartX = (int)(ScreenX0/Scale)-1;
diff --git a/src/game/client/ui.cpp b/src/game/client/ui.cpp
index 2161bc77..00a30c15 100644
--- a/src/game/client/ui.cpp
+++ b/src/game/client/ui.cpp
@@ -53,6 +53,13 @@ int CUI::MouseInside(const CUIRect *r)
 	return 0;
 }
 
+void CUI::ConvertMouseMove(float *x, float *y)
+{
+	float Fac = (float)(g_Config.m_UiMousesens)/g_Config.m_InpMousesens;
+	*x = *x*Fac;
+	*y = *y*Fac;
+}
+
 CUIRect *CUI::Screen()
 {
 	float Aspect = Graphics()->ScreenAspect();
diff --git a/src/game/client/ui.h b/src/game/client/ui.h
index 017abf7c..7cd78d6f 100644
--- a/src/game/client/ui.h
+++ b/src/game/client/ui.h
@@ -79,6 +79,7 @@ public:
 	const void *LastActiveItem() const { return m_pLastActiveItem; }
 
 	int MouseInside(const CUIRect *pRect);
+	void ConvertMouseMove(float *x, float *y);
 
 	CUIRect *Screen();
 	void ClipEnable(const CUIRect *pRect);
diff --git a/src/game/editor/auto_map.cpp b/src/game/editor/auto_map.cpp
new file mode 100644
index 00000000..528e459b
--- /dev/null
+++ b/src/game/editor/auto_map.cpp
@@ -0,0 +1,202 @@
+#include <stdio.h>	// sscanf
+
+#include <engine/console.h>
+#include <engine/storage.h>
+#include <engine/shared/linereader.h>
+
+#include "auto_map.h"
+#include "editor.h"
+
+CAutoMapper::CAutoMapper(CEditor *pEditor)
+{
+	m_pEditor = pEditor;
+	m_FileLoaded = false;
+}
+
+void CAutoMapper::Load(const char* pTileName)
+{
+	char aPath[256];
+	str_format(aPath, sizeof(aPath), "editor/%s.rules", pTileName);
+	IOHANDLE RulesFile = m_pEditor->Storage()->OpenFile(aPath, IOFLAG_READ, IStorage::TYPE_ALL);
+	if(!RulesFile)
+		return;
+	
+	CLineReader LineReader;
+	LineReader.Init(RulesFile);
+		
+	CConfiguration *pCurrentConf = 0;
+	CIndexRule *pCurrentIndex = 0;
+		
+	char aBuf[256];
+		
+	// read each line
+	while(char *pLine = LineReader.Get())
+	{
+		// skip blank/empty lines as well as comments
+		if(str_length(pLine) > 0 && pLine[0] != '#' && pLine[0] != '\n' && pLine[0] != '\r'
+			&& pLine[0] != '\t' && pLine[0] != '\v' && pLine[0] != ' ')
+		{
+			if(pLine[0]== '[')
+			{
+				// new configuration, get the name
+				pLine++;
+					
+				CConfiguration NewConf;
+				int ID = m_lConfigs.add(NewConf);
+				pCurrentConf = &m_lConfigs[ID];
+					
+				str_copy(pCurrentConf->m_aName, pLine, str_length(pLine));
+			}
+			else
+			{
+				if(!str_comp_num(pLine, "Index", 5))
+				{
+					// new index
+					int ID = 0;
+					char aFlip[128] = "";
+						
+					sscanf(pLine, "Index %d %127s", &ID, aFlip);
+						
+					CIndexRule NewIndexRule;
+					NewIndexRule.m_ID = ID;
+					NewIndexRule.m_Flag = 0;
+					NewIndexRule.m_RandomValue = 0;
+					NewIndexRule.m_BaseTile = false;
+						
+					if(str_length(aFlip) > 0)
+					{
+						if(!str_comp(aFlip, "XFLIP"))
+							NewIndexRule.m_Flag = TILEFLAG_VFLIP;
+						else if(!str_comp(aFlip, "YFLIP"))
+							NewIndexRule.m_Flag = TILEFLAG_HFLIP;
+					}
+						
+					// add the index rule object and make it current
+					int ArrayID = pCurrentConf->m_aIndexRules.add(NewIndexRule);
+					pCurrentIndex = &pCurrentConf->m_aIndexRules[ArrayID];
+				}
+				else if(!str_comp_num(pLine, "BaseTile", 8) && pCurrentIndex)
+				{
+					pCurrentIndex->m_BaseTile = true;
+				}
+				else if(!str_comp_num(pLine, "Pos", 3) && pCurrentIndex)
+				{
+					int x = 0, y = 0;
+					char aValue[128];
+					int Value = CPosRule::EMPTY;
+					bool IndexValue = false;
+						
+					sscanf(pLine, "Pos %d %d %127s", &x, &y, aValue);
+						
+					if(!str_comp(aValue, "FULL"))
+						Value = CPosRule::FULL;
+					else if(!str_comp_num(aValue, "INDEX", 5))
+					{
+						sscanf(pLine, "Pos %*d %*d INDEX %d", &Value);
+						IndexValue = true;
+					}
+
+					CPosRule NewPosRule = {x, y, Value, IndexValue};
+					pCurrentIndex->m_aRules.add(NewPosRule);
+				}
+				else if(!str_comp_num(pLine, "Random", 6) && pCurrentIndex)
+				{
+					sscanf(pLine, "Random %d", &pCurrentIndex->m_RandomValue);
+				}
+			}
+		}
+	}
+		
+	io_close(RulesFile);
+		
+	str_format(aBuf, sizeof(aBuf),"loaded %s", aPath);
+	m_pEditor->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "editor", aBuf);
+		
+	m_FileLoaded = true;
+}
+
+const char* CAutoMapper::GetConfigName(int Index)
+{
+	if(Index < 0 || Index >= m_lConfigs.size())
+		return "";
+
+	return m_lConfigs[Index].m_aName;
+}
+
+void CAutoMapper::Proceed(CLayerTiles *pLayer, int ConfigID)
+{
+	if(!m_FileLoaded || pLayer->m_Readonly || ConfigID < 0 || ConfigID >= m_lConfigs.size())
+		return;
+	
+	CConfiguration *pConf = &m_lConfigs[ConfigID];
+	
+	if(!pConf->m_aIndexRules.size())
+		return;
+	
+	int BaseTile = 1;
+	
+	// find base tile if there is one
+	for(int i = 0; i < pConf->m_aIndexRules.size(); ++i)
+	{
+		if(pConf->m_aIndexRules[i].m_BaseTile)
+		{
+			BaseTile = pConf->m_aIndexRules[i].m_ID;
+			break;
+		}
+	}
+	
+	// auto map !
+	int MaxIndex = pLayer->m_Width*pLayer->m_Height;
+	for(int y = 0; y < pLayer->m_Height; y++)
+		for(int x = 0; x < pLayer->m_Width; x++)
+		{
+			CTile *pTile = &(pLayer->m_pTiles[y*pLayer->m_Width+x]);
+			if(pTile->m_Index == 0)
+				continue;
+
+			pTile->m_Index = BaseTile;
+			m_pEditor->m_Map.m_Modified = true;
+
+			if(y == 0 || y == pLayer->m_Height-1 || x == 0 || x == pLayer->m_Width-1)
+				continue;
+				
+			for(int i = 0; i < pConf->m_aIndexRules.size(); ++i)
+			{
+				if(pConf->m_aIndexRules[i].m_BaseTile)
+					continue;
+
+				bool RespectRules = true;
+				for(int j = 0; j < pConf->m_aIndexRules[i].m_aRules.size() && RespectRules; ++j)
+				{
+					CPosRule *pRule = &pConf->m_aIndexRules[i].m_aRules[j];
+					int CheckIndex = (y+pRule->m_Y)*pLayer->m_Width+(x+pRule->m_X);
+							
+					if(CheckIndex < 0 || CheckIndex >= MaxIndex)
+						RespectRules = false;
+					else
+					{
+ 						if(pRule->m_IndexValue)
+						{
+							if(pLayer->m_pTiles[CheckIndex].m_Index != pRule->m_Value)
+								RespectRules = false;
+						}
+						else
+						{
+							if(pLayer->m_pTiles[CheckIndex].m_Index > 0 && pRule->m_Value == CPosRule::EMPTY)
+								RespectRules = false;
+									
+							if(pLayer->m_pTiles[CheckIndex].m_Index == 0 && pRule->m_Value == CPosRule::FULL)
+								RespectRules = false;
+						}
+					}
+				}
+					
+				if(RespectRules &&
+					(pConf->m_aIndexRules[i].m_RandomValue <= 1 || (int)((float)rand() / ((float)RAND_MAX + 1) * pConf->m_aIndexRules[i].m_RandomValue)  == 1))
+				{
+					pTile->m_Index = pConf->m_aIndexRules[i].m_ID;
+					pTile->m_Flags = pConf->m_aIndexRules[i].m_Flag;
+				}
+			}
+		}
+}
diff --git a/src/game/editor/auto_map.h b/src/game/editor/auto_map.h
new file mode 100644
index 00000000..ee570378
--- /dev/null
+++ b/src/game/editor/auto_map.h
@@ -0,0 +1,54 @@
+#ifndef GAME_EDITOR_AUTO_MAP_H
+#define GAME_EDITOR_AUTO_MAP_H
+
+#include <base/tl/array.h>
+
+class CAutoMapper
+{
+	struct CPosRule
+	{
+		int m_X;
+		int m_Y;
+		int m_Value;
+		bool m_IndexValue;
+		
+		enum
+		{
+			EMPTY=0,
+			FULL
+		};
+	};
+
+	struct CIndexRule
+	{
+		int m_ID;
+		array<CPosRule> m_aRules;
+		int m_Flag;
+		int m_RandomValue;
+		bool m_BaseTile;
+	};
+
+	struct CConfiguration
+	{
+		array<CIndexRule> m_aIndexRules;
+		char m_aName[128];
+	};
+	
+public:
+	CAutoMapper(class CEditor *pEditor);
+	
+	void Load(const char* pTileName);
+	void Proceed(class CLayerTiles *pLayer, int ConfigID);
+
+	int ConfigNamesNum() { return m_lConfigs.size(); }
+	const char* GetConfigName(int Index);
+	
+	const bool IsLoaded() { return m_FileLoaded; }
+private:
+	array<CConfiguration> m_lConfigs;
+	class CEditor *m_pEditor;
+	bool m_FileLoaded;
+};
+
+
+#endif
diff --git a/src/game/editor/ed_editor.cpp b/src/game/editor/editor.cpp
index 1163dc4a..500e600b 100644
--- a/src/game/editor/ed_editor.cpp
+++ b/src/game/editor/editor.cpp
@@ -8,20 +8,20 @@
 #include <engine/client.h>
 #include <engine/console.h>
 #include <engine/graphics.h>
-#include <engine/textrender.h>
 #include <engine/input.h>
 #include <engine/keys.h>
 #include <engine/storage.h>
+#include <engine/textrender.h>
 
-#include <game/client/ui.h>
 #include <game/gamecore.h>
+#include <game/localization.h>
+#include <game/client/lineinput.h>
 #include <game/client/render.h>
+#include <game/client/ui.h>
 #include <game/generated/client_data.h>
 
-#include "ed_editor.h"
-#include <game/client/lineinput.h>
-
-#include <game/localization.h>
+#include "auto_map.h"
+#include "editor.h"
 
 int CEditor::ms_CheckerTexture;
 int CEditor::ms_BackgroundTexture;
@@ -41,9 +41,10 @@ CEditorImage::~CEditorImage()
 
 CLayerGroup::CLayerGroup()
 {
-	m_pName = "";
+	m_aName[0] = 0;
 	m_Visible = true;
 	m_SaveToMap = true;
+	m_Collapse = false;
 	m_GameGroup = false;
 	m_OffsetX = 0;
 	m_OffsetY = 0;
@@ -189,6 +190,24 @@ void CEditorImage::AnalyseTileFlags()
 
 }
 
+void CEditor::EnvelopeEval(float TimeOffset, int Env, float *pChannels, void *pUser)
+{
+	CEditor *pThis = (CEditor *)pUser;
+	if(Env < 0 || Env >= pThis->m_Map.m_lEnvelopes.size())
+	{
+		pChannels[0] = 0;
+		pChannels[1] = 0;
+		pChannels[2] = 0;
+		pChannels[3] = 0;
+		return;
+	}
+
+	CEnvelope *e = pThis->m_Map.m_lEnvelopes[Env];
+	float t = pThis->m_AnimateTime+TimeOffset;
+	t *= pThis->m_AnimateSpeed;
+	e->Eval(t, pChannels);
+}
+
 /********************************************************
  OTHER
 *********************************************************/
@@ -196,32 +215,59 @@ void CEditorImage::AnalyseTileFlags()
 // copied from gc_menu.cpp, should be more generalized
 //extern int ui_do_edit_box(void *id, const CUIRect *rect, char *str, int str_size, float font_size, bool hidden=false);
 
-int CEditor::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, bool Hidden)
+int CEditor::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, float *Offset, bool Hidden, int Corners)
 {
-    int Inside = UI()->MouseInside(pRect);
+	int Inside = UI()->MouseInside(pRect);
 	bool ReturnValue = false;
+	bool UpdateOffset = false;
 	static int s_AtIndex = 0;
+	static bool s_DoScroll = false;
+	static float s_ScrollStart = 0.0f;
+
+	FontSize *= UI()->Scale();
 
 	if(UI()->LastActiveItem() == pID)
 	{
 		int Len = str_length(pStr);
+		if(Len == 0)
+			s_AtIndex = 0;
 
 		if(Inside && UI()->MouseButton(0))
 		{
+			s_DoScroll = true;
+			s_ScrollStart = UI()->MouseX();
 			int MxRel = (int)(UI()->MouseX() - pRect->x);
 
-			for (int i = 1; i <= Len; i++)
+			for(int i = 1; i <= Len; i++)
 			{
-				if (TextRender()->TextWidth(0, FontSize, pStr, i) + 10 > MxRel)
+				if(TextRender()->TextWidth(0, FontSize, pStr, i) - *Offset > MxRel)
 				{
 					s_AtIndex = i - 1;
 					break;
 				}
 
-				if (i == Len)
+				if(i == Len)
 					s_AtIndex = Len;
 			}
 		}
+		else if(!UI()->MouseButton(0))
+			s_DoScroll = false;
+		else if(s_DoScroll)
+		{
+			// do scrolling
+			if(UI()->MouseX() < pRect->x && s_ScrollStart-UI()->MouseX() > 10.0f)
+			{
+				s_AtIndex = max(0, s_AtIndex-1);
+				s_ScrollStart = UI()->MouseX();
+				UpdateOffset = true;
+			}
+			else if(UI()->MouseX() > pRect->x+pRect->w && UI()->MouseX()-s_ScrollStart > 10.0f)
+			{
+				s_AtIndex = min(Len, s_AtIndex+1);
+				s_ScrollStart = UI()->MouseX();
+				UpdateOffset = true;
+			}
+		}
 
 		for(int i = 0; i < Input()->NumEvents(); i++)
 		{
@@ -235,7 +281,11 @@ int CEditor::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned Str
 	if(UI()->ActiveItem() == pID)
 	{
 		if(!UI()->MouseButton(0))
+		{
+			s_AtIndex = min(s_AtIndex, str_length(pStr));
+			s_DoScroll = false;
 			UI()->SetActiveItem(0);
+		}
 	}
 	else if(UI()->HotItem() == pID)
 	{
@@ -251,8 +301,8 @@ int CEditor::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned Str
 		UI()->SetHotItem(pID);
 
 	CUIRect Textbox = *pRect;
-	RenderTools()->DrawUIRect(&Textbox, vec4(1,1,1,0.5f), CUI::CORNER_ALL, 3.0f);
-	Textbox.VMargin(3.0f, &Textbox);
+	RenderTools()->DrawUIRect(&Textbox, vec4(1, 1, 1, 0.5f), Corners, 3.0f);
+	Textbox.VMargin(2.0f, &Textbox);
 
 	const char *pDisplayStr = pStr;
 	char aStars[128];
@@ -268,19 +318,47 @@ int CEditor::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned Str
 		pDisplayStr = aStars;
 	}
 
+	// check if the text has to be moved
+	if(UI()->LastActiveItem() == pID && !JustGotActive && (UpdateOffset || Input()->NumEvents()))
+	{
+		float w = TextRender()->TextWidth(0, FontSize, pDisplayStr, s_AtIndex);
+		if(w-*Offset > Textbox.w)
+		{
+			// move to the left
+			float wt = TextRender()->TextWidth(0, FontSize, pDisplayStr, -1);
+			do
+			{
+				*Offset += min(wt-*Offset-Textbox.w, Textbox.w/3);
+			}
+			while(w-*Offset > Textbox.w);
+		}
+		else if(w-*Offset < 0.0f)
+		{
+			// move to the right
+			do
+			{
+				*Offset = max(0.0f, *Offset-Textbox.w/3);
+			}
+			while(w-*Offset < 0.0f);
+		}
+	}
+	UI()->ClipEnable(pRect);
+	Textbox.x -= *Offset;
+
 	UI()->DoLabel(&Textbox, pDisplayStr, FontSize, -1);
-	
-	//TODO: make it blink
+
+	// render the cursor
 	if(UI()->LastActiveItem() == pID && !JustGotActive)
 	{
 		float w = TextRender()->TextWidth(0, FontSize, pDisplayStr, s_AtIndex);
 		Textbox = *pRect;
 		Textbox.VSplitLeft(2.0f, 0, &Textbox);
-		Textbox.x += w*UI()->Scale();
-		Textbox.y -= FontSize/10.f;
-		
-		UI()->DoLabel(&Textbox, "|", FontSize*1.1f, -1);
+		Textbox.x += (w-*Offset-TextRender()->TextWidth(0, FontSize, "|", -1)/2);
+
+		if((2*time_get()/time_freq()) % 2)	// make it blink
+			UI()->DoLabel(&Textbox, "|", FontSize, -1);
 	}
+	UI()->ClipDisable();
 
 	return ReturnValue;
 }
@@ -443,12 +521,12 @@ int CEditor::DoButton_Tab(const void *pID, const char *pText, int Checked, const
 	return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
 }
 
-int CEditor::DoButton_Ex(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip, int Corners)
+int CEditor::DoButton_Ex(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip, int Corners, float FontSize)
 {
 	RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), Corners, 3.0f);
     CUIRect NewRect = *pRect;
-    NewRect.y += NewRect.h/2.0f-7.0f;
-    UI()->DoLabel(&NewRect, pText, 10, 0, -1);
+    NewRect.HMargin(NewRect.h/2.0f-FontSize/2.0f-1.0f, &NewRect);
+    UI()->DoLabel(&NewRect, pText, FontSize, 0, -1);
 	return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
 }
 
@@ -466,6 +544,49 @@ int CEditor::DoButton_ButtonDec(const void *pID, const char *pText, int Checked,
 	return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
 }
 
+void CEditor::RenderGrid(CLayerGroup *pGroup)
+{
+	if(!m_GridActive)
+		return;
+
+	float aGroupPoints[4];
+	pGroup->Mapping(aGroupPoints); 
+
+	float w = UI()->Screen()->w;
+	float h = UI()->Screen()->h;
+
+	int LineDistance = GetLineDistance();
+
+	int XOffset = aGroupPoints[0]/LineDistance;
+	int YOffset = aGroupPoints[1]/LineDistance;
+	int XGridOffset = XOffset % m_GridFactor;
+	int YGridOffset = YOffset % m_GridFactor;
+
+	Graphics()->TextureSet(-1);	
+	Graphics()->LinesBegin();
+
+	for(int i = 0; i < (int)w; i++)
+	{
+		if((i+YGridOffset) % m_GridFactor == 0)
+			Graphics()->SetColor(1.0f, 0.3f, 0.3f, 0.3f);
+		else
+			Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.15f);
+
+		IGraphics::CLineItem Line = IGraphics::CLineItem(LineDistance*XOffset, LineDistance*i+LineDistance*YOffset, w+aGroupPoints[2], LineDistance*i+LineDistance*YOffset);
+		Graphics()->LinesDraw(&Line, 1);
+
+		if((i+XGridOffset) % m_GridFactor == 0)
+			Graphics()->SetColor(1.0f, 0.3f, 0.3f, 0.3f);
+		else
+			Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.15f);
+
+		Line = IGraphics::CLineItem(LineDistance*i+LineDistance*XOffset, LineDistance*YOffset, LineDistance*i+LineDistance*XOffset, h+aGroupPoints[3]);
+		Graphics()->LinesDraw(&Line, 1);
+	}
+	Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
+	Graphics()->LinesEnd();
+}
+
 void CEditor::RenderBackground(CUIRect View, int Texture, float Size, float Brightness)
 {
 	Graphics()->TextureSet(Texture);
@@ -482,15 +603,12 @@ int CEditor::UiDoValueSelector(void *pID, CUIRect *pRect, const char *pLabel, in
 {
     // logic
     static float s_Value;
-    int Ret = 0;
     int Inside = UI()->MouseInside(pRect);
 
 	if(UI()->ActiveItem() == pID)
 	{
 		if(!UI()->MouseButton(0))
 		{
-			if(Inside)
-				Ret = 1;
 			m_LockMouse = false;
 			UI()->SetActiveItem(0);
 		}
@@ -741,14 +859,6 @@ void CEditor::DoToolbar(CUIRect ToolBar)
 			m_AnimateSpeed -= 0.5f;
 	}
 
-	if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP) && m_Dialog == DIALOG_NONE)
-		m_ZoomLevel -= 20;
-
-	if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN) && m_Dialog == DIALOG_NONE)
-		m_ZoomLevel += 20;
-
-	if(m_ZoomLevel < 50)
-		m_ZoomLevel = 50;
 	m_WorldZoom = m_ZoomLevel/100.0f;
 
 	TB_Top.VSplitLeft(10.0f, &Button, &TB_Top);
@@ -861,6 +971,41 @@ void CEditor::DoToolbar(CUIRect ToolBar)
 		m_WorldOffsetX = 0;
 		m_WorldOffsetY = 0;
 	}
+
+	TB_Bottom.VSplitLeft(5.0f, 0, &TB_Bottom);
+
+	// grid button
+	TB_Bottom.VSplitLeft(50.0f, &Button, &TB_Bottom);
+	static int s_GridButton = 0;
+	if(DoButton_Editor(&s_GridButton, "Grid", m_GridActive, &Button, 0, "Toggle Grid"))
+	{
+		m_GridActive = !m_GridActive;
+	}
+
+	TB_Bottom.VSplitLeft(30.0f, 0, &TB_Bottom);
+
+	// grid zoom
+	TB_Bottom.VSplitLeft(30.0f, &Button, &TB_Bottom);
+	static int s_GridIncreaseButton = 0;
+	if(DoButton_Ex(&s_GridIncreaseButton, "G-", 0, &Button, 0, "Decrease grid", CUI::CORNER_L))
+	{
+		if(m_GridFactor > 1)
+			m_GridFactor--;
+	}
+
+	TB_Bottom.VSplitLeft(30.0f, &Button, &TB_Bottom);
+	static int s_GridNormalButton = 0;
+	if(DoButton_Ex(&s_GridNormalButton, "1", 0, &Button, 0, "Normal grid", 0))
+		m_GridFactor = 1;
+
+	TB_Bottom.VSplitLeft(30.0f, &Button, &TB_Bottom);
+
+	static int s_GridDecreaseButton = 0;
+	if(DoButton_Ex(&s_GridDecreaseButton, "G+", 0, &Button, 0, "Increase grid", CUI::CORNER_R))
+	{
+		if(m_GridFactor < 15)
+			m_GridFactor++;
+	}
 }
 
 static void Rotate(CPoint *pCenter, CPoint *pPoint, float Rotation)
@@ -920,10 +1065,41 @@ void CEditor::DoQuad(CQuad *q, int Index)
 		else if(s_Operation == OP_MOVE_ALL)
 		{
 			// move all points including pivot
-			for(int v = 0; v < 5; v++)
+			if(m_GridActive)
 			{
-				q->m_aPoints[v].x += f2fx(wx-s_LastWx);
-				q->m_aPoints[v].y += f2fx(wy-s_LastWy);
+				int LineDistance = GetLineDistance();
+
+				float x = 0.0f;
+				float y = 0.0f;
+				if(wx >= 0)
+					x = (int)((wx+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
+				else
+					x = (int)((wx-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
+				if(wy >= 0)
+					y = (int)((wy+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
+				else
+					y = (int)((wy-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
+				
+				int OldX = q->m_aPoints[4].x;
+				int OldY = q->m_aPoints[4].y;
+				q->m_aPoints[4].x = f2fx(x);
+				q->m_aPoints[4].y = f2fx(y);
+				int DiffX = q->m_aPoints[4].x - OldX;
+				int DiffY = q->m_aPoints[4].y - OldY;
+				
+				for(int v = 0; v < 4; v++)
+				{
+					q->m_aPoints[v].x += DiffX;
+					q->m_aPoints[v].y += DiffY;
+				}
+			}
+			else
+			{
+				for(int v = 0; v < 5; v++)
+				{
+						q->m_aPoints[v].x += f2fx(wx-s_LastWx);
+						q->m_aPoints[v].y += f2fx(wy-s_LastWy);
+				}
 			}
 		}
 		else if(s_Operation == OP_ROTATE)
@@ -1054,12 +1230,37 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V)
 		{
 			if(s_Operation == OP_MOVEPOINT)
 			{
-				for(int m = 0; m < 4; m++)
-					if(m_SelectedPoints&(1<<m))
-					{
-						pQuad->m_aPoints[m].x += f2fx(dx);
-						pQuad->m_aPoints[m].y += f2fx(dy);
-					}
+				if(m_GridActive)
+				{
+					for(int m = 0; m < 4; m++)
+						if(m_SelectedPoints&(1<<m))
+						{
+							int LineDistance = GetLineDistance();
+
+							float x = 0.0f;
+							float y = 0.0f;
+							if(wx >= 0)
+								x = (int)((wx+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
+							else
+								x = (int)((wx-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
+							if(wy >= 0)
+								y = (int)((wy+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
+							else
+								y = (int)((wy-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
+
+							pQuad->m_aPoints[m].x = f2fx(x);
+							pQuad->m_aPoints[m].y = f2fx(y);
+						}
+				}
+				else
+				{
+					for(int m = 0; m < 4; m++)
+						if(m_SelectedPoints&(1<<m))
+						{
+							pQuad->m_aPoints[m].x += f2fx(dx);
+							pQuad->m_aPoints[m].y += f2fx(dy);
+						}
+				}
 			}
 			else if(s_Operation == OP_MOVEUV)
 			{
@@ -1159,12 +1360,8 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V)
 
 void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar)
 {
-	//UI()->ClipEnable(&view);
-
-	bool ShowPicker = Input()->KeyPressed(KEY_SPACE) != 0 && m_Dialog == DIALOG_NONE;
-
 	// render all good stuff
-	if(!ShowPicker)
+	if(!m_ShowPicker)
 	{
 		for(int g = 0; g < m_Map.m_lGroups.size(); g++)
 		{
@@ -1182,7 +1379,10 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar)
 
 		CLayerTiles *pT = static_cast<CLayerTiles *>(GetSelectedLayerType(0, LAYERTYPE_TILES));
 		if(m_ShowTileInfo && pT && pT->m_Visible && m_ZoomLevel <= 300)
+		{
+			GetSelectedGroup()->MapScreen();
 			pT->ShowInfo();
+		}
 	}
 
 	static void *s_pEditorID = (void *)&s_pEditorID;
@@ -1196,8 +1396,6 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar)
 
 	static float s_StartWx = 0;
 	static float s_StartWy = 0;
-	static float s_StartMx = 0;
-	static float s_StartMy = 0;
 
 	enum
 	{
@@ -1210,7 +1408,7 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar)
 	};
 
 	// remap the screen so it can display the whole tileset
-	if(ShowPicker)
+	if(m_ShowPicker)
 	{
 		CUIRect Screen = *UI()->Screen();
 		float Size = 32.0*16.0f;
@@ -1239,7 +1437,7 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar)
 	int NumEditLayers = 0;
 	NumEditLayers = 0;
 
-	if(ShowPicker)
+	if(m_ShowPicker)
 	{
 		pEditLayers[0] = &m_TilesetPicker;
 		NumEditLayers++;
@@ -1255,6 +1453,8 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar)
 		{
 			g->MapScreen();
 
+			RenderGrid(g);
+
 			for(int i = 0; i < NumEditLayers; i++)
 			{
 				if(pEditLayers[i]->m_Type != LAYERTYPE_TILES)
@@ -1285,8 +1485,6 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar)
 		{
 			s_StartWx = wx;
 			s_StartWy = wy;
-			s_StartMx = mx;
-			s_StartMy = my;
 
 			if(Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL) || UI()->MouseButton(2))
 			{
@@ -1445,7 +1643,7 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar)
 
 		// quad editing
 		{
-			if(!ShowPicker && m_Brush.IsEmpty())
+			if(!m_ShowPicker && m_Brush.IsEmpty())
 			{
 				// fetch layers
 				CLayerGroup *g = GetSelectedGroup();
@@ -1772,22 +1970,18 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View)
 	CUIRect Slot, Button;
 	char aBuf[64];
 
-	int ValidGroup = 0;
-	int ValidLayer = 0;
-	if(m_SelectedGroup >= 0 && m_SelectedGroup < m_Map.m_lGroups.size())
-		ValidGroup = 1;
-
-	if(ValidGroup && m_SelectedLayer >= 0 && m_SelectedLayer < m_Map.m_lGroups[m_SelectedGroup]->m_lLayers.size())
-		ValidLayer = 1;
-
 	float LayersHeight = 12.0f;	 // Height of AddGroup button
 	static int s_ScrollBar = 0;
 	static float s_ScrollValue = 0;
 
 	for(int g = 0; g < m_Map.m_lGroups.size(); g++)
+	{
 		// Each group is 19.0f
 		// Each layer is 14.0f
-		LayersHeight += 19.0f + m_Map.m_lGroups[g]->m_lLayers.size() * 14.0f;
+		LayersHeight += 19.0f;
+		if(!m_Map.m_lGroups[g]->m_Collapse)
+			LayersHeight += m_Map.m_lGroups[g]->m_lLayers.size() * 14.0f;
+	}
 
 	float ScrollDifference = LayersHeight - LayersBox.h;
 
@@ -1798,6 +1992,20 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View)
 		LayersBox.VSplitRight(3.0f, &LayersBox, 0);	// extra spacing
 		Scroll.HMargin(5.0f, &Scroll);
 		s_ScrollValue = UiDoScrollbarV(&s_ScrollBar, &Scroll, s_ScrollValue);
+
+		if(UI()->MouseInside(&Scroll) || UI()->MouseInside(&LayersBox))
+		{
+			int ScrollNum = (int)((LayersHeight-LayersBox.h)/15.0f)+1;
+			if(ScrollNum > 0)
+			{
+				if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP))
+					s_ScrollValue = clamp(s_ScrollValue - 1.0f/ScrollNum, 0.0f, 1.0f);
+				if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN))
+					s_ScrollValue = clamp(s_ScrollValue + 1.0f/ScrollNum, 0.0f, 1.0f);
+			}
+			else
+				ScrollNum = 0;
+		}
 	}
 
 	float LayerStartAt = ScrollDifference * s_ScrollValue;
@@ -1824,7 +2032,7 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View)
 			{
 				LayersBox.HSplitTop(12.0f, &Slot, &LayersBox);
 				Slot.VSplitLeft(12, &VisibleToggle, &Slot);
-				if(DoButton_Ex(&m_Map.m_lGroups[g]->m_Visible, m_Map.m_lGroups[g]->m_Visible?"V":"H", 0, &VisibleToggle, 0, "Toggle group visibility", CUI::CORNER_L))
+				if(DoButton_Ex(&m_Map.m_lGroups[g]->m_Visible, m_Map.m_lGroups[g]->m_Visible?"V":"H", m_Map.m_lGroups[g]->m_Collapse ? 1 : 0, &VisibleToggle, 0, "Toggle group visibility", CUI::CORNER_L))
 					m_Map.m_lGroups[g]->m_Visible = !m_Map.m_lGroups[g]->m_Visible;
 
 				Slot.VSplitRight(12.0f, &Slot, &SaveCheck);
@@ -1832,16 +2040,22 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View)
 					if(!m_Map.m_lGroups[g]->m_GameGroup)
 						m_Map.m_lGroups[g]->m_SaveToMap = !m_Map.m_lGroups[g]->m_SaveToMap;
 
-				str_format(aBuf, sizeof(aBuf),"#%d %s", g, m_Map.m_lGroups[g]->m_pName);
+				str_format(aBuf, sizeof(aBuf),"#%d %s", g, m_Map.m_lGroups[g]->m_aName);
+				float FontSize = 10.0f;
+				while(TextRender()->TextWidth(0, FontSize, aBuf, -1) > Slot.w)
+					FontSize--;
 				if(int Result = DoButton_Ex(&m_Map.m_lGroups[g], aBuf, g==m_SelectedGroup, &Slot,
-					BUTTON_CONTEXT, "Select group. Right click for properties.", 0))
+					BUTTON_CONTEXT, m_Map.m_lGroups[g]->m_Collapse ? "Select group. Double click to expand." : "Select group. Double click to collapse.", 0, FontSize))
 				{
 					m_SelectedGroup = g;
 					m_SelectedLayer = 0;
 
 					static int s_GroupPopupId = 0;
 					if(Result == 2)
-						UiInvokePopupMenu(&s_GroupPopupId, 0, UI()->MouseX(), UI()->MouseY(), 120, 200, PopupGroup);
+						UiInvokePopupMenu(&s_GroupPopupId, 0, UI()->MouseX(), UI()->MouseY(), 120, 220, PopupGroup);
+
+					if(m_Map.m_lGroups[g]->m_lLayers.size() && Input()->MouseDoubleClick())
+						m_Map.m_lGroups[g]->m_Collapse ^= 1;
 				}
 				LayersBox.HSplitTop(2.0f, &Slot, &LayersBox);
 			}
@@ -1857,6 +2071,9 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View)
 					continue;
 				}
 
+				if(m_Map.m_lGroups[g]->m_Collapse)
+					continue;
+
 				//visible
 				LayersBox.HSplitTop(12.0f, &Slot, &LayersBox);
 				Slot.VSplitLeft(12.0f, 0, &Button);
@@ -1870,15 +2087,24 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View)
 					if(m_Map.m_lGroups[g]->m_lLayers[i] != m_Map.m_pGameLayer)
 						m_Map.m_lGroups[g]->m_lLayers[i]->m_SaveToMap = !m_Map.m_lGroups[g]->m_lLayers[i]->m_SaveToMap;
 
-				str_format(aBuf, sizeof(aBuf),"#%d %s ", i, m_Map.m_lGroups[g]->m_lLayers[i]->m_pTypeName);
+				if(m_Map.m_lGroups[g]->m_lLayers[i]->m_aName[0])
+					str_format(aBuf, sizeof(aBuf), "%s", m_Map.m_lGroups[g]->m_lLayers[i]->m_aName);
+				else if(m_Map.m_lGroups[g]->m_lLayers[i]->m_Type == LAYERTYPE_TILES)
+					str_copy(aBuf, "Tiles", sizeof(aBuf));
+				else
+					str_copy(aBuf, "Quads", sizeof(aBuf));
+
+				float FontSize = 10.0f;
+				while(TextRender()->TextWidth(0, FontSize, aBuf, -1) > Button.w)
+					FontSize--;
 				if(int Result = DoButton_Ex(m_Map.m_lGroups[g]->m_lLayers[i], aBuf, g==m_SelectedGroup&&i==m_SelectedLayer, &Button,
-					BUTTON_CONTEXT, "Select layer. Right click for properties.", 0))
+					BUTTON_CONTEXT, "Select layer.", 0, FontSize))
 				{
 					m_SelectedLayer = i;
 					m_SelectedGroup = g;
 					static int s_LayerPopupID = 0;
 					if(Result == 2)
-						UiInvokePopupMenu(&s_LayerPopupID, 0, UI()->MouseX(), UI()->MouseY(), 120, 220, PopupLayer);
+						UiInvokePopupMenu(&s_LayerPopupID, 0, UI()->MouseX(), UI()->MouseY(), 120, 245, PopupLayer);
 				}
 
 				LayerCur += 14.0f;
@@ -1916,6 +2142,7 @@ void CEditor::ReplaceImage(const char *pFileName, int StorageType, void *pUser)
 	*pImg = ImgInfo;
 	pImg->m_External = External;
 	pEditor->ExtractName(pFileName, pImg->m_aName, sizeof(pImg->m_aName));
+	pImg->m_AutoMapper.Load(pImg->m_aName);
 	pImg->m_TexID = pEditor->Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, ImgInfo.m_pData, CImageInfo::FORMAT_AUTO, 0);
 	pEditor->SortImages();
 	for(int i = 0; i < pEditor->m_Map.m_lImages.size(); ++i)
@@ -1947,6 +2174,7 @@ void CEditor::AddImage(const char *pFileName, int StorageType, void *pUser)
 	pImg->m_TexID = pEditor->Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, ImgInfo.m_pData, CImageInfo::FORMAT_AUTO, 0);
 	pImg->m_External = 1;	// external by default
 	str_copy(pImg->m_aName, aBuf, sizeof(pImg->m_aName));
+	pImg->m_AutoMapper.Load(pImg->m_aName);
 	pEditor->m_Map.m_lImages.add(pImg);
 	pEditor->SortImages();
 	if(pEditor->m_SelectedImage > -1 && pEditor->m_SelectedImage < pEditor->m_Map.m_lImages.size())
@@ -2080,6 +2308,20 @@ void CEditor::RenderImages(CUIRect ToolBox, CUIRect ToolBar, CUIRect View)
 		ToolBox.VSplitRight(3.0f, &ToolBox, 0);	// extra spacing
 		Scroll.HMargin(5.0f, &Scroll);
 		s_ScrollValue = UiDoScrollbarV(&s_ScrollBar, &Scroll, s_ScrollValue);
+
+		if(UI()->MouseInside(&Scroll) || UI()->MouseInside(&ToolBox))
+		{
+			int ScrollNum = (int)((ImagesHeight-ToolBox.h)/14.0f)+1;
+			if(ScrollNum > 0)
+			{
+				if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP))
+					s_ScrollValue = clamp(s_ScrollValue - 1.0f/ScrollNum, 0.0f, 1.0f);
+				if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN))
+					s_ScrollValue = clamp(s_ScrollValue + 1.0f/ScrollNum, 0.0f, 1.0f);
+			}
+			else
+				ScrollNum = 0;
+		}
 	}
 
 	float ImageStartAt = ScrollDifference * s_ScrollValue;
@@ -2217,7 +2459,7 @@ void CEditor::AddFileDialogEntry(int Index, CUIRect *pView)
 	Graphics()->QuadsDrawTL(&QuadItem, 1);
 	Graphics()->QuadsEnd();
 
-	if(DoButton_File((void*)(10+(int)Button.y), m_FileList[Index].m_aName, m_FilesSelectedIndex == Index, &Button, 0, 0))
+	if(DoButton_File(&m_FileList[Index], m_FileList[Index].m_aName, m_FilesSelectedIndex == Index, &Button, 0, 0))
 	{
 		if(!m_FileList[Index].m_IsDir)
 			str_copy(m_aFileDialogFileName, m_FileList[Index].m_aFilename, sizeof(m_aFileDialogFileName));
@@ -2261,9 +2503,9 @@ void CEditor::RenderFileDialog()
 	// filebox
 	if(m_FileDialogStorageType == IStorage::TYPE_SAVE)
 	{
-		static int s_FileBoxID = 0;
+		static float s_FileBoxID = 0;
 		UI()->DoLabel(&FileBoxLabel, "Filename:", 10.0f, -1, -1);
-		if(DoEditBox(&s_FileBoxID, &FileBox, m_aFileDialogFileName, sizeof(m_aFileDialogFileName), 10.0f))
+		if(DoEditBox(&s_FileBoxID, &FileBox, m_aFileDialogFileName, sizeof(m_aFileDialogFileName), 10.0f, &s_FileBoxID))
 		{
 			// remove '/' and '\'
 			for(int i = 0; m_aFileDialogFileName[i]; ++i)
@@ -2612,8 +2854,8 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
 
 			ToolBar.VSplitLeft(80.0f, &Button, &ToolBar);
 
-			static int s_NameBox = 0;
-			if(DoEditBox(&s_NameBox, &Button, pEnvelope->m_aName, sizeof(pEnvelope->m_aName), 10.0f))
+			static float s_NameBox = 0;
+			if(DoEditBox(&s_NameBox, &Button, pEnvelope->m_aName, sizeof(pEnvelope->m_aName), 10.0f, &s_NameBox))
 				m_Map.m_Modified = true;
 		}
 	}
@@ -2811,8 +3053,6 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
 
 		// render handles
 		{
-			static bool s_Move = false;
-
 			int CurrentValue = 0, CurrentTime = 0;
 
 			Graphics()->TextureSet(-1);
@@ -2846,7 +3086,6 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
 						if(!UI()->MouseButton(0))
 						{
 							UI()->SetActiveItem(0);
-							s_Move = false;
 						}
 						else
 						{							
@@ -3050,6 +3289,7 @@ void CEditor::Render()
 	RenderBackground(View, ms_CheckerTexture, 32.0f, 1.0f);
 
 	CUIRect MenuBar, CModeBar, ToolBar, StatusBar, EnvelopeEditor, ToolBox;
+	m_ShowPicker = Input()->KeyPressed(KEY_SPACE) != 0 && m_Dialog == DIALOG_NONE;
 
 	if(m_GuiActive)
 	{
@@ -3059,7 +3299,7 @@ void CEditor::Render()
 		View.VSplitLeft(100.0f, &ToolBox, &View);
 		View.HSplitBottom(16.0f, &View, &StatusBar);
 
-		if(m_ShowEnvelopeEditor)
+		if(m_ShowEnvelopeEditor && !m_ShowPicker)
 		{
 			float size = 125.0f;
 			if(m_ShowEnvelopeEditor == 2)
@@ -3074,6 +3314,18 @@ void CEditor::Render()
 	if(m_Mode == MODE_LAYERS)
 		DoMapEditor(View, ToolBar);
 
+	// do the scrolling
+	if(m_Dialog == DIALOG_NONE && UI()->MouseInside(&View))
+	{
+		if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP))
+			m_ZoomLevel -= 20;
+
+		if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN))
+			m_ZoomLevel += 20;
+
+		m_ZoomLevel = clamp(m_ZoomLevel, 50, 2000);
+	}
+
 	if(m_GuiActive)
 	{
 		float Brightness = 0.25f;
@@ -3171,7 +3423,6 @@ void CEditor::Render()
 		Graphics()->QuadsDrawTL(&QuadItem, 1);
 		Graphics()->QuadsEnd();
 	}
-
 }
 
 void CEditor::Reset(bool CreateDefault)
@@ -3209,6 +3460,24 @@ void CEditor::Reset(bool CreateDefault)
 	m_Map.m_Modified = false;
 }
 
+int CEditor::GetLineDistance()
+{
+	int LineDistance = 512;
+
+	if(m_ZoomLevel <= 100)
+		LineDistance = 16;
+	else if(m_ZoomLevel <= 250)
+		LineDistance = 32;
+	else if(m_ZoomLevel <= 450)
+		LineDistance = 64;
+	else if(m_ZoomLevel <= 850)
+		LineDistance = 128;
+	else if(m_ZoomLevel <= 1550)
+		LineDistance = 256;
+
+	return LineDistance;
+}
+
 void CEditorMap::DeleteEnvelope(int Index)
 {
 	if(Index < 0 || Index >= m_lEnvelopes.size())
@@ -3249,7 +3518,7 @@ void CEditorMap::MakeGameGroup(CLayerGroup *pGroup)
 {
 	m_pGameGroup = pGroup;
 	m_pGameGroup->m_GameGroup = true;
-	m_pGameGroup->m_pName = "Game";
+	str_copy(m_pGameGroup->m_aName, "Game", sizeof(m_pGameGroup->m_aName));
 }
 
 
@@ -3356,6 +3625,7 @@ void CEditor::UpdateAndRender()
 	float rx, ry;
 	{
 		Input()->MouseRelative(&rx, &ry);
+		UI()->ConvertMouseMove(&rx, &ry);
 		m_MouseDeltaX = rx;
 		m_MouseDeltaY = ry;
 
@@ -3402,18 +3672,6 @@ void CEditor::UpdateAndRender()
 	if(Input()->KeyDown(KEY_TAB))
 		m_GuiActive = !m_GuiActive;
 
-	if(Input()->KeyDown(KEY_F5))
-		Save("maps/debug_test2.map");
-
-	if(Input()->KeyDown(KEY_F6))
-		Load("maps/debug_test2.map", IStorage::TYPE_ALL);
-	
-	if(Input()->KeyDown(KEY_F8))
-		Load("maps/debug_test.map", IStorage::TYPE_ALL);
-	
-	if(Input()->KeyDown(KEY_F7))
-		Save("maps/quicksave.map");
-
 	if(Input()->KeyDown(KEY_F10))
 		m_ShowMousePointer = false;
 
diff --git a/src/game/editor/ed_editor.h b/src/game/editor/editor.h
index aab757e1..1a904953 100644
--- a/src/game/editor/ed_editor.h
+++ b/src/game/editor/editor.h
@@ -1,25 +1,28 @@
 /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
 /* If you are missing that file, acquire a complete release at teeworlds.com.                */
-#ifndef GAME_EDITOR_ED_EDITOR_H
-#define GAME_EDITOR_ED_EDITOR_H
+#ifndef GAME_EDITOR_EDITOR_H
+#define GAME_EDITOR_EDITOR_H
+
+#include <math.h>
 
-#include <base/system.h>
 #include <base/math.h>
-#include <base/tl/array.h>
+#include <base/system.h>
+
 #include <base/tl/algorithm.h>
+#include <base/tl/array.h>
 #include <base/tl/sorted_array.h>
 #include <base/tl/string.h>
 
-#include <math.h>
+#include <game/client/ui.h>
 #include <game/mapitems.h>
 #include <game/client/render.h>
 
-#include <engine/shared/datafile.h>
 #include <engine/shared/config.h>
+#include <engine/shared/datafile.h>
 #include <engine/editor.h>
 #include <engine/graphics.h>
 
-#include <game/client/ui.h>
+#include "auto_map.h"
 
 typedef void (*INDEX_MODIFY_FUNC)(int *pIndex);
 
@@ -123,7 +126,7 @@ public:
 	CLayer()
 	{
 		m_Type = LAYERTYPE_INVALID;
-		m_pTypeName = "(invalid)";
+		str_copy(m_aName, "(invalid)", sizeof(m_aName));
 		m_Visible = true;
 		m_Readonly = false;
 		m_SaveToMap = true;
@@ -153,7 +156,7 @@ public:
 
 	virtual void GetSize(float *w, float *h) { *w = 0; *h = 0;}
 
-	const char *m_pTypeName;
+	char m_aName[12];
 	int m_Type;
 	int m_Flags;
 
@@ -181,10 +184,11 @@ public:
 	int m_ClipW;
 	int m_ClipH;
 
-	const char *m_pName;
+	char m_aName[12];
 	bool m_GameGroup;
 	bool m_Visible;
 	bool m_SaveToMap;
+	bool m_Collapse;
 
 	CLayerGroup();
 	~CLayerGroup();
@@ -230,6 +234,7 @@ public:
 	CEditor *m_pEditor;
 
 	CEditorImage(CEditor *pEditor)
+	: m_AutoMapper(pEditor)
 	{
 		m_pEditor = pEditor;
 		m_TexID = -1;
@@ -249,6 +254,7 @@ public:
 	int m_External;
 	char m_aName[128];
 	unsigned char m_aTileFlags[256];
+	class CAutoMapper m_AutoMapper;
 };
 
 class CEditorMap
@@ -400,6 +406,8 @@ public:
 	int m_Width;
 	int m_Height;
 	CColor m_Color;
+	int m_ColorEnv;
+	int m_ColorEnvOffset;
 	CTile *m_pTiles;
 };
 
@@ -470,6 +478,9 @@ public:
 		m_Dialog = 0;
 		m_pTooltip = 0;
 
+		m_GridActive = false;
+		m_GridFactor = 1;
+
 		m_aFileName[0] = 0;
 		m_aFileSaveName[0] = 0;
 		m_ValidSaveFilename = false;
@@ -551,6 +562,9 @@ public:
 	int m_Dialog;
 	const char *m_pTooltip;
 
+	bool m_GridActive;
+	int m_GridFactor;
+
 	char m_aFileName[512];
 	char m_aFileSaveName[512];
 	bool m_ValidSaveFilename;
@@ -630,6 +644,7 @@ public:
 	float m_AnimateSpeed;
 
 	int m_ShowEnvelopeEditor;
+	bool m_ShowPicker;
 
 	int m_SelectedLayer;
 	int m_SelectedGroup;
@@ -650,12 +665,14 @@ public:
 
 	CEditorMap m_Map;
 
+	static void EnvelopeEval(float TimeOffset, int Env, float *pChannels, void *pUser);
+
 	void DoMapBorder();
 	int DoButton_Editor_Common(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip);
 	int DoButton_Editor(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip);
 
 	int DoButton_Tab(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip);
-	int DoButton_Ex(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip, int Corners);
+	int DoButton_Ex(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip, int Corners, float FontSize=10.0f);
 	int DoButton_ButtonDec(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip);
 	int DoButton_ButtonInc(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip);
 
@@ -664,10 +681,12 @@ public:
 	int DoButton_Menu(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip);
 	int DoButton_MenuItem(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags=0, const char *pToolTip=0);
 
-	int DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, bool Hidden=false);
+	int DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, float *Offset, bool Hidden=false, int Corners=CUI::CORNER_ALL);
 
 	void RenderBackground(CUIRect View, int Texture, float Size, float Brightness);
 
+	void RenderGrid(CLayerGroup *pGroup);
+
 	void UiInvokePopupMenu(void *pID, int Flags, float X, float Y, float W, float H, int (*pfnFunc)(CEditor *pEditor, CUIRect Rect), void *pExtra=0);
 	void UiDoPopupMenu();
 
@@ -683,6 +702,7 @@ public:
 	static int PopupSelectGametileOp(CEditor *pEditor, CUIRect View);
 	static int PopupImage(CEditor *pEditor, CUIRect View);
 	static int PopupMenuFile(CEditor *pEditor, CUIRect View);
+	static int PopupSelectConfigAutoMap(CEditor *pEditor, CUIRect View);
 
 	static void CallbackOpenMap(const char *pFileName, int StorageType, void *pUser);
 	static void CallbackAppendMap(const char *pFileName, int StorageType, void *pUser);
@@ -693,6 +713,9 @@ public:
 
 	void PopupSelectGametileOpInvoke(float x, float y);
 	int PopupSelectGameTileOpResult();
+	
+	void PopupSelectConfigAutoMapInvoke(float x, float y);
+	int PopupSelectConfigAutoMapResult();
 
 	vec4 ButtonColorMul(const void *pID);
 
@@ -732,6 +755,8 @@ public:
 		int Length = pEnd > pExtractedName ? min(BufferSize, (int)(pEnd-pExtractedName+1)) : BufferSize;
 		str_copy(pName, pExtractedName, Length);
 	}
+
+	int GetLineDistance();
 };
 
 // make sure to inline this function
diff --git a/src/game/editor/ed_io.cpp b/src/game/editor/io.cpp
index a5ead97a..68330f03 100644
--- a/src/game/editor/ed_io.cpp
+++ b/src/game/editor/io.cpp
@@ -6,7 +6,7 @@
 #include <engine/serverbrowser.h>
 #include <engine/storage.h>
 #include <game/gamecore.h>
-#include "ed_editor.h"
+#include "editor.h"
 
 template<typename T>
 static int MakeVersion(int i, const T &v)
@@ -266,6 +266,9 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName)
 		GItem.m_StartLayer = LayerCount;
 		GItem.m_NumLayers = 0;
 
+		// save group name
+		StrToInts(GItem.m_aName, sizeof(GItem.m_aName)/sizeof(int), pGroup->m_aName);
+
 		for(int l = 0; l < pGroup->m_lLayers.size(); l++)
 		{
 			if(!pGroup->m_lLayers[l]->m_SaveToMap)
@@ -278,23 +281,24 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName)
 				pLayer->PrepareForSave();
 
 				CMapItemLayerTilemap Item;
-				Item.m_Version = 2;
+				Item.m_Version = 3;
 
 				Item.m_Layer.m_Flags = pLayer->m_Flags;
 				Item.m_Layer.m_Type = pLayer->m_Type;
 
-				Item.m_Color.r = pLayer->m_Color.r;
-				Item.m_Color.g = pLayer->m_Color.g;
-				Item.m_Color.b = pLayer->m_Color.b;
-				Item.m_Color.a = pLayer->m_Color.a;
-				Item.m_ColorEnv = -1; // not in use right now
-				Item.m_ColorEnvOffset = 0;
+				Item.m_Color = pLayer->m_Color;
+				Item.m_ColorEnv = pLayer->m_ColorEnv;
+				Item.m_ColorEnvOffset = pLayer->m_ColorEnvOffset;
 
 				Item.m_Width = pLayer->m_Width;
 				Item.m_Height = pLayer->m_Height;
-				Item.m_Flags = pLayer->m_Game;
+				Item.m_Flags = pLayer->m_Game ? TILESLAYERFLAG_GAME : 0;
 				Item.m_Image = pLayer->m_Image;
 				Item.m_Data = df.AddData(pLayer->m_Width*pLayer->m_Height*sizeof(CTile), pLayer->m_pTiles);
+
+				// save layer name
+				StrToInts(Item.m_aName, sizeof(Item.m_aName)/sizeof(int), pLayer->m_aName);
+
 				df.AddItem(MAPITEMTYPE_LAYER, LayerCount, sizeof(Item), &Item);
 
 				GItem.m_NumLayers++;
@@ -307,7 +311,7 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName)
 				if(pLayer->m_lQuads.size())
 				{
 					CMapItemLayerQuads Item;
-					Item.m_Version = 1;
+					Item.m_Version = 2;
 					Item.m_Layer.m_Flags = pLayer->m_Flags;
 					Item.m_Layer.m_Type = pLayer->m_Type;
 					Item.m_Image = pLayer->m_Image;
@@ -315,6 +319,10 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName)
 					// add the data
 					Item.m_NumQuads = pLayer->m_lQuads.size();
 					Item.m_Data = df.AddDataSwapped(pLayer->m_lQuads.size()*sizeof(CQuad), pLayer->m_lQuads.base_ptr());
+
+					// save layer name
+					StrToInts(Item.m_aName, sizeof(Item.m_aName)/sizeof(int), pLayer->m_aName);
+
 					df.AddItem(MAPITEMTYPE_LAYER, LayerCount, sizeof(Item), &Item);
 
 					// clean up
@@ -449,6 +457,9 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag
 				if(pName)
 					str_copy(pImg->m_aName, pName, 128);
 
+				// load auto mapper file
+				pImg->m_AutoMapper.Load(pImg->m_aName);
+
 				m_lImages.add(pImg);
 
 				// unload image
@@ -486,6 +497,10 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag
 					pGroup->m_ClipH = pGItem->m_ClipH;
 				}
 
+				// load group name
+				if(pGItem->m_Version >= 3)
+					IntsToStr(pGItem->m_aName, sizeof(pGroup->m_aName)/sizeof(int), pGroup->m_aName);
+
 				for(int l = 0; l < pGItem->m_NumLayers; l++)
 				{
 					CLayer *pLayer = 0;
@@ -498,7 +513,7 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag
 						CMapItemLayerTilemap *pTilemapItem = (CMapItemLayerTilemap *)pLayerItem;
 						CLayerTiles *pTiles = 0;
 
-						if(pTilemapItem->m_Flags&1)
+						if(pTilemapItem->m_Flags&TILESLAYERFLAG_GAME)
 						{
 							pTiles = new CLayerGame(pTilemapItem->m_Width, pTilemapItem->m_Height);
 							MakeGameLayer(pTiles);
@@ -508,10 +523,9 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag
 						{
 							pTiles = new CLayerTiles(pTilemapItem->m_Width, pTilemapItem->m_Height);
 							pTiles->m_pEditor = m_pEditor;
-							pTiles->m_Color.r = pTilemapItem->m_Color.r;
-							pTiles->m_Color.g = pTilemapItem->m_Color.g;
-							pTiles->m_Color.b = pTilemapItem->m_Color.b;
-							pTiles->m_Color.a = pTilemapItem->m_Color.a;
+							pTiles->m_Color = pTilemapItem->m_Color;
+							pTiles->m_ColorEnv = pTilemapItem->m_ColorEnv;
+							pTiles->m_ColorEnvOffset = pTilemapItem->m_ColorEnvOffset;
 						}
 
 						pLayer = pTiles;
@@ -519,7 +533,11 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag
 						pGroup->AddLayer(pTiles);
 						void *pData = DataFile.GetData(pTilemapItem->m_Data);
 						pTiles->m_Image = pTilemapItem->m_Image;
-						pTiles->m_Game = pTilemapItem->m_Flags&1;
+						pTiles->m_Game = pTilemapItem->m_Flags&TILESLAYERFLAG_GAME;
+
+						// load layer name
+						if(pTilemapItem->m_Version >= 3)
+							IntsToStr(pTilemapItem->m_aName, sizeof(pTiles->m_aName)/sizeof(int), pTiles->m_aName);
 
 						mem_copy(pTiles->m_pTiles, pData, pTiles->m_Width*pTiles->m_Height*sizeof(CTile));
 
@@ -543,6 +561,11 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag
 						pQuads->m_Image = pQuadsItem->m_Image;
 						if(pQuads->m_Image < -1 || pQuads->m_Image >= m_lImages.size())
 							pQuads->m_Image = -1;
+
+						// load layer name
+						if(pQuadsItem->m_Version >= 2)
+							IntsToStr(pQuadsItem->m_aName, sizeof(pQuads->m_aName)/sizeof(int), pQuads->m_aName);
+
 						void *pData = DataFile.GetDataSwapped(pQuadsItem->m_Data);
 						pGroup->AddLayer(pQuads);
 						pQuads->m_lQuads.set_size(pQuadsItem->m_NumQuads);
diff --git a/src/game/editor/ed_layer_game.cpp b/src/game/editor/layer_game.cpp
index cf48845e..7e879c3e 100644
--- a/src/game/editor/ed_layer_game.cpp
+++ b/src/game/editor/layer_game.cpp
@@ -1,12 +1,12 @@
 /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
 /* If you are missing that file, acquire a complete release at teeworlds.com.                */
-#include "ed_editor.h"
+#include "editor.h"
 
 
 CLayerGame::CLayerGame(int w, int h)
 : CLayerTiles(w, h)
 {
-	m_pTypeName = "Game";
+	str_copy(m_aName, "Game", sizeof(m_aName));
 	m_Game = 1;
 }
 
diff --git a/src/game/editor/ed_layer_quads.cpp b/src/game/editor/layer_quads.cpp
index 680a54cd..d0b66405 100644
--- a/src/game/editor/ed_layer_quads.cpp
+++ b/src/game/editor/layer_quads.cpp
@@ -5,7 +5,7 @@
 #include <engine/console.h>
 #include <engine/graphics.h>
 
-#include "ed_editor.h"
+#include "editor.h"
 #include <game/generated/client_data.h>
 #include <game/client/render.h>
 #include <game/localization.h>
@@ -13,7 +13,7 @@
 CLayerQuads::CLayerQuads()
 {
 	m_Type = LAYERTYPE_QUADS;
-	m_pTypeName = "Quads";
+	str_copy(m_aName, "Quads", sizeof(m_aName));
 	m_Image = -1;
 }
 
@@ -21,31 +21,13 @@ CLayerQuads::~CLayerQuads()
 {
 }
 
-static void EnvelopeEval(float TimeOffset, int Env, float *pChannels, void *pUser)
-{
-	CEditor *pEditor = (CEditor *)pUser;
-	if(Env < 0 || Env > pEditor->m_Map.m_lEnvelopes.size())
-	{
-		pChannels[0] = 0;
-		pChannels[1] = 0;
-		pChannels[2] = 0;
-		pChannels[3] = 0;
-		return;
-	}
-
-	CEnvelope *e = pEditor->m_Map.m_lEnvelopes[Env];
-	float t = pEditor->m_AnimateTime+TimeOffset;
-	t *= pEditor->m_AnimateSpeed;
-	e->Eval(t, pChannels);
-}
-
 void CLayerQuads::Render()
 {
 	Graphics()->TextureSet(-1);
 	if(m_Image >= 0 && m_Image < m_pEditor->m_Map.m_lImages.size())
 		Graphics()->TextureSet(m_pEditor->m_Map.m_lImages[m_Image]->m_TexID);
 
-	m_pEditor->RenderTools()->RenderQuads(m_lQuads.base_ptr(), m_lQuads.size(), LAYERRENDERFLAG_OPAQUE|LAYERRENDERFLAG_TRANSPARENT, EnvelopeEval, m_pEditor);
+	m_pEditor->RenderTools()->RenderQuads(m_lQuads.base_ptr(), m_lQuads.size(), LAYERRENDERFLAG_OPAQUE|LAYERRENDERFLAG_TRANSPARENT, m_pEditor->EnvelopeEval, m_pEditor);
 }
 
 CQuad *CLayerQuads::NewQuad()
diff --git a/src/game/editor/ed_layer_tiles.cpp b/src/game/editor/layer_tiles.cpp
index d0c9041c..5662613c 100644
--- a/src/game/editor/ed_layer_tiles.cpp
+++ b/src/game/editor/layer_tiles.cpp
@@ -8,14 +8,14 @@
 
 #include <game/generated/client_data.h>
 #include <game/client/render.h>
-#include "ed_editor.h"
+#include "editor.h"
 
 #include <game/localization.h>
 
 CLayerTiles::CLayerTiles(int w, int h)
 {
 	m_Type = LAYERTYPE_TILES;
-	m_pTypeName = "Tiles";
+	str_copy(m_aName, "Tiles", sizeof(m_aName));
 	m_Width = w;
 	m_Height = h;
 	m_Image = -1;
@@ -25,6 +25,8 @@ CLayerTiles::CLayerTiles(int w, int h)
 	m_Color.g = 255;
 	m_Color.b = 255;
 	m_Color.a = 255;
+	m_ColorEnv = -1;
+	m_ColorEnvOffset = 0;
 
 	m_pTiles = new CTile[m_Width*m_Height];
 	mem_zero(m_pTiles, m_Width*m_Height*sizeof(CTile));
@@ -62,7 +64,8 @@ void CLayerTiles::Render()
 		m_TexID = m_pEditor->m_Map.m_lImages[m_Image]->m_TexID;
 	Graphics()->TextureSet(m_TexID);
 	vec4 Color = vec4(m_Color.r/255.0f, m_Color.g/255.0f, m_Color.b/255.0f, m_Color.a/255.0f);
-	m_pEditor->RenderTools()->RenderTilemap(m_pTiles, m_Width, m_Height, 32.0f, Color, LAYERRENDERFLAG_OPAQUE|LAYERRENDERFLAG_TRANSPARENT);
+	m_pEditor->RenderTools()->RenderTilemap(m_pTiles, m_Width, m_Height, 32.0f, Color, LAYERRENDERFLAG_OPAQUE|LAYERRENDERFLAG_TRANSPARENT,
+												m_pEditor->EnvelopeEval, m_pEditor, m_ColorEnv, m_ColorEnvOffset);
 }
 
 int CLayerTiles::ConvertX(float x) const { return (int)(x/32.0f); }
@@ -123,7 +126,7 @@ void CLayerTiles::BrushSelecting(CUIRect Rect)
 	m_pEditor->Graphics()->QuadsEnd();
 	char aBuf[16];
 	str_format(aBuf, sizeof(aBuf), "%d,%d", ConvertX(Rect.w), ConvertY(Rect.h));
-	TextRender()->Text(0, Rect.x+3.0f, Rect.y+3.0f, 15.0f*m_pEditor->m_WorldZoom, aBuf, -1);
+	TextRender()->Text(0, Rect.x+3.0f, Rect.y+3.0f, m_pEditor->m_ShowPicker?15.0f:15.0f*m_pEditor->m_WorldZoom, aBuf, -1);
 }
 
 int CLayerTiles::BrushGrab(CLayerGroup *pBrush, CUIRect Rect)
@@ -353,19 +356,39 @@ void CLayerTiles::ShowInfo()
 			}
 			x += m_pTiles[c].m_Skip;
 		}
+
+	Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1);
 }
 
 int CLayerTiles::RenderProperties(CUIRect *pToolBox)
 {
 	CUIRect Button;
-	pToolBox->HSplitBottom(12.0f, pToolBox, &Button);
-
+	
 	bool InGameGroup = !find_linear(m_pEditor->m_Map.m_pGameGroup->m_lLayers.all(), this).empty();
-	if(m_pEditor->m_Map.m_pGameLayer == this)
+	if(m_pEditor->m_Map.m_pGameLayer != this)
+	{
+		if(m_Image >= 0 && m_Image < m_pEditor->m_Map.m_lImages.size() && m_pEditor->m_Map.m_lImages[m_Image]->m_AutoMapper.IsLoaded())
+		{
+			static int s_AutoMapperButton = 0;
+			pToolBox->HSplitBottom(12.0f, pToolBox, &Button);
+			if(m_pEditor->DoButton_Editor(&s_AutoMapperButton, "Auto map", 0, &Button, 0, ""))
+				m_pEditor->PopupSelectConfigAutoMapInvoke(m_pEditor->UI()->MouseX(), m_pEditor->UI()->MouseY());
+			
+			int Result = m_pEditor->PopupSelectConfigAutoMapResult();
+			if(Result > -1)
+			{
+				m_pEditor->m_Map.m_lImages[m_Image]->m_AutoMapper.Proceed(this, Result);
+				return 1;
+			}
+		}
+	}
+	else
 		InGameGroup = false;
 
 	if(InGameGroup)
 	{
+		pToolBox->HSplitBottom(2.0f, pToolBox, 0);
+		pToolBox->HSplitBottom(12.0f, pToolBox, &Button);
 		static int s_ColclButton = 0;
 		if(m_pEditor->DoButton_Editor(&s_ColclButton, "Game tiles", 0, &Button, 0, "Constructs game tiles from this layer"))
 			m_pEditor->PopupSelectGametileOpInvoke(m_pEditor->UI()->MouseX(), m_pEditor->UI()->MouseY());
@@ -392,6 +415,8 @@ int CLayerTiles::RenderProperties(CUIRect *pToolBox)
 		PROP_SHIFT,
 		PROP_IMAGE,
 		PROP_COLOR,
+		PROP_COLOR_ENV,
+		PROP_COLOR_ENV_OFFSET,
 		NUM_PROPS,
 	};
 
@@ -407,6 +432,8 @@ int CLayerTiles::RenderProperties(CUIRect *pToolBox)
 		{"Shift", 0, PROPTYPE_SHIFT, 0, 0},
 		{"Image", m_Image, PROPTYPE_IMAGE, 0, 0},
 		{"Color", Color, PROPTYPE_COLOR, 0, 0},
+		{"Color Env", m_ColorEnv+1, PROPTYPE_INT_STEP, 0, m_pEditor->m_Map.m_lEnvelopes.size()+1},
+		{"Color TO", m_ColorEnvOffset, PROPTYPE_INT_SCROLL, -1000000, 1000000},
 		{0},
 	};
 
@@ -445,6 +472,10 @@ int CLayerTiles::RenderProperties(CUIRect *pToolBox)
 		m_Color.b = (NewVal>>8)&0xff;
 		m_Color.a = NewVal&0xff;
 	}
+	if(Prop == PROP_COLOR_ENV)
+		m_ColorEnv = clamp(NewVal-1, -1, m_pEditor->m_Map.m_lEnvelopes.size()-1);
+	if(Prop == PROP_COLOR_ENV_OFFSET)
+		m_ColorEnvOffset = NewVal;
 
 	return 0;
 }
diff --git a/src/game/editor/ed_popups.cpp b/src/game/editor/popups.cpp
index f572e43c..f29ae7e2 100644
--- a/src/game/editor/ed_popups.cpp
+++ b/src/game/editor/popups.cpp
@@ -1,11 +1,15 @@
 /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
 /* If you are missing that file, acquire a complete release at teeworlds.com.                */
+
+#include <base/tl/array.h>
+
 #include <engine/console.h>
 #include <engine/graphics.h>
 #include <engine/input.h>
 #include <engine/keys.h>
 #include <engine/storage.h>
-#include "ed_editor.h"
+
+#include "editor.h"
 
 
 // popup menu handling
@@ -147,6 +151,7 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View)
 		l->m_pEditor = pEditor;
 		pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->AddLayer(l);
 		pEditor->m_SelectedLayer = pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_lLayers.size()-1;
+		pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_Collapse = false;
 		return 1;
 	}
 
@@ -160,9 +165,22 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View)
 		l->m_pEditor = pEditor;
 		pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->AddLayer(l);
 		pEditor->m_SelectedLayer = pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_lLayers.size()-1;
+		pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_Collapse = false;
 		return 1;
 	}
 
+	// group name
+	if(!pEditor->GetSelectedGroup()->m_GameGroup)
+	{
+		View.HSplitBottom(5.0f, &View, &Button);
+		View.HSplitBottom(12.0f, &View, &Button);
+		static float s_Name = 0;
+		pEditor->UI()->DoLabel(&Button, "Name:", 10.0f, -1, -1);
+		Button.VSplitLeft(40.0f, 0, &Button);
+		if(pEditor->DoEditBox(&s_Name, &Button, pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_aName, sizeof(pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_aName), 10.0f, &s_Name))
+			pEditor->m_Map.m_Modified = true;
+	}
+
 	enum
 	{
 		PROP_ORDER=0,
@@ -239,6 +257,18 @@ int CEditor::PopupLayer(CEditor *pEditor, CUIRect View)
 		return 1;
 	}
 
+	// layer name
+	if(pEditor->m_Map.m_pGameLayer != pEditor->GetSelectedLayer(0))
+	{
+		View.HSplitBottom(5.0f, &View, &Button);
+		View.HSplitBottom(12.0f, &View, &Button);
+		static float s_Name = 0;
+		pEditor->UI()->DoLabel(&Button, "Name:", 10.0f, -1, -1);
+		Button.VSplitLeft(40.0f, 0, &Button);
+		if(pEditor->DoEditBox(&s_Name, &Button, pEditor->GetSelectedLayer(0)->m_aName, sizeof(pEditor->GetSelectedLayer(0)->m_aName), 10.0f, &s_Name))
+			pEditor->m_Map.m_Modified = true;
+	}
+
 	View.HSplitBottom(10.0f, &View, 0);
 
 	CLayerGroup *pCurrentGroup = pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup];
@@ -528,8 +558,8 @@ int CEditor::PopupNewFolder(CEditor *pEditor, CUIRect View)
 		View.HSplitBottom(40.0f, &View, 0);
 		View.VMargin(40.0f, &View);
 		View.HSplitBottom(20.0f, &View, &Label);
-		static int s_FolderBox = 0;
-		pEditor->DoEditBox(&s_FolderBox, &Label, pEditor->m_FileDialogNewFolderName, sizeof(pEditor->m_FileDialogNewFolderName), 15.0f);
+		static float s_FolderBox = 0;
+		pEditor->DoEditBox(&s_FolderBox, &Label, pEditor->m_FileDialogNewFolderName, sizeof(pEditor->m_FileDialogNewFolderName), 15.0f, &s_FolderBox);
 		View.HSplitBottom(20.0f, &View, &Label);
 		pEditor->UI()->DoLabel(&Label, "Name:", 10.0f, -1);
 
@@ -737,3 +767,43 @@ int CEditor::PopupSelectGameTileOpResult()
 	s_GametileOpSelected = -1;
 	return Result;
 }
+
+static int s_AutoMapConfigSelected = -1;
+
+int CEditor::PopupSelectConfigAutoMap(CEditor *pEditor, CUIRect View)
+{
+	CLayerTiles *pLayer = static_cast<CLayerTiles*>(pEditor->GetSelectedLayer(0));
+	CUIRect Button;
+	static int s_AutoMapperConfigButtons[256];
+	CAutoMapper *pAutoMapper = &pEditor->m_Map.m_lImages[pLayer->m_Image]->m_AutoMapper;
+	
+	for(int i = 0; i < pAutoMapper->ConfigNamesNum(); ++i)
+	{
+		View.HSplitTop(2.0f, 0, &View);
+		View.HSplitTop(12.0f, &Button, &View);
+		if(pEditor->DoButton_Editor(&s_AutoMapperConfigButtons[i], pAutoMapper->GetConfigName(i), 0, &Button, 0, 0))
+			s_AutoMapConfigSelected = i;
+	}
+
+	return 0;
+}
+
+void CEditor::PopupSelectConfigAutoMapInvoke(float x, float y)
+{
+	static int s_AutoMapConfigSelectID = 0;
+	s_AutoMapConfigSelected = -1;
+	CLayerTiles *pLayer = static_cast<CLayerTiles*>(GetSelectedLayer(0));
+	if(pLayer && pLayer->m_Image >= 0 && pLayer->m_Image < m_Map.m_lImages.size() &&
+		m_Map.m_lImages[pLayer->m_Image]->m_AutoMapper.ConfigNamesNum())
+		UiInvokePopupMenu(&s_AutoMapConfigSelectID, 0, x, y, 120.0f, 12.0f+14.0f*m_Map.m_lImages[pLayer->m_Image]->m_AutoMapper.ConfigNamesNum(), PopupSelectConfigAutoMap);
+}
+
+int CEditor::PopupSelectConfigAutoMapResult()
+{
+	if(s_AutoMapConfigSelected < 0)
+		return -1;
+	
+	int Result = s_AutoMapConfigSelected;
+	s_AutoMapConfigSelected = -1;
+	return Result;
+}
diff --git a/src/game/gamecore.cpp b/src/game/gamecore.cpp
index d2a1652c..af086df2 100644
--- a/src/game/gamecore.cpp
+++ b/src/game/gamecore.cpp
@@ -362,35 +362,37 @@ void CCharacterCore::Move()
 
 	m_Vel.x = m_Vel.x*RampValue;
 
-	vec2 NewPos = m_Pos;

+	vec2 NewPos = m_Pos;
 	m_pCollision->MoveBox(&NewPos, &m_Vel, vec2(28.0f, 28.0f), 0);
 
 	m_Vel.x = m_Vel.x*(1.0f/RampValue);
 
 	if(m_pWorld && m_pWorld->m_Tuning.m_PlayerCollision)
 	{
-		// check player collision

-		float Distance = distance(m_Pos, NewPos);

-		int End = Distance+1;

-		for(int i = 0; i < End; i++)

-		{

-			float a = i/Distance;

-			vec2 Pos = mix(m_Pos, NewPos, a);

-			for(int p = 0; p < MAX_CLIENTS; p++)

-			{

-				CCharacterCore *pCharCore = m_pWorld->m_apCharacters[p];

-				if(!pCharCore || pCharCore == this)

-					continue;

-				float D = distance(Pos, pCharCore->m_Pos);

-				if(D < 28.0f*1.25f && D > 0.0f)

-				{

-					if(a > 0.0f)

-						m_Pos = Pos;

-					else

-						m_Pos = NewPos;

-					return;

-				}

-			}

+		// check player collision
+		float Distance = distance(m_Pos, NewPos);
+		int End = Distance+1;
+		vec2 LastPos = m_Pos;
+		for(int i = 0; i < End; i++)
+		{
+			float a = i/Distance;
+			vec2 Pos = mix(m_Pos, NewPos, a);
+			for(int p = 0; p < MAX_CLIENTS; p++)
+			{
+				CCharacterCore *pCharCore = m_pWorld->m_apCharacters[p];
+				if(!pCharCore || pCharCore == this)
+					continue;
+				float D = distance(Pos, pCharCore->m_Pos);
+				if(D < 28.0f && D > 0.0f)
+				{
+					if(a > 0.0f)
+						m_Pos = LastPos;
+					else if(distance(NewPos, pCharCore->m_Pos) > D)
+						m_Pos = NewPos;
+					return;
+				}
+			}
+			LastPos = Pos;
 		}
 	}
 
diff --git a/src/game/mapitems.h b/src/game/mapitems.h
index d99d6724..fb66e12d 100644
--- a/src/game/mapitems.h
+++ b/src/game/mapitems.h
@@ -53,6 +53,7 @@ enum
 	TILEFLAG_ROTATE=8,
 
 	LAYERFLAG_DETAIL=1,
+	TILESLAYERFLAG_GAME=1,
 
 	ENTITY_OFFSET=255-16*4,
 };
@@ -114,13 +115,15 @@ struct CMapItemGroup_v1
 
 struct CMapItemGroup : public CMapItemGroup_v1
 {
-	enum { CURRENT_VERSION=2 };
+	enum { CURRENT_VERSION=3 };
 
 	int m_UseClipping;
 	int m_ClipX;
 	int m_ClipY;
 	int m_ClipW;
 	int m_ClipH;
+
+	int m_aName[3];
 } ;
 
 struct CMapItemLayer
@@ -145,6 +148,8 @@ struct CMapItemLayerTilemap
 
 	int m_Image;
 	int m_Data;
+
+	int m_aName[3];
 } ;
 
 struct CMapItemLayerQuads
@@ -155,6 +160,8 @@ struct CMapItemLayerQuads
 	int m_NumQuads;
 	int m_Data;
 	int m_Image;
+
+	int m_aName[3];
 } ;
 
 struct CMapItemVersion
diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp
index 821aade5..06c1c913 100644
--- a/src/game/server/entities/character.cpp
+++ b/src/game/server/entities/character.cpp
@@ -56,8 +56,30 @@ bool CCharacter::Spawn(CPlayer *pPlayer, vec2 Pos)
 {
 	m_EmoteStop = -1;
 	m_LastAction = -1;
-	m_ActiveWeapon = WEAPON_GUN;
-	m_LastWeapon = WEAPON_HAMMER;
+	
+	/*zCatch */
+	if(GameServer()->m_pController->IsZCatch() && g_Config.m_SvMode == 1)
+	{
+		m_ActiveWeapon = WEAPON_RIFLE;
+		m_LastWeapon = WEAPON_RIFLE;
+	}
+	else if(GameServer()->m_pController->IsZCatch() && g_Config.m_SvMode == 3)
+	{
+		m_ActiveWeapon = WEAPON_HAMMER;
+		m_LastWeapon = WEAPON_HAMMER;
+	}
+	else if(GameServer()->m_pController->IsZCatch() && g_Config.m_SvMode == 4)
+	{
+		m_ActiveWeapon = WEAPON_GRENADE;
+		m_LastWeapon = WEAPON_GRENADE;
+	}
+	else
+	{
+		m_ActiveWeapon = WEAPON_GUN;
+		m_LastWeapon = WEAPON_HAMMER;
+	}
+    /* end zCatch */
+	
 	m_QueuedWeapon = -1;
 
 	m_pPlayer = pPlayer;
@@ -115,20 +137,21 @@ void CCharacter::HandleNinja()
 	if(m_ActiveWeapon != WEAPON_NINJA)
 		return;
 
-	vec2 Direction = normalize(vec2(m_LatestInput.m_TargetX, m_LatestInput.m_TargetY));
-
-	if ((Server()->Tick() - m_Ninja.m_ActivationTick) > (g_pData->m_Weapons.m_Ninja.m_Duration * Server()->TickSpeed() / 1000))
+	/* zCatch */
+	if(GameServer()->m_pController->IsZCatch() == false || (GameServer()->m_pController->IsZCatch() && g_Config.m_SvMode == 0))
 	{
-		// time's up, return
-		m_aWeapons[WEAPON_NINJA].m_Got = false;
-		m_ActiveWeapon = m_LastWeapon;
-		if(m_ActiveWeapon == WEAPON_NINJA)
-			m_ActiveWeapon = WEAPON_GUN;
+		if ((Server()->Tick() - m_Ninja.m_ActivationTick) > (g_pData->m_Weapons.m_Ninja.m_Duration * Server()->TickSpeed() / 1000))
+		{
+			// time's up, return
+			m_aWeapons[WEAPON_NINJA].m_Got = false;
+			m_ActiveWeapon = m_LastWeapon;
 
-		SetWeapon(m_ActiveWeapon);
-		return;
+			SetWeapon(m_ActiveWeapon);
+			return;
+		}
 	}
-
+	/* zCatch end*/
+	
 	// force ninja Weapon
 	SetWeapon(WEAPON_NINJA);
 
@@ -183,7 +206,7 @@ void CCharacter::HandleNinja()
 				if(m_NumObjectsHit < 10)
 					m_apHitObjects[m_NumObjectsHit++] = aEnts[i];
 
-				aEnts[i]->TakeDamage(vec2(0, 10.0f), g_pData->m_Weapons.m_Ninja.m_pBase->m_Damage, m_pPlayer->GetCID(), WEAPON_NINJA);
+				aEnts[i]->TakeDamage(vec2(0, -10.0f), g_pData->m_Weapons.m_Ninja.m_pBase->m_Damage, m_pPlayer->GetCID(), WEAPON_NINJA);
 			}
 		}
 
@@ -436,8 +459,6 @@ void CCharacter::HandleWeapons()
 	//ninja
 	HandleNinja();
 
-	vec2 Direction = normalize(vec2(m_LatestInput.m_TargetX, m_LatestInput.m_TargetY));
-
 	// check reload timer
 	if(m_ReloadTimer)
 	{
@@ -490,7 +511,8 @@ void CCharacter::GiveNinja()
 	m_Ninja.m_ActivationTick = Server()->Tick();
 	m_aWeapons[WEAPON_NINJA].m_Got = true;
 	m_aWeapons[WEAPON_NINJA].m_Ammo = -1;
-	m_LastWeapon = m_ActiveWeapon;
+	if (m_ActiveWeapon != WEAPON_NINJA)
+		m_LastWeapon = m_ActiveWeapon;
 	m_ActiveWeapon = WEAPON_NINJA;
 
 	GameServer()->CreateSound(m_Pos, SOUND_PICKUP_NINJA);
@@ -531,6 +553,18 @@ void CCharacter::OnDirectInput(CNetObj_PlayerInput *pNewInput)
 	mem_copy(&m_LatestPrevInput, &m_LatestInput, sizeof(m_LatestInput));
 }
 
+void CCharacter::ResetInput()
+{
+	m_Input.m_Direction = 0;
+	m_Input.m_Hook = 0;
+	// simulate releasing the fire button
+	if((m_Input.m_Fire&1) != 0)
+		m_Input.m_Fire++;
+	m_Input.m_Fire &= INPUT_STATE_MASK;
+	m_Input.m_Jump = 0;
+	m_LatestPrevInput = m_LatestInput = m_Input;
+}
+
 void CCharacter::Tick()
 {
 	if(m_pPlayer->m_ForceBalanced)
@@ -700,11 +734,18 @@ bool CCharacter::TakeDamage(vec2 Force, int Dmg, int From, int Weapon)
 
 	if(GameServer()->m_pController->IsFriendlyFire(m_pPlayer->GetCID(), From) && !g_Config.m_SvTeamdamage)
 		return false;
-
-	// m_pPlayer only inflicts half damage on self
+	
+	/* zCatch */
+	bool Is_zCatch = GameServer()->m_pController->IsZCatch();
+	
 	if(From == m_pPlayer->GetCID())
-		Dmg = max(1, Dmg/2);
-
+		if(Is_zCatch && g_Config.m_SvMode != 1)
+			Dmg = 0;	//No selfdamage, except in vanilla-mode
+		// m_pPlayer only inflicts half damage on self
+		else
+			Dmg = max(1, Dmg/2);
+	/* end zCatch */
+	
 	m_DamageTaken++;
 
 	// create healthmod indicator
@@ -718,8 +759,15 @@ bool CCharacter::TakeDamage(vec2 Force, int Dmg, int From, int Weapon)
 		m_DamageTaken = 0;
 		GameServer()->CreateDamageInd(m_Pos, 0, Dmg);
 	}
-
-	if(Dmg)
+	/* zCatch*/
+	//One-Shot-One-Kill
+	if(Is_zCatch && (g_Config.m_SvMode != 0 && g_Config.m_SvMode != 2)) // all except vanilla-mode and all weapons
+	{
+		m_Health = 0;
+		m_Armor = 0;
+	}
+	/* end zCatch*/
+	else if(Dmg)
 	{
 		if(m_Armor)
 		{
@@ -748,7 +796,15 @@ bool CCharacter::TakeDamage(vec2 Force, int Dmg, int From, int Weapon)
 
 	// do damage Hit sound
 	if(From >= 0 && From != m_pPlayer->GetCID() && GameServer()->m_apPlayers[From])
-		GameServer()->CreateSound(GameServer()->m_apPlayers[From]->m_ViewPos, SOUND_HIT, CmaskOne(From));
+	{
+		int Mask = CmaskOne(From);
+		for(int i = 0; i < MAX_CLIENTS; i++)
+		{
+			if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetTeam() == TEAM_SPECTATORS && GameServer()->m_apPlayers[i]->m_SpectatorID == From)
+				Mask |= CmaskOne(i);
+		}
+		GameServer()->CreateSound(GameServer()->m_apPlayers[From]->m_ViewPos, SOUND_HIT, Mask);
+	}
 
 	// check for death
 	if(m_Health <= 0)
@@ -821,7 +877,8 @@ void CCharacter::Snap(int SnappingClient)
 
 	pCharacter->m_Direction = m_Input.m_Direction;
 
-	if(m_pPlayer->GetCID() == SnappingClient || SnappingClient == -1 || m_pPlayer->GetCID() == GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID)
+	if(m_pPlayer->GetCID() == SnappingClient || SnappingClient == -1 ||
+		(!g_Config.m_SvStrictSpectateMode && m_pPlayer->GetCID() == GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID))
 	{
 		pCharacter->m_Health = m_Health;
 		pCharacter->m_Armor = m_Armor;
diff --git a/src/game/server/entities/character.h b/src/game/server/entities/character.h
index 611dc427..a311a6f9 100644
--- a/src/game/server/entities/character.h
+++ b/src/game/server/entities/character.h
@@ -11,6 +11,7 @@
 
 enum
 {
+	WEAPON_ANTICAMPER = -4,
 	WEAPON_GAME = -3, // team switching etc
 	WEAPON_SELF = -2, // console kill command
 	WEAPON_WORLD = -1, // death tiles etc
@@ -43,6 +44,7 @@ public:
 
 	void OnPredictedInput(CNetObj_PlayerInput *pNewInput);
 	void OnDirectInput(CNetObj_PlayerInput *pNewInput);
+	void ResetInput();
 	void FireWeapon();
 
 	void Die(int Killer, int Weapon);
diff --git a/src/game/server/entities/laser.cpp b/src/game/server/entities/laser.cpp
index 30ed0a9b..af66fe0c 100644
--- a/src/game/server/entities/laser.cpp
+++ b/src/game/server/entities/laser.cpp
@@ -44,7 +44,6 @@ void CLaser::DoBounce()
 	}
 
 	vec2 To = m_Pos + m_Dir * m_Energy;
-	vec2 OrgTo = To;
 
 	if(GameServer()->Collision()->IntersectLine(m_Pos, To, 0x0, &To))
 	{
diff --git a/src/game/server/eventhandler.cpp b/src/game/server/eventhandler.cpp
index deb1ca4e..354bd4ab 100644
--- a/src/game/server/eventhandler.cpp
+++ b/src/game/server/eventhandler.cpp
@@ -46,7 +46,7 @@ void CEventHandler::Snap(int SnappingClient)
 	{
 		if(SnappingClient == -1 || CmaskIsSet(m_aClientMasks[i], SnappingClient))
 		{
-			NETEVENT_COMMON *ev = (NETEVENT_COMMON *)&m_aData[m_aOffsets[i]];
+			CNetEvent_Common *ev = (CNetEvent_Common *)&m_aData[m_aOffsets[i]];
 			if(SnappingClient == -1 || distance(GameServer()->m_apPlayers[SnappingClient]->m_ViewPos, vec2(ev->m_X, ev->m_Y)) < 1500.0f)
 			{
 				void *d = GameServer()->Server()->SnapNewItem(m_aTypes[i], i, m_aSizes[i]);
diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp
index 82adcbef..a34890e5 100644
--- a/src/game/server/gamecontext.cpp
+++ b/src/game/server/gamecontext.cpp
@@ -13,6 +13,7 @@
 #include "gamemodes/tdm.h"
 #include "gamemodes/ctf.h"
 #include "gamemodes/mod.h"
+#include "gamemodes/zcatch.hpp"
 
 enum
 {
@@ -36,6 +37,9 @@ void CGameContext::Construct(int Resetting)
 
 	if(Resetting==NO_RESET)
 		m_pVoteOptionHeap = new CHeap();
+	
+	for(int i = 0; i < MAX_MUTES; i++)
+		m_aMutes[i].m_IP[0] = 0;
 }
 
 CGameContext::CGameContext(int Resetting)
@@ -93,7 +97,7 @@ void CGameContext::CreateDamageInd(vec2 Pos, float Angle, int Amount)
 	for(int i = 0; i < Amount; i++)
 	{
 		float f = mix(s, e, float(i+1)/float(Amount+2));
-		NETEVENT_DAMAGEIND *pEvent = (NETEVENT_DAMAGEIND *)m_Events.Create(NETEVENTTYPE_DAMAGEIND, sizeof(NETEVENT_DAMAGEIND));
+		CNetEvent_DamageInd *pEvent = (CNetEvent_DamageInd *)m_Events.Create(NETEVENTTYPE_DAMAGEIND, sizeof(CNetEvent_DamageInd));
 		if(pEvent)
 		{
 			pEvent->m_X = (int)Pos.x;
@@ -106,7 +110,7 @@ void CGameContext::CreateDamageInd(vec2 Pos, float Angle, int Amount)
 void CGameContext::CreateHammerHit(vec2 Pos)
 {
 	// create the event
-	NETEVENT_HAMMERHIT *pEvent = (NETEVENT_HAMMERHIT *)m_Events.Create(NETEVENTTYPE_HAMMERHIT, sizeof(NETEVENT_HAMMERHIT));
+	CNetEvent_HammerHit *pEvent = (CNetEvent_HammerHit *)m_Events.Create(NETEVENTTYPE_HAMMERHIT, sizeof(CNetEvent_HammerHit));
 	if(pEvent)
 	{
 		pEvent->m_X = (int)Pos.x;
@@ -118,7 +122,7 @@ void CGameContext::CreateHammerHit(vec2 Pos)
 void CGameContext::CreateExplosion(vec2 Pos, int Owner, int Weapon, bool NoDamage)
 {
 	// create the event
-	NETEVENT_EXPLOSION *pEvent = (NETEVENT_EXPLOSION *)m_Events.Create(NETEVENTTYPE_EXPLOSION, sizeof(NETEVENT_EXPLOSION));
+	CNetEvent_Explosion *pEvent = (CNetEvent_Explosion *)m_Events.Create(NETEVENTTYPE_EXPLOSION, sizeof(CNetEvent_Explosion));
 	if(pEvent)
 	{
 		pEvent->m_X = (int)Pos.x;
@@ -162,7 +166,7 @@ void create_smoke(vec2 Pos)
 void CGameContext::CreatePlayerSpawn(vec2 Pos)
 {
 	// create the event
-	NETEVENT_SPAWN *ev = (NETEVENT_SPAWN *)m_Events.Create(NETEVENTTYPE_SPAWN, sizeof(NETEVENT_SPAWN));
+	CNetEvent_Spawn *ev = (CNetEvent_Spawn *)m_Events.Create(NETEVENTTYPE_SPAWN, sizeof(CNetEvent_Spawn));
 	if(ev)
 	{
 		ev->m_X = (int)Pos.x;
@@ -173,7 +177,7 @@ void CGameContext::CreatePlayerSpawn(vec2 Pos)
 void CGameContext::CreateDeath(vec2 Pos, int ClientID)
 {
 	// create the event
-	NETEVENT_DEATH *pEvent = (NETEVENT_DEATH *)m_Events.Create(NETEVENTTYPE_DEATH, sizeof(NETEVENT_DEATH));
+	CNetEvent_Death *pEvent = (CNetEvent_Death *)m_Events.Create(NETEVENTTYPE_DEATH, sizeof(CNetEvent_Death));
 	if(pEvent)
 	{
 		pEvent->m_X = (int)Pos.x;
@@ -188,7 +192,7 @@ void CGameContext::CreateSound(vec2 Pos, int Sound, int Mask)
 		return;
 
 	// create a sound
-	NETEVENT_SOUNDWORLD *pEvent = (NETEVENT_SOUNDWORLD *)m_Events.Create(NETEVENTTYPE_SOUNDWORLD, sizeof(NETEVENT_SOUNDWORLD), Mask);
+	CNetEvent_SoundWorld *pEvent = (CNetEvent_SoundWorld *)m_Events.Create(NETEVENTTYPE_SOUNDWORLD, sizeof(CNetEvent_SoundWorld), Mask);
 	if(pEvent)
 	{
 		pEvent->m_X = (int)Pos.x;
@@ -225,7 +229,7 @@ void CGameContext::SendChat(int ChatterClientID, int Team, const char *pText)
 		str_format(aBuf, sizeof(aBuf), "%d:%d:%s: %s", ChatterClientID, Team, Server()->ClientName(ChatterClientID), pText);
 	else
 		str_format(aBuf, sizeof(aBuf), "*** %s", pText);
-	Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "chat", aBuf);
+	Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, Team!=CHAT_ALL?"teamchat":"chat", aBuf);
 
 	if(Team == CHAT_ALL)
 	{
@@ -343,7 +347,8 @@ void CGameContext::SendVoteStatus(int ClientID, int Total, int Yes, int No)
 
 void CGameContext::AbortVoteKickOnDisconnect(int ClientID)
 {
-	if(m_VoteCloseTime && !str_comp_num(m_aVoteCommand, "kick ", 5) && str_toint(&m_aVoteCommand[5]) == ClientID)
+	if(m_VoteCloseTime && ((!str_comp_num(m_aVoteCommand, "kick ", 5) && str_toint(&m_aVoteCommand[5]) == ClientID) ||
+		(!str_comp_num(m_aVoteCommand, "set_team ", 9) && str_toint(&m_aVoteCommand[9]) == ClientID)))
 		m_VoteCloseTime = -1;
 }
 
@@ -421,7 +426,10 @@ void CGameContext::OnTick()
 				bool aVoteChecked[MAX_CLIENTS] = {0};
 				for(int i = 0; i < MAX_CLIENTS; i++)
 				{
-					if(!m_apPlayers[i] || m_apPlayers[i]->GetTeam() == TEAM_SPECTATORS || aVoteChecked[i])	// don't count in votes by spectators
+					/* zCatch - Allow voting from players in spectators (needed or the last 2 players ingame can kick the whole server),
+					 * but deny votes from players who are explicit in spec
+					*/
+					if(!m_apPlayers[i] || m_apPlayers[i]->m_SpecExplicit == 1 || aVoteChecked[i])	// don't count in votes by spectators
 						continue;
 
 					int ActVote = m_apPlayers[i]->m_Vote;
@@ -507,6 +515,74 @@ void CGameContext::OnClientEnter(int ClientID)
 {
 	//world.insert_entity(&players[client_id]);
 	m_apPlayers[ClientID]->Respawn();
+	
+	/* begin zCatch */
+	int leader_id = -1;
+	int StartTeam = m_pController->ClampTeam(1);
+	
+	if(m_pController->IsZCatch())
+	{
+		int num = 0;
+		
+		for(int i=0; i<MAX_CLIENTS; i++)
+		{
+			if(IsClientReady(i))
+				num++;
+		}
+		if(num < 3)
+			m_pController->EndRound();
+		
+		if(g_Config.m_SvAllowJoin == 1)
+		{
+			m_apPlayers[ClientID]->m_CatchedBy = ZCATCH_NOT_CATCHED;
+			m_apPlayers[ClientID]->m_SpecExplicit = (num < 3) ? 0 : 1;
+			StartTeam = (num < 3) ? m_pController->ClampTeam(1) : TEAM_SPECTATORS;
+			SendBroadcast("You can join the game", ClientID);
+			
+		}
+		else if(g_Config.m_SvAllowJoin == 2)
+		{
+			int num2 = 0, num_prev = 0;
+			
+			for(int i = 0; i < MAX_CLIENTS; i++)
+			{
+				if(m_apPlayers[i])
+				{
+					num2 = 0;
+					for(int j = 0; j < MAX_CLIENTS; j++)
+		   			{
+			    			if(m_apPlayers[j] && m_apPlayers[j]->m_CatchedBy == i)
+				   			num2++;
+		    			}
+		    			if(num2 > num_prev)
+		   	 		{
+			    			leader_id = i;
+			    			num_prev = num2;
+		    			}
+		    		}
+		    	}
+		    	
+		    	if(leader_id > -1)
+			{
+				m_apPlayers[ClientID]->m_CatchedBy = leader_id;
+				m_apPlayers[ClientID]->m_SpecExplicit = 0;
+				m_apPlayers[ClientID]->m_SpectatorID = leader_id;
+				StartTeam = TEAM_SPECTATORS;
+			}
+			else
+			{
+				m_apPlayers[ClientID]->m_CatchedBy = ZCATCH_NOT_CATCHED;
+				m_apPlayers[ClientID]->m_SpecExplicit = 0;
+			}
+		}
+		else
+			StartTeam = m_pController->GetAutoTeam(ClientID);
+	}
+	
+	m_apPlayers[ClientID]->SetTeamDirect(StartTeam);
+	
+	/* end zCatch */
+	
 	char aBuf[512];
 	str_format(aBuf, sizeof(aBuf), "'%s' entered and joined the %s", Server()->ClientName(ClientID), m_pController->GetTeamName(m_apPlayers[ClientID]->GetTeam()));
 	SendChat(-1, CGameContext::CHAT_ALL, aBuf);
@@ -515,6 +591,21 @@ void CGameContext::OnClientEnter(int ClientID)
 	Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf);
 
 	m_VoteUpdate = true;
+	
+	/* zCatch begin */
+   	if(m_pController->IsZCatch())
+	{
+	    SendChatTarget(ClientID, "Welcome to zCatch!");
+	    SendChatTarget(ClientID, "type /cmdlist to get all commands");
+	    SendChatTarget(ClientID, "type /help for instructions");
+	    if(g_Config.m_SvAllowJoin == 2 && leader_id > -1)
+	    {
+	    	char buf[128];
+	    	str_format(buf, sizeof(buf), "You will join the game when %s dies", Server()->ClientName(leader_id));
+	    	SendChatTarget(ClientID, buf);	
+	    }
+	}
+	/* zCatch end */
 }
 
 void CGameContext::OnClientConnected(int ClientID)
@@ -599,8 +690,66 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
 				*pMessage = ' ';
 			pMessage++;
 		}
+		
+		//Check if the player is muted
+		char aAddrStr[NETADDR_MAXSTRSIZE] = {0};
+		Server()->GetClientAddr(ClientID, aAddrStr, sizeof(aAddrStr));
+		int Pos;
+        if((Pos = Muted(aAddrStr)) > -1)
+		{
+			char aBuf[128];
+			int Expires = (m_aMutes[Pos].m_Expires - Server()->Tick())/Server()->TickSpeed(); 
+			str_format(aBuf, sizeof(aBuf), "You are muted for %d minutes and %d seconds.", Expires/60, Expires%60);
+			SendChatTarget(ClientID, aBuf);
+			return;
+		}
+		else if(g_Config.m_SvMuteDuration && ((pPlayer->m_ChatTicks += g_Config.m_SvChatValue) > g_Config.m_SvChatThreshold)) //is he spamming?
+		{
+			AddMute(ClientID, g_Config.m_SvMuteDuration);
+			pPlayer->m_ChatTicks = 0;
+			return;
+		}
 
-		SendChat(ClientID, Team, pMsg->m_pMessage);
+		/* begin zCatch*/
+		if(!str_comp("/info", pMsg->m_pMessage) || !str_comp("/about", pMsg->m_pMessage))
+		{
+			char aBuf[128];
+			str_format(aBuf, sizeof(aBuf), "zCatch version %s by erd and Teetime. Type /cmdlist for all commands.", ZCATCH_VERSION);
+			SendChatTarget(ClientID, " ");
+			SendChatTarget(ClientID, aBuf);
+		}
+		else if(!str_comp("/cmdlist", pMsg->m_pMessage))
+		{
+			SendChatTarget(ClientID, " ");
+			SendChatTarget(ClientID, "/info or /about - see information about author.");
+			SendChatTarget(ClientID, "/help - learn how to play.");
+			SendChatTarget(ClientID, "/follow 1 or /follow 0 - Enables/Disables following of the catcher.");
+		}
+		else if(!str_comp("/help", pMsg->m_pMessage))
+		{
+			SendChatTarget(ClientID, " ");
+			SendChatTarget(ClientID, "The winner is the tee which is left over at the end.");
+			SendChatTarget(ClientID, "If you die, all players that you killed will respawn.");
+			SendChatTarget(ClientID, "So the only way to win is to kill every player without beeing killed.");
+			SendChatTarget(ClientID, "Have fun!");
+		}
+		else if(!str_comp("/follow 0", pMsg->m_pMessage))
+		{
+			pPlayer->m_PlayerWantToFollowCatcher = 0;
+			pPlayer->m_SpectatorID = SPEC_FREEVIEW;
+			SendChatTarget(ClientID, "Follow of catcher disabled.");
+		}
+		else if(!str_comp("/follow 1", pMsg->m_pMessage))
+		{
+			pPlayer->m_PlayerWantToFollowCatcher = 1;
+			SendChatTarget(ClientID, "Follow of catcher enabled.");
+		}	
+		else if(!str_comp_num("/", pMsg->m_pMessage, 1))
+			SendChatTarget(ClientID, "Unknown command.");
+		else
+			SendChat(ClientID, Team, pMsg->m_pMessage);
+
+		/* end zCatch */
 	}
 	else if(MsgID == NETMSGTYPE_CL_CALLVOTE)
 	{
@@ -609,7 +758,8 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
 
 		int64 Now = Server()->Tick();
 		pPlayer->m_LastVoteTry = Now;
-		if(pPlayer->GetTeam() == TEAM_SPECTATORS)
+		// zCatch - Only People who are explicit in Spectators can't vote!
+		if(pPlayer->m_SpecExplicit == 1) //zCatch
 		{
 			SendChatTarget(ClientID, "Spectators aren't allowed to start a vote.");
 			return;
@@ -672,7 +822,7 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
 			{
 				int PlayerNum = 0;
 				for(int i = 0; i < MAX_CLIENTS; ++i)
-					if(m_apPlayers[i] && m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS)
+					if(m_apPlayers[i] && m_apPlayers[i]->m_SpecExplicit != 1) // zCatch - Count all Players who are not explicit in spectator
 						++PlayerNum;
 
 				if(PlayerNum < g_Config.m_SvVoteKickMin)
@@ -691,12 +841,12 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
 			}
 			if(KickID == ClientID)
 			{
-				SendChatTarget(ClientID, "You cant kick yourself");
+				SendChatTarget(ClientID, "You can't kick yourself");
 				return;
 			}
 			if(Server()->IsAuthed(KickID))
 			{
-				SendChatTarget(ClientID, "You cant kick admins");
+				SendChatTarget(ClientID, "You can't kick admins");
 				char aBufKick[128];
 				str_format(aBufKick, sizeof(aBufKick), "'%s' called for vote to kick you", Server()->ClientName(ClientID));
 				SendChatTarget(KickID, aBufKick);
@@ -731,13 +881,13 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
 			}
 			if(SpectateID == ClientID)
 			{
-				SendChatTarget(ClientID, "You cant move yourself");
+				SendChatTarget(ClientID, "You can't move yourself");
 				return;
 			}
 
 			str_format(aChatmsg, sizeof(aChatmsg), "'%s' called for vote to move '%s' to spectators (%s)", Server()->ClientName(ClientID), Server()->ClientName(SpectateID), pReason);
 			str_format(aDesc, sizeof(aDesc), "move '%s' to spectators", Server()->ClientName(SpectateID));
-			str_format(aCmd, sizeof(aCmd), "set_team %d -1", SpectateID);
+			str_format(aCmd, sizeof(aCmd), "set_team %d -1 %d", SpectateID, g_Config.m_SvVoteSpectateRejoindelay);
 		}
 
 		if(aCmd[0])
@@ -773,17 +923,45 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
 		if(pPlayer->GetTeam() == pMsg->m_Team || (g_Config.m_SvSpamprotection && pPlayer->m_LastSetTeam && pPlayer->m_LastSetTeam+Server()->TickSpeed()*3 > Server()->Tick()))
 			return;
 
+		if(pPlayer->m_TeamChangeTick > Server()->Tick())
+		{
+			pPlayer->m_LastSetTeam = Server()->Tick();
+			int TimeLeft = (pPlayer->m_TeamChangeTick - Server()->Tick())/Server()->TickSpeed();
+			char aBuf[128];
+			str_format(aBuf, sizeof(aBuf), "Time to wait before changing team: %02d:%02d", TimeLeft/60, TimeLeft%60);
+			SendBroadcast(aBuf, ClientID);
+			return;
+		}
+
 		// Switch team on given client and kill/respawn him
 		if(m_pController->CanJoinTeam(pMsg->m_Team, ClientID))
 		{
-			if(m_pController->CanChangeTeam(pPlayer, pMsg->m_Team))
+			if(m_pController->CanChangeTeam(pPlayer, pMsg->m_Team) && !m_pController->IsZCatch()) //zCatch)
 			{
 				pPlayer->m_LastSetTeam = Server()->Tick();
 				if(pPlayer->GetTeam() == TEAM_SPECTATORS || pMsg->m_Team == TEAM_SPECTATORS)
 					m_VoteUpdate = true;
 				pPlayer->SetTeam(pMsg->m_Team);
 				(void)m_pController->CheckTeamBalance();
+				pPlayer->m_TeamChangeTick = Server()->Tick();
+			}
+			/* begin zCatch*/			
+			else if(m_pController->IsZCatch())
+			{	
+				if(pPlayer->m_CatchedBy >= 0)
+				{
+					char buf[256];
+					str_format(buf, sizeof(buf), "You will join automatically when \"%s\" dies.", Server()->ClientName(pPlayer->m_CatchedBy));
+					SendChatTarget(ClientID, buf);
+					return;
+				}
+				else if(pPlayer->m_CatchedBy == ZCATCH_NOT_CATCHED)
+				{
+					pPlayer->m_LastSetTeam = Server()->Tick();
+					pPlayer->SetTeam(pMsg->m_Team);
+				}
 			}
+            /* end zCatch*/
 			else
 				SendBroadcast("Teams must be balanced, please join other team", ClientID);
 		}
@@ -945,14 +1123,75 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
 	}
 	else if (MsgID == NETMSGTYPE_CL_KILL && !m_World.m_Paused)
 	{
-		if(pPlayer->m_LastKill && pPlayer->m_LastKill+Server()->TickSpeed()*3 > Server()->Tick())
-			return;
-
-		pPlayer->m_LastKill = Server()->Tick();
-		pPlayer->KillCharacter(WEAPON_SELF);
+		/* begin zCatch*/
+		if(pPlayer->m_LastKill && pPlayer->m_LastKill + Server()->TickSpeed()*15 > Server()->Tick())
+		{	
+			if((pPlayer->GetTeam() == TEAM_SPECTATORS) || (pPlayer->m_LastKillTry && pPlayer->m_LastKillTry+Server()->TickSpeed()*2 > Server()->Tick()))
+				return;			
+			SendChatTarget(ClientID, "Only one kill in 15sec is allowed.");
+			pPlayer->m_LastKillTry = Server()->Tick();
+		}
+		else
+		{
+			pPlayer->m_LastKill = Server()->Tick();
+			pPlayer->KillCharacter(WEAPON_SELF);
+			pPlayer->m_Deaths++;
+		}
+		/* end zCatch*/
 	}
 }
 
+void CGameContext::AddMute(const char* IP, int Secs)
+{
+	int Pos = Muted(IP);
+	if(Pos > -1)	
+		m_aMutes[Pos].m_Expires = Server()->TickSpeed() * Secs + Server()->Tick();	// overwrite mute
+	else
+		for(int i = 0; i < MAX_MUTES; i++)	// find free slot
+			if(!m_aMutes[i].m_IP[0])
+			{
+				str_copy(m_aMutes[i].m_IP, IP, sizeof(m_aMutes[i].m_IP));
+				m_aMutes[i].m_Expires = Server()->TickSpeed() * Secs + Server()->Tick();
+				break;
+			}
+}
+
+void CGameContext::AddMute(int ClientID, int Secs)
+{
+	char aAddrStr[NETADDR_MAXSTRSIZE] = {0};
+	Server()->GetClientAddr(ClientID, aAddrStr, sizeof(aAddrStr));
+	AddMute(aAddrStr, Secs);
+	
+	char aBuf[128];
+	if(Secs > 0)
+		str_format(aBuf, sizeof(aBuf), "\"%s\" has been muted for %d seconds.", Server()->ClientName(ClientID), Secs); 
+	else
+		str_format(aBuf, sizeof(aBuf), "\"%s\" has been unmuted.", Server()->ClientName(ClientID));
+	SendChatTarget(-1, aBuf);
+}
+
+int CGameContext::Muted(const char* IP)
+{	
+	CleanMutes();
+	int Pos = -1;
+	if(!IP[0])
+		return -1;
+	for(int i = 0; i < MAX_MUTES; i++)
+		if(!str_comp_num(IP, m_aMutes[i].m_IP, sizeof(m_aMutes[i].m_IP)))
+		{
+			Pos = i;
+			break;
+		}
+	return Pos;
+}
+
+void CGameContext::CleanMutes()
+{
+	for(int i = 0; i < MAX_MUTES; i++)
+		if(m_aMutes[i].m_Expires < Server()->Tick())
+			m_aMutes[i].m_IP[0] = 0;
+}
+
 void CGameContext::ConTuneParam(IConsole::IResult *pResult, void *pUserData)
 {
 	CGameContext *pSelf = (CGameContext *)pUserData;
@@ -1024,6 +1263,9 @@ void CGameContext::ConSetTeam(IConsole::IResult *pResult, void *pUserData)
 	CGameContext *pSelf = (CGameContext *)pUserData;
 	int ClientID = clamp(pResult->GetInteger(0), 0, (int)MAX_CLIENTS-1);
 	int Team = clamp(pResult->GetInteger(1), -1, 1);
+	int Delay = 0;
+	if(pResult->NumArguments() > 2)
+		Delay = pResult->GetInteger(2);
 
 	char aBuf[256];
 	str_format(aBuf, sizeof(aBuf), "moved client %d to team %d", ClientID, Team);
@@ -1032,6 +1274,7 @@ void CGameContext::ConSetTeam(IConsole::IResult *pResult, void *pUserData)
 	if(!pSelf->m_apPlayers[ClientID])
 		return;
 
+	pSelf->m_apPlayers[ClientID]->m_TeamChangeTick = pSelf->Server()->Tick()+pSelf->Server()->TickSpeed()*Delay*60;
 	pSelf->m_apPlayers[ClientID]->SetTeam(Team);
 	(void)pSelf->m_pController->CheckTeamBalance();
 }
@@ -1248,7 +1491,9 @@ void CGameContext::ConForceVote(IConsole::IResult *pResult, void *pUserData)
 			return;
 		}
 
-		str_format(aBuf, sizeof(aBuf), "set_team %d -1", SpectateID);
+		str_format(aBuf, sizeof(aBuf), "admin moved '%s' to spectator (%s)", pSelf->Server()->ClientName(SpectateID), pReason);
+		pSelf->SendChatTarget(-1, aBuf);
+		str_format(aBuf, sizeof(aBuf), "set_team %d -1 %d", SpectateID, g_Config.m_SvVoteSpectateRejoindelay);
 		pSelf->Console()->ExecuteLine(aBuf);
 	}
 }
@@ -1274,6 +1519,8 @@ void CGameContext::ConVote(IConsole::IResult *pResult, void *pUserData)
 	else if(str_comp_nocase(pResult->GetString(0), "no") == 0)
 		pSelf->m_VoteEnforce = CGameContext::VOTE_ENFORCE_NO;
 	char aBuf[256];
+	str_format(aBuf, sizeof(aBuf), "admin forced vote %s", pResult->GetString(0));
+	pSelf->SendChatTarget(-1, aBuf);
 	str_format(aBuf, sizeof(aBuf), "forcing vote %s", pResult->GetString(0));
 	pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
 }
@@ -1292,28 +1539,81 @@ void CGameContext::ConchainSpecialMotdupdate(IConsole::IResult *pResult, void *p
 	}
 }
 
-void CGameContext::OnConsoleInit()
+void CGameContext::ConMute(IConsole::IResult *pResult, void *pUserData)
 {
-	m_pServer = Kernel()->RequestInterface<IServer>();
-	m_pConsole = Kernel()->RequestInterface<IConsole>();
+	CGameContext *pSelf = (CGameContext *)pUserData;
+	int CID = pResult->GetInteger(0);
+	if(CID < 0 || CID >= MAX_CLIENTS || !pSelf->m_apPlayers[CID])
+		return;
+		
+	pSelf->AddMute(CID, pResult->GetInteger(1));
+}
 
-	Console()->Register("tune", "si", CFGFLAG_SERVER, ConTuneParam, this, "");
-	Console()->Register("tune_reset", "", CFGFLAG_SERVER, ConTuneReset, this, "");
-	Console()->Register("tune_dump", "", CFGFLAG_SERVER, ConTuneDump, this, "");
+void CGameContext::ConMutes(IConsole::IResult *pResult, void *pUserData)
+{
+	CGameContext *pSelf = (CGameContext *)pUserData;
+	char aBuf[128];
+	int Sec, Count = 0;
+	pSelf->CleanMutes();
+	for(int i = 0; i < MAX_MUTES; i++)
+	{
+		if(pSelf->m_aMutes[i].m_IP[0] == 0)
+			continue;
+		
+		Sec = (pSelf->m_aMutes[i].m_Expires - pSelf->Server()->Tick())/pSelf->Server()->TickSpeed();
+		str_format(aBuf, sizeof(aBuf), "#%d: %s for %d minutes and %d sec", i, pSelf->m_aMutes[i].m_IP, Sec/60, Sec%60);
+		pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "Server", aBuf);
+		Count++;
+	}
+	str_format(aBuf, sizeof(aBuf), "%d mute(s)", Count);
+	pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "Server", aBuf);	
+}
 
-	Console()->Register("change_map", "?r", CFGFLAG_SERVER|CFGFLAG_STORE, ConChangeMap, this, "");
-	Console()->Register("restart", "?i", CFGFLAG_SERVER|CFGFLAG_STORE, ConRestart, this, "");
-	Console()->Register("broadcast", "r", CFGFLAG_SERVER, ConBroadcast, this, "");
-	Console()->Register("say", "r", CFGFLAG_SERVER, ConSay, this, "");
-	Console()->Register("set_team", "ii", CFGFLAG_SERVER, ConSetTeam, this, "");
-	Console()->Register("set_team_all", "i", CFGFLAG_SERVER, ConSetTeamAll, this, "");
+void CGameContext::ConUnmute(IConsole::IResult *pResult, void *pUserData)
+{
+	CGameContext *pSelf = (CGameContext *)pUserData;
+	int MuteID = pResult->GetInteger(0);
+	char aBuf[128];
+	
+	if(MuteID < 0 || MuteID >= MAX_MUTES)
+		return;
+	
+	if(pSelf->Muted(pSelf->m_aMutes[MuteID].m_IP) > -1)
+	{
+		str_format(aBuf, sizeof(aBuf), "unmuted %s", pSelf->m_aMutes[MuteID].m_IP);
+		pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "Server", aBuf);
+		pSelf->AddMute(pSelf->m_aMutes[MuteID].m_IP, 0);
+	}
+	else
+		pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "Server", "mute not found");
+}
 
-	Console()->Register("add_vote", "sr", CFGFLAG_SERVER, ConAddVote, this, "");
-	Console()->Register("remove_vote", "s", CFGFLAG_SERVER, ConRemoveVote, this, "");
-	Console()->Register("force_vote", "ss?r", CFGFLAG_SERVER, ConForceVote, this, "");
-	Console()->Register("clear_votes", "", CFGFLAG_SERVER, ConClearVotes, this, "");
-	Console()->Register("vote", "r", CFGFLAG_SERVER, ConVote, this, "");
+void CGameContext::OnConsoleInit()
+{
+	m_pServer = Kernel()->RequestInterface<IServer>();
+	m_pConsole = Kernel()->RequestInterface<IConsole>();
 
+	Console()->Register("tune", "si", CFGFLAG_SERVER, ConTuneParam, this, "Tune variable to value");
+	Console()->Register("tune_reset", "", CFGFLAG_SERVER, ConTuneReset, this, "Reset tuning");
+	Console()->Register("tune_dump", "", CFGFLAG_SERVER, ConTuneDump, this, "Dump tuning");
+
+	Console()->Register("change_map", "?r", CFGFLAG_SERVER|CFGFLAG_STORE, ConChangeMap, this, "Change map");
+	Console()->Register("restart", "?i", CFGFLAG_SERVER|CFGFLAG_STORE, ConRestart, this, "Restart in x seconds");
+	Console()->Register("broadcast", "r", CFGFLAG_SERVER, ConBroadcast, this, "Broadcast message");
+	Console()->Register("say", "r", CFGFLAG_SERVER, ConSay, this, "Say in chat");
+	Console()->Register("set_team", "ii?i", CFGFLAG_SERVER, ConSetTeam, this, "Set team of player to team");
+	Console()->Register("set_team_all", "i", CFGFLAG_SERVER, ConSetTeamAll, this, "Set team of all players to team");
+
+	Console()->Register("add_vote", "sr", CFGFLAG_SERVER, ConAddVote, this, "Add a voting option");
+	Console()->Register("remove_vote", "s", CFGFLAG_SERVER, ConRemoveVote, this, "remove a voting option");
+	Console()->Register("force_vote", "ss?r", CFGFLAG_SERVER, ConForceVote, this, "Force a voting option");
+	Console()->Register("clear_votes", "", CFGFLAG_SERVER, ConClearVotes, this, "Clears the voting options");
+	Console()->Register("vote", "r", CFGFLAG_SERVER, ConVote, this, "Force a vote to yes/no");
+
+	Console()->Register("mute", "ii", CFGFLAG_SERVER, ConMute, this, "Mutes a player for x sec");
+	Console()->Register("unmute", "i", CFGFLAG_SERVER, ConUnmute, this, "Removes a mute by its index");
+	Console()->Register("mutes", "", CFGFLAG_SERVER, ConMutes, this, "Show all mutes");
+		
 	Console()->Chain("sv_motd", ConchainSpecialMotdupdate, this);
 }
 
@@ -1344,6 +1644,8 @@ void CGameContext::OnInit(/*class IKernel *pKernel*/)
 		m_pController = new CGameControllerCTF(this);
 	else if(str_comp(g_Config.m_SvGametype, "tdm") == 0)
 		m_pController = new CGameControllerTDM(this);
+	else if(str_comp_nocase(g_Config.m_SvGametype, "zcatch") == 0)
+		m_pController = new CGameController_zCatch(this);
 	else
 		m_pController = new CGameControllerDM(this);
 
diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h
index 6288850d..267c388a 100644
--- a/src/game/server/gamecontext.h
+++ b/src/game/server/gamecontext.h
@@ -15,6 +15,9 @@
 #include "gameworld.h"
 #include "player.h"
 
+#define MAX_MUTES 25
+#define ZCATCH_VERSION "0.4.2 BETA"
+
 /*
 	Tick
 		Game Context (CGameContext::tick)
@@ -60,6 +63,10 @@ class CGameContext : public IGameServer
 	static void ConClearVotes(IConsole::IResult *pResult, void *pUserData);
 	static void ConVote(IConsole::IResult *pResult, void *pUserData);
 	static void ConchainSpecialMotdupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
+	
+	static void ConMute(IConsole::IResult *pResult, void *pUserData);
+	static void ConUnmute(IConsole::IResult *pResult, void *pUserData);
+	static void ConMutes(IConsole::IResult *pResult, void *pUserData);
 
 	CGameContext(int Resetting);
 	void Construct(int Resetting);
@@ -128,6 +135,23 @@ public:
 		CHAT_RED=0,
 		CHAT_BLUE=1
 	};
+	
+	enum
+	{
+		ZCATCH_NOT_CATCHED = -1,
+	}; 
+	
+	struct CMutes
+	{
+		char m_IP[NETADDR_MAXSTRSIZE];
+		int m_Expires;
+	}; 
+	CMutes m_aMutes[MAX_MUTES];
+	// helper functions
+	void AddMute(const char* IP, int Secs);
+	void AddMute(int ClientID, int Secs);
+	int Muted(const char* IP);
+	void CleanMutes();
 
 	// network
 	void SendChatTarget(int To, const char *pText);
diff --git a/src/game/server/gamecontroller.cpp b/src/game/server/gamecontroller.cpp
index 6685bba3..eb9dc9b5 100644
--- a/src/game/server/gamecontroller.cpp
+++ b/src/game/server/gamecontroller.cpp
@@ -63,10 +63,25 @@ void IGameController::EvaluateSpawnType(CSpawnEval *pEval, int Type)
 	for(int i = 0; i < m_aNumSpawnPoints[Type]; i++)
 	{
 		// check if the position is occupado
-		if(GameServer()->m_World.FindEntities(m_aaSpawnPoints[Type][i], 64, 0, 1, CGameWorld::ENTTYPE_CHARACTER))
-			continue;
+		CCharacter *aEnts[MAX_CLIENTS];
+		int Num = GameServer()->m_World.FindEntities(m_aaSpawnPoints[Type][i], 64, (CEntity**)aEnts, MAX_CLIENTS, CGameWorld::ENTTYPE_CHARACTER);
+		vec2 Positions[5] = { vec2(0.0f, 0.0f), vec2(-32.0f, 0.0f), vec2(0.0f, -32.0f), vec2(32.0f, 0.0f), vec2(0.0f, 32.0f) };	// start, left, up, right, down
+		int Result = -1;
+		for(int Index = 0; Index < 5 && Result == -1; ++Index)
+		{
+			Result = Index;
+			for(int c = 0; c < Num; ++c)
+				if(GameServer()->Collision()->CheckPoint(m_aaSpawnPoints[Type][i]+Positions[Index]) ||
+					distance(aEnts[c]->m_Pos, m_aaSpawnPoints[Type][i]+Positions[Index]) <= aEnts[c]->m_ProximityRadius)
+				{
+					Result = -1;
+					break;
+				}
+		}
+		if(Result == -1)
+			continue;	// try next spawn point
 
-		vec2 P = m_aaSpawnPoints[Type][i];
+		vec2 P = m_aaSpawnPoints[Type][i]+Positions[Result];
 		float S = EvaluateSpawnPos(pEval, P);
 		if(!pEval->m_Got || pEval->m_Score > S)
 		{
@@ -77,47 +92,6 @@ void IGameController::EvaluateSpawnType(CSpawnEval *pEval, int Type)
 	}
 }
 
-void IGameController::FindFreeSpawn(CSpawnEval *pEval, int Type)
-{
-	// pick the spawn point that is least occupied and has free space for spawning around it
-	for(int i = 0; i < m_aNumSpawnPoints[Type]; i++)
-	{
-
-		CCharacter *aEnts[MAX_CLIENTS];
-		int Num = GameServer()->m_World.FindEntities(m_aaSpawnPoints[Type][i], 64, (CEntity**)aEnts, MAX_CLIENTS, CGameWorld::ENTTYPE_CHARACTER);
-		float Score = 0.0f;
-		for(int c = 0; c < Num; ++c)
-			Score += 96.0f - distance(aEnts[c]->m_Pos, m_aaSpawnPoints[Type][i]);
-
-		if(!pEval->m_Got || pEval->m_Score > Score)
-		{
-			// start, left, up, right, down
-			vec2 Positions[5] = { vec2(0.0f, 0.0f), vec2(-32.0f, 0.0f), vec2(0.0f, -32.0f), vec2(32.0f, 0.0f), vec2(0.0f, 32.0f) };
-
-			// check for free space
-			int Result = -1;
-			for(int Index = 0; Index < 5 && Result == -1; ++Index)
-			{
-				Result = Index;
-				for(int c = 0; c < Num; ++c)
-					if(GameServer()->Collision()->CheckPoint(m_aaSpawnPoints[Type][i]+Positions[Index]) ||
-						distance(aEnts[c]->m_Pos, m_aaSpawnPoints[Type][i]+Positions[Index]) <= aEnts[c]->m_ProximityRadius)
-					{
-						Result = -1;
-						break;
-					}
-			}
-
-			if(Result == -1)
-				continue;	// try next spawn point
-
-			pEval->m_Got = true;
-			pEval->m_Score = Score;
-			pEval->m_Pos = m_aaSpawnPoints[Type][i]+Positions[Result];
-		}
-	}
-}
-
 bool IGameController::CanSpawn(int Team, vec2 *pOutPos)
 {
 	CSpawnEval Eval;
@@ -146,28 +120,6 @@ bool IGameController::CanSpawn(int Team, vec2 *pOutPos)
 		EvaluateSpawnType(&Eval, 2);
 	}
 
-	// handle crappy maps
-	if(!Eval.m_Got)
-	{
-		if(IsTeamplay())
-		{
-			// first try own team spawn, then normal spawn and then enemy
-			FindFreeSpawn(&Eval, 1+(Team&1));
-			if(!Eval.m_Got)
-			{
-				FindFreeSpawn(&Eval, 0);
-				if(!Eval.m_Got)
-					FindFreeSpawn(&Eval, 1+((Team+1)&1));
-			}
-		}
-		else
-		{
-			FindFreeSpawn(&Eval, 0);
-			FindFreeSpawn(&Eval, 1);
-			FindFreeSpawn(&Eval, 2);
-		}
-	}
-
 	*pOutPos = Eval.m_Pos;
 	return Eval.m_Got;
 }
@@ -184,30 +136,35 @@ bool IGameController::OnEntity(int Index, vec2 Pos)
 		m_aaSpawnPoints[1][m_aNumSpawnPoints[1]++] = Pos;
 	else if(Index == ENTITY_SPAWN_BLUE)
 		m_aaSpawnPoints[2][m_aNumSpawnPoints[2]++] = Pos;
-	else if(Index == ENTITY_ARMOR_1)
-		Type = POWERUP_ARMOR;
-	else if(Index == ENTITY_HEALTH_1)
-		Type = POWERUP_HEALTH;
-	else if(Index == ENTITY_WEAPON_SHOTGUN)
-	{
-		Type = POWERUP_WEAPON;
-		SubType = WEAPON_SHOTGUN;
-	}
-	else if(Index == ENTITY_WEAPON_GRENADE)
-	{
-		Type = POWERUP_WEAPON;
-		SubType = WEAPON_GRENADE;
-	}
-	else if(Index == ENTITY_WEAPON_RIFLE)
-	{
-		Type = POWERUP_WEAPON;
-		SubType = WEAPON_RIFLE;
-	}
-	else if(Index == ENTITY_POWERUP_NINJA && g_Config.m_SvPowerups)
-	{
-		Type = POWERUP_NINJA;
-		SubType = WEAPON_NINJA;
-	}
+	/* zCatch */
+   	else if(!GameServer()->m_pController->IsZCatch() || g_Config.m_SvMode == 0)
+    	{
+        if(Index == ENTITY_ARMOR_1)
+            Type = POWERUP_ARMOR;
+        else if(Index == ENTITY_HEALTH_1)
+            Type = POWERUP_HEALTH;
+        else if(Index == ENTITY_WEAPON_SHOTGUN)
+        {
+            Type = POWERUP_WEAPON;
+            SubType = WEAPON_SHOTGUN;
+        }
+        else if(Index == ENTITY_WEAPON_GRENADE)
+        {
+            Type = POWERUP_WEAPON;
+            SubType = WEAPON_GRENADE;
+        }
+        else if(Index == ENTITY_WEAPON_RIFLE)
+        {
+            Type = POWERUP_WEAPON;
+            SubType = WEAPON_RIFLE;
+        }
+        else if(Index == ENTITY_POWERUP_NINJA && g_Config.m_SvPowerups)
+        {
+            Type = POWERUP_NINJA;
+            SubType = WEAPON_NINJA;
+        }
+    }
+    /* end zCatch*/
 
 	if(Type != -1)
 	{
@@ -219,6 +176,11 @@ bool IGameController::OnEntity(int Index, vec2 Pos)
 	return false;
 }
 
+bool IGameController::IsZCatch()
+{
+	return false;
+}
+
 void IGameController::EndRound()
 {
 	if(m_Warmup) // game can't end when we are running warmup
@@ -265,6 +227,7 @@ void IGameController::StartRound()
 	m_aTeamscore[TEAM_RED] = 0;
 	m_aTeamscore[TEAM_BLUE] = 0;
 	m_ForceBalanced = false;
+	Server()->DemoRecorder_HandleAutoStart();
 	char aBuf[256];
 	str_format(aBuf, sizeof(aBuf), "start round type='%s' teamplay='%d'", m_pGameType, m_GameFlags&GAMEFLAG_TEAMS);
 	GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf);
@@ -568,6 +531,8 @@ void IGameController::Tick()
 			}
 		}
 	}
+
+	DoWincheck();
 }
 
 
@@ -706,51 +671,50 @@ bool IGameController::CanChangeTeam(CPlayer *pPlayer, int JoinTeam)
 		return true;
 }
 
-void IGameController::DoPlayerScoreWincheck()
+void IGameController::DoWincheck()
 {
-	if(m_GameOverTick == -1 && !m_Warmup)
+	if(m_GameOverTick == -1 && !m_Warmup && !GameServer()->m_World.m_ResetRequested)
 	{
-		// gather some stats
-		int Topscore = 0;
-		int TopscoreCount = 0;
-		for(int i = 0; i < MAX_CLIENTS; i++)
+		if(IsTeamplay())
 		{
-			if(GameServer()->m_apPlayers[i])
+			// check score win condition
+			if((g_Config.m_SvScorelimit > 0 && (m_aTeamscore[TEAM_RED] >= g_Config.m_SvScorelimit || m_aTeamscore[TEAM_BLUE] >= g_Config.m_SvScorelimit)) ||
+				(g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60))
 			{
-				if(GameServer()->m_apPlayers[i]->m_Score > Topscore)
-				{
-					Topscore = GameServer()->m_apPlayers[i]->m_Score;
-					TopscoreCount = 1;
-				}
-				else if(GameServer()->m_apPlayers[i]->m_Score == Topscore)
-					TopscoreCount++;
+				if(m_aTeamscore[TEAM_RED] != m_aTeamscore[TEAM_BLUE])
+					EndRound();
+				else
+					m_SuddenDeath = 1;
 			}
 		}
-
-		// check score win condition
-		if((g_Config.m_SvScorelimit > 0 && Topscore >= g_Config.m_SvScorelimit) ||
-			(g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60))
+		else
 		{
-			if(TopscoreCount == 1)
-				EndRound();
-			else
-				m_SuddenDeath = 1;
-		}
-	}
-}
+			// gather some stats
+			int Topscore = 0;
+			int TopscoreCount = 0;
+			for(int i = 0; i < MAX_CLIENTS; i++)
+			{
+				if(GameServer()->m_apPlayers[i])
+				{
+					if(GameServer()->m_apPlayers[i]->m_Score > Topscore)
+					{
+						Topscore = GameServer()->m_apPlayers[i]->m_Score;
+						TopscoreCount = 1;
+					}
+					else if(GameServer()->m_apPlayers[i]->m_Score == Topscore)
+						TopscoreCount++;
+				}
+			}
 
-void IGameController::DoTeamScoreWincheck()
-{
-	if(m_GameOverTick == -1 && !m_Warmup)
-	{
-		// check score win condition
-		if((g_Config.m_SvScorelimit > 0 && (m_aTeamscore[TEAM_RED] >= g_Config.m_SvScorelimit || m_aTeamscore[TEAM_BLUE] >= g_Config.m_SvScorelimit)) ||
-			(g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60))
-		{
-			if(m_aTeamscore[TEAM_RED] != m_aTeamscore[TEAM_BLUE])
-				EndRound();
-			else
-				m_SuddenDeath = 1;
+			// check score win condition
+			if((g_Config.m_SvScorelimit > 0 && Topscore >= g_Config.m_SvScorelimit) ||
+				(g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60))
+			{
+				if(TopscoreCount == 1)
+					EndRound();
+				else
+					m_SuddenDeath = 1;
+			}
 		}
 	}
 }
diff --git a/src/game/server/gamecontroller.h b/src/game/server/gamecontroller.h
index 6ccfe977..914f7985 100644
--- a/src/game/server/gamecontroller.h
+++ b/src/game/server/gamecontroller.h
@@ -39,7 +39,6 @@ protected:
 
 	float EvaluateSpawnPos(CSpawnEval *pEval, vec2 Pos);
 	void EvaluateSpawnType(CSpawnEval *pEval, int Type);
-	void FindFreeSpawn(CSpawnEval *pEval, int Type);
 	bool EvaluateSpawn(class CPlayer *pP, vec2 *pPos);
 
 	void CycleMap();
@@ -69,13 +68,12 @@ public:
 	IGameController(class CGameContext *pGameServer);
 	virtual ~IGameController();
 
-	void DoTeamScoreWincheck();
-	void DoPlayerScoreWincheck();
+	virtual void DoWincheck();
 
 	void DoWarmup(int Seconds);
 
-	void StartRound();
-	void EndRound();
+	virtual void StartRound();
+	virtual void EndRound();
 	void ChangeMap(const char *pToMap);
 
 	bool IsFriendlyFire(int ClientID1, int ClientID2);
@@ -143,6 +141,8 @@ public:
 	int ClampTeam(int Team);
 
 	virtual void PostReset();
+	
+	virtual bool IsZCatch();
 };
 
 #endif
diff --git a/src/game/server/gamemodes/ctf.cpp b/src/game/server/gamemodes/ctf.cpp
index b1d3d2fa..66cc4c2c 100644
--- a/src/game/server/gamemodes/ctf.cpp
+++ b/src/game/server/gamemodes/ctf.cpp
@@ -1,6 +1,9 @@
 /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
 /* If you are missing that file, acquire a complete release at teeworlds.com.                */
+#include <engine/shared/config.h>
+
 #include <game/mapitems.h>
+
 #include <game/server/entities/character.h>
 #include <game/server/entities/flag.h>
 #include <game/server/player.h>
@@ -63,6 +66,30 @@ int CGameControllerCTF::OnCharacterDeath(class CCharacter *pVictim, class CPlaye
 	return HadFlag;
 }
 
+void CGameControllerCTF::DoWincheck()
+{
+	if(m_GameOverTick == -1 && !m_Warmup)
+	{
+		// check score win condition
+		if((g_Config.m_SvScorelimit > 0 && (m_aTeamscore[TEAM_RED] >= g_Config.m_SvScorelimit || m_aTeamscore[TEAM_BLUE] >= g_Config.m_SvScorelimit)) ||
+			(g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60))
+		{
+			if(m_SuddenDeath)
+			{
+				if(m_aTeamscore[TEAM_RED]/100 != m_aTeamscore[TEAM_BLUE]/100)
+					EndRound();
+			}
+			else
+			{
+				if(m_aTeamscore[TEAM_RED] != m_aTeamscore[TEAM_BLUE])
+					EndRound();
+				else
+					m_SuddenDeath = 1;
+			}
+		}
+	}
+}
+
 bool CGameControllerCTF::CanBeMovedOnBalance(int ClientID)
 {
 	CCharacter* Character = GameServer()->m_apPlayers[ClientID]->GetCharacter();
@@ -117,7 +144,8 @@ void CGameControllerCTF::Tick()
 {
 	IGameController::Tick();
 
-	DoTeamScoreWincheck();
+	if(GameServer()->m_World.m_ResetRequested || GameServer()->m_World.m_Paused)
+		return;
 
 	for(int fi = 0; fi < 2; fi++)
 	{
@@ -220,13 +248,16 @@ void CGameControllerCTF::Tick()
 
 					for(int c = 0; c < MAX_CLIENTS; c++)
 					{
-						if(!GameServer()->m_apPlayers[c])
+						CPlayer *pPlayer = GameServer()->m_apPlayers[c];
+						if(!pPlayer)
 							continue;
 
-						if(GameServer()->m_apPlayers[c]->GetTeam() == fi)
-							GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_EN, GameServer()->m_apPlayers[c]->GetCID());
+						if(pPlayer->GetTeam() == TEAM_SPECTATORS && pPlayer->m_SpectatorID != SPEC_FREEVIEW && GameServer()->m_apPlayers[pPlayer->m_SpectatorID] && GameServer()->m_apPlayers[pPlayer->m_SpectatorID]->GetTeam() == fi)
+							GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_EN, c);
+						else if(pPlayer->GetTeam() == fi)
+							GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_EN, c);
 						else
-							GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_PL, GameServer()->m_apPlayers[c]->GetCID());
+							GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_PL, c);
 					}
 					break;
 				}
diff --git a/src/game/server/gamemodes/ctf.h b/src/game/server/gamemodes/ctf.h
index 44bd9e8e..72747ed7 100644
--- a/src/game/server/gamemodes/ctf.h
+++ b/src/game/server/gamemodes/ctf.h
@@ -11,6 +11,7 @@ public:
 	class CFlag *m_apFlags[2];
 
 	CGameControllerCTF(class CGameContext *pGameServer);
+	virtual void DoWincheck();
 	virtual bool CanBeMovedOnBalance(int ClientID);
 	virtual void Snap(int SnappingClient);
 	virtual void Tick();
diff --git a/src/game/server/gamemodes/dm.cpp b/src/game/server/gamemodes/dm.cpp
index d2b69b43..bdca4c9a 100644
--- a/src/game/server/gamemodes/dm.cpp
+++ b/src/game/server/gamemodes/dm.cpp
@@ -11,6 +11,5 @@ CGameControllerDM::CGameControllerDM(class CGameContext *pGameServer)
 
 void CGameControllerDM::Tick()
 {
-	DoPlayerScoreWincheck();
 	IGameController::Tick();
 }
diff --git a/src/game/server/gamemodes/mod.cpp b/src/game/server/gamemodes/mod.cpp
index 127be2bc..eb8fd7c8 100644
--- a/src/game/server/gamemodes/mod.cpp
+++ b/src/game/server/gamemodes/mod.cpp
@@ -15,8 +15,6 @@ CGameControllerMOD::CGameControllerMOD(class CGameContext *pGameServer)
 void CGameControllerMOD::Tick()
 {
 	// this is the main part of the gamemode, this function is run every tick
-	DoPlayerScoreWincheck(); // checks for winners, no teams version
-	//DoTeamScoreWincheck(); // checks for winners, two teams version
 
 	IGameController::Tick();
 }
diff --git a/src/game/server/gamemodes/tdm.cpp b/src/game/server/gamemodes/tdm.cpp
index 54e645b3..50ecd93e 100644
--- a/src/game/server/gamemodes/tdm.cpp
+++ b/src/game/server/gamemodes/tdm.cpp
@@ -48,6 +48,5 @@ void CGameControllerTDM::Snap(int SnappingClient)
 
 void CGameControllerTDM::Tick()
 {
-	DoTeamScoreWincheck();
 	IGameController::Tick();
 }
diff --git a/src/game/server/gamemodes/zcatch.cpp b/src/game/server/gamemodes/zcatch.cpp
new file mode 100644
index 00000000..eac8a4d3
--- /dev/null
+++ b/src/game/server/gamemodes/zcatch.cpp
@@ -0,0 +1,230 @@
+/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
+/* If you are missing that file, acquire a complete release at teeworlds.com.                */
+/* zCatch by erd and Teetime */
+
+#include <engine/shared/config.h>
+#include <game/server/gamecontext.h>
+#include "zcatch.hpp"
+
+CGameController_zCatch::CGameController_zCatch(class CGameContext *pGameServer) 
+: IGameController(pGameServer)
+{
+	m_pGameType = "zCatch";
+	m_OldMode = g_Config.m_SvMode;
+}
+
+void CGameController_zCatch::Tick()
+{
+	DoWincheck();
+	IGameController::Tick();
+	
+	if(m_OldMode != g_Config.m_SvMode)
+	{
+		Server()->MapReload();
+		m_OldMode = g_Config.m_SvMode;
+	}
+}
+
+bool CGameController_zCatch::IsZCatch()
+{
+	return true;
+}
+
+void CGameController_zCatch::DoWincheck()
+{
+	int Players = 0, Players_Spec = 0, Players_SpecExplicit = 0;	
+	
+	for(int i = 0; i < MAX_CLIENTS; i++)
+	{
+		if(GameServer()->m_apPlayers[i])
+		{
+			Players++;
+			if(GameServer()->m_apPlayers[i]->GetTeam() == TEAM_SPECTATORS)
+				Players_Spec++;
+			if(GameServer()->m_apPlayers[i]->m_SpecExplicit == 1)
+				Players_SpecExplicit++;
+		}
+	}
+	
+	if(Players == 1)
+	{
+		//Do nothing
+	}
+	else if((Players - Players_Spec == 1) && (Players != Players_Spec) && (Players - Players_SpecExplicit != 1)) 
+	{
+		for(int i = 0; i < MAX_CLIENTS; i++)
+		{
+			if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS)
+				GameServer()->m_apPlayers[i]->m_Score += g_Config.m_SvBonus;
+		}
+		EndRound();
+	}
+	
+	IGameController::DoWincheck(); //do also usual wincheck
+}
+
+int CGameController_zCatch::OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int WeaponID)
+{
+	int VictimID =  pVictim->GetPlayer()->GetCID();
+	char buf[256];
+	if(pKiller !=  pVictim->GetPlayer())
+	{
+		pKiller->m_Kills++;
+		pVictim->GetPlayer()->m_Deaths++; 
+		
+		pKiller->m_Score++;
+		pVictim->GetPlayer()->m_Score--;
+		
+		/* Check if the killer is already killed and in spectator (victim may died through wallshot) */
+		if(pKiller->GetTeam() != TEAM_SPECTATORS)
+		{
+			pVictim->GetPlayer()->m_CatchedBy = pKiller->GetCID();
+			pVictim->GetPlayer()->SetTeamDirect(TEAM_SPECTATORS);
+		
+			if(pVictim->GetPlayer()->m_PlayerWantToFollowCatcher)
+				pVictim->GetPlayer()->m_SpectatorID = pKiller->GetCID(); // Let the victim follow his catcher
+		
+			str_format(buf, sizeof(buf), "Caught by \"%s\". You will join the game automatically when \"%s\" dies.", Server()->ClientName(pKiller->GetCID()), Server()->ClientName(pKiller->GetCID()));	
+			GameServer()->SendChatTarget(VictimID, buf);
+		}
+	}
+	else
+	{
+		//Punish selfkill/death
+		if(WeaponID == WEAPON_SELF || WeaponID == WEAPON_WORLD)
+			pVictim->GetPlayer()->m_Score -= 15;
+	}
+	
+	for(int i=0; i < MAX_CLIENTS; i++)
+	{
+		if(GameServer()->m_apPlayers[i])
+		{
+			if(GameServer()->m_apPlayers[i]->m_CatchedBy == VictimID)
+			{
+				GameServer()->m_apPlayers[i]->m_CatchedBy = ZCATCH_NOT_CATCHED;
+				GameServer()->m_apPlayers[i]->SetTeamDirect(GameServer()->m_pController->ClampTeam(1));
+				
+				if(pKiller != pVictim->GetPlayer())
+					pKiller->m_Score++;
+			}
+		}
+	}
+		
+	// Update color of the killer
+	OnPlayerInfoChange(pKiller);
+	
+	return 0;
+}
+
+void CGameController_zCatch::OnPlayerInfoChange(class CPlayer *pP)
+{
+	if(g_Config.m_SvColorIndicator)
+	{
+		int Num = 161;
+		for(int i = 0; i < MAX_CLIENTS; i++)
+			if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->m_CatchedBy == pP->GetCID())
+				Num -= 10;
+		pP->m_TeeInfos.m_ColorBody = Num * 0x010000 + 0xff00;
+		pP->m_TeeInfos.m_ColorFeet = Num * 0x010000 + 0xff00;
+		pP->m_TeeInfos.m_UseCustomColor = 1;
+	}
+}
+
+void CGameController_zCatch::StartRound()
+{
+	ResetGame();
+
+	m_RoundStartTick = Server()->Tick();
+	m_SuddenDeath = 0;
+	m_GameOverTick = -1;
+	GameServer()->m_World.m_Paused = false;
+	m_aTeamscore[TEAM_RED] = 0;
+	m_aTeamscore[TEAM_BLUE] = 0;
+	m_UnbalancedTick = -1;
+	m_ForceBalanced = false;
+	for(int i=0; i<MAX_CLIENTS; i++)
+	{
+		if(GameServer()->m_apPlayers[i])
+		{		
+			GameServer()->m_apPlayers[i]->m_CatchedBy = ZCATCH_NOT_CATCHED;
+			GameServer()->m_apPlayers[i]->m_Kills = 0;
+			GameServer()->m_apPlayers[i]->m_Deaths = 0;
+			GameServer()->m_apPlayers[i]->m_TicksSpec = 0;
+			GameServer()->m_apPlayers[i]->m_TicksIngame = 0;
+		}
+	}
+	char aBufMsg[256];
+	str_format(aBufMsg, sizeof(aBufMsg), "start round type='%s' teamplay='%d'", m_pGameType, m_GameFlags&GAMEFLAG_TEAMS);
+	GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBufMsg);
+}
+
+void CGameController_zCatch::OnCharacterSpawn(class CCharacter *pChr)
+{
+	// default health and armor
+	pChr->IncreaseHealth(10);
+	if(g_Config.m_SvMode == 2)
+		pChr->IncreaseArmor(10);
+	// give default weapons
+	switch(g_Config.m_SvMode)
+		{
+			case 0:
+				pChr->GiveWeapon(WEAPON_HAMMER, -1);
+				pChr->GiveWeapon(WEAPON_GUN, 10);
+				break;
+			case 1:
+				pChr->GiveWeapon(WEAPON_RIFLE, -1);
+				break;
+			case 2:
+				pChr->GiveWeapon(WEAPON_HAMMER, -1);
+				pChr->GiveWeapon(WEAPON_GUN, -1);
+				pChr->GiveWeapon(WEAPON_GRENADE, -1);
+				pChr->GiveWeapon(WEAPON_SHOTGUN, -1);
+				pChr->GiveWeapon(WEAPON_RIFLE, -1);
+				break;
+			case 3:
+				pChr->GiveWeapon(WEAPON_HAMMER, -1);
+				break;
+			case 4:
+				pChr->GiveWeapon(WEAPON_GRENADE, -1);
+				break;
+			case 5:
+				pChr->GiveNinja();
+				break;
+		}
+	//Update color of spawning tees
+	OnPlayerInfoChange(pChr->GetPlayer());
+}
+
+void CGameController_zCatch::EndRound()
+{
+	for(int i = 0; i < MAX_CLIENTS; i++)
+	{
+		if(GameServer()->m_apPlayers[i])
+		{
+				
+			if(GameServer()->m_apPlayers[i]->m_SpecExplicit == 0)
+			{
+				GameServer()->m_apPlayers[i]->SetTeamDirect(GameServer()->m_pController->ClampTeam(1));
+				
+				char abuf[128];
+				str_format(abuf, sizeof(abuf), "Kills: %d | Deaths: %d", GameServer()->m_apPlayers[i]->m_Kills, GameServer()->m_apPlayers[i]->m_Deaths);				
+				GameServer()->SendChatTarget(i, abuf);
+				
+				if(GameServer()->m_apPlayers[i]->m_TicksSpec != 0 || GameServer()->m_apPlayers[i]->m_TicksIngame != 0)
+				{
+					double TimeInSpec = (GameServer()->m_apPlayers[i]->m_TicksSpec*100.0) / (GameServer()->m_apPlayers[i]->m_TicksIngame + GameServer()->m_apPlayers[i]->m_TicksSpec);
+					str_format(abuf, sizeof(abuf), "Spec: %.2f%% | Ingame: %.2f%%", (double)TimeInSpec, (double)(100.0 - TimeInSpec));
+					GameServer()->SendChatTarget(i, abuf);	
+				}
+				GameServer()->m_apPlayers[i]->m_CatchedBy = ZCATCH_NOT_CATCHED; //Set all players in server as non-catched
+			}
+		}
+	}
+
+	if(m_Warmup) // game can't end when we are running warmup
+		return;
+
+	GameServer()->m_World.m_Paused = true;
+	m_GameOverTick = Server()->Tick();
+	m_SuddenDeath = 0;
+}
diff --git a/src/game/server/gamemodes/zcatch.hpp b/src/game/server/gamemodes/zcatch.hpp
new file mode 100644
index 00000000..58e43f61
--- /dev/null
+++ b/src/game/server/gamemodes/zcatch.hpp
@@ -0,0 +1,33 @@
+/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
+/* If you are missing that file, acquire a complete release at teeworlds.com.                */
+/* zCatch by erd and Teetime */
+
+#ifndef GAME_SERVER_GAMEMODES_ZCATCH_H
+#define GAME_SERVER_GAMEMODES_ZCATCH_H
+
+#include <game/server/gamecontroller.h>
+
+class CGameController_zCatch : public IGameController
+{
+	private:
+	int m_OldMode;
+	
+	public:
+	CGameController_zCatch(class CGameContext *pGameServer);
+	virtual void Tick();
+	virtual void DoWincheck();
+	virtual bool IsZCatch();
+	
+	enum
+	{
+		ZCATCH_NOT_CATCHED = -1,
+	}; 
+	
+	virtual int OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int WeaponID);
+	virtual void StartRound();
+	virtual void OnCharacterSpawn(class CCharacter *pChr);
+	virtual void OnPlayerInfoChange(class CPlayer *pP);
+	virtual void EndRound();
+};
+
+#endif
diff --git a/src/game/server/gameworld.cpp b/src/game/server/gameworld.cpp
index 6444cce5..89f4808d 100644
--- a/src/game/server/gameworld.cpp
+++ b/src/game/server/gameworld.cpp
@@ -183,7 +183,6 @@ CCharacter *CGameWorld::IntersectCharacter(vec2 Pos0, vec2 Pos1, float Radius, v
 {
 	// Find other players
 	float ClosestLen = distance(Pos0, Pos1) * 100.0f;
-	vec2 LineDir = normalize(Pos1-Pos0);
 	CCharacter *pClosest = 0;
 
 	CCharacter *p = (CCharacter *)FindFirst(ENTTYPE_CHARACTER);
diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp
index aeafe3a4..7464e3c7 100644
--- a/src/game/server/player.cpp
+++ b/src/game/server/player.cpp
@@ -15,17 +15,29 @@ CPlayer::CPlayer(CGameContext *pGameServer, int ClientID, int Team)
 	m_RespawnTick = Server()->Tick();
 	m_DieTick = Server()->Tick();
 	m_ScoreStartTick = Server()->Tick();
-	Character = 0;
-	this->m_ClientID = ClientID;
+	m_pCharacter = 0;
+	m_ClientID = ClientID;
 	m_Team = GameServer()->m_pController->ClampTeam(Team);
 	m_SpectatorID = SPEC_FREEVIEW;
 	m_LastActionTick = Server()->Tick();
+	m_TeamChangeTick = Server()->Tick();
+	
+	//zCatch
+	m_CatchedBy = -1;
+	m_SpecExplicit = 0;
+	m_Kills = 0;
+	m_Deaths = 0;
+	m_PlayerWantToFollowCatcher = g_Config.m_SvFollowCatcher;
+	m_LastKillTry = Server()->Tick();
+	m_TicksSpec = 0;
+	m_TicksIngame = 0;
+	m_ChatTicks = 0;
 }
 
 CPlayer::~CPlayer()
 {
-	delete Character;
-	Character = 0;
+	delete m_pCharacter;
+	m_pCharacter = 0;
 }
 
 void CPlayer::Tick()
@@ -37,6 +49,20 @@ void CPlayer::Tick()
 		return;
 
 	Server()->SetClientScore(m_ClientID, m_Score);
+	
+	/* begin zCatch*/
+	
+	if(m_Team == TEAM_SPECTATORS)
+		m_TicksSpec++;
+	else
+		m_TicksIngame++;
+	
+	if(m_ChatTicks > 0)
+		m_ChatTicks--;
+	
+	if(g_Config.m_SvAnticamper && m_pCharacter && !GameServer()->m_World.m_Paused)
+		Anticamper();
+	/* end zCatch*/
 
 	// do latency stuff
 	{
@@ -59,19 +85,22 @@ void CPlayer::Tick()
 		}
 	}
 
-	if(!Character && m_DieTick+Server()->TickSpeed()*3 <= Server()->Tick())
+	if(!m_pCharacter && m_Team == TEAM_SPECTATORS && m_SpectatorID == SPEC_FREEVIEW)
+		m_ViewPos -= vec2(clamp(m_ViewPos.x-m_LatestActivity.m_TargetX, -500.0f, 500.0f), clamp(m_ViewPos.y-m_LatestActivity.m_TargetY, -400.0f, 400.0f));
+
+	if(!m_pCharacter && m_DieTick+Server()->TickSpeed()*3 <= Server()->Tick())
 		m_Spawning = true;
 
-	if(Character)
+	if(m_pCharacter)
 	{
-		if(Character->IsAlive())
+		if(m_pCharacter->IsAlive())
 		{
-			m_ViewPos = Character->m_Pos;
+			m_ViewPos = m_pCharacter->m_Pos;
 		}
 		else
 		{
-			delete Character;
-			Character = 0;
+			delete m_pCharacter;
+			m_pCharacter = 0;
 		}
 	}
 	else if(m_Spawning && m_RespawnTick <= Server()->Tick())
@@ -160,23 +189,38 @@ void CPlayer::OnDisconnect(const char *pReason)
 
 void CPlayer::OnPredictedInput(CNetObj_PlayerInput *NewInput)
 {
-	if(Character)
-		Character->OnPredictedInput(NewInput);
+	// skip the input if chat is active
+	if((m_PlayerFlags&PLAYERFLAG_CHATTING) && (NewInput->m_PlayerFlags&PLAYERFLAG_CHATTING))
+		return;
+
+	if(m_pCharacter)
+		m_pCharacter->OnPredictedInput(NewInput);
 }
 
 void CPlayer::OnDirectInput(CNetObj_PlayerInput *NewInput)
 {
+	if(NewInput->m_PlayerFlags&PLAYERFLAG_CHATTING)
+	{
+		// skip the input if chat is active
+		if(m_PlayerFlags&PLAYERFLAG_CHATTING)
+			return;
+
+		// reset input
+		if(m_pCharacter)
+			m_pCharacter->ResetInput();
+
+		m_PlayerFlags = NewInput->m_PlayerFlags;
+ 		return;
+	}
+
 	m_PlayerFlags = NewInput->m_PlayerFlags;
 
-	if(Character)
-		Character->OnDirectInput(NewInput);
+	if(m_pCharacter)
+		m_pCharacter->OnDirectInput(NewInput);
 
-	if(!Character && m_Team != TEAM_SPECTATORS && (NewInput->m_Fire&1))
+	if(!m_pCharacter && m_Team != TEAM_SPECTATORS && (NewInput->m_Fire&1))
 		m_Spawning = true;
 
-	if(!Character && m_Team == TEAM_SPECTATORS && m_SpectatorID == SPEC_FREEVIEW)
-		m_ViewPos = vec2(NewInput->m_TargetX, NewInput->m_TargetY);
-
 	// check for activity
 	if(NewInput->m_Direction || m_LatestActivity.m_TargetX != NewInput->m_TargetX ||
 		m_LatestActivity.m_TargetY != NewInput->m_TargetY || NewInput->m_Jump ||
@@ -190,18 +234,18 @@ void CPlayer::OnDirectInput(CNetObj_PlayerInput *NewInput)
 
 CCharacter *CPlayer::GetCharacter()
 {
-	if(Character && Character->IsAlive())
-		return Character;
+	if(m_pCharacter && m_pCharacter->IsAlive())
+		return m_pCharacter;
 	return 0;
 }
 
 void CPlayer::KillCharacter(int Weapon)
 {
-	if(Character)
+	if(m_pCharacter)
 	{
-		Character->Die(m_ClientID, Weapon);
-		delete Character;
-		Character = 0;
+		m_pCharacter->Die(m_ClientID, Weapon);
+		delete m_pCharacter;
+		m_pCharacter = 0;
 	}
 }
 
@@ -244,6 +288,11 @@ void CPlayer::SetTeam(int Team)
 	}
 }
 
+void CPlayer::SetTeamDirect(int Team)
+{
+	m_Team = Team;
+}
+
 void CPlayer::TryRespawn()
 {
 	vec2 SpawnPos;
@@ -252,7 +301,43 @@ void CPlayer::TryRespawn()
 		return;
 
 	m_Spawning = false;
-	Character = new(m_ClientID) CCharacter(&GameServer()->m_World);
-	Character->Spawn(this, SpawnPos);
+	m_pCharacter = new(m_ClientID) CCharacter(&GameServer()->m_World);
+	m_pCharacter->Spawn(this, SpawnPos);
 	GameServer()->CreatePlayerSpawn(SpawnPos);
 }
+
+int CPlayer::Anticamper()
+{
+	int AnticamperTime = g_Config.m_SvAnticamperTime;
+	int AnticamperRange = g_Config.m_SvAnticamperRange;
+
+	if(m_CampTick == -1)
+	{
+		m_CampPos = m_pCharacter->m_Pos;
+		m_CampTick = Server()->Tick() + Server()->TickSpeed()*AnticamperTime;
+	}
+
+	// Check if the player is moving
+	if((m_CampPos.x - m_pCharacter->m_Pos.x >= (float)AnticamperRange || m_CampPos.x - m_pCharacter->m_Pos.x <= -(float)AnticamperRange)
+	|| (m_CampPos.y - m_pCharacter->m_Pos.y >= (float)AnticamperRange || m_CampPos.y - m_pCharacter->m_Pos.y <= -(float)AnticamperRange))
+		{
+			m_CampTick = -1;
+		}
+
+	// Send warning to the player
+	if(m_CampTick <= Server()->Tick() + Server()->TickSpeed() * AnticamperTime/2 && m_CampTick != -1 && !m_SentCampMsg)
+	{
+		GameServer()->SendBroadcast("ANTICAMPER: Move or die", m_ClientID);
+		m_SentCampMsg = true;
+	}
+
+	// Kill him
+	if((m_CampTick <= Server()->Tick()) && (m_CampTick > 0))
+	{
+		m_pCharacter->Die(m_ClientID, WEAPON_ANTICAMPER);
+		m_CampTick = -1;
+		m_SentCampMsg = false;
+		return 1;
+	}
+	return 0;
+}
diff --git a/src/game/server/player.h b/src/game/server/player.h
index 9d5e462c..99de2952 100644
--- a/src/game/server/player.h
+++ b/src/game/server/player.h
@@ -21,6 +21,7 @@ public:
 	void TryRespawn();
 	void Respawn();
 	void SetTeam(int Team);
+	void SetTeamDirect(int Team); //zCatch
 	int GetTeam() const { return m_Team; };
 	int GetCID() const { return m_ClientID; };
 
@@ -78,6 +79,7 @@ public:
 	int m_ScoreStartTick;
 	bool m_ForceBalanced;
 	int m_LastActionTick;
+	int m_TeamChangeTick;
 	struct
 	{
 		int m_TargetX;
@@ -94,9 +96,26 @@ public:
 		int m_Min;
 		int m_Max;
 	} m_Latency;
-
+	
+		//zCatch:
+	int m_CatchedBy;
+	int m_SpecExplicit;
+	int m_Deaths;
+	int m_Kills;
+	int m_LastKillTry;
+	bool m_PlayerWantToFollowCatcher;
+	
+	int m_TicksSpec;
+	int m_TicksIngame;
+	int m_ChatTicks;
+	//Anticamper
+	int Anticamper();
+	bool m_SentCampMsg;
+	int m_CampTick;
+	vec2 m_CampPos;
+	
 private:
-	CCharacter *Character;
+	CCharacter *m_pCharacter;
 	CGameContext *m_pGameServer;
 
 	CGameContext *GameServer() const { return m_pGameServer; }
diff --git a/src/game/variables.h b/src/game/variables.h
index ee8cb74a..3f0d7bf9 100644
--- a/src/game/variables.h
+++ b/src/game/variables.h
@@ -45,6 +45,7 @@ MACRO_CONFIG_INT(UiPage, ui_page, 5, 0, 10, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interf
 MACRO_CONFIG_INT(UiToolboxPage, ui_toolbox_page, 0, 0, 2, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Toolbox page")
 MACRO_CONFIG_STR(UiServerAddress, ui_server_address, 64, "localhost:8303", CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface server address")
 MACRO_CONFIG_INT(UiScale, ui_scale, 100, 50, 150, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface scale")
+MACRO_CONFIG_INT(UiMousesens, ui_mousesens, 100, 5, 100000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Mouse sensitivity for menus/editor")
 
 MACRO_CONFIG_INT(UiColorHue, ui_color_hue, 160, 0, 255, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface color hue")
 MACRO_CONFIG_INT(UiColorSat, ui_color_sat, 70, 0, 255, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface color saturation")
@@ -73,7 +74,9 @@ MACRO_CONFIG_INT(SvTeambalanceTime, sv_teambalance_time, 1, 0, 1000, CFGFLAG_SER
 MACRO_CONFIG_INT(SvInactiveKickTime, sv_inactivekick_time, 3, 0, 1000, CFGFLAG_SERVER, "How many minutes to wait before taking care of inactive players")
 MACRO_CONFIG_INT(SvInactiveKick, sv_inactivekick, 1, 0, 2, CFGFLAG_SERVER, "How to deal with inactive players (0=move to spectator, 1=move to free spectator slot/kick, 2=kick)")
 
+MACRO_CONFIG_INT(SvStrictSpectateMode, sv_strict_spectate_mode, 0, 0, 1, CFGFLAG_SERVER, "Restricts information in spectator mode")
 MACRO_CONFIG_INT(SvVoteSpectate, sv_vote_spectate, 1, 0, 1, CFGFLAG_SERVER, "Allow voting to move players to spectators")
+MACRO_CONFIG_INT(SvVoteSpectateRejoindelay, sv_vote_spectate_rejoindelay, 3, 0, 1000, CFGFLAG_SERVER, "How many minutes to wait before a player can rejoin after being moved to spectators by vote")
 MACRO_CONFIG_INT(SvVoteKick, sv_vote_kick, 1, 0, 1, CFGFLAG_SERVER, "Allow voting to kick players")
 MACRO_CONFIG_INT(SvVoteKickMin, sv_vote_kick_min, 0, 0, MAX_CLIENTS, CFGFLAG_SERVER, "Minimum number of players required to start a kick vote")
 MACRO_CONFIG_INT(SvVoteKickBantime, sv_vote_kick_bantime, 5, 0, 1440, CFGFLAG_SERVER, "The time to ban a player if kicked by vote. 0 makes it just use kick")
@@ -85,4 +88,19 @@ MACRO_CONFIG_INT(SvVoteKickBantime, sv_vote_kick_bantime, 5, 0, 1440, CFGFLAG_SE
 
 MACRO_CONFIG_INT(DbgFocus, dbg_focus, 0, 0, 1, CFGFLAG_CLIENT, "")
 MACRO_CONFIG_INT(DbgTuning, dbg_tuning, 0, 0, 1, CFGFLAG_CLIENT, "")
+
+//zCatch:
+MACRO_CONFIG_INT(SvMode, sv_mode, 1, 0, 5, CFGFLAG_SERVER, "0 - Normal; 1 - Instagib; 2 - Rocket area; 3 - Hammerparty; 4 - Grenade; 5 - Ninja")
+MACRO_CONFIG_INT(SvAllowJoin, sv_allow_join, 2, 1, 2, CFGFLAG_SERVER, "Allow new Players to join without waiting for the next round")
+//1 = Allowed to join; 2 = Will join when person with the most kills die
+MACRO_CONFIG_INT(SvColorIndicator, sv_color_indicator, 1, 0, 1, CFGFLAG_SERVER, "Color tees apropriate to the number of currently catched players")
+MACRO_CONFIG_INT(SvBonus, sv_bonus, 0, 0, 10000, CFGFLAG_SERVER, "Give the last player extra points")
+MACRO_CONFIG_INT(SvFollowCatcher, sv_follow_catcher, 1, 0, 1, CFGFLAG_SERVER, "If a victim should follow his catcher")
+
+MACRO_CONFIG_INT(SvChatValue, sv_chat_value, 250, 100, 1000, CFGFLAG_SERVER, "A value wich is added on each message and decreased on each tick")
+MACRO_CONFIG_INT(SvChatThreshold, sv_chat_threshold, 1000, 250, 10000, CFGFLAG_SERVER, "If this threshold will exceed by too many messages the player will be muted")
+MACRO_CONFIG_INT(SvMuteDuration, sv_mute_duration, 60, 0, 3600, CFGFLAG_SERVER, "How long the player will be muted (in seconds)")
+MACRO_CONFIG_INT(SvAnticamper, sv_anticamper, 1, 0, 1, CFGFLAG_SERVER, "Toggle to enable/disable Anticamper")
+MACRO_CONFIG_INT(SvAnticamperTime, sv_anticamper_time, 10, 5, 120, CFGFLAG_SERVER, "How long to wait till the player dies/freezes")
+MACRO_CONFIG_INT(SvAnticamperRange, sv_anticamper_range, 200, 0, 1000, CFGFLAG_SERVER, "Distance how far away the player must move to escape anticamper")
 #endif
diff --git a/src/game/version.h b/src/game/version.h
index 8cf1a7c0..76b6d4ab 100644
--- a/src/game/version.h
+++ b/src/game/version.h
@@ -2,7 +2,7 @@
 /* If you are missing that file, acquire a complete release at teeworlds.com.                */
 #ifndef GAME_VERSION_H
 #define GAME_VERSION_H
-#include "generated/nethash.c"
-#define GAME_VERSION "0.6 trunk"
+#include "generated/nethash.cpp"
+#define GAME_VERSION "0.6.1"
 #define GAME_NETVERSION "0.6 " GAME_NETVERSION_HASH
 #endif
diff --git a/src/tools/dilate.cpp b/src/tools/dilate.cpp
index ef862270..eb770a90 100644
--- a/src/tools/dilate.cpp
+++ b/src/tools/dilate.cpp
@@ -48,18 +48,18 @@ static void CopyAlpha(int w, int h, CPixel *pSrc, CPixel *pDest)
 			pDest[m].a = pSrc[m].a;
 }
 
-int main(int argc, char **argv)
+int DilateFile(const char *pFileName)
 {
 	png_t Png;
 	CPixel *pBuffer[3] = {0,0,0};
 
 	png_init(0, 0);
-	png_open_file(&Png, argv[1]);
+	png_open_file(&Png, pFileName);
 
 	if(Png.color_type != PNG_TRUECOLOR_ALPHA)
 	{
-		dbg_msg("dilate", "not an RGBA image");
-		return -1;
+		dbg_msg("dilate", "%s: not an RGBA image", pFileName);
+		return 1;
 	}
 
 	pBuffer[0] = (CPixel*)mem_alloc(Png.width*Png.height*sizeof(CPixel), 1);
@@ -81,9 +81,23 @@ int main(int argc, char **argv)
 	CopyAlpha(w, h, pBuffer[0], pBuffer[1]);
 
 	// save here
-	png_open_file_write(&Png, argv[1]);
+	png_open_file_write(&Png, pFileName);
 	png_set_data(&Png, w, h, 8, PNG_TRUECOLOR_ALPHA, (unsigned char *)pBuffer[1]);
 	png_close_file(&Png);
 
 	return 0;
 }
+
+int main(int argc, const char **argv)
+{
+	dbg_logger_stdout();
+	if(argc == 1)
+	{
+		dbg_msg("Usage", "%s FILE1 [ FILE2... ]", argv[0]);
+		return -1;
+	}
+	
+	for(int i = 1; i < argc; i++)
+		DilateFile(argv[i]);
+	return 0;
+}
diff --git a/src/tools/tileset_borderfix.cpp b/src/tools/tileset_borderfix.cpp
index b5eef77a..0facb9a3 100644
--- a/src/tools/tileset_borderfix.cpp
+++ b/src/tools/tileset_borderfix.cpp
@@ -1,6 +1,5 @@
 /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
 /* If you are missing that file, acquire a complete release at teeworlds.com.                */
-#include <stdlib.h>
 #include <base/system.h>
 #include <engine/external/pnglite/pnglite.h>
 
@@ -52,18 +51,18 @@ static void TilesetBorderfix(int w, int h, CPixel *pSrc, CPixel *pDest)
 }
 
 
-int main(int argc, char **argv)
+int FixFile(const char *pFileName)
 {
 	png_t Png;
 	CPixel *pBuffer[2] = {0,0};
 
 	png_init(0, 0);
-	png_open_file(&Png, argv[1]);
+	png_open_file(&Png, pFileName);
 
 	if(Png.color_type != PNG_TRUECOLOR_ALPHA)
 	{
-		dbg_msg("dilate", "not an RGBA image");
-		return -1;
+		dbg_msg("tileset_borderfix", "%s: not an RGBA image", pFileName);
+		return 1;
 	}
 
 	int w = Png.width;
@@ -77,9 +76,23 @@ int main(int argc, char **argv)
 	TilesetBorderfix(w, h, pBuffer[0], pBuffer[1]);
 
 	// save here
-	png_open_file_write(&Png, argv[1]);
+	png_open_file_write(&Png, pFileName);
 	png_set_data(&Png, w, h, 8, PNG_TRUECOLOR_ALPHA, (unsigned char *)pBuffer[1]);
 	png_close_file(&Png);
 
 	return 0;
 }
+
+int main(int argc, const char **argv)
+{
+	dbg_logger_stdout();
+	if(argc == 1)
+	{
+		dbg_msg("Usage", "%s FILE1 [ FILE2... ]", argv[0]);
+		return -1;
+	}
+
+	for(int i = 1; i < argc; i++)
+		FixFile(argv[i]);
+	return 0;
+}