aboutsummaryrefslogtreecommitdiff
path: root/src/utils
diff options
context:
space:
mode:
authorFneufneu <fneufneu@xbmc.org>2014-10-10 15:09:51 +0200
committerFneufneu <fneufneu@xbmc.org>2014-12-15 21:20:40 +0100
commit135fe8734924f79cedace50986a0fa4f12d76647 (patch)
tree5f7612f64385d75e72b78d3f71a97fc2058f4015 /src/utils
parentf981c1dd3c364c05901b3d51ae53899127a4f2e6 (diff)
rename xbmc folder to src
Diffstat (limited to 'src/utils')
-rw-r--r--src/utils/AMLUtils.cpp454
-rw-r--r--src/utils/AMLUtils.h55
-rw-r--r--src/utils/ActorProtocol.cpp297
-rw-r--r--src/utils/ActorProtocol.h89
-rw-r--r--src/utils/AlarmClock.cpp155
-rw-r--r--src/utils/AlarmClock.h79
-rw-r--r--src/utils/AliasShortcutUtils.cpp132
-rw-r--r--src/utils/AliasShortcutUtils.h25
-rw-r--r--src/utils/Archive.cpp441
-rw-r--r--src/utils/Archive.h196
-rw-r--r--src/utils/AsyncFileCopy.cpp113
-rw-r--r--src/utils/AsyncFileCopy.h54
-rw-r--r--src/utils/AutoPtrHandle.cpp136
-rw-r--r--src/utils/AutoPtrHandle.h110
-rw-r--r--src/utils/Base64.cpp142
-rw-r--r--src/utils/Base64.h38
-rw-r--r--src/utils/BitstreamConverter.cpp1216
-rw-r--r--src/utils/BitstreamConverter.h210
-rw-r--r--src/utils/BitstreamStats.cpp85
-rw-r--r--src/utils/BitstreamStats.h61
-rw-r--r--src/utils/BooleanLogic.cpp139
-rw-r--r--src/utils/BooleanLogic.h101
-rw-r--r--src/utils/CPUInfo.cpp957
-rw-r--r--src/utils/CPUInfo.h142
-rw-r--r--src/utils/CharsetConverter.cpp892
-rw-r--r--src/utils/CharsetConverter.h179
-rw-r--r--src/utils/CharsetDetection.cpp649
-rw-r--r--src/utils/CharsetDetection.h106
-rw-r--r--src/utils/Crc32.cpp119
-rw-r--r--src/utils/Crc32.h43
-rw-r--r--src/utils/CryptThreading.cpp123
-rw-r--r--src/utils/CryptThreading.h44
-rw-r--r--src/utils/DatabaseUtils.cpp658
-rw-r--r--src/utils/DatabaseUtils.h161
-rw-r--r--src/utils/EdenVideoArtUpdater.cpp397
-rw-r--r--src/utils/EdenVideoArtUpdater.h56
-rw-r--r--src/utils/EndianSwap.cpp42
-rw-r--r--src/utils/EndianSwap.h108
-rw-r--r--src/utils/Environment.cpp263
-rw-r--r--src/utils/Environment.h106
-rw-r--r--src/utils/Fanart.cpp172
-rw-r--r--src/utils/Fanart.h120
-rw-r--r--src/utils/FileOperationJob.cpp374
-rw-r--r--src/utils/FileOperationJob.h93
-rw-r--r--src/utils/FileUtils.cpp162
-rw-r--r--src/utils/FileUtils.h31
-rw-r--r--src/utils/GLUtils.cpp148
-rw-r--r--src/utils/GLUtils.h46
-rw-r--r--src/utils/GlobalsHandling.h224
-rw-r--r--src/utils/GroupUtils.cpp160
-rw-r--r--src/utils/GroupUtils.h42
-rw-r--r--src/utils/HTMLTable.cpp121
-rw-r--r--src/utils/HTMLTable.h52
-rw-r--r--src/utils/HTMLUtil.cpp334
-rw-r--r--src/utils/HTMLUtil.h39
-rw-r--r--src/utils/HttpHeader.cpp252
-rw-r--r--src/utils/HttpHeader.h65
-rw-r--r--src/utils/HttpParser.cpp266
-rw-r--r--src/utils/HttpParser.h112
-rw-r--r--src/utils/HttpResponse.cpp179
-rw-r--r--src/utils/HttpResponse.h135
-rw-r--r--src/utils/IArchivable.h31
-rw-r--r--src/utils/IRssObserver.h32
-rw-r--r--src/utils/ISerializable.h29
-rw-r--r--src/utils/ISortable.h31
-rw-r--r--src/utils/IXmlDeserializable.h30
-rw-r--r--src/utils/InfoLoader.cpp72
-rw-r--r--src/utils/InfoLoader.h44
-rw-r--r--src/utils/JSONVariantParser.cpp236
-rw-r--r--src/utils/JSONVariantParser.h102
-rw-r--r--src/utils/JSONVariantWriter.cpp142
-rw-r--r--src/utils/JSONVariantWriter.h35
-rw-r--r--src/utils/Job.h170
-rw-r--r--src/utils/JobManager.cpp432
-rw-r--r--src/utils/JobManager.h333
-rw-r--r--src/utils/LabelFormatter.cpp428
-rw-r--r--src/utils/LabelFormatter.h88
-rw-r--r--src/utils/LangCodeExpander.cpp1679
-rw-r--r--src/utils/LangCodeExpander.h114
-rw-r--r--src/utils/LegacyPathTranslation.cpp116
-rw-r--r--src/utils/LegacyPathTranslation.h58
-rw-r--r--src/utils/Makefile.in90
-rw-r--r--src/utils/MarkWatchedJob.cpp135
-rw-r--r--src/utils/MarkWatchedJob.h44
-rw-r--r--src/utils/MathUtils.h204
-rw-r--r--src/utils/Mime.cpp697
-rw-r--r--src/utils/Mime.h56
-rw-r--r--src/utils/Observer.cpp155
-rw-r--r--src/utils/Observer.h146
-rw-r--r--src/utils/POUtils.cpp317
-rw-r--r--src/utils/POUtils.h172
-rw-r--r--src/utils/PerformanceSample.cpp109
-rw-r--r--src/utils/PerformanceSample.h68
-rw-r--r--src/utils/PerformanceStats.cpp78
-rw-r--r--src/utils/PerformanceStats.h58
-rw-r--r--src/utils/RecentlyAddedJob.cpp363
-rw-r--r--src/utils/RecentlyAddedJob.h41
-rw-r--r--src/utils/RegExp.cpp650
-rw-r--r--src/utils/RegExp.h182
-rw-r--r--src/utils/RingBuffer.cpp257
-rw-r--r--src/utils/RingBuffer.h51
-rw-r--r--src/utils/RssManager.cpp200
-rw-r--r--src/utils/RssManager.h80
-rw-r--r--src/utils/RssReader.cpp422
-rw-r--r--src/utils/RssReader.h73
-rw-r--r--src/utils/SaveFileStateJob.cpp195
-rw-r--r--src/utils/SaveFileStateJob.h50
-rw-r--r--src/utils/ScraperParser.cpp616
-rw-r--r--src/utils/ScraperParser.h94
-rw-r--r--src/utils/ScraperUrl.cpp366
-rw-r--r--src/utils/ScraperUrl.h92
-rw-r--r--src/utils/Screenshot.cpp290
-rw-r--r--src/utils/Screenshot.h44
-rw-r--r--src/utils/SeekHandler.cpp94
-rw-r--r--src/utils/SeekHandler.h41
-rw-r--r--src/utils/SortUtils.cpp906
-rw-r--r--src/utils/SortUtils.h154
-rw-r--r--src/utils/Splash.cpp136
-rw-r--r--src/utils/Splash.h59
-rw-r--r--src/utils/StdString.h3154
-rw-r--r--src/utils/Stopwatch.cpp59
-rw-r--r--src/utils/Stopwatch.h112
-rw-r--r--src/utils/StreamDetails.cpp598
-rw-r--r--src/utils/StreamDetails.h138
-rw-r--r--src/utils/StreamUtils.cpp44
-rw-r--r--src/utils/StreamUtils.h28
-rw-r--r--src/utils/StringUtils.cpp1197
-rw-r--r--src/utils/StringUtils.h197
-rw-r--r--src/utils/StringValidation.cpp61
-rw-r--r--src/utils/StringValidation.h36
-rw-r--r--src/utils/SystemInfo.cpp1390
-rw-r--r--src/utils/SystemInfo.h157
-rw-r--r--src/utils/TextSearch.cpp165
-rw-r--r--src/utils/TextSearch.h49
-rw-r--r--src/utils/TimeSmoother.cpp250
-rw-r--r--src/utils/TimeSmoother.h187
-rw-r--r--src/utils/TimeUtils.cpp109
-rw-r--r--src/utils/TimeUtils.h43
-rw-r--r--src/utils/TuxBoxUtil.cpp1649
-rw-r--r--src/utils/TuxBoxUtil.h191
-rw-r--r--src/utils/URIUtils.cpp1335
-rw-r--r--src/utils/URIUtils.h225
-rw-r--r--src/utils/UrlOptions.cpp188
-rw-r--r--src/utils/UrlOptions.h57
-rw-r--r--src/utils/Utf8Utils.cpp160
-rw-r--r--src/utils/Utf8Utils.h55
-rw-r--r--src/utils/Variant.cpp794
-rw-r--r--src/utils/Variant.h154
-rw-r--r--src/utils/Vector.cpp75
-rw-r--r--src/utils/Vector.h41
-rw-r--r--src/utils/Weather.cpp501
-rw-r--r--src/utils/Weather.h173
-rw-r--r--src/utils/WindowsShortcut.cpp166
-rw-r--r--src/utils/WindowsShortcut.h43
-rw-r--r--src/utils/XBMCTinyXML.cpp276
-rw-r--r--src/utils/XBMCTinyXML.h77
-rw-r--r--src/utils/XMLUtils.cpp343
-rw-r--r--src/utils/XMLUtils.h86
-rw-r--r--src/utils/XSLTUtils.cpp120
-rw-r--r--src/utils/XSLTUtils.h62
-rw-r--r--src/utils/auto_buffer.cpp95
-rw-r--r--src/utils/auto_buffer.h105
-rw-r--r--src/utils/fastmemcpy-arm.S528
-rw-r--r--src/utils/fastmemcpy.c396
-rw-r--r--src/utils/fastmemcpy.h35
-rw-r--r--src/utils/fft.cpp176
-rw-r--r--src/utils/fft.h52
-rw-r--r--src/utils/fstrcmp.c114
-rw-r--r--src/utils/fstrcmp.h36
-rw-r--r--src/utils/log.cpp218
-rw-r--r--src/utils/log.h86
-rw-r--r--src/utils/md5.cpp317
-rw-r--r--src/utils/md5.h55
-rw-r--r--src/utils/params_check_macros.h68
-rw-r--r--src/utils/posix/PosixInterfaceForCLog.cpp108
-rw-r--r--src/utils/posix/PosixInterfaceForCLog.h38
-rw-r--r--src/utils/test/CXBMCTinyXML-test.xml6
-rw-r--r--src/utils/test/Makefile60
-rw-r--r--src/utils/test/TestAlarmClock.cpp37
-rw-r--r--src/utils/test/TestAliasShortcutUtils.cpp44
-rw-r--r--src/utils/test/TestArchive.cpp420
-rw-r--r--src/utils/test/TestAsyncFileCopy.cpp55
-rw-r--r--src/utils/test/TestBase64.cpp89
-rw-r--r--src/utils/test/TestBitstreamStats.cpp70
-rw-r--r--src/utils/test/TestCPUInfo.cpp133
-rw-r--r--src/utils/test/TestCharsetConverter.cpp407
-rw-r--r--src/utils/test/TestCrc32.cpp67
-rw-r--r--src/utils/test/TestCryptThreading.cpp29
-rw-r--r--src/utils/test/TestDatabaseUtils.cpp1312
-rw-r--r--src/utils/test/TestEndianSwap.cpp145
-rw-r--r--src/utils/test/TestFileOperationJob.cpp301
-rw-r--r--src/utils/test/TestFileUtils.cpp53
-rw-r--r--src/utils/test/TestGlobalsHandling.cpp37
-rw-r--r--src/utils/test/TestGlobalsHandlingPattern1.h52
-rw-r--r--src/utils/test/TestHTMLTable.cpp84
-rw-r--r--src/utils/test/TestHTMLUtil.cpp103
-rw-r--r--src/utils/test/TestHttpHeader.cpp515
-rw-r--r--src/utils/test/TestHttpParser.cpp62
-rw-r--r--src/utils/test/TestHttpResponse.cpp59
-rw-r--r--src/utils/test/TestJSONVariantParser.cpp33
-rw-r--r--src/utils/test/TestJSONVariantWriter.cpp32
-rw-r--r--src/utils/test/TestJobManager.cpp179
-rw-r--r--src/utils/test/TestLabelFormatter.cpp91
-rw-r--r--src/utils/test/TestLangCodeExpander.cpp41
-rw-r--r--src/utils/test/TestMathUtils.cpp71
-rw-r--r--src/utils/test/TestMime.cpp41
-rw-r--r--src/utils/test/TestPOUtils.cpp59
-rw-r--r--src/utils/test/TestPerformanceSample.cpp53
-rw-r--r--src/utils/test/TestRegExp.cpp183
-rw-r--r--src/utils/test/TestRingBuffer.cpp45
-rw-r--r--src/utils/test/TestScraperParser.cpp39
-rw-r--r--src/utils/test/TestScraperUrl.cpp45
-rw-r--r--src/utils/test/TestSortUtils.cpp133
-rw-r--r--src/utils/test/TestStdString.cpp50
-rw-r--r--src/utils/test/TestStopwatch.cpp118
-rw-r--r--src/utils/test/TestStreamDetails.cpp87
-rw-r--r--src/utils/test/TestStreamUtils.cpp35
-rw-r--r--src/utils/test/TestStringUtils.cpp480
-rw-r--r--src/utils/test/TestSystemInfo.cpp349
-rw-r--r--src/utils/test/TestTimeSmoother.cpp41
-rw-r--r--src/utils/test/TestTimeUtils.cpp63
-rw-r--r--src/utils/test/TestURIUtils.cpp622
-rw-r--r--src/utils/test/TestUrlOptions.cpp204
-rw-r--r--src/utils/test/TestVariant.cpp350
-rw-r--r--src/utils/test/TestXBMCTinyXML.cpp70
-rw-r--r--src/utils/test/TestXMLUtils.cpp368
-rw-r--r--src/utils/test/Testfastmemcpy.cpp39
-rw-r--r--src/utils/test/Testfft.cpp301
-rw-r--r--src/utils/test/Testfstrcmp.cpp47
-rw-r--r--src/utils/test/Testlog.cpp153
-rw-r--r--src/utils/test/Testmd5.cpp56
-rw-r--r--src/utils/uXstrings.h61
-rw-r--r--src/utils/win32/Win32InterfaceForCLog.cpp120
-rw-r--r--src/utils/win32/Win32InterfaceForCLog.h38
-rw-r--r--src/utils/win32/Win32Log.cpp65
-rw-r--r--src/utils/win32/Win32Log.h40
236 files changed, 50601 insertions, 0 deletions
diff --git a/src/utils/AMLUtils.cpp b/src/utils/AMLUtils.cpp
new file mode 100644
index 0000000000..95537453c7
--- /dev/null
+++ b/src/utils/AMLUtils.cpp
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2011-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string>
+
+#include "AMLUtils.h"
+#include "utils/CPUInfo.h"
+#include "utils/log.h"
+#include "utils/StringUtils.h"
+#include "utils/AMLUtils.h"
+#include "guilib/gui3d.h"
+
+int aml_set_sysfs_str(const char *path, const char *val)
+{
+ int fd = open(path, O_CREAT | O_RDWR | O_TRUNC, 0644);
+ if (fd >= 0)
+ {
+ write(fd, val, strlen(val));
+ close(fd);
+ return 0;
+ }
+ return -1;
+}
+
+int aml_get_sysfs_str(const char *path, char *valstr, const int size)
+{
+ int fd = open(path, O_RDONLY);
+ if (fd >= 0)
+ {
+ read(fd, valstr, size - 1);
+ valstr[strlen(valstr)] = '\0';
+ close(fd);
+ return 0;
+ }
+
+ sprintf(valstr, "%s", "fail");
+ return -1;
+}
+
+int aml_set_sysfs_int(const char *path, const int val)
+{
+ int fd = open(path, O_CREAT | O_RDWR | O_TRUNC, 0644);
+ if (fd >= 0)
+ {
+ char bcmd[16];
+ sprintf(bcmd, "%d", val);
+ write(fd, bcmd, strlen(bcmd));
+ close(fd);
+ return 0;
+ }
+ return -1;
+}
+
+int aml_get_sysfs_int(const char *path)
+{
+ int val = -1;
+ int fd = open(path, O_RDONLY);
+ if (fd >= 0)
+ {
+ char bcmd[16];
+ read(fd, bcmd, sizeof(bcmd));
+ val = strtol(bcmd, NULL, 16);
+ close(fd);
+ }
+ return val;
+}
+
+bool aml_present()
+{
+ static int has_aml = -1;
+ if (has_aml == -1)
+ {
+ int rtn = aml_get_sysfs_int("/sys/class/audiodsp/digital_raw");
+ if (rtn != -1)
+ has_aml = 1;
+ else
+ has_aml = 0;
+ if (has_aml)
+ CLog::Log(LOGNOTICE, "aml_present, rtn(%d)", rtn);
+ }
+ return has_aml == 1;
+}
+
+bool aml_hw3d_present()
+{
+ static int has_hw3d = -1;
+ if (has_hw3d == -1)
+ {
+ if (aml_get_sysfs_int("/sys/class/ppmgr/ppmgr_3d_mode") != -1)
+ has_hw3d = 1;
+ else
+ has_hw3d = 0;
+ }
+ return has_hw3d == 1;
+}
+
+bool aml_wired_present()
+{
+ static int has_wired = -1;
+ if (has_wired == -1)
+ {
+ char test[64] = {0};
+ if (aml_get_sysfs_str("/sys/class/net/eth0/operstate", test, 63) != -1)
+ has_wired = 1;
+ else
+ has_wired = 0;
+ }
+ return has_wired == 1;
+}
+
+void aml_permissions()
+{
+ if (!aml_present())
+ return;
+
+ // most all aml devices are already rooted.
+ int ret = system("ls /system/xbin/su");
+ if (ret != 0)
+ {
+ CLog::Log(LOGWARNING, "aml_permissions: missing su, playback might fail");
+ }
+ else
+ {
+ // certain aml devices have 664 permission, we need 666.
+ system("su -c chmod 666 /dev/amvideo");
+ system("su -c chmod 666 /dev/amstream*");
+ system("su -c chmod 666 /sys/class/video/axis");
+ system("su -c chmod 666 /sys/class/video/screen_mode");
+ system("su -c chmod 666 /sys/class/video/disable_video");
+ system("su -c chmod 666 /sys/class/tsync/pts_pcrscr");
+ system("su -c chmod 666 /sys/class/audiodsp/digital_raw");
+ system("su -c chmod 666 /sys/class/ppmgr/ppmgr_3d_mode");
+ system("su -c chmod 666 /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq");
+ system("su -c chmod 666 /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq");
+ system("su -c chmod 666 /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor");
+ CLog::Log(LOGINFO, "aml_permissions: permissions changed");
+ }
+}
+
+enum AML_DEVICE_TYPE aml_get_device_type()
+{
+ static enum AML_DEVICE_TYPE aml_device_type = AML_DEVICE_TYPE_UNINIT;
+ if (aml_device_type == AML_DEVICE_TYPE_UNINIT)
+ {
+ std::string cpu_hardware = g_cpuInfo.getCPUHardware();
+
+ if (cpu_hardware.find("MESON-M1") != std::string::npos)
+ aml_device_type = AML_DEVICE_TYPE_M1;
+ else if (cpu_hardware.find("MESON-M3") != std::string::npos
+ || cpu_hardware.find("MESON3") != std::string::npos)
+ aml_device_type = AML_DEVICE_TYPE_M3;
+ else if (cpu_hardware.find("Meson6") != std::string::npos)
+ aml_device_type = AML_DEVICE_TYPE_M6;
+ else if (cpu_hardware.find("Meson8") != std::string::npos)
+ aml_device_type = AML_DEVICE_TYPE_M8;
+ else
+ aml_device_type = AML_DEVICE_TYPE_UNKNOWN;
+ }
+
+ return aml_device_type;
+}
+
+void aml_cpufreq_min(bool limit)
+{
+// do not touch scaling_min_freq on android
+#if !defined(TARGET_ANDROID)
+ // only needed for m1/m3 SoCs
+ if ( aml_get_device_type() != AML_DEVICE_TYPE_UNKNOWN
+ && aml_get_device_type() <= AML_DEVICE_TYPE_M3)
+ {
+ int cpufreq = 300000;
+ if (limit)
+ cpufreq = 600000;
+
+ aml_set_sysfs_int("/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq", cpufreq);
+ }
+#endif
+}
+
+void aml_cpufreq_max(bool limit)
+{
+ if (!aml_wired_present() && aml_get_device_type() == AML_DEVICE_TYPE_M6)
+ {
+ // this is a MX Stick, they cannot substain 1GHz
+ // operation without overheating so limit them to 800MHz.
+ int cpufreq = 1000000;
+ if (limit)
+ cpufreq = 800000;
+
+ aml_set_sysfs_int("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq", cpufreq);
+ aml_set_sysfs_str("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor", "ondemand");
+ }
+}
+
+void aml_set_audio_passthrough(bool passthrough)
+{
+ if ( aml_present()
+ && aml_get_device_type() != AML_DEVICE_TYPE_UNKNOWN
+ && aml_get_device_type() <= AML_DEVICE_TYPE_M8)
+ {
+ // m1 uses 1, m3 and above uses 2
+ int raw = aml_get_device_type() == AML_DEVICE_TYPE_M1 ? 1:2;
+ aml_set_sysfs_int("/sys/class/audiodsp/digital_raw", passthrough ? raw:0);
+ }
+}
+
+void aml_probe_hdmi_audio()
+{
+ // Audio {format, channel, freq, cce}
+ // {1, 7, 7f, 7}
+ // {7, 5, 1e, 0}
+ // {2, 5, 7, 0}
+ // {11, 7, 7e, 1}
+ // {10, 7, 6, 0}
+ // {12, 7, 7e, 0}
+
+ int fd = open("/sys/class/amhdmitx/amhdmitx0/edid", O_RDONLY);
+ if (fd >= 0)
+ {
+ char valstr[1024] = {0};
+
+ read(fd, valstr, sizeof(valstr) - 1);
+ valstr[strlen(valstr)] = '\0';
+ close(fd);
+
+ std::vector<std::string> probe_str = StringUtils::Split(valstr, "\n");
+
+ for (std::vector<std::string>::const_iterator i = probe_str.begin(); i != probe_str.end(); ++i)
+ {
+ if (i->find("Audio") == std::string::npos)
+ {
+ for (std::vector<std::string>::const_iterator j = i + 1; j != probe_str.end(); ++j)
+ {
+ if (j->find("{1,") != std::string::npos)
+ printf(" PCM found {1,\n");
+ else if (j->find("{2,") != std::string::npos)
+ printf(" AC3 found {2,\n");
+ else if (j->find("{3,") != std::string::npos)
+ printf(" MPEG1 found {3,\n");
+ else if (j->find("{4,") != std::string::npos)
+ printf(" MP3 found {4,\n");
+ else if (j->find("{5,") != std::string::npos)
+ printf(" MPEG2 found {5,\n");
+ else if (j->find("{6,") != std::string::npos)
+ printf(" AAC found {6,\n");
+ else if (j->find("{7,") != std::string::npos)
+ printf(" DTS found {7,\n");
+ else if (j->find("{8,") != std::string::npos)
+ printf(" ATRAC found {8,\n");
+ else if (j->find("{9,") != std::string::npos)
+ printf(" One_Bit_Audio found {9,\n");
+ else if (j->find("{10,") != std::string::npos)
+ printf(" Dolby found {10,\n");
+ else if (j->find("{11,") != std::string::npos)
+ printf(" DTS_HD found {11,\n");
+ else if (j->find("{12,") != std::string::npos)
+ printf(" MAT found {12,\n");
+ else if (j->find("{13,") != std::string::npos)
+ printf(" ATRAC found {13,\n");
+ else if (j->find("{14,") != std::string::npos)
+ printf(" WMA found {14,\n");
+ else
+ break;
+ }
+ break;
+ }
+ }
+ }
+}
+
+int aml_axis_value(AML_DISPLAY_AXIS_PARAM param)
+{
+ char axis[20] = {0};
+ int value[8];
+
+ aml_get_sysfs_str("/sys/class/display/axis", axis, 19);
+ sscanf(axis, "%d %d %d %d %d %d %d %d", &value[0], &value[1], &value[2], &value[3], &value[4], &value[5], &value[6], &value[7]);
+
+ return value[param];
+}
+
+bool aml_mode_to_resolution(const char *mode, RESOLUTION_INFO *res)
+{
+ if (!res)
+ return false;
+
+ res->iWidth = 0;
+ res->iHeight= 0;
+
+ if(!mode)
+ return false;
+
+ CStdString fromMode = mode;
+ StringUtils::Trim(fromMode);
+ // strips, for example, 720p* to 720p
+ // the * indicate the 'native' mode of the display
+ if (StringUtils::EndsWith(fromMode, "*"))
+ fromMode.erase(fromMode.size() - 1);
+
+ if (fromMode.Equals("panel"))
+ {
+ res->iWidth = aml_axis_value(AML_DISPLAY_AXIS_PARAM_WIDTH);
+ res->iHeight= aml_axis_value(AML_DISPLAY_AXIS_PARAM_HEIGHT);
+ res->iScreenWidth = aml_axis_value(AML_DISPLAY_AXIS_PARAM_WIDTH);
+ res->iScreenHeight= aml_axis_value(AML_DISPLAY_AXIS_PARAM_HEIGHT);
+ res->fRefreshRate = 60;
+ res->dwFlags = D3DPRESENTFLAG_PROGRESSIVE;
+ }
+ else if (fromMode.Equals("720p"))
+ {
+ res->iWidth = 1280;
+ res->iHeight= 720;
+ res->iScreenWidth = 1280;
+ res->iScreenHeight= 720;
+ res->fRefreshRate = 60;
+ res->dwFlags = D3DPRESENTFLAG_PROGRESSIVE;
+ }
+ else if (fromMode.Equals("720p50hz"))
+ {
+ res->iWidth = 1280;
+ res->iHeight= 720;
+ res->iScreenWidth = 1280;
+ res->iScreenHeight= 720;
+ res->fRefreshRate = 50;
+ res->dwFlags = D3DPRESENTFLAG_PROGRESSIVE;
+ }
+ else if (fromMode.Equals("1080p"))
+ {
+ res->iWidth = 1920;
+ res->iHeight= 1080;
+ res->iScreenWidth = 1920;
+ res->iScreenHeight= 1080;
+ res->fRefreshRate = 60;
+ res->dwFlags = D3DPRESENTFLAG_PROGRESSIVE;
+ }
+ else if (fromMode.Equals("1080p24hz"))
+ {
+ res->iWidth = 1920;
+ res->iHeight= 1080;
+ res->iScreenWidth = 1920;
+ res->iScreenHeight= 1080;
+ res->fRefreshRate = 24;
+ res->dwFlags = D3DPRESENTFLAG_PROGRESSIVE;
+ }
+ else if (fromMode.Equals("1080p30hz"))
+ {
+ res->iWidth = 1920;
+ res->iHeight= 1080;
+ res->iScreenWidth = 1920;
+ res->iScreenHeight= 1080;
+ res->fRefreshRate = 30;
+ res->dwFlags = D3DPRESENTFLAG_PROGRESSIVE;
+ }
+ else if (fromMode.Equals("1080p50hz"))
+ {
+ res->iWidth = 1920;
+ res->iHeight= 1080;
+ res->iScreenWidth = 1920;
+ res->iScreenHeight= 1080;
+ res->fRefreshRate = 50;
+ res->dwFlags = D3DPRESENTFLAG_PROGRESSIVE;
+ }
+ else if (fromMode.Equals("1080i"))
+ {
+ res->iWidth = 1920;
+ res->iHeight= 1080;
+ res->iScreenWidth = 1920;
+ res->iScreenHeight= 1080;
+ res->fRefreshRate = 60;
+ res->dwFlags = D3DPRESENTFLAG_INTERLACED;
+ }
+ else if (fromMode.Equals("1080i50hz"))
+ {
+ res->iWidth = 1920;
+ res->iHeight= 1080;
+ res->iScreenWidth = 1920;
+ res->iScreenHeight= 1080;
+ res->fRefreshRate = 50;
+ res->dwFlags = D3DPRESENTFLAG_INTERLACED;
+ }
+ else if (fromMode.Equals("4k2ksmpte"))
+ {
+ res->iWidth = 1920;
+ res->iHeight= 1080;
+ res->iScreenWidth = 4096;
+ res->iScreenHeight= 2160;
+ res->fRefreshRate = 24;
+ res->dwFlags = D3DPRESENTFLAG_PROGRESSIVE;
+ }
+ else if (fromMode.Equals("4k2k24hz"))
+ {
+ res->iWidth = 1920;
+ res->iHeight= 1080;
+ res->iScreenWidth = 3840;
+ res->iScreenHeight= 2160;
+ res->fRefreshRate = 24;
+ res->dwFlags = D3DPRESENTFLAG_PROGRESSIVE;
+ }
+ else if (fromMode.Equals("4k2k25hz"))
+ {
+ res->iWidth = 1920;
+ res->iHeight= 1080;
+ res->iScreenWidth = 3840;
+ res->iScreenHeight= 2160;
+ res->fRefreshRate = 25;
+ res->dwFlags = D3DPRESENTFLAG_PROGRESSIVE;
+ }
+ else if (fromMode.Equals("4k2k30hz"))
+ {
+ res->iWidth = 1920;
+ res->iHeight= 1080;
+ res->iScreenWidth = 3840;
+ res->iScreenHeight= 2160;
+ res->fRefreshRate = 30;
+ res->dwFlags = D3DPRESENTFLAG_PROGRESSIVE;
+ }
+ else
+ {
+ return false;
+ }
+
+
+ res->iScreen = 0;
+ res->bFullScreen = true;
+ res->iSubtitles = (int)(0.965 * res->iHeight);
+ res->fPixelRatio = 1.0f;
+ res->strMode = StringUtils::Format("%dx%d @ %.2f%s - Full Screen", res->iScreenWidth, res->iScreenHeight, res->fRefreshRate,
+ res->dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : "");
+
+ return res->iWidth > 0 && res->iHeight> 0;
+}
+
diff --git a/src/utils/AMLUtils.h b/src/utils/AMLUtils.h
new file mode 100644
index 0000000000..9778e9b65d
--- /dev/null
+++ b/src/utils/AMLUtils.h
@@ -0,0 +1,55 @@
+#pragma once
+/*
+ * Copyright (C) 2011-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "guilib/Resolution.h"
+
+enum AML_DEVICE_TYPE
+{
+ AML_DEVICE_TYPE_UNINIT = -2,
+ AML_DEVICE_TYPE_UNKNOWN = -1,
+ AML_DEVICE_TYPE_M1,
+ AML_DEVICE_TYPE_M3,
+ AML_DEVICE_TYPE_M6,
+ AML_DEVICE_TYPE_M8
+};
+
+enum AML_DISPLAY_AXIS_PARAM
+{
+ AML_DISPLAY_AXIS_PARAM_X = 0,
+ AML_DISPLAY_AXIS_PARAM_Y,
+ AML_DISPLAY_AXIS_PARAM_WIDTH,
+ AML_DISPLAY_AXIS_PARAM_HEIGHT
+};
+
+int aml_set_sysfs_str(const char *path, const char *val);
+int aml_get_sysfs_str(const char *path, char *valstr, const int size);
+int aml_set_sysfs_int(const char *path, const int val);
+int aml_get_sysfs_int(const char *path);
+
+bool aml_present();
+void aml_permissions();
+bool aml_hw3d_present();
+bool aml_wired_present();
+enum AML_DEVICE_TYPE aml_get_device_type();
+void aml_cpufreq_min(bool limit);
+void aml_cpufreq_max(bool limit);
+void aml_set_audio_passthrough(bool passthrough);
+bool aml_mode_to_resolution(const char *mode, RESOLUTION_INFO *res);
diff --git a/src/utils/ActorProtocol.cpp b/src/utils/ActorProtocol.cpp
new file mode 100644
index 0000000000..9ea07bd5d6
--- /dev/null
+++ b/src/utils/ActorProtocol.cpp
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "ActorProtocol.h"
+
+using namespace Actor;
+
+void Message::Release()
+{
+ bool skip;
+ origin->Lock();
+ skip = isSync ? !isSyncFini : false;
+ isSyncFini = true;
+ origin->Unlock();
+
+ if (skip)
+ return;
+
+ // free data buffer
+ if (data != buffer)
+ delete [] data;
+
+ // delete event in case of sync message
+ if (event)
+ delete event;
+
+ origin->ReturnMessage(this);
+}
+
+bool Message::Reply(int sig, void *data /* = NULL*/, int size /* = 0 */)
+{
+ if (!isSync)
+ {
+ if (isOut)
+ return origin->SendInMessage(sig, data, size);
+ else
+ return origin->SendOutMessage(sig, data, size);
+ }
+
+ origin->Lock();
+
+ if (!isSyncTimeout)
+ {
+ Message *msg = origin->GetMessage();
+ msg->signal = sig;
+ msg->isOut = !isOut;
+ replyMessage = msg;
+ if (data)
+ {
+ if (size > MSG_INTERNAL_BUFFER_SIZE)
+ msg->data = new uint8_t[size];
+ else
+ msg->data = msg->buffer;
+ memcpy(msg->data, data, size);
+ }
+ }
+
+ origin->Unlock();
+
+ if (event)
+ event->Set();
+
+ return true;
+}
+
+Protocol::~Protocol()
+{
+ Message *msg;
+ Purge();
+ while (!freeMessageQueue.empty())
+ {
+ msg = freeMessageQueue.front();
+ freeMessageQueue.pop();
+ delete msg;
+ }
+}
+
+Message *Protocol::GetMessage()
+{
+ Message *msg;
+
+ CSingleLock lock(criticalSection);
+
+ if (!freeMessageQueue.empty())
+ {
+ msg = freeMessageQueue.front();
+ freeMessageQueue.pop();
+ }
+ else
+ msg = new Message();
+
+ msg->isSync = false;
+ msg->isSyncFini = false;
+ msg->isSyncTimeout = false;
+ msg->event = NULL;
+ msg->data = NULL;
+ msg->payloadSize = 0;
+ msg->replyMessage = NULL;
+ msg->origin = this;
+
+ return msg;
+}
+
+void Protocol::ReturnMessage(Message *msg)
+{
+ CSingleLock lock(criticalSection);
+
+ freeMessageQueue.push(msg);
+}
+
+bool Protocol::SendOutMessage(int signal, void *data /* = NULL */, int size /* = 0 */, Message *outMsg /* = NULL */)
+{
+ Message *msg;
+ if (outMsg)
+ msg = outMsg;
+ else
+ msg = GetMessage();
+
+ msg->signal = signal;
+ msg->isOut = true;
+
+ if (data)
+ {
+ if (size > MSG_INTERNAL_BUFFER_SIZE)
+ msg->data = new uint8_t[size];
+ else
+ msg->data = msg->buffer;
+ memcpy(msg->data, data, size);
+ }
+
+ { CSingleLock lock(criticalSection);
+ outMessages.push(msg);
+ }
+ containerOutEvent->Set();
+
+ return true;
+}
+
+bool Protocol::SendInMessage(int signal, void *data /* = NULL */, int size /* = 0 */, Message *outMsg /* = NULL */)
+{
+ Message *msg;
+ if (outMsg)
+ msg = outMsg;
+ else
+ msg = GetMessage();
+
+ msg->signal = signal;
+ msg->isOut = false;
+
+ if (data)
+ {
+ if (size > MSG_INTERNAL_BUFFER_SIZE)
+ msg->data = new uint8_t[size];
+ else
+ msg->data = msg->buffer;
+ memcpy(msg->data, data, size);
+ }
+
+ { CSingleLock lock(criticalSection);
+ inMessages.push(msg);
+ }
+ containerInEvent->Set();
+
+ return true;
+}
+
+
+bool Protocol::SendOutMessageSync(int signal, Message **retMsg, int timeout, void *data /* = NULL */, int size /* = 0 */)
+{
+ Message *msg = GetMessage();
+ msg->isOut = true;
+ msg->isSync = true;
+ msg->event = new CEvent;
+ msg->event->Reset();
+ SendOutMessage(signal, data, size, msg);
+
+ if (!msg->event->WaitMSec(timeout))
+ {
+ msg->origin->Lock();
+ if (msg->replyMessage)
+ *retMsg = msg->replyMessage;
+ else
+ {
+ *retMsg = NULL;
+ msg->isSyncTimeout = true;
+ }
+ msg->origin->Unlock();
+ }
+ else
+ *retMsg = msg->replyMessage;
+
+ msg->Release();
+
+ if (*retMsg)
+ return true;
+ else
+ return false;
+}
+
+bool Protocol::ReceiveOutMessage(Message **msg)
+{
+ CSingleLock lock(criticalSection);
+
+ if (outMessages.empty() || outDefered)
+ return false;
+
+ *msg = outMessages.front();
+ outMessages.pop();
+
+ return true;
+}
+
+bool Protocol::ReceiveInMessage(Message **msg)
+{
+ CSingleLock lock(criticalSection);
+
+ if (inMessages.empty() || inDefered)
+ return false;
+
+ *msg = inMessages.front();
+ inMessages.pop();
+
+ return true;
+}
+
+
+void Protocol::Purge()
+{
+ Message *msg;
+
+ while (ReceiveInMessage(&msg))
+ msg->Release();
+
+ while (ReceiveOutMessage(&msg))
+ msg->Release();
+}
+
+void Protocol::PurgeIn(int signal)
+{
+ Message *msg;
+ std::queue<Message*> msgs;
+
+ CSingleLock lock(criticalSection);
+
+ while (!inMessages.empty())
+ {
+ msg = inMessages.front();
+ inMessages.pop();
+ if (msg->signal != signal)
+ msgs.push(msg);
+ }
+ while (!msgs.empty())
+ {
+ msg = msgs.front();
+ msgs.pop();
+ inMessages.push(msg);
+ }
+}
+
+void Protocol::PurgeOut(int signal)
+{
+ Message *msg;
+ std::queue<Message*> msgs;
+
+ CSingleLock lock(criticalSection);
+
+ while (!outMessages.empty())
+ {
+ msg = outMessages.front();
+ outMessages.pop();
+ if (msg->signal != signal)
+ msgs.push(msg);
+ }
+ while (!msgs.empty())
+ {
+ msg = msgs.front();
+ msgs.pop();
+ outMessages.push(msg);
+ }
+}
diff --git a/src/utils/ActorProtocol.h b/src/utils/ActorProtocol.h
new file mode 100644
index 0000000000..100b1acd0b
--- /dev/null
+++ b/src/utils/ActorProtocol.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#pragma once
+
+#include "threads/Thread.h"
+#include "utils/log.h"
+#include <queue>
+#include "memory.h"
+
+#define MSG_INTERNAL_BUFFER_SIZE 32
+
+namespace Actor
+{
+
+class Protocol;
+
+class Message
+{
+ friend class Protocol;
+public:
+ int signal;
+ bool isSync;
+ bool isSyncFini;
+ bool isOut;
+ bool isSyncTimeout;
+ int payloadSize;
+ uint8_t buffer[MSG_INTERNAL_BUFFER_SIZE];
+ uint8_t *data;
+ Message *replyMessage;
+ Protocol *origin;
+ CEvent *event;
+
+ void Release();
+ bool Reply(int sig, void *data = NULL, int size = 0);
+
+private:
+ Message() {isSync = false; data = NULL; event = NULL; replyMessage = NULL;};
+};
+
+class Protocol
+{
+public:
+ Protocol(std::string name, CEvent* inEvent, CEvent *outEvent)
+ : portName(name), inDefered(false), outDefered(false) {containerInEvent = inEvent; containerOutEvent = outEvent;};
+ virtual ~Protocol();
+ Message *GetMessage();
+ void ReturnMessage(Message *msg);
+ bool SendOutMessage(int signal, void *data = NULL, int size = 0, Message *outMsg = NULL);
+ bool SendInMessage(int signal, void *data = NULL, int size = 0, Message *outMsg = NULL);
+ bool SendOutMessageSync(int signal, Message **retMsg, int timeout, void *data = NULL, int size = 0);
+ bool ReceiveOutMessage(Message **msg);
+ bool ReceiveInMessage(Message **msg);
+ void Purge();
+ void PurgeIn(int signal);
+ void PurgeOut(int signal);
+ void DeferIn(bool value) {inDefered = value;};
+ void DeferOut(bool value) {outDefered = value;};
+ void Lock() {criticalSection.lock();};
+ void Unlock() {criticalSection.unlock();};
+ std::string portName;
+
+protected:
+ CEvent *containerInEvent, *containerOutEvent;
+ CCriticalSection criticalSection;
+ std::queue<Message*> outMessages;
+ std::queue<Message*> inMessages;
+ std::queue<Message*> freeMessageQueue;
+ bool inDefered, outDefered;
+};
+
+}
diff --git a/src/utils/AlarmClock.cpp b/src/utils/AlarmClock.cpp
new file mode 100644
index 0000000000..24ca8fb5cb
--- /dev/null
+++ b/src/utils/AlarmClock.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "AlarmClock.h"
+#include "ApplicationMessenger.h"
+#include "guilib/LocalizeStrings.h"
+#include "threads/SingleLock.h"
+#include "log.h"
+#include "dialogs/GUIDialogKaiToast.h"
+#include "utils/StringUtils.h"
+
+using namespace std;
+
+CAlarmClock::CAlarmClock() : CThread("AlarmClock"), m_bIsRunning(false)
+{
+}
+
+CAlarmClock::~CAlarmClock()
+{
+}
+
+void CAlarmClock::Start(const std::string& strName, float n_secs, const std::string& strCommand, bool bSilent /* false */, bool bLoop /* false */)
+{
+ // make lower case so that lookups are case-insensitive
+ std::string lowerName(strName);
+ StringUtils::ToLower(lowerName);
+ Stop(lowerName);
+ SAlarmClockEvent event;
+ event.m_fSecs = n_secs;
+ event.m_strCommand = strCommand;
+ event.m_loop = bLoop;
+ if (!m_bIsRunning)
+ {
+ StopThread();
+ Create();
+ m_bIsRunning = true;
+ }
+
+ std::string strAlarmClock;
+ std::string strStarted;
+ if (StringUtils::EqualsNoCase(strName, "shutdowntimer"))
+ {
+ strAlarmClock = g_localizeStrings.Get(20144);
+ strStarted = g_localizeStrings.Get(20146);
+ }
+ else
+ {
+ strAlarmClock = g_localizeStrings.Get(13208);
+ strStarted = g_localizeStrings.Get(13210);
+ }
+
+ std::string strMessage = StringUtils::Format(strStarted.c_str(),
+ static_cast<int>(event.m_fSecs)/60,
+ static_cast<int>(event.m_fSecs)%60);
+
+ if(!bSilent)
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, strAlarmClock, strMessage);
+
+ event.watch.StartZero();
+ CSingleLock lock(m_events);
+ m_event.insert(make_pair(lowerName,event));
+ CLog::Log(LOGDEBUG,"started alarm with name: %s",lowerName.c_str());
+}
+
+void CAlarmClock::Stop(const std::string& strName, bool bSilent /* false */)
+{
+ CSingleLock lock(m_events);
+
+ std::string lowerName(strName);
+ StringUtils::ToLower(lowerName); // lookup as lowercase only
+ map<std::string,SAlarmClockEvent>::iterator iter = m_event.find(lowerName);
+
+ if (iter == m_event.end())
+ return;
+
+ std::string strAlarmClock;
+ if (StringUtils::EqualsNoCase(strName, "shutdowntimer"))
+ strAlarmClock = g_localizeStrings.Get(20144);
+ else
+ strAlarmClock = g_localizeStrings.Get(13208);
+
+ std::string strMessage;
+ float elapsed = 0.f;
+
+ if (iter->second.watch.IsRunning())
+ elapsed = iter->second.watch.GetElapsedSeconds();
+
+ if( elapsed > iter->second.m_fSecs )
+ strMessage = g_localizeStrings.Get(13211);
+ else
+ {
+ float remaining = static_cast<float>(iter->second.m_fSecs-elapsed);
+ std::string strStarted = g_localizeStrings.Get(13212);
+ strMessage = StringUtils::Format(strStarted.c_str(),
+ static_cast<int>(remaining)/60,
+ static_cast<int>(remaining)%60);
+ }
+ if (iter->second.m_strCommand.empty() || iter->second.m_fSecs > elapsed)
+ {
+ if(!bSilent)
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, strAlarmClock, strMessage);
+ }
+ else
+ {
+ CApplicationMessenger::Get().ExecBuiltIn(iter->second.m_strCommand);
+ if (iter->second.m_loop)
+ {
+ iter->second.watch.Reset();
+ return;
+ }
+ }
+
+ iter->second.watch.Stop();
+ m_event.erase(iter);
+}
+
+void CAlarmClock::Process()
+{
+ while( !m_bStop)
+ {
+ std::string strLast;
+ {
+ CSingleLock lock(m_events);
+ for (map<std::string,SAlarmClockEvent>::iterator iter=m_event.begin();iter != m_event.end(); ++iter)
+ if ( iter->second.watch.IsRunning()
+ && iter->second.watch.GetElapsedSeconds() >= iter->second.m_fSecs)
+ {
+ Stop(iter->first);
+ if ((iter = m_event.find(strLast)) == m_event.end())
+ break;
+ }
+ else
+ strLast = iter->first;
+ }
+ Sleep(100);
+ }
+}
+
diff --git a/src/utils/AlarmClock.h b/src/utils/AlarmClock.h
new file mode 100644
index 0000000000..ed9fc49fbb
--- /dev/null
+++ b/src/utils/AlarmClock.h
@@ -0,0 +1,79 @@
+#pragma once
+
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "Stopwatch.h"
+#include "threads/CriticalSection.h"
+#include "threads/Thread.h"
+
+#include <map>
+#include <string>
+
+struct SAlarmClockEvent
+{
+ CStopWatch watch;
+ double m_fSecs;
+ std::string m_strCommand;
+ bool m_loop;
+};
+
+class CAlarmClock : public CThread
+{
+public:
+ CAlarmClock();
+ ~CAlarmClock();
+ void Start(const std::string& strName, float n_secs, const std::string& strCommand, bool bSilent = false, bool bLoop = false);
+ inline bool IsRunning() const
+ {
+ return m_bIsRunning;
+ }
+
+ inline bool HasAlarm(const std::string& strName)
+ {
+ // note: strName should be lower case only here
+ // No point checking it at the moment due to it only being called
+ // from GUIInfoManager (which is always lowercase)
+ // CLog::Log(LOGDEBUG,"checking for %s",strName.c_str());
+ return (m_event.find(strName) != m_event.end());
+ }
+
+ double GetRemaining(const std::string& strName)
+ {
+ std::map<std::string,SAlarmClockEvent>::iterator iter;
+ if ((iter=m_event.find(strName)) != m_event.end())
+ {
+ return iter->second.m_fSecs-(iter->second.watch.IsRunning() ? iter->second.watch.GetElapsedSeconds() : 0.f);
+ }
+
+ return 0.f;
+ }
+
+ void Stop(const std::string& strName, bool bSilent = false);
+ virtual void Process();
+private:
+ std::map<std::string,SAlarmClockEvent> m_event;
+ CCriticalSection m_events;
+
+ bool m_bIsRunning;
+};
+
+extern CAlarmClock g_alarmClock;
+
diff --git a/src/utils/AliasShortcutUtils.cpp b/src/utils/AliasShortcutUtils.cpp
new file mode 100644
index 0000000000..ae46610fec
--- /dev/null
+++ b/src/utils/AliasShortcutUtils.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2009-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if defined(TARGET_DARWIN_OSX)
+#include <CoreServices/CoreServices.h>
+#include "utils/URIUtils.h"
+#elif defined(TARGET_POSIX)
+#else
+#endif
+
+#include "AliasShortcutUtils.h"
+
+bool IsAliasShortcut(const std::string& path)
+{
+ bool rtn = false;
+
+#if defined(TARGET_DARWIN_OSX)
+ // Note: regular files that have an .alias extension can be
+ // reported as an alias when clearly, they are not. Trap them out.
+ if (!URIUtils::HasExtension(path, ".alias"))
+ {
+ FSRef fileRef;
+ Boolean targetIsFolder, wasAliased;
+
+ // It is better to call FSPathMakeRefWithOptions and pass kFSPathMakeRefDefaultOptions
+ // since it will succeed for paths such as "/Volumes" unlike FSPathMakeRef.
+ if (noErr == FSPathMakeRefWithOptions((UInt8*)path.c_str(), kFSPathMakeRefDefaultOptions, &fileRef, NULL))
+ {
+ if (noErr == FSIsAliasFile(&fileRef, &wasAliased, &targetIsFolder))
+ {
+ if (wasAliased)
+ {
+ rtn = true;
+ }
+ }
+ }
+ }
+#elif defined(TARGET_POSIX)
+ // Linux does not use alias or shortcut methods
+#elif defined(TARGET_WINDOWS)
+/* Needs testing under Windows platform so ignore shortcuts for now
+ if (CUtil::GetExtension(path) == ".lnk")
+ {
+ rtn = true;
+ }
+*/
+#endif
+ return(rtn);
+}
+
+void TranslateAliasShortcut(std::string& path)
+{
+#if defined(TARGET_DARWIN_OSX)
+ FSRef fileRef;
+ Boolean targetIsFolder, wasAliased;
+
+ if (noErr == FSPathMakeRefWithOptions((UInt8*)path.c_str(), kFSPathMakeRefDefaultOptions, &fileRef, NULL))
+ {
+ if (noErr == FSResolveAliasFileWithMountFlags(&fileRef, TRUE, &targetIsFolder, &wasAliased, kResolveAliasFileNoUI))
+ {
+ if (wasAliased)
+ {
+ char real_path[PATH_MAX];
+ if (noErr == FSRefMakePath(&fileRef, (UInt8*)real_path, PATH_MAX))
+ {
+ path = real_path;
+ }
+ }
+ }
+ }
+#elif defined(TARGET_POSIX)
+ // Linux does not use alias or shortcut methods
+
+#elif defined(TARGET_WINDOWS)
+/* Needs testing under Windows platform so ignore shortcuts for now
+ CComPtr<IShellLink> ipShellLink;
+
+ // Get a pointer to the IShellLink interface
+ if (NOERROR == CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void**)&ipShellLink))
+ WCHAR wszTemp[MAX_PATH];
+
+ // Get a pointer to the IPersistFile interface
+ CComQIPtr<IPersistFile> ipPersistFile(ipShellLink);
+
+ // IPersistFile is using LPCOLESTR so make sure that the string is Unicode
+#if !defined _UNICODE
+ MultiByteToWideChar(CP_ACP, 0, lpszShortcutPath, -1, wszTemp, MAX_PATH);
+#else
+ wcsncpy(wszTemp, lpszShortcutPath, MAX_PATH);
+#endif
+
+ // Open the shortcut file and initialize it from its contents
+ if (NOERROR == ipPersistFile->Load(wszTemp, STGM_READ))
+ {
+ // Try to find the target of a shortcut even if it has been moved or renamed
+ if (NOERROR == ipShellLink->Resolve(NULL, SLR_UPDATE))
+ {
+ WIN32_FIND_DATA wfd;
+ TCHAR real_path[PATH_MAX];
+ // Get the path to the shortcut target
+ if (NOERROR == ipShellLink->GetPath(real_path, MAX_PATH, &wfd, SLGP_RAWPATH))
+ {
+ // Get the description of the target
+ TCHAR szDesc[MAX_PATH];
+ if (NOERROR == ipShellLink->GetDescription(szDesc, MAX_PATH))
+ {
+ path = real_path;
+ }
+ }
+ }
+ }
+ }
+*/
+#endif
+}
diff --git a/src/utils/AliasShortcutUtils.h b/src/utils/AliasShortcutUtils.h
new file mode 100644
index 0000000000..73b50834f5
--- /dev/null
+++ b/src/utils/AliasShortcutUtils.h
@@ -0,0 +1,25 @@
+#pragma once
+/*
+ * Copyright (C) 2009-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string>
+
+bool IsAliasShortcut(const std::string& path);
+void TranslateAliasShortcut(std::string &path);
diff --git a/src/utils/Archive.cpp b/src/utils/Archive.cpp
new file mode 100644
index 0000000000..14cd61c404
--- /dev/null
+++ b/src/utils/Archive.cpp
@@ -0,0 +1,441 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <cstring>
+#include "Archive.h"
+#include "IArchivable.h"
+#include "filesystem/File.h"
+#include "Variant.h"
+#include "utils/log.h"
+
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wlong-long"
+#endif
+
+using namespace XFILE;
+
+CArchive::CArchive(CFile* pFile, int mode)
+{
+ m_pFile = pFile;
+ m_iMode = mode;
+
+ m_pBuffer = new uint8_t[CARCHIVE_BUFFER_MAX];
+ memset(m_pBuffer, 0, CARCHIVE_BUFFER_MAX);
+ if (mode == load)
+ {
+ m_BufferPos = m_pBuffer + CARCHIVE_BUFFER_MAX;
+ m_BufferRemain = 0;
+ }
+ else
+ {
+ m_BufferPos = m_pBuffer;
+ m_BufferRemain = CARCHIVE_BUFFER_MAX;
+ }
+}
+
+CArchive::~CArchive()
+{
+ FlushBuffer();
+ delete[] m_pBuffer;
+}
+
+void CArchive::Close()
+{
+ FlushBuffer();
+}
+
+bool CArchive::IsLoading() const
+{
+ return (m_iMode == load);
+}
+
+bool CArchive::IsStoring() const
+{
+ return (m_iMode == store);
+}
+
+CArchive& CArchive::operator<<(float f)
+{
+ return streamout(&f, sizeof(f));
+}
+
+CArchive& CArchive::operator<<(double d)
+{
+ return streamout(&d, sizeof(d));
+}
+
+CArchive& CArchive::operator<<(short int s)
+{
+ return streamout(&s, sizeof(s));
+}
+
+CArchive& CArchive::operator<<(unsigned short int us)
+{
+ return streamout(&us, sizeof(us));
+}
+
+CArchive& CArchive::operator<<(int i)
+{
+ return streamout(&i, sizeof(i));
+}
+
+CArchive& CArchive::operator<<(unsigned int ui)
+{
+ return streamout(&ui, sizeof(ui));
+}
+
+CArchive& CArchive::operator<<(long int l)
+{
+ return streamout(&l, sizeof(l));
+}
+
+CArchive& CArchive::operator<<(unsigned long int ul)
+{
+ return streamout(&ul, sizeof(ul));
+}
+
+CArchive& CArchive::operator<<(long long int ll)
+{
+ return streamout(&ll, sizeof(ll));
+}
+
+CArchive& CArchive::operator<<(unsigned long long int ull)
+{
+ return streamout(&ull, sizeof(ull));
+}
+
+CArchive& CArchive::operator<<(bool b)
+{
+ return streamout(&b, sizeof(b));
+}
+
+CArchive& CArchive::operator<<(char c)
+{
+ return streamout(&c, sizeof(c));
+}
+
+CArchive& CArchive::operator<<(const std::string& str)
+{
+ *this << str.size();
+
+ return streamout(str.data(), str.size() * sizeof(char));
+}
+
+CArchive& CArchive::operator<<(const std::wstring& wstr)
+{
+ *this << wstr.size();
+
+ return streamout(wstr.data(), wstr.size() * sizeof(wchar_t));
+}
+
+CArchive& CArchive::operator<<(const SYSTEMTIME& time)
+{
+ return streamout(&time, sizeof(SYSTEMTIME));
+}
+
+CArchive& CArchive::operator<<(IArchivable& obj)
+{
+ obj.Archive(*this);
+
+ return *this;
+}
+
+CArchive& CArchive::operator<<(const CVariant& variant)
+{
+ *this << (int)variant.type();
+ switch (variant.type())
+ {
+ case CVariant::VariantTypeInteger:
+ *this << variant.asInteger();
+ break;
+ case CVariant::VariantTypeUnsignedInteger:
+ *this << variant.asUnsignedInteger();
+ break;
+ case CVariant::VariantTypeBoolean:
+ *this << variant.asBoolean();
+ break;
+ case CVariant::VariantTypeString:
+ *this << variant.asString();
+ break;
+ case CVariant::VariantTypeWideString:
+ *this << variant.asWideString();
+ break;
+ case CVariant::VariantTypeDouble:
+ *this << variant.asDouble();
+ break;
+ case CVariant::VariantTypeArray:
+ *this << variant.size();
+ for (unsigned int index = 0; index < variant.size(); index++)
+ *this << variant[index];
+ break;
+ case CVariant::VariantTypeObject:
+ *this << variant.size();
+ for (CVariant::const_iterator_map itr = variant.begin_map(); itr != variant.end_map(); ++itr)
+ {
+ *this << itr->first;
+ *this << itr->second;
+ }
+ break;
+ case CVariant::VariantTypeNull:
+ case CVariant::VariantTypeConstNull:
+ default:
+ break;
+ }
+
+ return *this;
+}
+
+CArchive& CArchive::operator<<(const std::vector<std::string>& strArray)
+{
+ *this << strArray.size();
+ for (size_t index = 0; index < strArray.size(); index++)
+ *this << strArray.at(index);
+
+ return *this;
+}
+
+CArchive& CArchive::operator<<(const std::vector<int>& iArray)
+{
+ *this << iArray.size();
+ for (size_t index = 0; index < iArray.size(); index++)
+ *this << iArray.at(index);
+
+ return *this;
+}
+
+CArchive& CArchive::operator>>(std::string& str)
+{
+ size_t iLength = 0;
+ *this >> iLength;
+
+ char *s = new char[iLength];
+ streamin(s, iLength * sizeof(char));
+ str.assign(s, iLength);
+ delete[] s;
+
+ return *this;
+}
+
+CArchive& CArchive::operator>>(std::wstring& wstr)
+{
+ size_t iLength = 0;
+ *this >> iLength;
+
+ wchar_t * const p = new wchar_t[iLength];
+ streamin(p, iLength * sizeof(wchar_t));
+ wstr.assign(p, iLength);
+ delete[] p;
+
+ return *this;
+}
+
+CArchive& CArchive::operator>>(SYSTEMTIME& time)
+{
+ return streamin(&time, sizeof(SYSTEMTIME));
+}
+
+CArchive& CArchive::operator>>(IArchivable& obj)
+{
+ obj.Archive(*this);
+
+ return *this;
+}
+
+CArchive& CArchive::operator>>(CVariant& variant)
+{
+ int type;
+ *this >> type;
+ variant = CVariant((CVariant::VariantType)type);
+
+ switch (variant.type())
+ {
+ case CVariant::VariantTypeInteger:
+ {
+ int64_t value;
+ *this >> value;
+ variant = value;
+ break;
+ }
+ case CVariant::VariantTypeUnsignedInteger:
+ {
+ uint64_t value;
+ *this >> value;
+ variant = value;
+ break;
+ }
+ case CVariant::VariantTypeBoolean:
+ {
+ bool value;
+ *this >> value;
+ variant = value;
+ break;
+ }
+ case CVariant::VariantTypeString:
+ {
+ std::string value;
+ *this >> value;
+ variant = value;
+ break;
+ }
+ case CVariant::VariantTypeWideString:
+ {
+ std::wstring value;
+ *this >> value;
+ variant = value;
+ break;
+ }
+ case CVariant::VariantTypeDouble:
+ {
+ double value;
+ *this >> value;
+ variant = value;
+ break;
+ }
+ case CVariant::VariantTypeArray:
+ {
+ unsigned int size;
+ *this >> size;
+ for (; size > 0; size--)
+ {
+ CVariant value;
+ *this >> value;
+ variant.append(value);
+ }
+ break;
+ }
+ case CVariant::VariantTypeObject:
+ {
+ unsigned int size;
+ *this >> size;
+ for (; size > 0; size--)
+ {
+ std::string name;
+ CVariant value;
+ *this >> name;
+ *this >> value;
+ variant[name] = value;
+ }
+ break;
+ }
+ case CVariant::VariantTypeNull:
+ case CVariant::VariantTypeConstNull:
+ default:
+ break;
+ }
+
+ return *this;
+}
+
+CArchive& CArchive::operator>>(std::vector<std::string>& strArray)
+{
+ size_t size;
+ *this >> size;
+ strArray.clear();
+ for (size_t index = 0; index < size; index++)
+ {
+ std::string str;
+ *this >> str;
+ strArray.push_back(str);
+ }
+
+ return *this;
+}
+
+CArchive& CArchive::operator>>(std::vector<int>& iArray)
+{
+ size_t size;
+ *this >> size;
+ iArray.clear();
+ for (size_t index = 0; index < size; index++)
+ {
+ int i;
+ *this >> i;
+ iArray.push_back(i);
+ }
+
+ return *this;
+}
+
+void CArchive::FlushBuffer()
+{
+ if (m_iMode == store && m_BufferPos != m_pBuffer)
+ {
+ if (m_pFile->Write(m_pBuffer, m_BufferPos - m_pBuffer) != m_BufferPos - m_pBuffer)
+ CLog::Log(LOGERROR, "%s: Error flushing buffer", __FUNCTION__);
+ else
+ {
+ m_BufferPos = m_pBuffer;
+ m_BufferRemain = CARCHIVE_BUFFER_MAX;
+ }
+ }
+}
+
+CArchive &CArchive::streamout_bufferwrap(const uint8_t *ptr, size_t size)
+{
+ do
+ {
+ size_t chunkSize = std::min(size, m_BufferRemain);
+ m_BufferPos = std::copy(ptr, ptr + chunkSize, m_BufferPos);
+ ptr += chunkSize;
+ size -= chunkSize;
+ m_BufferRemain -= chunkSize;
+ if (m_BufferRemain == 0)
+ FlushBuffer();
+ } while (size > 0);
+ return *this;
+}
+
+void CArchive::FillBuffer()
+{
+ if (m_iMode == load && m_BufferRemain == 0)
+ {
+ ssize_t read = m_pFile->Read(m_pBuffer, CARCHIVE_BUFFER_MAX);
+ if (read > 0)
+ {
+ m_BufferRemain = read;
+ m_BufferPos = m_pBuffer;
+ }
+ }
+}
+
+CArchive &CArchive::streamin_bufferwrap(uint8_t *ptr, size_t size)
+{
+ uint8_t *orig_ptr = ptr;
+ size_t orig_size = size;
+ do
+ {
+ if (m_BufferRemain == 0)
+ {
+ FillBuffer();
+ if (m_BufferRemain < CARCHIVE_BUFFER_MAX && m_BufferRemain < size)
+ {
+ CLog::Log(LOGERROR, "%s: can't stream in: requested %lu bytes, was read %lu bytes", __FUNCTION__, (unsigned long) orig_size, (unsigned long) (ptr - orig_ptr + m_BufferRemain));
+ memset(orig_ptr, 0, orig_size);
+ return *this;
+ }
+ }
+ size_t chunkSize = std::min(size, m_BufferRemain);
+ ptr = std::copy(m_BufferPos, m_BufferPos + chunkSize, ptr);
+ m_BufferPos += chunkSize;
+ m_BufferRemain -= chunkSize;
+ size -= chunkSize;
+ } while (size > 0);
+ return *this;
+}
diff --git a/src/utils/Archive.h b/src/utils/Archive.h
new file mode 100644
index 0000000000..6ed0f8fe37
--- /dev/null
+++ b/src/utils/Archive.h
@@ -0,0 +1,196 @@
+#pragma once
+
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string>
+#include <vector>
+#include "PlatformDefs.h" // for SYSTEMTIME
+
+#define CARCHIVE_BUFFER_MAX 4096
+
+namespace XFILE
+{
+ class CFile;
+}
+class CVariant;
+class IArchivable;
+
+class CArchive
+{
+public:
+ CArchive(XFILE::CFile* pFile, int mode);
+ ~CArchive();
+
+ /* CArchive support storing and loading of all C basic integer types
+ * C basic types was chosen instead of fixed size ints (int16_t - int64_t) to support all integer typedefs
+ * For example size_t can be typedef of unsigned int, long or long long depending on platform
+ * while int32_t and int64_t are usually unsigned short, int or long long, but not long
+ * and even if int and long can have same binary representation they are different types for compiler
+ * According to section 5.2.4.2.1 of C99 http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf
+ * minimal size of short int is 16 bits
+ * minimal size of int is 16 bits (usually 32 or 64 bits, larger or equal to short int)
+ * minimal size of long int is 32 bits (larger or equal to int)
+ * minimal size of long long int is 64 bits (larger or equal to long int) */
+ // storing
+ CArchive& operator<<(float f);
+ CArchive& operator<<(double d);
+ CArchive& operator<<(short int s);
+ CArchive& operator<<(unsigned short int us);
+ CArchive& operator<<(int i);
+ CArchive& operator<<(unsigned int ui);
+ CArchive& operator<<(long int l);
+ CArchive& operator<<(unsigned long int ul);
+ CArchive& operator<<(long long int ll);
+ CArchive& operator<<(unsigned long long int ull);
+ CArchive& operator<<(bool b);
+ CArchive& operator<<(char c);
+ CArchive& operator<<(const std::string &str);
+ CArchive& operator<<(const std::wstring& wstr);
+ CArchive& operator<<(const SYSTEMTIME& time);
+ CArchive& operator<<(IArchivable& obj);
+ CArchive& operator<<(const CVariant& variant);
+ CArchive& operator<<(const std::vector<std::string>& strArray);
+ CArchive& operator<<(const std::vector<int>& iArray);
+
+ // loading
+ inline CArchive& operator>>(float& f)
+ {
+ return streamin(&f, sizeof(f));
+ }
+
+ inline CArchive& operator>>(double& d)
+ {
+ return streamin(&d, sizeof(d));
+ }
+
+ inline CArchive& operator>>(short int& s)
+ {
+ return streamin(&s, sizeof(s));
+ }
+
+ inline CArchive& operator>>(unsigned short int& us)
+ {
+ return streamin(&us, sizeof(us));
+ }
+
+ inline CArchive& operator>>(int& i)
+ {
+ return streamin(&i, sizeof(i));
+ }
+
+ inline CArchive& operator>>(unsigned int& ui)
+ {
+ return streamin(&ui, sizeof(ui));
+ }
+
+ inline CArchive& operator>>(long int& l)
+ {
+ return streamin(&l, sizeof(l));
+ }
+
+ inline CArchive& operator>>(unsigned long int& ul)
+ {
+ return streamin(&ul, sizeof(ul));
+ }
+
+ inline CArchive& operator>>(long long int& ll)
+ {
+ return streamin(&ll, sizeof(ll));
+ }
+
+ inline CArchive& operator>>(unsigned long long int& ull)
+ {
+ return streamin(&ull, sizeof(ull));
+ }
+
+ inline CArchive& operator>>(bool& b)
+ {
+ return streamin(&b, sizeof(b));
+ }
+
+ inline CArchive& operator>>(char& c)
+ {
+ return streamin(&c, sizeof(c));
+ }
+
+ CArchive& operator>>(std::string &str);
+ CArchive& operator>>(std::wstring& wstr);
+ CArchive& operator>>(SYSTEMTIME& time);
+ CArchive& operator>>(IArchivable& obj);
+ CArchive& operator>>(CVariant& variant);
+ CArchive& operator>>(std::vector<std::string>& strArray);
+ CArchive& operator>>(std::vector<int>& iArray);
+
+ bool IsLoading() const;
+ bool IsStoring() const;
+
+ void Close();
+
+ enum Mode {load = 0, store};
+
+protected:
+ inline CArchive &streamout(const void *dataPtr, size_t size)
+ {
+ const uint8_t *ptr = (const uint8_t *) dataPtr;
+ /* Note, the buffer is flushed as soon as it is full (m_BufferRemain == size) rather
+ * than waiting until we attempt to put more data into an already full buffer */
+ if (m_BufferRemain > size)
+ {
+ memcpy(m_BufferPos, ptr, size);
+ m_BufferPos += size;
+ m_BufferRemain -= size;
+ return *this;
+ }
+ else
+ {
+ return streamout_bufferwrap(ptr, size);
+ }
+ }
+
+ inline CArchive &streamin(void *dataPtr, size_t size)
+ {
+ uint8_t *ptr = (uint8_t *) dataPtr;
+ /* Note, refilling the buffer is deferred until we know we need to read more from it */
+ if (m_BufferRemain >= size)
+ {
+ memcpy(ptr, m_BufferPos, size);
+ m_BufferPos += size;
+ m_BufferRemain -= size;
+ return *this;
+ }
+ else
+ {
+ return streamin_bufferwrap(ptr, size);
+ }
+ }
+
+ XFILE::CFile* m_pFile;
+ int m_iMode;
+ uint8_t *m_pBuffer;
+ uint8_t *m_BufferPos;
+ size_t m_BufferRemain;
+
+private:
+ void FlushBuffer();
+ CArchive &streamout_bufferwrap(const uint8_t *ptr, size_t size);
+ void FillBuffer();
+ CArchive &streamin_bufferwrap(uint8_t *ptr, size_t size);
+};
diff --git a/src/utils/AsyncFileCopy.cpp b/src/utils/AsyncFileCopy.cpp
new file mode 100644
index 0000000000..d48a0fd3ba
--- /dev/null
+++ b/src/utils/AsyncFileCopy.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "threads/SystemClock.h"
+#include "AsyncFileCopy.h"
+#include "dialogs/GUIDialogProgress.h"
+#include "guilib/GUIWindowManager.h"
+#include "log.h"
+#include "utils/TimeUtils.h"
+#include "utils/StringUtils.h"
+#include "URL.h"
+
+CAsyncFileCopy::CAsyncFileCopy() : CThread("AsyncFileCopy")
+{
+ m_cancelled = false;
+ m_succeeded = false;
+ m_running = false;
+ m_percent = 0;
+ m_speed = 0;
+}
+
+CAsyncFileCopy::~CAsyncFileCopy()
+{
+ StopThread();
+}
+
+bool CAsyncFileCopy::Copy(const CStdString &from, const CStdString &to, const CStdString &heading)
+{
+ // reset the variables to their appropriate states
+ m_from = from;
+ m_to = to;
+ m_cancelled = false;
+ m_succeeded = false;
+ m_percent = 0;
+ m_speed = 0;
+ m_running = true;
+ CURL url1(from);
+ CURL url2(to);
+
+ // create our thread, which starts the file copy operation
+ Create();
+ CGUIDialogProgress *dlg = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
+ unsigned int time = XbmcThreads::SystemClockMillis();
+ while (m_running)
+ {
+ m_event.WaitMSec(1000 / 30);
+ if (!m_running)
+ break;
+ // start the dialog up as needed
+ if (dlg && !dlg->IsDialogRunning() && (XbmcThreads::SystemClockMillis() - time) > 500) // wait 0.5 seconds before starting dialog
+ {
+ dlg->SetHeading(heading);
+ dlg->SetLine(0, url1.GetWithoutUserDetails());
+ dlg->SetLine(1, url2.GetWithoutUserDetails());
+ dlg->SetPercentage(0);
+ dlg->StartModal();
+ }
+ // and update the dialog as we go
+ if (dlg && dlg->IsDialogRunning())
+ {
+ CStdString speedString = StringUtils::Format("%2.2f KB/s", m_speed / 1024);
+ dlg->SetHeading(heading);
+ dlg->SetLine(0, url1.Get());
+ dlg->SetLine(1, url2.Get());
+ dlg->SetLine(2, speedString);
+ dlg->SetPercentage(m_percent);
+ dlg->Progress();
+ m_cancelled = dlg->IsCanceled();
+ }
+ }
+ if (dlg)
+ dlg->Close();
+ return !m_cancelled && m_succeeded;
+}
+
+bool CAsyncFileCopy::OnFileCallback(void *pContext, int ipercent, float avgSpeed)
+{
+ m_percent = ipercent;
+ m_speed = avgSpeed;
+ m_event.Set();
+ return !m_cancelled;
+}
+
+void CAsyncFileCopy::Process()
+{
+ try
+ {
+ m_succeeded = XFILE::CFile::Copy(m_from, m_to, this);
+ }
+ catch (...)
+ {
+ m_succeeded = false;
+ CLog::Log(LOGERROR, "%s: unhandled exception copying file", __FUNCTION__);
+ }
+ m_running = false;
+}
diff --git a/src/utils/AsyncFileCopy.h b/src/utils/AsyncFileCopy.h
new file mode 100644
index 0000000000..c4bd9cde67
--- /dev/null
+++ b/src/utils/AsyncFileCopy.h
@@ -0,0 +1,54 @@
+#pragma once
+
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "threads/Thread.h"
+#include "filesystem/File.h"
+#include "utils/StdString.h"
+
+class CAsyncFileCopy : public CThread, public XFILE::IFileCallback
+{
+public:
+ CAsyncFileCopy();
+ virtual ~CAsyncFileCopy();
+
+ /// \brief Main routine to copy files from one source to another.
+ /// \return true if successful, and false if it failed or was cancelled.
+ bool Copy(const CStdString &from, const CStdString &to, const CStdString &heading);
+
+ /// \brief callback from CFile::Copy()
+ virtual bool OnFileCallback(void *pContext, int ipercent, float avgSpeed);
+
+protected:
+ virtual void Process();
+
+private:
+ /// volatile variables as we access these from both threads
+ volatile int m_percent; ///< current percentage (0..100)
+ volatile float m_speed; ///< current speed (in bytes per second)
+ volatile bool m_cancelled; ///< whether or not we cancelled the operation
+ volatile bool m_running; ///< whether or not the copy operation is still in progress
+
+ bool m_succeeded; ///< whether or not the copy operation was successful
+ CStdString m_from; ///< source URL to copy from
+ CStdString m_to; ///< destination URL to copy to
+ CEvent m_event; ///< event to set to force an update
+};
diff --git a/src/utils/AutoPtrHandle.cpp b/src/utils/AutoPtrHandle.cpp
new file mode 100644
index 0000000000..1b82d9e249
--- /dev/null
+++ b/src/utils/AutoPtrHandle.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "AutoPtrHandle.h"
+
+using namespace AUTOPTR;
+
+CAutoPtrHandle::CAutoPtrHandle(HANDLE hHandle)
+ : m_hHandle(hHandle)
+{}
+
+CAutoPtrHandle::~CAutoPtrHandle(void)
+{
+ Cleanup();
+}
+
+CAutoPtrHandle::operator HANDLE()
+{
+ return m_hHandle;
+}
+
+void CAutoPtrHandle::attach(HANDLE hHandle)
+{
+ Cleanup();
+ m_hHandle = hHandle;
+}
+
+HANDLE CAutoPtrHandle::release()
+{
+ HANDLE hTmp = m_hHandle;
+ m_hHandle = INVALID_HANDLE_VALUE;
+ return hTmp;
+}
+
+void CAutoPtrHandle::Cleanup()
+{
+ if ( isValid() )
+ {
+ CloseHandle(m_hHandle);
+ m_hHandle = INVALID_HANDLE_VALUE;
+ }
+}
+
+bool CAutoPtrHandle::isValid() const
+{
+ if ( INVALID_HANDLE_VALUE != m_hHandle)
+ return true;
+ return false;
+}
+void CAutoPtrHandle::reset()
+{
+ Cleanup();
+}
+
+//-------------------------------------------------------------------------------
+CAutoPtrFind ::CAutoPtrFind(HANDLE hHandle)
+ : CAutoPtrHandle(hHandle)
+{}
+CAutoPtrFind::~CAutoPtrFind(void)
+{
+ Cleanup();
+}
+
+void CAutoPtrFind::Cleanup()
+{
+ if ( isValid() )
+ {
+ FindClose(m_hHandle);
+ m_hHandle = INVALID_HANDLE_VALUE;
+ }
+}
+
+//-------------------------------------------------------------------------------
+CAutoPtrSocket::CAutoPtrSocket(SOCKET hSocket)
+ : m_hSocket(hSocket)
+{}
+
+CAutoPtrSocket::~CAutoPtrSocket(void)
+{
+ Cleanup();
+}
+
+CAutoPtrSocket::operator SOCKET()
+{
+ return m_hSocket;
+}
+
+void CAutoPtrSocket::attach(SOCKET hSocket)
+{
+ Cleanup();
+ m_hSocket = hSocket;
+}
+
+SOCKET CAutoPtrSocket::release()
+{
+ SOCKET hTmp = m_hSocket;
+ m_hSocket = INVALID_SOCKET;
+ return hTmp;
+}
+
+void CAutoPtrSocket::Cleanup()
+{
+ if ( isValid() )
+ {
+ closesocket(m_hSocket);
+ m_hSocket = INVALID_SOCKET;
+ }
+}
+
+bool CAutoPtrSocket::isValid() const
+{
+ if ( INVALID_SOCKET != m_hSocket)
+ return true;
+ return false;
+}
+void CAutoPtrSocket::reset()
+{
+ Cleanup();
+}
diff --git a/src/utils/AutoPtrHandle.h b/src/utils/AutoPtrHandle.h
new file mode 100644
index 0000000000..cd72c3ae64
--- /dev/null
+++ b/src/utils/AutoPtrHandle.h
@@ -0,0 +1,110 @@
+#pragma once
+
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "system.h" // for HANDLE and SOCKET
+#include <stdlib.h>
+
+namespace AUTOPTR
+{
+class CAutoPtrHandle
+{
+public:
+ CAutoPtrHandle(HANDLE hHandle);
+ virtual ~CAutoPtrHandle(void);
+ operator HANDLE();
+ void attach(HANDLE hHandle);
+ HANDLE release();
+ bool isValid() const;
+ void reset();
+protected:
+ virtual void Cleanup();
+ HANDLE m_hHandle;
+};
+
+class CAutoPtrFind : public CAutoPtrHandle
+{
+public:
+ CAutoPtrFind(HANDLE hHandle);
+ virtual ~CAutoPtrFind(void);
+protected:
+ virtual void Cleanup();
+};
+
+
+class CAutoPtrSocket
+{
+public:
+ CAutoPtrSocket(SOCKET hSocket);
+ virtual ~CAutoPtrSocket(void);
+ operator SOCKET();
+ void attach(SOCKET hSocket);
+ SOCKET release();
+ bool isValid() const;
+ void reset();
+protected:
+ virtual void Cleanup();
+ SOCKET m_hSocket;
+};
+
+/*
+ * This template class is very similar to the standard "auto_ptr", but it is
+ * used for *array* pointers rather than *object* pointers, i.e. the pointer
+ * passed to it must have been allocated with "new[]", and "auto_aptr" will
+ * delete it with "delete[]".
+ *
+ * Class released under GPL and was taken from:
+ * http://userpage.fu-berlin.de/~mbayer/tools/html2text.html
+ */
+template <class T>
+class auto_aptr
+{
+
+public:
+
+ // Constructor/copy/destroy
+
+ explicit auto_aptr(T *x = 0) : p(x) {}
+ auto_aptr(const auto_aptr<T> &x) : p(x.p) { ((auto_aptr<T> *) &x)->p = 0; }
+ auto_aptr<T>& operator=(const auto_aptr<T> &x)
+ { delete[] p; p = x.p; ((auto_aptr<T> *) &x)->p = 0; return *this; }
+ // Extension: "operator=(T *)" is identical to "auto_aptr::reset(T *)".
+ void operator=(T *x) { delete[] p; p = x; }
+ ~auto_aptr() { delete[] p; }
+
+ // Members
+
+ T &operator[](size_t idx) const { if (!p) abort(); return p[idx]; }
+T *get() const { return (T *) p; }
+ T *release() { T *tmp = p; p = 0; return tmp; }
+ void reset(T *x = 0) { delete[] p; p = x; }
+
+ // These would make a nice extension, but are not provided by many other
+ // implementations.
+ //operator const void *() const { return p; }
+ //int operator!() const { return p == 0; }
+
+private:
+ T *p;
+};
+
+
+}
diff --git a/src/utils/Base64.cpp b/src/utils/Base64.cpp
new file mode 100644
index 0000000000..54e16f7836
--- /dev/null
+++ b/src/utils/Base64.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2011-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "Base64.h"
+
+#define PADDING '='
+
+using namespace std;
+
+const std::string Base64::m_characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
+
+void Base64::Encode(const char* input, unsigned int length, std::string &output)
+{
+ if (input == NULL || length == 0)
+ return;
+
+ long l;
+ output.clear();
+ output.reserve(((length + 2) / 3) * 4);
+
+ for (unsigned int i = 0; i < length; i += 3)
+ {
+ l = ((((unsigned long) input[i]) << 16) & 0xFFFFFF) |
+ ((((i + 1) < length) ? (((unsigned long) input[i + 1]) << 8) : 0) & 0xFFFF) |
+ ((((i + 2) < length) ? (((unsigned long) input[i + 2]) << 0) : 0) & 0x00FF);
+
+ output.push_back(m_characters[(l >> 18) & 0x3F]);
+ output.push_back(m_characters[(l >> 12) & 0x3F]);
+
+ if (i + 1 < length)
+ output.push_back(m_characters[(l >> 6) & 0x3F]);
+ if (i + 2 < length)
+ output.push_back(m_characters[(l >> 0) & 0x3F]);
+ }
+
+ int left = 3 - (length % 3);
+
+ if (length % 3)
+ {
+ for (int i = 0; i < left; i++)
+ output.push_back(PADDING);
+ }
+}
+
+std::string Base64::Encode(const char* input, unsigned int length)
+{
+ std::string output;
+ Encode(input, length, output);
+
+ return output;
+}
+
+void Base64::Encode(const std::string &input, std::string &output)
+{
+ Encode(input.c_str(), input.size(), output);
+}
+
+std::string Base64::Encode(const std::string &input)
+{
+ std::string output;
+ Encode(input, output);
+
+ return output;
+}
+
+void Base64::Decode(const char* input, unsigned int length, std::string &output)
+{
+ if (input == NULL || length == 0)
+ return;
+
+ long l;
+ output.clear();
+
+ for (unsigned int index = 0; index < length; index++)
+ {
+ if (input[index] == '=')
+ {
+ length = index;
+ break;
+ }
+ }
+
+ output.reserve(length - ((length + 2) / 4));
+
+ for (unsigned int i = 0; i < length; i += 4)
+ {
+ l = ((((unsigned long) m_characters.find(input[i])) & 0x3F) << 18);
+ l |= (((i + 1) < length) ? ((((unsigned long) m_characters.find(input[i + 1])) & 0x3F) << 12) : 0);
+ l |= (((i + 2) < length) ? ((((unsigned long) m_characters.find(input[i + 2])) & 0x3F) << 6) : 0);
+ l |= (((i + 3) < length) ? ((((unsigned long) m_characters.find(input[i + 3])) & 0x3F) << 0) : 0);
+
+ output.push_back((char)((l >> 16) & 0xFF));
+ if (i + 2 < length)
+ output.push_back((char)((l >> 8) & 0xFF));
+ if (i + 3 < length)
+ output.push_back((char)((l >> 0) & 0xFF));
+ }
+}
+
+std::string Base64::Decode(const char* input, unsigned int length)
+{
+ std::string output;
+ Decode(input, length, output);
+
+ return output;
+}
+
+void Base64::Decode(const std::string &input, std::string &output)
+{
+ size_t length = input.find_first_of(PADDING);
+ if (length == string::npos)
+ length = input.size();
+
+ Decode(input.c_str(), length, output);
+}
+
+std::string Base64::Decode(const std::string &input)
+{
+ std::string output;
+ Decode(input, output);
+
+ return output;
+}
diff --git a/src/utils/Base64.h b/src/utils/Base64.h
new file mode 100644
index 0000000000..a37bbe49d8
--- /dev/null
+++ b/src/utils/Base64.h
@@ -0,0 +1,38 @@
+#pragma once
+/*
+ * Copyright (C) 2011-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string>
+
+class Base64
+{
+public:
+ static void Encode(const char* input, unsigned int length, std::string &output);
+ static std::string Encode(const char* input, unsigned int length);
+ static void Encode(const std::string &input, std::string &output);
+ static std::string Encode(const std::string &input);
+ static void Decode(const char* input, unsigned int length, std::string &output);
+ static std::string Decode(const char* input, unsigned int length);
+ static void Decode(const std::string &input, std::string &output);
+ static std::string Decode(const std::string &input);
+
+private:
+ static const std::string m_characters;
+};
diff --git a/src/utils/BitstreamConverter.cpp b/src/utils/BitstreamConverter.cpp
new file mode 100644
index 0000000000..11d0673ec2
--- /dev/null
+++ b/src/utils/BitstreamConverter.cpp
@@ -0,0 +1,1216 @@
+/*
+ * Copyright (C) 2010-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/log.h"
+#include "assert.h"
+
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+
+#include "BitstreamConverter.h"
+
+enum {
+ NAL_SLICE=1,
+ NAL_DPA,
+ NAL_DPB,
+ NAL_DPC,
+ NAL_IDR_SLICE,
+ NAL_SEI,
+ NAL_SPS,
+ NAL_PPS,
+ NAL_AUD,
+ NAL_END_SEQUENCE,
+ NAL_END_STREAM,
+ NAL_FILLER_DATA,
+ NAL_SPS_EXT,
+ NAL_AUXILIARY_SLICE=19
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////
+// GStreamer h264 parser
+// Copyright (C) 2005 Michal Benes <michal.benes@itonis.tv>
+// (C) 2008 Wim Taymans <wim.taymans@gmail.com>
+// gsth264parse.c:
+// * License as published by the Free Software Foundation; either
+// * version 2.1 of the License, or (at your option) any later version.
+static void nal_bs_init(nal_bitstream *bs, const uint8_t *data, size_t size)
+{
+ bs->data = data;
+ bs->end = data + size;
+ bs->head = 0;
+ // fill with something other than 0 to detect
+ // emulation prevention bytes
+ bs->cache = 0xffffffff;
+}
+
+static uint32_t nal_bs_read(nal_bitstream *bs, int n)
+{
+ uint32_t res = 0;
+ int shift;
+
+ if (n == 0)
+ return res;
+
+ // fill up the cache if we need to
+ while (bs->head < n)
+ {
+ uint8_t a_byte;
+ bool check_three_byte;
+
+ check_three_byte = true;
+next_byte:
+ if (bs->data >= bs->end)
+ {
+ // we're at the end, can't produce more than head number of bits
+ n = bs->head;
+ break;
+ }
+ // get the byte, this can be an emulation_prevention_three_byte that we need
+ // to ignore.
+ a_byte = *bs->data++;
+ if (check_three_byte && a_byte == 0x03 && ((bs->cache & 0xffff) == 0))
+ {
+ // next byte goes unconditionally to the cache, even if it's 0x03
+ check_three_byte = false;
+ goto next_byte;
+ }
+ // shift bytes in cache, moving the head bits of the cache left
+ bs->cache = (bs->cache << 8) | a_byte;
+ bs->head += 8;
+ }
+
+ // bring the required bits down and truncate
+ if ((shift = bs->head - n) > 0)
+ res = bs->cache >> shift;
+ else
+ res = bs->cache;
+
+ // mask out required bits
+ if (n < 32)
+ res &= (1 << n) - 1;
+ bs->head = shift;
+
+ return res;
+}
+
+static bool nal_bs_eos(nal_bitstream *bs)
+{
+ return (bs->data >= bs->end) && (bs->head == 0);
+}
+
+// read unsigned Exp-Golomb code
+static int nal_bs_read_ue(nal_bitstream *bs)
+{
+ int i = 0;
+
+ while (nal_bs_read(bs, 1) == 0 && !nal_bs_eos(bs) && i < 32)
+ i++;
+
+ return ((1 << i) - 1 + nal_bs_read(bs, i));
+}
+
+static const uint8_t* avc_find_startcode_internal(const uint8_t *p, const uint8_t *end)
+{
+ const uint8_t *a = p + 4 - ((intptr_t)p & 3);
+
+ for (end -= 3; p < a && p < end; p++)
+ {
+ if (p[0] == 0 && p[1] == 0 && p[2] == 1)
+ return p;
+ }
+
+ for (end -= 3; p < end; p += 4)
+ {
+ uint32_t x = *(const uint32_t*)p;
+ if ((x - 0x01010101) & (~x) & 0x80808080) // generic
+ {
+ if (p[1] == 0)
+ {
+ if (p[0] == 0 && p[2] == 1)
+ return p;
+ if (p[2] == 0 && p[3] == 1)
+ return p+1;
+ }
+ if (p[3] == 0)
+ {
+ if (p[2] == 0 && p[4] == 1)
+ return p+2;
+ if (p[4] == 0 && p[5] == 1)
+ return p+3;
+ }
+ }
+ }
+
+ for (end += 3; p < end; p++)
+ {
+ if (p[0] == 0 && p[1] == 0 && p[2] == 1)
+ return p;
+ }
+
+ return end + 3;
+}
+
+static const uint8_t* avc_find_startcode(const uint8_t *p, const uint8_t *end)
+{
+ const uint8_t *out = avc_find_startcode_internal(p, end);
+ if (p<out && out<end && !out[-1])
+ out--;
+ return out;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////
+CBitstreamParser::CBitstreamParser()
+{
+}
+
+CBitstreamParser::~CBitstreamParser()
+{
+ Close();
+}
+
+bool CBitstreamParser::Open()
+{
+ return true;
+}
+
+void CBitstreamParser::Close()
+{
+}
+
+const uint8_t* CBitstreamParser::find_start_code(const uint8_t *p,
+ const uint8_t *end, uint32_t *state)
+{
+ assert(p <= end);
+ if (p >= end)
+ return end;
+
+ for (int i = 0; i < 3; i++) {
+ uint32_t tmp = *state << 8;
+ *state = tmp + *(p++);
+ if (tmp == 0x100 || p == end)
+ return p;
+ }
+
+ while (p < end) {
+ if (p[-1] > 1 ) p += 3;
+ else if (p[-2] ) p += 2;
+ else if (p[-3]|(p[-1]-1)) p++;
+ else {
+ p++;
+ break;
+ }
+ }
+
+ p = FFMIN(p, end) - 4;
+ *state = BS_RB32(p);
+
+ return p + 4;
+}
+
+bool CBitstreamParser::FindIdrSlice(const uint8_t *buf, int buf_size)
+{
+ if (!buf)
+ return false;
+
+ bool rtn = false;
+ uint32_t state = -1;
+ const uint8_t *buf_end = buf + buf_size;
+
+ for(;;)
+ {
+ buf = find_start_code(buf, buf_end, &state);
+ if (buf >= buf_end)
+ {
+ //CLog::Log(LOGDEBUG, "FindIdrSlice: buf(%p), buf_end(%p)", buf, buf_end);
+ break;
+ }
+
+ --buf;
+ int src_length = buf_end - buf;
+ switch (state & 0x1f)
+ {
+ default:
+ CLog::Log(LOGDEBUG, "FindIdrSlice: found nal_type(%d)", state & 0x1f);
+ break;
+ case NAL_SLICE:
+ CLog::Log(LOGDEBUG, "FindIdrSlice: found NAL_SLICE");
+ break;
+ case NAL_IDR_SLICE:
+ CLog::Log(LOGDEBUG, "FindIdrSlice: found NAL_IDR_SLICE");
+ rtn = true;
+ break;
+ case NAL_SEI:
+ CLog::Log(LOGDEBUG, "FindIdrSlice: found NAL_SEI");
+ break;
+ case NAL_SPS:
+ CLog::Log(LOGDEBUG, "FindIdrSlice: found NAL_SPS");
+ break;
+ case NAL_PPS:
+ CLog::Log(LOGDEBUG, "FindIdrSlice: found NAL_PPS");
+ break;
+ }
+ buf += src_length;
+ }
+
+ return rtn;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////
+CBitstreamConverter::CBitstreamConverter()
+{
+ m_convert_bitstream = false;
+ m_convertBuffer = NULL;
+ m_convertSize = 0;
+ m_inputBuffer = NULL;
+ m_inputSize = 0;
+ m_to_annexb = false;
+ m_extradata = NULL;
+ m_extrasize = 0;
+ m_convert_3byteTo4byteNALSize = false;
+ m_convert_bytestream = false;
+ m_sps_pps_context.sps_pps_data = NULL;
+}
+
+CBitstreamConverter::~CBitstreamConverter()
+{
+ Close();
+}
+
+bool CBitstreamConverter::Open(enum AVCodecID codec, uint8_t *in_extradata, int in_extrasize, bool to_annexb)
+{
+ m_to_annexb = to_annexb;
+
+ m_codec = codec;
+ switch(m_codec)
+ {
+ case AV_CODEC_ID_H264:
+ if (in_extrasize < 7 || in_extradata == NULL)
+ {
+ CLog::Log(LOGERROR, "CBitstreamConverter::Open avcC data too small or missing");
+ return false;
+ }
+ // valid avcC data (bitstream) always starts with the value 1 (version)
+ if(m_to_annexb)
+ {
+ if ( in_extradata[0] == 1 )
+ {
+ CLog::Log(LOGINFO, "CBitstreamConverter::Open bitstream to annexb init");
+ m_extrasize = in_extrasize;
+ m_extradata = (uint8_t*)av_malloc(in_extrasize);
+ memcpy(m_extradata, in_extradata, in_extrasize);
+ m_convert_bitstream = BitstreamConvertInit(m_extradata, m_extrasize);
+ return true;
+ }
+ }
+ else
+ {
+ // valid avcC atom data always starts with the value 1 (version)
+ if ( in_extradata[0] != 1 )
+ {
+ if ( (in_extradata[0] == 0 && in_extradata[1] == 0 && in_extradata[2] == 0 && in_extradata[3] == 1) ||
+ (in_extradata[0] == 0 && in_extradata[1] == 0 && in_extradata[2] == 1) )
+ {
+ CLog::Log(LOGINFO, "CBitstreamConverter::Open annexb to bitstream init");
+ // video content is from x264 or from bytestream h264 (AnnexB format)
+ // NAL reformating to bitstream format needed
+ AVIOContext *pb;
+ if (avio_open_dyn_buf(&pb) < 0)
+ return false;
+ m_convert_bytestream = true;
+ // create a valid avcC atom data from ffmpeg's extradata
+ isom_write_avcc(pb, in_extradata, in_extrasize);
+ // unhook from ffmpeg's extradata
+ in_extradata = NULL;
+ // extract the avcC atom data into extradata then write it into avcCData for VDADecoder
+ in_extrasize = avio_close_dyn_buf(pb, &in_extradata);
+ // make a copy of extradata contents
+ m_extradata = (uint8_t *)av_malloc(in_extrasize);
+ memcpy(m_extradata, in_extradata, in_extrasize);
+ m_extrasize = in_extrasize;
+ // done with the converted extradata, we MUST free using av_free
+ av_free(in_extradata);
+ return true;
+ }
+ else
+ {
+ CLog::Log(LOGNOTICE, "CBitstreamConverter::Open invalid avcC atom data");
+ return false;
+ }
+ }
+ else
+ {
+ if (in_extradata[4] == 0xFE)
+ {
+ CLog::Log(LOGINFO, "CBitstreamConverter::Open annexb to bitstream init 3 byte to 4 byte nal");
+ // video content is from so silly encoder that think 3 byte NAL sizes
+ // are valid, setup to convert 3 byte NAL sizes to 4 byte.
+ in_extradata[4] = 0xFF;
+ m_convert_3byteTo4byteNALSize = true;
+
+ m_extradata = (uint8_t *)av_malloc(in_extrasize);
+ memcpy(m_extradata, in_extradata, in_extrasize);
+ m_extrasize = in_extrasize;
+ return true;
+ }
+ }
+ // valid avcC atom
+ m_extradata = (uint8_t*)av_malloc(in_extrasize);
+ memcpy(m_extradata, in_extradata, in_extrasize);
+ m_extrasize = in_extrasize;
+ return true;
+ }
+ return false;
+ break;
+ default:
+ return false;
+ break;
+ }
+ return false;
+}
+
+void CBitstreamConverter::Close(void)
+{
+ if (m_sps_pps_context.sps_pps_data)
+ av_free(m_sps_pps_context.sps_pps_data), m_sps_pps_context.sps_pps_data = NULL;
+
+ if (m_convertBuffer)
+ av_free(m_convertBuffer), m_convertBuffer = NULL;
+ m_convertSize = 0;
+
+ if (m_extradata)
+ av_free(m_extradata), m_extradata = NULL;
+ m_extrasize = 0;
+
+ m_inputSize = 0;
+ m_inputBuffer = NULL;
+
+ m_convert_bitstream = false;
+ m_convert_bytestream = false;
+ m_convert_3byteTo4byteNALSize = false;
+}
+
+bool CBitstreamConverter::Convert(uint8_t *pData, int iSize)
+{
+ if (m_convertBuffer)
+ {
+ av_free(m_convertBuffer);
+ m_convertBuffer = NULL;
+ }
+ m_inputSize = 0;
+ m_convertSize = 0;
+ m_inputBuffer = NULL;
+
+ if (pData)
+ {
+ if (m_codec == AV_CODEC_ID_H264)
+ {
+ if (m_to_annexb)
+ {
+ int demuxer_bytes = iSize;
+ uint8_t *demuxer_content = pData;
+
+ if (m_convert_bitstream)
+ {
+ // convert demuxer packet from bitstream to bytestream (AnnexB)
+ int bytestream_size = 0;
+ uint8_t *bytestream_buff = NULL;
+
+ BitstreamConvert(demuxer_content, demuxer_bytes, &bytestream_buff, &bytestream_size);
+ if (bytestream_buff && (bytestream_size > 0))
+ {
+ m_convertSize = bytestream_size;
+ m_convertBuffer = bytestream_buff;
+ return true;
+ }
+ else
+ {
+ m_convertSize = 0;
+ m_convertBuffer = NULL;
+ CLog::Log(LOGERROR, "CBitstreamConverter::Convert: error converting.");
+ return false;
+ }
+ }
+ else
+ {
+ m_inputSize = iSize;
+ m_inputBuffer = pData;
+ return true;
+ }
+ }
+ else
+ {
+ m_inputSize = iSize;
+ m_inputBuffer = pData;
+
+ if (m_convert_bytestream)
+ {
+ if(m_convertBuffer)
+ {
+ av_free(m_convertBuffer);
+ m_convertBuffer = NULL;
+ }
+ m_convertSize = 0;
+
+ // convert demuxer packet from bytestream (AnnexB) to bitstream
+ AVIOContext *pb;
+
+ if(avio_open_dyn_buf(&pb) < 0)
+ {
+ return false;
+ }
+ m_convertSize = avc_parse_nal_units(pb, pData, iSize);
+ m_convertSize = avio_close_dyn_buf(pb, &m_convertBuffer);
+ }
+ else if (m_convert_3byteTo4byteNALSize)
+ {
+ if(m_convertBuffer)
+ {
+ av_free(m_convertBuffer);
+ m_convertBuffer = NULL;
+ }
+ m_convertSize = 0;
+
+ // convert demuxer packet from 3 byte NAL sizes to 4 byte
+ AVIOContext *pb;
+ if (avio_open_dyn_buf(&pb) < 0)
+ return false;
+
+ uint32_t nal_size;
+ uint8_t *end = pData + iSize;
+ uint8_t *nal_start = pData;
+ while (nal_start < end)
+ {
+ nal_size = BS_RB24(nal_start);
+ avio_wb16(pb, nal_size);
+ nal_start += 3;
+ avio_write(pb, nal_start, nal_size);
+ nal_start += nal_size;
+ }
+
+ m_convertSize = avio_close_dyn_buf(pb, &m_convertBuffer);
+ }
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+
+uint8_t *CBitstreamConverter::GetConvertBuffer() const
+{
+ if((m_convert_bitstream || m_convert_bytestream || m_convert_3byteTo4byteNALSize) && m_convertBuffer != NULL)
+ return m_convertBuffer;
+ else
+ return m_inputBuffer;
+}
+
+int CBitstreamConverter::GetConvertSize() const
+{
+ if((m_convert_bitstream || m_convert_bytestream || m_convert_3byteTo4byteNALSize) && m_convertBuffer != NULL)
+ return m_convertSize;
+ else
+ return m_inputSize;
+}
+
+uint8_t *CBitstreamConverter::GetExtraData() const
+{
+ if(m_convert_bitstream)
+ return m_sps_pps_context.sps_pps_data;
+ else
+ return m_extradata;
+}
+int CBitstreamConverter::GetExtraSize() const
+{
+ if(m_convert_bitstream)
+ return m_sps_pps_context.size;
+ else
+ return m_extrasize;
+}
+
+bool CBitstreamConverter::BitstreamConvertInit(void *in_extradata, int in_extrasize)
+{
+ // based on h264_mp4toannexb_bsf.c (ffmpeg)
+ // which is Copyright (c) 2007 Benoit Fouet <benoit.fouet@free.fr>
+ // and Licensed GPL 2.1 or greater
+
+ m_sps_pps_size = 0;
+ m_sps_pps_context.sps_pps_data = NULL;
+
+ // nothing to filter
+ if (!in_extradata || in_extrasize < 6)
+ return false;
+
+ uint16_t unit_size;
+ uint32_t total_size = 0;
+ uint8_t *out = NULL, unit_nb, sps_done = 0, sps_seen = 0, pps_seen = 0;
+ const uint8_t *extradata = (uint8_t*)in_extradata + 4;
+ static const uint8_t nalu_header[4] = {0, 0, 0, 1};
+
+ // retrieve length coded size
+ m_sps_pps_context.length_size = (*extradata++ & 0x3) + 1;
+
+ // retrieve sps and pps unit(s)
+ unit_nb = *extradata++ & 0x1f; // number of sps unit(s)
+ if (!unit_nb)
+ {
+ goto pps;
+ }
+ else
+ {
+ sps_seen = 1;
+ }
+
+ while (unit_nb--)
+ {
+ void *tmp;
+
+ unit_size = extradata[0] << 8 | extradata[1];
+ total_size += unit_size + 4;
+
+ if (total_size > INT_MAX - FF_INPUT_BUFFER_PADDING_SIZE ||
+ (extradata + 2 + unit_size) > ((uint8_t*)in_extradata + in_extrasize))
+ {
+ av_free(out);
+ return false;
+ }
+ tmp = av_realloc(out, total_size + FF_INPUT_BUFFER_PADDING_SIZE);
+ if (!tmp)
+ {
+ av_free(out);
+ return false;
+ }
+ out = (uint8_t*)tmp;
+ memcpy(out + total_size - unit_size - 4, nalu_header, 4);
+ memcpy(out + total_size - unit_size, extradata + 2, unit_size);
+ extradata += 2 + unit_size;
+
+pps:
+ if (!unit_nb && !sps_done++)
+ {
+ unit_nb = *extradata++; // number of pps unit(s)
+ if (unit_nb)
+ pps_seen = 1;
+ }
+ }
+
+ if (out)
+ memset(out + total_size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
+
+ if (!sps_seen)
+ CLog::Log(LOGDEBUG, "SPS NALU missing or invalid. The resulting stream may not play");
+ if (!pps_seen)
+ CLog::Log(LOGDEBUG, "PPS NALU missing or invalid. The resulting stream may not play");
+
+ m_sps_pps_context.sps_pps_data = out;
+ m_sps_pps_context.size = total_size;
+ m_sps_pps_context.first_idr = 1;
+ m_sps_pps_context.idr_sps_pps_seen = 0;
+
+ return true;
+}
+
+bool CBitstreamConverter::BitstreamConvert(uint8_t* pData, int iSize, uint8_t **poutbuf, int *poutbuf_size)
+{
+ // based on h264_mp4toannexb_bsf.c (ffmpeg)
+ // which is Copyright (c) 2007 Benoit Fouet <benoit.fouet@free.fr>
+ // and Licensed GPL 2.1 or greater
+
+ int i;
+ uint8_t *buf = pData;
+ uint32_t buf_size = iSize;
+ uint8_t unit_type;
+ int32_t nal_size;
+ uint32_t cumul_size = 0;
+ const uint8_t *buf_end = buf + buf_size;
+
+ do
+ {
+ if (buf + m_sps_pps_context.length_size > buf_end)
+ goto fail;
+
+ for (nal_size = 0, i = 0; i < m_sps_pps_context.length_size; i++)
+ nal_size = (nal_size << 8) | buf[i];
+
+ buf += m_sps_pps_context.length_size;
+ unit_type = *buf & 0x1f;
+
+ if (buf + nal_size > buf_end || nal_size < 0)
+ goto fail;
+
+ // Don't add sps/pps if the unit already contain them
+ if (m_sps_pps_context.first_idr && (unit_type == 7 || unit_type == 8))
+ m_sps_pps_context.idr_sps_pps_seen = 1;
+
+ // prepend only to the first access unit of an IDR picture, if no sps/pps already present
+ if (m_sps_pps_context.first_idr && unit_type == 5 && !m_sps_pps_context.idr_sps_pps_seen)
+ {
+ BitstreamAllocAndCopy(poutbuf, poutbuf_size,
+ m_sps_pps_context.sps_pps_data, m_sps_pps_context.size, buf, nal_size);
+ m_sps_pps_context.first_idr = 0;
+ }
+ else
+ {
+ BitstreamAllocAndCopy(poutbuf, poutbuf_size, NULL, 0, buf, nal_size);
+ if (!m_sps_pps_context.first_idr && unit_type == 1)
+ {
+ m_sps_pps_context.first_idr = 1;
+ m_sps_pps_context.idr_sps_pps_seen = 0;
+ }
+ }
+
+ buf += nal_size;
+ cumul_size += nal_size + m_sps_pps_context.length_size;
+ } while (cumul_size < buf_size);
+
+ return true;
+
+fail:
+ av_free(*poutbuf), *poutbuf = NULL;
+ *poutbuf_size = 0;
+ return false;
+}
+
+void CBitstreamConverter::BitstreamAllocAndCopy( uint8_t **poutbuf, int *poutbuf_size,
+ const uint8_t *sps_pps, uint32_t sps_pps_size, const uint8_t *in, uint32_t in_size)
+{
+ // based on h264_mp4toannexb_bsf.c (ffmpeg)
+ // which is Copyright (c) 2007 Benoit Fouet <benoit.fouet@free.fr>
+ // and Licensed GPL 2.1 or greater
+
+ uint32_t offset = *poutbuf_size;
+ uint8_t nal_header_size = offset ? 3 : 4;
+ void *tmp;
+
+ *poutbuf_size += sps_pps_size + in_size + nal_header_size;
+ tmp = av_realloc(*poutbuf, *poutbuf_size);
+ if (!tmp)
+ return;
+ *poutbuf = (uint8_t*)tmp;
+ if (sps_pps)
+ memcpy(*poutbuf + offset, sps_pps, sps_pps_size);
+
+ memcpy(*poutbuf + sps_pps_size + nal_header_size + offset, in, in_size);
+ if (!offset)
+ {
+ BS_WB32(*poutbuf + sps_pps_size, 1);
+ }
+ else
+ {
+ (*poutbuf + offset + sps_pps_size)[0] = 0;
+ (*poutbuf + offset + sps_pps_size)[1] = 0;
+ (*poutbuf + offset + sps_pps_size)[2] = 1;
+ }
+}
+
+const int CBitstreamConverter::avc_parse_nal_units(AVIOContext *pb, const uint8_t *buf_in, int size)
+{
+ const uint8_t *p = buf_in;
+ const uint8_t *end = p + size;
+ const uint8_t *nal_start, *nal_end;
+
+ size = 0;
+ nal_start = avc_find_startcode(p, end);
+
+ for (;;) {
+ while (nal_start < end && !*(nal_start++));
+ if (nal_start == end)
+ break;
+
+ nal_end = avc_find_startcode(nal_start, end);
+ avio_wb32(pb, nal_end - nal_start);
+ avio_write(pb, nal_start, nal_end - nal_start);
+ size += 4 + nal_end - nal_start;
+ nal_start = nal_end;
+ }
+ return size;
+}
+
+const int CBitstreamConverter::avc_parse_nal_units_buf(const uint8_t *buf_in, uint8_t **buf, int *size)
+{
+ AVIOContext *pb;
+ int ret = avio_open_dyn_buf(&pb);
+ if (ret < 0)
+ return ret;
+
+ avc_parse_nal_units(pb, buf_in, *size);
+
+ av_freep(buf);
+ *size = avio_close_dyn_buf(pb, buf);
+ return 0;
+}
+
+const int CBitstreamConverter::isom_write_avcc(AVIOContext *pb, const uint8_t *data, int len)
+{
+ // extradata from bytestream h264, convert to avcC atom data for bitstream
+ if (len > 6)
+ {
+ /* check for h264 start code */
+ if (BS_RB32(data) == 0x00000001 || BS_RB24(data) == 0x000001)
+ {
+ uint8_t *buf=NULL, *end, *start;
+ uint32_t sps_size=0, pps_size=0;
+ uint8_t *sps=0, *pps=0;
+
+ int ret = avc_parse_nal_units_buf(data, &buf, &len);
+ if (ret < 0)
+ return ret;
+ start = buf;
+ end = buf + len;
+
+ /* look for sps and pps */
+ while (end - buf > 4)
+ {
+ uint32_t size;
+ uint8_t nal_type;
+ size = FFMIN(BS_RB32(buf), end - buf - 4);
+ buf += 4;
+ nal_type = buf[0] & 0x1f;
+ if (nal_type == 7) /* SPS */
+ {
+ sps = buf;
+ sps_size = size;
+ }
+ else if (nal_type == 8) /* PPS */
+ {
+ pps = buf;
+ pps_size = size;
+ }
+ buf += size;
+ }
+ if (!sps || !pps || sps_size < 4 || sps_size > UINT16_MAX || pps_size > UINT16_MAX)
+ assert(0);
+
+ avio_w8(pb, 1); /* version */
+ avio_w8(pb, sps[1]); /* profile */
+ avio_w8(pb, sps[2]); /* profile compat */
+ avio_w8(pb, sps[3]); /* level */
+ avio_w8(pb, 0xff); /* 6 bits reserved (111111) + 2 bits nal size length - 1 (11) */
+ avio_w8(pb, 0xe1); /* 3 bits reserved (111) + 5 bits number of sps (00001) */
+
+ avio_wb16(pb, sps_size);
+ avio_write(pb, sps, sps_size);
+ if (pps)
+ {
+ avio_w8(pb, 1); /* number of pps */
+ avio_wb16(pb, pps_size);
+ avio_write(pb, pps, pps_size);
+ }
+ av_free(start);
+ }
+ else
+ {
+ avio_write(pb, data, len);
+ }
+ }
+ return 0;
+}
+
+void CBitstreamConverter::bits_reader_set( bits_reader_t *br, uint8_t *buf, int len )
+{
+ br->buffer = br->start = buf;
+ br->offbits = 0;
+ br->length = len;
+ br->oflow = 0;
+}
+
+uint32_t CBitstreamConverter::read_bits( bits_reader_t *br, int nbits )
+{
+ int i, nbytes;
+ uint32_t ret = 0;
+ uint8_t *buf;
+
+ buf = br->buffer;
+ nbytes = (br->offbits + nbits)/8;
+ if ( ((br->offbits + nbits) %8 ) > 0 )
+ nbytes++;
+ if ( (buf + nbytes) > (br->start + br->length) ) {
+ br->oflow = 1;
+ return 0;
+ }
+ for ( i=0; i<nbytes; i++ )
+ ret += buf[i]<<((nbytes-i-1)*8);
+ i = (4-nbytes)*8+br->offbits;
+ ret = ((ret<<i)>>i)>>((nbytes*8)-nbits-br->offbits);
+
+ br->offbits += nbits;
+ br->buffer += br->offbits / 8;
+ br->offbits %= 8;
+
+ return ret;
+}
+
+void CBitstreamConverter::skip_bits( bits_reader_t *br, int nbits )
+{
+ br->offbits += nbits;
+ br->buffer += br->offbits / 8;
+ br->offbits %= 8;
+ if ( br->buffer > (br->start + br->length) ) {
+ br->oflow = 1;
+ }
+}
+
+uint32_t CBitstreamConverter::get_bits( bits_reader_t *br, int nbits )
+{
+ int i, nbytes;
+ uint32_t ret = 0;
+ uint8_t *buf;
+
+ buf = br->buffer;
+ nbytes = (br->offbits + nbits)/8;
+ if ( ((br->offbits + nbits) %8 ) > 0 )
+ nbytes++;
+ if ( (buf + nbytes) > (br->start + br->length) ) {
+ br->oflow = 1;
+ return 0;
+ }
+ for ( i=0; i<nbytes; i++ )
+ ret += buf[i]<<((nbytes-i-1)*8);
+ i = (4-nbytes)*8+br->offbits;
+ ret = ((ret<<i)>>i)>>((nbytes*8)-nbits-br->offbits);
+
+ return ret;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////
+void CBitstreamConverter::init_bits_writer(bits_writer_t *s, uint8_t *buffer, int buffer_size, int writer_le)
+{
+ if (buffer_size < 0)
+ {
+ buffer_size = 0;
+ buffer = NULL;
+ }
+
+ s->size_in_bits = 8 * buffer_size;
+ s->buf = buffer;
+ s->buf_end = s->buf + buffer_size;
+ s->buf_ptr = s->buf;
+ s->bit_left = 32;
+ s->bit_buf = 0;
+ s->writer_le = writer_le;
+}
+
+void CBitstreamConverter::write_bits(bits_writer_t *s, int n, unsigned int value)
+{
+ // Write up to 32 bits into a bitstream.
+ unsigned int bit_buf;
+ int bit_left;
+
+ if (n == 32)
+ {
+ // Write exactly 32 bits into a bitstream.
+ // danger, recursion in play.
+ int lo = value & 0xffff;
+ int hi = value >> 16;
+ if (s->writer_le)
+ {
+ write_bits(s, 16, lo);
+ write_bits(s, 16, hi);
+ }
+ else
+ {
+ write_bits(s, 16, hi);
+ write_bits(s, 16, lo);
+ }
+ return;
+ }
+
+ bit_buf = s->bit_buf;
+ bit_left = s->bit_left;
+
+ if (s->writer_le)
+ {
+ bit_buf |= value << (32 - bit_left);
+ if (n >= bit_left) {
+ BS_WL32(s->buf_ptr, bit_buf);
+ s->buf_ptr += 4;
+ bit_buf = (bit_left == 32) ? 0 : value >> bit_left;
+ bit_left += 32;
+ }
+ bit_left -= n;
+ }
+ else
+ {
+ if (n < bit_left) {
+ bit_buf = (bit_buf << n) | value;
+ bit_left -= n;
+ } else {
+ bit_buf <<= bit_left;
+ bit_buf |= value >> (n - bit_left);
+ BS_WB32(s->buf_ptr, bit_buf);
+ s->buf_ptr += 4;
+ bit_left += 32 - n;
+ bit_buf = value;
+ }
+ }
+
+ s->bit_buf = bit_buf;
+ s->bit_left = bit_left;
+}
+
+void CBitstreamConverter::skip_bits(bits_writer_t *s, int n)
+{
+ // Skip the given number of bits.
+ // Must only be used if the actual values in the bitstream do not matter.
+ // If n is 0 the behavior is undefined.
+ s->bit_left -= n;
+ s->buf_ptr -= 4 * (s->bit_left >> 5);
+ s->bit_left &= 31;
+}
+
+void CBitstreamConverter::flush_bits(bits_writer_t *s)
+{
+ if (!s->writer_le)
+ {
+ if (s->bit_left < 32)
+ s->bit_buf <<= s->bit_left;
+ }
+ while (s->bit_left < 32)
+ {
+
+ if (s->writer_le)
+ {
+ *s->buf_ptr++ = s->bit_buf;
+ s->bit_buf >>= 8;
+ }
+ else
+ {
+ *s->buf_ptr++ = s->bit_buf >> 24;
+ s->bit_buf <<= 8;
+ }
+ s->bit_left += 8;
+ }
+ s->bit_left = 32;
+ s->bit_buf = 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////
+bool CBitstreamConverter::mpeg2_sequence_header(const uint8_t *data, const uint32_t size, mpeg2_sequence *sequence)
+{
+ // parse nal's until a sequence_header_code is found
+ // and return the width, height, aspect ratio and frame rate if changed.
+ bool changed = false;
+
+ if (!data)
+ return changed;
+
+ const uint8_t *p = data;
+ const uint8_t *end = p + size;
+ const uint8_t *nal_start, *nal_end;
+
+ nal_start = avc_find_startcode(p, end);
+ while (nal_start < end)
+ {
+ while (!*(nal_start++));
+ nal_end = avc_find_startcode(nal_start, end);
+ if (*nal_start == 0xB3)
+ {
+ nal_bitstream bs;
+ nal_bs_init(&bs, nal_start, end - nal_start);
+
+ // sequence_header_code
+ nal_bs_read(&bs, 8);
+
+ // width
+ // nal_start + 12 bits == horizontal_size_value
+ uint32_t width = nal_bs_read(&bs, 12);
+ if (width != sequence->width)
+ {
+ changed = true;
+ sequence->width = width;
+ }
+ // height
+ // nal_start + 24 bits == vertical_size_value
+ uint32_t height = nal_bs_read(&bs, 12);
+ if (height != sequence->height)
+ {
+ changed = true;
+ sequence->height = height;
+ }
+
+ // aspect ratio
+ // nal_start + 28 bits == aspect_ratio_information
+ float ratio = sequence->ratio;
+ uint32_t ratio_info = nal_bs_read(&bs, 4);
+ switch(ratio_info)
+ {
+ case 0x01:
+ ratio = 1.0;
+ break;
+ default:
+ case 0x02:
+ ratio = 4.0/3.0;
+ break;
+ case 0x03:
+ ratio = 16.0/9.0;
+ break;
+ case 0x04:
+ ratio = 2.21;
+ break;
+ }
+ if (ratio_info != sequence->ratio_info)
+ {
+ changed = true;
+ sequence->ratio = ratio;
+ sequence->ratio_info = ratio_info;
+ }
+
+ // frame rate
+ // nal_start + 32 bits == frame_rate_code
+ float rate = sequence->rate;
+ uint32_t rate_info = nal_bs_read(&bs, 4);
+ switch(rate_info)
+ {
+ default:
+ case 0x01:
+ rate = 24000.0 / 1001.0;
+ break;
+ case 0x02:
+ rate = 24000.0 / 1000.0;
+ break;
+ case 0x03:
+ rate = 25000.0 / 1000.0;
+ break;
+ case 0x04:
+ rate = 30000.0 / 1001.0;
+ break;
+ case 0x05:
+ rate = 30000.0 / 1000.0;
+ break;
+ case 0x06:
+ rate = 50000.0 / 1000.0;
+ break;
+ case 0x07:
+ rate = 60000.0 / 1001.0;
+ break;
+ case 0x08:
+ rate = 60000.0 / 1000.0;
+ break;
+ }
+ if (rate_info != sequence->rate_info)
+ {
+ changed = true;
+ sequence->rate = rate;
+ sequence->rate_info = rate_info;
+ }
+ /*
+ if (changed)
+ {
+ CLog::Log(LOGDEBUG, "CBitstreamConverter::mpeg2_sequence_header: "
+ "width(%d), height(%d), ratio(%f), rate(%f)", width, height, ratio, rate);
+ }
+ */
+ }
+ nal_start = nal_end;
+ }
+
+ return changed;
+}
+
+void CBitstreamConverter::parseh264_sps(const uint8_t *sps, const uint32_t sps_size, bool *interlaced, int32_t *max_ref_frames)
+{
+ nal_bitstream bs;
+ sps_info_struct sps_info;
+
+ nal_bs_init(&bs, sps, sps_size);
+
+ sps_info.profile_idc = nal_bs_read(&bs, 8);
+ nal_bs_read(&bs, 1); // constraint_set0_flag
+ nal_bs_read(&bs, 1); // constraint_set1_flag
+ nal_bs_read(&bs, 1); // constraint_set2_flag
+ nal_bs_read(&bs, 1); // constraint_set3_flag
+ nal_bs_read(&bs, 4); // reserved
+ sps_info.level_idc = nal_bs_read(&bs, 8);
+ sps_info.sps_id = nal_bs_read_ue(&bs);
+
+ if (sps_info.profile_idc == 100 ||
+ sps_info.profile_idc == 110 ||
+ sps_info.profile_idc == 122 ||
+ sps_info.profile_idc == 244 ||
+ sps_info.profile_idc == 44 ||
+ sps_info.profile_idc == 83 ||
+ sps_info.profile_idc == 86)
+ {
+ sps_info.chroma_format_idc = nal_bs_read_ue(&bs);
+ if (sps_info.chroma_format_idc == 3)
+ sps_info.separate_colour_plane_flag = nal_bs_read(&bs, 1);
+ sps_info.bit_depth_luma_minus8 = nal_bs_read_ue(&bs);
+ sps_info.bit_depth_chroma_minus8 = nal_bs_read_ue(&bs);
+ sps_info.qpprime_y_zero_transform_bypass_flag = nal_bs_read(&bs, 1);
+
+ sps_info.seq_scaling_matrix_present_flag = nal_bs_read (&bs, 1);
+ if (sps_info.seq_scaling_matrix_present_flag)
+ {
+ /* TODO: unfinished */
+ }
+ }
+ sps_info.log2_max_frame_num_minus4 = nal_bs_read_ue(&bs);
+ if (sps_info.log2_max_frame_num_minus4 > 12)
+ { // must be between 0 and 12
+ return;
+ }
+ sps_info.pic_order_cnt_type = nal_bs_read_ue(&bs);
+ if (sps_info.pic_order_cnt_type == 0)
+ {
+ sps_info.log2_max_pic_order_cnt_lsb_minus4 = nal_bs_read_ue(&bs);
+ }
+ else if (sps_info.pic_order_cnt_type == 1)
+ { // TODO: unfinished
+ /*
+ delta_pic_order_always_zero_flag = gst_nal_bs_read (bs, 1);
+ offset_for_non_ref_pic = gst_nal_bs_read_se (bs);
+ offset_for_top_to_bottom_field = gst_nal_bs_read_se (bs);
+
+ num_ref_frames_in_pic_order_cnt_cycle = gst_nal_bs_read_ue (bs);
+ for( i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++ )
+ offset_for_ref_frame[i] = gst_nal_bs_read_se (bs);
+ */
+ }
+
+ sps_info.max_num_ref_frames = nal_bs_read_ue(&bs);
+ sps_info.gaps_in_frame_num_value_allowed_flag = nal_bs_read(&bs, 1);
+ sps_info.pic_width_in_mbs_minus1 = nal_bs_read_ue(&bs);
+ sps_info.pic_height_in_map_units_minus1 = nal_bs_read_ue(&bs);
+
+ sps_info.frame_mbs_only_flag = nal_bs_read(&bs, 1);
+ if (!sps_info.frame_mbs_only_flag)
+ sps_info.mb_adaptive_frame_field_flag = nal_bs_read(&bs, 1);
+
+ sps_info.direct_8x8_inference_flag = nal_bs_read(&bs, 1);
+
+ sps_info.frame_cropping_flag = nal_bs_read(&bs, 1);
+ if (sps_info.frame_cropping_flag)
+ {
+ sps_info.frame_crop_left_offset = nal_bs_read_ue(&bs);
+ sps_info.frame_crop_right_offset = nal_bs_read_ue(&bs);
+ sps_info.frame_crop_top_offset = nal_bs_read_ue(&bs);
+ sps_info.frame_crop_bottom_offset = nal_bs_read_ue(&bs);
+ }
+
+ *interlaced = !sps_info.frame_mbs_only_flag;
+ *max_ref_frames = sps_info.max_num_ref_frames;
+}
diff --git a/src/utils/BitstreamConverter.h b/src/utils/BitstreamConverter.h
new file mode 100644
index 0000000000..9ab5ac3dea
--- /dev/null
+++ b/src/utils/BitstreamConverter.h
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2010-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _BITSTREAMCONVERTER_H_
+#define _BITSTREAMCONVERTER_H_
+
+#include <stdint.h>
+
+extern "C" {
+#include "libavutil/avutil.h"
+#include "libavformat/avformat.h"
+#include "libavfilter/avfilter.h"
+#include "libavcodec/avcodec.h"
+}
+
+typedef struct {
+ int writer_le;
+ uint32_t bit_buf;
+ int bit_left;
+ uint8_t *buf, *buf_ptr, *buf_end;
+ int size_in_bits;
+} bits_writer_t;
+
+typedef struct {
+ uint8_t *buffer, *start;
+ int offbits, length, oflow;
+} bits_reader_t;
+
+////////////////////////////////////////////////////////////////////////////////////////////
+// TODO: refactor this so as not to need these ffmpeg routines.
+// These are not exposed in ffmpeg's API so we dupe them here.
+// AVC helper functions for muxers,
+// * Copyright (c) 2006 Baptiste Coudurier <baptiste.coudurier@smartjog.com>
+// This is part of FFmpeg
+// * License as published by the Free Software Foundation; either
+// * version 2.1 of the License, or (at your option) any later version.
+#define BS_RB16(x) \
+ ((((const uint8_t*)(x))[0] << 8) | \
+ ((const uint8_t*)(x)) [1])
+
+#define BS_RB24(x) \
+ ((((const uint8_t*)(x))[0] << 16) | \
+ (((const uint8_t*)(x))[1] << 8) | \
+ ((const uint8_t*)(x))[2])
+
+#define BS_RB32(x) \
+ ((((const uint8_t*)(x))[0] << 24) | \
+ (((const uint8_t*)(x))[1] << 16) | \
+ (((const uint8_t*)(x))[2] << 8) | \
+ ((const uint8_t*)(x))[3])
+
+#define BS_WB32(p, d) { \
+ ((uint8_t*)(p))[3] = (d); \
+ ((uint8_t*)(p))[2] = (d) >> 8; \
+ ((uint8_t*)(p))[1] = (d) >> 16; \
+ ((uint8_t*)(p))[0] = (d) >> 24; }
+
+#define BS_WL32(p, d) { \
+ ((uint8_t*)(p))[0] = (d); \
+ ((uint8_t*)(p))[1] = (d) >> 8; \
+ ((uint8_t*)(p))[2] = (d) >> 16; \
+ ((uint8_t*)(p))[3] = (d) >> 24; }
+
+typedef struct
+{
+ const uint8_t *data;
+ const uint8_t *end;
+ int head;
+ uint64_t cache;
+} nal_bitstream;
+
+typedef struct mpeg2_sequence
+{
+ uint32_t width;
+ uint32_t height;
+ float rate;
+ uint32_t rate_info;
+ float ratio;
+ uint32_t ratio_info;
+} mpeg2_sequence;
+
+typedef struct
+{
+ int profile_idc;
+ int level_idc;
+ int sps_id;
+
+ int chroma_format_idc;
+ int separate_colour_plane_flag;
+ int bit_depth_luma_minus8;
+ int bit_depth_chroma_minus8;
+ int qpprime_y_zero_transform_bypass_flag;
+ int seq_scaling_matrix_present_flag;
+
+ int log2_max_frame_num_minus4;
+ int pic_order_cnt_type;
+ int log2_max_pic_order_cnt_lsb_minus4;
+
+ int max_num_ref_frames;
+ int gaps_in_frame_num_value_allowed_flag;
+ int pic_width_in_mbs_minus1;
+ int pic_height_in_map_units_minus1;
+
+ int frame_mbs_only_flag;
+ int mb_adaptive_frame_field_flag;
+
+ int direct_8x8_inference_flag;
+
+ int frame_cropping_flag;
+ int frame_crop_left_offset;
+ int frame_crop_right_offset;
+ int frame_crop_top_offset;
+ int frame_crop_bottom_offset;
+} sps_info_struct;
+
+class CBitstreamParser
+{
+public:
+ CBitstreamParser();
+ ~CBitstreamParser();
+
+ static bool Open();
+ static void Close();
+ static bool FindIdrSlice(const uint8_t *buf, int buf_size);
+
+protected:
+ static const uint8_t* find_start_code(const uint8_t *p, const uint8_t *end, uint32_t *state);
+};
+
+class CBitstreamConverter
+{
+public:
+ CBitstreamConverter();
+ ~CBitstreamConverter();
+
+ bool Open(enum AVCodecID codec, uint8_t *in_extradata, int in_extrasize, bool to_annexb);
+ void Close(void);
+ bool NeedConvert(void) const { return m_convert_bitstream; };
+ bool Convert(uint8_t *pData, int iSize);
+ uint8_t* GetConvertBuffer(void) const;
+ int GetConvertSize() const;
+ uint8_t* GetExtraData(void) const;
+ int GetExtraSize() const;
+
+ static void bits_reader_set( bits_reader_t *br, uint8_t *buf, int len );
+ static uint32_t read_bits( bits_reader_t *br, int nbits );
+ static void skip_bits( bits_reader_t *br, int nbits );
+ static uint32_t get_bits( bits_reader_t *br, int nbits );
+
+ static void init_bits_writer(bits_writer_t *s, uint8_t *buffer, int buffer_size, int writer_le);
+ static void write_bits(bits_writer_t *s, int n, unsigned int value);
+ static void skip_bits( bits_writer_t *s, int n);
+ static void flush_bits(bits_writer_t *s);
+
+ static void parseh264_sps(const uint8_t *sps, const uint32_t sps_size, bool *interlaced, int32_t *max_ref_frames);
+ static bool mpeg2_sequence_header(const uint8_t *data, const uint32_t size, mpeg2_sequence *sequence);
+
+protected:
+ static const int avc_parse_nal_units(AVIOContext *pb, const uint8_t *buf_in, int size);
+ static const int avc_parse_nal_units_buf(const uint8_t *buf_in, uint8_t **buf, int *size);
+ const int isom_write_avcc(AVIOContext *pb, const uint8_t *data, int len);
+ // bitstream to bytestream (Annex B) conversion support.
+ bool BitstreamConvertInit(void *in_extradata, int in_extrasize);
+ bool BitstreamConvert(uint8_t* pData, int iSize, uint8_t **poutbuf, int *poutbuf_size);
+ static void BitstreamAllocAndCopy(uint8_t **poutbuf, int *poutbuf_size,
+ const uint8_t *sps_pps, uint32_t sps_pps_size, const uint8_t *in, uint32_t in_size);
+
+ typedef struct omx_bitstream_ctx {
+ uint8_t length_size;
+ uint8_t first_idr;
+ uint8_t idr_sps_pps_seen;
+ uint8_t *sps_pps_data;
+ uint32_t size;
+ } omx_bitstream_ctx;
+
+ uint8_t *m_convertBuffer;
+ int m_convertSize;
+ uint8_t *m_inputBuffer;
+ int m_inputSize;
+
+ uint32_t m_sps_pps_size;
+ omx_bitstream_ctx m_sps_pps_context;
+ bool m_convert_bitstream;
+ bool m_to_annexb;
+
+ uint8_t *m_extradata;
+ int m_extrasize;
+ bool m_convert_3byteTo4byteNALSize;
+ bool m_convert_bytestream;
+ AVCodecID m_codec;
+};
+
+#endif
diff --git a/src/utils/BitstreamStats.cpp b/src/utils/BitstreamStats.cpp
new file mode 100644
index 0000000000..af325e91b1
--- /dev/null
+++ b/src/utils/BitstreamStats.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "BitstreamStats.h"
+#include "utils/TimeUtils.h"
+
+int64_t BitstreamStats::m_tmFreq;
+
+BitstreamStats::BitstreamStats(unsigned int nEstimatedBitrate)
+{
+ m_dBitrate = 0.0;
+ m_dMaxBitrate = 0.0;
+ m_dMinBitrate = -1.0;
+
+ m_nBitCount = 0;
+ m_nEstimatedBitrate = nEstimatedBitrate;
+ m_tmStart = 0LL;
+
+ if (m_tmFreq == 0LL)
+ m_tmFreq = CurrentHostFrequency();
+}
+
+BitstreamStats::~BitstreamStats()
+{
+}
+
+void BitstreamStats::AddSampleBytes(unsigned int nBytes)
+{
+ AddSampleBits(nBytes*8);
+}
+
+void BitstreamStats::AddSampleBits(unsigned int nBits)
+{
+ m_nBitCount += nBits;
+ if (m_nBitCount >= m_nEstimatedBitrate)
+ CalculateBitrate();
+}
+
+void BitstreamStats::Start()
+{
+ m_nBitCount = 0;
+ m_tmStart = CurrentHostCounter();
+}
+
+void BitstreamStats::CalculateBitrate()
+{
+ int64_t tmNow;
+ tmNow = CurrentHostCounter();
+
+ double elapsed = (double)(tmNow - m_tmStart) / (double)m_tmFreq;
+ // only update once every 2 seconds
+ if (elapsed >= 2)
+ {
+ m_dBitrate = (double)m_nBitCount / elapsed;
+
+ if (m_dBitrate > m_dMaxBitrate)
+ m_dMaxBitrate = m_dBitrate;
+
+ if (m_dBitrate < m_dMinBitrate || m_dMinBitrate == -1)
+ m_dMinBitrate = m_dBitrate;
+
+ Start();
+ }
+}
+
+
+
+
diff --git a/src/utils/BitstreamStats.h b/src/utils/BitstreamStats.h
new file mode 100644
index 0000000000..1f2e5cd72a
--- /dev/null
+++ b/src/utils/BitstreamStats.h
@@ -0,0 +1,61 @@
+#ifndef BITSTREAM_STATS__H__
+#define BITSTREAM_STATS__H__
+
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string>
+#ifdef TARGET_POSIX
+#include "linux/PlatformDefs.h"
+#else
+#include <stdint.h>
+#endif
+
+class BitstreamStats
+{
+public:
+ // in order not to cause a performance hit, we should only check the clock when
+ // we reach m_nEstimatedBitrate bits.
+ // if this value is 1, we will calculate bitrate on every sample.
+ BitstreamStats(unsigned int nEstimatedBitrate=(10240*8) /*10Kbit*/);
+ virtual ~BitstreamStats();
+
+ void AddSampleBytes(unsigned int nBytes);
+ void AddSampleBits(unsigned int nBits);
+
+ inline double GetBitrate() const { return m_dBitrate; }
+ inline double GetMaxBitrate() const { return m_dMaxBitrate; }
+ inline double GetMinBitrate() const { return m_dMinBitrate; }
+
+ void Start();
+ void CalculateBitrate();
+
+private:
+ double m_dBitrate;
+ double m_dMaxBitrate;
+ double m_dMinBitrate;
+ unsigned int m_nBitCount;
+ unsigned int m_nEstimatedBitrate; // when we reach this amount of bits we check current bitrate.
+ int64_t m_tmStart;
+ static int64_t m_tmFreq;
+};
+
+#endif
+
diff --git a/src/utils/BooleanLogic.cpp b/src/utils/BooleanLogic.cpp
new file mode 100644
index 0000000000..867db67a7d
--- /dev/null
+++ b/src/utils/BooleanLogic.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2012-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "BooleanLogic.h"
+#include "utils/log.h"
+#include "utils/StringUtils.h"
+#include "utils/XBMCTinyXML.h"
+
+bool CBooleanLogicValue::Deserialize(const TiXmlNode *node)
+{
+ if (node == NULL)
+ return false;
+
+ const TiXmlElement *elem = node->ToElement();
+ if (elem == NULL)
+ return false;
+
+ if (node->FirstChild() != NULL && node->FirstChild()->Type() == TiXmlNode::TINYXML_TEXT)
+ m_value = node->FirstChild()->ValueStr();
+
+ m_negated = false;
+ const char *strNegated = elem->Attribute("negated");
+ if (strNegated != NULL)
+ {
+ if (StringUtils::EqualsNoCase(strNegated, "true"))
+ m_negated = true;
+ else if (!StringUtils::EqualsNoCase(strNegated, "false"))
+ {
+ CLog::Log(LOGDEBUG, "CBooleanLogicValue: invalid negated value \"%s\"", strNegated);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+CBooleanLogicOperation::~CBooleanLogicOperation()
+{
+ m_operations.clear();
+ m_values.clear();
+}
+
+bool CBooleanLogicOperation::Deserialize(const TiXmlNode *node)
+{
+ if (node == NULL)
+ return false;
+
+ // check if this is a simple operation with a single value directly expressed
+ // in the parent tag
+ if (node->FirstChild() == NULL || node->FirstChild()->Type() == TiXmlNode::TINYXML_TEXT)
+ {
+ CBooleanLogicValuePtr value = CBooleanLogicValuePtr(newValue());
+ if (value == NULL || !value->Deserialize(node))
+ {
+ CLog::Log(LOGDEBUG, "CBooleanLogicOperation: failed to deserialize implicit boolean value definition");
+ return false;
+ }
+
+ m_values.push_back(value);
+ return true;
+ }
+
+ const TiXmlNode *operationNode = node->FirstChild();
+ while (operationNode != NULL)
+ {
+ std::string tag = operationNode->ValueStr();
+ if (StringUtils::EqualsNoCase(tag, "and") || StringUtils::EqualsNoCase(tag, "or"))
+ {
+ CBooleanLogicOperationPtr operation = CBooleanLogicOperationPtr(newOperation());
+ if (operation == NULL)
+ return false;
+
+ operation->SetOperation(StringUtils::EqualsNoCase(tag, "and") ? BooleanLogicOperationAnd : BooleanLogicOperationOr);
+ if (!operation->Deserialize(operationNode))
+ {
+ CLog::Log(LOGDEBUG, "CBooleanLogicOperation: failed to deserialize <%s> definition", tag.c_str());
+ return false;
+ }
+
+ m_operations.push_back(operation);
+ }
+ else
+ {
+ CBooleanLogicValuePtr value = CBooleanLogicValuePtr(newValue());
+ if (value == NULL)
+ return false;
+
+ if (StringUtils::EqualsNoCase(tag, value->GetTag()))
+ {
+ if (!value->Deserialize(operationNode))
+ {
+ CLog::Log(LOGDEBUG, "CBooleanLogicOperation: failed to deserialize <%s> definition", tag.c_str());
+ return false;
+ }
+
+ m_values.push_back(value);
+ }
+ else if (operationNode->Type() == TiXmlNode::TINYXML_ELEMENT)
+ CLog::Log(LOGDEBUG, "CBooleanLogicOperation: unknown <%s> definition encountered", tag.c_str());
+ }
+
+ operationNode = operationNode->NextSibling();
+ }
+
+ return true;
+}
+
+bool CBooleanLogic::Deserialize(const TiXmlNode *node)
+{
+ if (node == NULL)
+ return false;
+
+ if (m_operation == NULL)
+ {
+ m_operation = CBooleanLogicOperationPtr(new CBooleanLogicOperation());
+
+ if (m_operation == NULL)
+ return false;
+ }
+
+ return m_operation->Deserialize(node);
+}
diff --git a/src/utils/BooleanLogic.h b/src/utils/BooleanLogic.h
new file mode 100644
index 0000000000..e1af268996
--- /dev/null
+++ b/src/utils/BooleanLogic.h
@@ -0,0 +1,101 @@
+#pragma once
+/*
+ * Copyright (C) 2012-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string>
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+
+#include "utils/IXmlDeserializable.h"
+
+typedef enum {
+ BooleanLogicOperationOr = 0,
+ BooleanLogicOperationAnd
+} BooleanLogicOperation;
+
+class CBooleanLogicValue : public IXmlDeserializable
+{
+public:
+ CBooleanLogicValue(const std::string &value = "", bool negated = false)
+ : m_value(value), m_negated(negated)
+ { }
+ virtual ~CBooleanLogicValue() { }
+
+ virtual bool Deserialize(const TiXmlNode *node);
+
+ virtual const std::string& GetValue() const { return m_value; }
+ virtual bool IsNegated() const { return m_negated; }
+ virtual const char* GetTag() const { return "value"; }
+
+ virtual void SetValue(const std::string &value) { m_value = value; }
+ virtual void SetNegated(bool negated) { m_negated = negated; }
+
+protected:
+ std::string m_value;
+ bool m_negated;
+};
+
+typedef boost::shared_ptr<CBooleanLogicValue> CBooleanLogicValuePtr;
+typedef std::vector<CBooleanLogicValuePtr> CBooleanLogicValues;
+
+class CBooleanLogicOperation;
+typedef boost::shared_ptr<CBooleanLogicOperation> CBooleanLogicOperationPtr;
+typedef std::vector<CBooleanLogicOperationPtr> CBooleanLogicOperations;
+
+class CBooleanLogicOperation : public IXmlDeserializable
+{
+public:
+ CBooleanLogicOperation(BooleanLogicOperation op = BooleanLogicOperationAnd)
+ : m_operation(op)
+ { }
+ virtual ~CBooleanLogicOperation();
+
+ virtual bool Deserialize(const TiXmlNode *node);
+
+ virtual BooleanLogicOperation GetOperation() const { return m_operation; }
+ virtual const CBooleanLogicOperations& GetOperations() const { return m_operations; }
+ virtual const CBooleanLogicValues& GetValues() const { return m_values; }
+
+ virtual void SetOperation(BooleanLogicOperation op) { m_operation = op; }
+
+protected:
+ virtual CBooleanLogicOperation* newOperation() { return new CBooleanLogicOperation(); }
+ virtual CBooleanLogicValue* newValue() { return new CBooleanLogicValue(); }
+
+ BooleanLogicOperation m_operation;
+ CBooleanLogicOperations m_operations;
+ CBooleanLogicValues m_values;
+};
+
+class CBooleanLogic : public IXmlDeserializable
+{
+public:
+ CBooleanLogic() { }
+ virtual ~CBooleanLogic() { }
+
+ virtual bool Deserialize(const TiXmlNode *node);
+
+ virtual const CBooleanLogicOperationPtr& Get() const { return m_operation; }
+ virtual CBooleanLogicOperationPtr Get() { return m_operation; }
+
+protected:
+ CBooleanLogicOperationPtr m_operation;
+};
diff --git a/src/utils/CPUInfo.cpp b/src/utils/CPUInfo.cpp
new file mode 100644
index 0000000000..71aa745481
--- /dev/null
+++ b/src/utils/CPUInfo.cpp
@@ -0,0 +1,957 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "CPUInfo.h"
+#include "Temperature.h"
+#include <string>
+#include <string.h>
+
+#if defined(TARGET_DARWIN)
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#if defined(__ppc__) || defined (TARGET_DARWIN_IOS)
+#include <mach-o/arch.h>
+#endif // defined(__ppc__) || defined (TARGET_DARWIN_IOS)
+#ifdef TARGET_DARWIN_OSX
+#include "osx/smc.h"
+#endif
+#endif
+
+#if defined(TARGET_FREEBSD)
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/resource.h>
+#endif
+
+#if defined(TARGET_LINUX) && defined(__ARM_NEON__) && !defined(TARGET_ANDROID)
+#include <fcntl.h>
+#include <unistd.h>
+#include <elf.h>
+#include <linux/auxvec.h>
+#include <asm/hwcap.h>
+#endif
+
+#if defined(TARGET_ANDROID)
+#include "android/activity/AndroidFeatures.h"
+#endif
+
+#ifdef TARGET_WINDOWS
+#include "utils/CharsetConverter.h"
+#include <algorithm>
+#include <intrin.h>
+#include <Pdh.h>
+#include <PdhMsg.h>
+#pragma comment(lib, "Pdh.lib")
+
+// Defines to help with calls to CPUID
+#define CPUID_INFOTYPE_STANDARD 0x00000001
+#define CPUID_INFOTYPE_EXTENDED 0x80000001
+
+// Standard Features
+// Bitmasks for the values returned by a call to cpuid with eax=0x00000001
+#define CPUID_00000001_ECX_SSE3 (1<<0)
+#define CPUID_00000001_ECX_SSSE3 (1<<9)
+#define CPUID_00000001_ECX_SSE4 (1<<19)
+#define CPUID_00000001_ECX_SSE42 (1<<20)
+
+#define CPUID_00000001_EDX_MMX (1<<23)
+#define CPUID_00000001_EDX_SSE (1<<25)
+#define CPUID_00000001_EDX_SSE2 (1<<26)
+
+// Extended Features
+// Bitmasks for the values returned by a call to cpuid with eax=0x80000001
+#define CPUID_80000001_EDX_MMX2 (1<<22)
+#define CPUID_80000001_EDX_MMX (1<<23)
+#define CPUID_80000001_EDX_3DNOWEXT (1<<30)
+#define CPUID_80000001_EDX_3DNOW (1<<31)
+
+
+// Help with the __cpuid intrinsic of MSVC
+#define CPUINFO_EAX 0
+#define CPUINFO_EBX 1
+#define CPUINFO_ECX 2
+#define CPUINFO_EDX 3
+
+#endif
+
+#include "log.h"
+#include "settings/AdvancedSettings.h"
+#include "utils/StringUtils.h"
+
+using namespace std;
+
+// In milliseconds
+#define MINIMUM_TIME_BETWEEN_READS 500
+
+CCPUInfo::CCPUInfo(void)
+{
+#ifdef TARGET_POSIX
+ m_fProcStat = m_fProcTemperature = m_fCPUFreq = NULL;
+ m_cpuInfoForFreq = false;
+#elif defined(TARGET_WINDOWS)
+ m_cpuQueryFreq = NULL;
+ m_cpuQueryLoad = NULL;
+#endif
+ m_lastUsedPercentage = 0;
+ m_cpuFeatures = 0;
+
+#if defined(TARGET_DARWIN)
+ size_t len = 4;
+ std::string cpuVendor;
+
+ // The number of cores.
+ if (sysctlbyname("hw.activecpu", &m_cpuCount, &len, NULL, 0) == -1)
+ m_cpuCount = 1;
+
+ // The model.
+#if defined(__ppc__) || defined (TARGET_DARWIN_IOS)
+ const NXArchInfo *info = NXGetLocalArchInfo();
+ if (info != NULL)
+ m_cpuModel = info->description;
+#else
+ // NXGetLocalArchInfo is ugly for intel so keep using this method
+ char buffer[512];
+ len = 512;
+ if (sysctlbyname("machdep.cpu.brand_string", &buffer, &len, NULL, 0) == 0)
+ m_cpuModel = buffer;
+
+ // The CPU vendor
+ len = 512;
+ if (sysctlbyname("machdep.cpu.vendor", &buffer, &len, NULL, 0) == 0)
+ cpuVendor = buffer;
+
+#endif
+ // Go through each core.
+ for (int i=0; i<m_cpuCount; i++)
+ {
+ CoreInfo core;
+ core.m_id = i;
+ core.m_strModel = m_cpuModel;
+ core.m_strVendor = cpuVendor;
+ m_cores[core.m_id] = core;
+ }
+
+#elif defined(TARGET_WINDOWS)
+ HKEY hKeyCpuRoot;
+
+ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor", 0, KEY_READ, &hKeyCpuRoot) == ERROR_SUCCESS)
+ {
+ DWORD num = 0;
+ std::vector<CoreInfo> cpuCores;
+ wchar_t subKeyName[200]; // more than enough
+ DWORD subKeyNameLen = sizeof(subKeyName) / sizeof(wchar_t);
+ while (RegEnumKeyExW(hKeyCpuRoot, num++, subKeyName, &subKeyNameLen, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
+ {
+ HKEY hCpuKey;
+ if (RegOpenKeyExW(hKeyCpuRoot, subKeyName, 0, KEY_QUERY_VALUE, &hCpuKey) == ERROR_SUCCESS)
+ {
+ CoreInfo cpuCore;
+ if (swscanf_s(subKeyName, L"%i", &cpuCore.m_id) != 1)
+ cpuCore.m_id = num - 1;
+ wchar_t buf[300]; // more than enough
+ DWORD bufSize = sizeof(buf);
+ DWORD valType;
+ if (RegQueryValueExW(hCpuKey, L"ProcessorNameString", NULL, &valType, (LPBYTE)buf, &bufSize) == ERROR_SUCCESS &&
+ valType == REG_SZ)
+ {
+ g_charsetConverter.wToUTF8(std::wstring(buf, bufSize / sizeof(wchar_t)), cpuCore.m_strModel);
+ cpuCore.m_strModel = cpuCore.m_strModel.substr(0, cpuCore.m_strModel.find(char(0))); // remove extra null terminations
+ StringUtils::RemoveDuplicatedSpacesAndTabs(cpuCore.m_strModel);
+ StringUtils::Trim(cpuCore.m_strModel);
+ }
+ bufSize = sizeof(buf);
+ if (RegQueryValueExW(hCpuKey, L"VendorIdentifier", NULL, &valType, (LPBYTE)buf, &bufSize) == ERROR_SUCCESS &&
+ valType == REG_SZ)
+ {
+ g_charsetConverter.wToUTF8(std::wstring(buf, bufSize / sizeof(wchar_t)), cpuCore.m_strVendor);
+ cpuCore.m_strVendor = cpuCore.m_strVendor.substr(0, cpuCore.m_strVendor.find(char(0))); // remove extra null terminations
+ }
+ DWORD mhzVal;
+ bufSize = sizeof(mhzVal);
+ if (RegQueryValueExW(hCpuKey, L"~MHz", NULL, &valType, (LPBYTE)&mhzVal, &bufSize) == ERROR_SUCCESS &&
+ valType == REG_DWORD)
+ cpuCore.m_fSpeed = double(mhzVal);
+
+ RegCloseKey(hCpuKey);
+
+ if (cpuCore.m_strModel.empty())
+ cpuCore.m_strModel = "Unknown";
+ cpuCores.push_back(cpuCore);
+ }
+ subKeyNameLen = sizeof(subKeyName) / sizeof(wchar_t); // restore length value
+ }
+ RegCloseKey(hKeyCpuRoot);
+ std::sort(cpuCores.begin(), cpuCores.end()); // sort cores by id
+ for (size_t i = 0; i < cpuCores.size(); i++)
+ m_cores[i] = cpuCores[i]; // add in sorted order
+ }
+
+ if (!m_cores.empty())
+ m_cpuModel = m_cores.begin()->second.m_strModel;
+ else
+ m_cpuModel = "Unknown";
+
+ SYSTEM_INFO siSysInfo;
+ GetNativeSystemInfo(&siSysInfo);
+ m_cpuCount = siSysInfo.dwNumberOfProcessors;
+
+ if (PdhOpenQueryW(NULL, 0, &m_cpuQueryFreq) == ERROR_SUCCESS)
+ {
+ if (PdhAddEnglishCounterW(m_cpuQueryFreq, L"\\Processor Information(0,0)\\Processor Frequency", 0, &m_cpuFreqCounter) != ERROR_SUCCESS)
+ m_cpuFreqCounter = NULL;
+ }
+ else
+ m_cpuQueryFreq = NULL;
+
+ if (PdhOpenQueryW(NULL, 0, &m_cpuQueryLoad) == ERROR_SUCCESS)
+ {
+ for (size_t i = 0; i < m_cores.size(); i++)
+ {
+ if (PdhAddEnglishCounterW(m_cpuQueryLoad, StringUtils::Format(L"\\Processor(%d)\\%% Idle Time", int(i)).c_str(), 0, &m_cores[i].m_coreCounter) != ERROR_SUCCESS)
+ m_cores[i].m_coreCounter = NULL;
+ }
+ }
+ else
+ m_cpuQueryLoad = NULL;
+#elif defined(TARGET_FREEBSD)
+ size_t len;
+ int i;
+ char cpumodel[512];
+
+ len = sizeof(m_cpuCount);
+ if (sysctlbyname("hw.ncpu", &m_cpuCount, &len, NULL, 0) != 0)
+ m_cpuCount = 1;
+
+ len = sizeof(cpumodel);
+ if (sysctlbyname("hw.model", &cpumodel, &len, NULL, 0) != 0)
+ (void)strncpy(cpumodel, "Unknown", 8);
+ m_cpuModel = cpumodel;
+
+ for (i = 0; i < m_cpuCount; i++)
+ {
+ CoreInfo core;
+ core.m_id = i;
+ core.m_strModel = m_cpuModel;
+ m_cores[core.m_id] = core;
+ }
+#else
+ m_fProcStat = fopen("/proc/stat", "r");
+ m_fProcTemperature = fopen("/proc/acpi/thermal_zone/THM0/temperature", "r");
+ if (m_fProcTemperature == NULL)
+ m_fProcTemperature = fopen("/proc/acpi/thermal_zone/THRM/temperature", "r");
+ if (m_fProcTemperature == NULL)
+ m_fProcTemperature = fopen("/proc/acpi/thermal_zone/THR0/temperature", "r");
+ if (m_fProcTemperature == NULL)
+ m_fProcTemperature = fopen("/proc/acpi/thermal_zone/TZ0/temperature", "r");
+ // read from the new location of the temperature data on new kernels, 2.6.39, 3.0 etc
+ if (m_fProcTemperature == NULL)
+ m_fProcTemperature = fopen("/sys/class/hwmon/hwmon0/temp1_input", "r");
+ if (m_fProcTemperature == NULL)
+ m_fProcTemperature = fopen("/sys/class/thermal/thermal_zone0/temp", "r"); // On Raspberry PIs
+
+ m_fCPUFreq = fopen ("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq", "r");
+ if (!m_fCPUFreq)
+ {
+ m_cpuInfoForFreq = true;
+ m_fCPUFreq = fopen("/proc/cpuinfo", "r");
+ }
+ else
+ m_cpuInfoForFreq = false;
+
+
+ FILE* fCPUInfo = fopen("/proc/cpuinfo", "r");
+ m_cpuCount = 0;
+ if (fCPUInfo)
+ {
+ char buffer[512];
+
+ int nCurrId = 0;
+ while (fgets(buffer, sizeof(buffer), fCPUInfo))
+ {
+ if (strncmp(buffer, "processor", strlen("processor"))==0)
+ {
+ char *needle = strstr(buffer, ":");
+ if (needle)
+ {
+ CoreInfo core;
+ core.m_id = atoi(needle+2);
+ nCurrId = core.m_id;
+ m_cores[core.m_id] = core;
+ }
+ m_cpuCount++;
+ }
+ else if (strncmp(buffer, "vendor_id", strlen("vendor_id"))==0)
+ {
+ char *needle = strstr(buffer, ":");
+ if (needle && strlen(needle)>3)
+ {
+ needle+=2;
+ m_cores[nCurrId].m_strVendor = needle;
+ StringUtils::Trim(m_cores[nCurrId].m_strVendor);
+ }
+ }
+ else if (strncmp(buffer, "Processor", strlen("Processor"))==0)
+ {
+ char *needle = strstr(buffer, ":");
+ if (needle && strlen(needle)>3)
+ {
+ needle+=2;
+ m_cpuModel = needle;
+ m_cores[nCurrId].m_strModel = m_cpuModel;
+ StringUtils::Trim(m_cores[nCurrId].m_strModel);
+ }
+ }
+ else if (strncmp(buffer, "BogoMIPS", strlen("BogoMIPS"))==0)
+ {
+ char *needle = strstr(buffer, ":");
+ if (needle && strlen(needle)>3)
+ {
+ needle+=2;
+ m_cpuBogoMips = needle;
+ m_cores[nCurrId].m_strBogoMips = m_cpuBogoMips;
+ StringUtils::Trim(m_cores[nCurrId].m_strBogoMips);
+ }
+ }
+ else if (strncmp(buffer, "Hardware", strlen("Hardware"))==0)
+ {
+ char *needle = strstr(buffer, ":");
+ if (needle && strlen(needle)>3)
+ {
+ needle+=2;
+ m_cpuHardware = needle;
+ m_cores[nCurrId].m_strHardware = m_cpuHardware;
+ StringUtils::Trim(m_cores[nCurrId].m_strHardware);
+ }
+ }
+ else if (strncmp(buffer, "Revision", strlen("Revision"))==0)
+ {
+ char *needle = strstr(buffer, ":");
+ if (needle && strlen(needle)>3)
+ {
+ needle+=2;
+ m_cpuRevision = needle;
+ m_cores[nCurrId].m_strRevision = m_cpuRevision;
+ StringUtils::Trim(m_cores[nCurrId].m_strRevision);
+ }
+ }
+ else if (strncmp(buffer, "Serial", strlen("Serial"))==0)
+ {
+ char *needle = strstr(buffer, ":");
+ if (needle && strlen(needle)>3)
+ {
+ needle+=2;
+ m_cpuSerial = needle;
+ m_cores[nCurrId].m_strSerial = m_cpuSerial;
+ StringUtils::Trim(m_cores[nCurrId].m_strSerial);
+ }
+ }
+ else if (strncmp(buffer, "model name", strlen("model name"))==0)
+ {
+ char *needle = strstr(buffer, ":");
+ if (needle && strlen(needle)>3)
+ {
+ needle+=2;
+ m_cpuModel = needle;
+ m_cores[nCurrId].m_strModel = m_cpuModel;
+ StringUtils::Trim(m_cores[nCurrId].m_strModel);
+ }
+ }
+ else if (strncmp(buffer, "flags", 5) == 0)
+ {
+ char* needle = strchr(buffer, ':');
+ if (needle)
+ {
+ char* tok = NULL,
+ * save;
+ needle++;
+ tok = strtok_r(needle, " ", &save);
+ while (tok)
+ {
+ if (0 == strcmp(tok, "mmx"))
+ m_cpuFeatures |= CPU_FEATURE_MMX;
+ else if (0 == strcmp(tok, "mmxext"))
+ m_cpuFeatures |= CPU_FEATURE_MMX2;
+ else if (0 == strcmp(tok, "sse"))
+ m_cpuFeatures |= CPU_FEATURE_SSE;
+ else if (0 == strcmp(tok, "sse2"))
+ m_cpuFeatures |= CPU_FEATURE_SSE2;
+ else if (0 == strcmp(tok, "sse3"))
+ m_cpuFeatures |= CPU_FEATURE_SSE3;
+ else if (0 == strcmp(tok, "ssse3"))
+ m_cpuFeatures |= CPU_FEATURE_SSSE3;
+ else if (0 == strcmp(tok, "sse4_1"))
+ m_cpuFeatures |= CPU_FEATURE_SSE4;
+ else if (0 == strcmp(tok, "sse4_2"))
+ m_cpuFeatures |= CPU_FEATURE_SSE42;
+ else if (0 == strcmp(tok, "3dnow"))
+ m_cpuFeatures |= CPU_FEATURE_3DNOW;
+ else if (0 == strcmp(tok, "3dnowext"))
+ m_cpuFeatures |= CPU_FEATURE_3DNOWEXT;
+ tok = strtok_r(NULL, " ", &save);
+ }
+ }
+ }
+ }
+ fclose(fCPUInfo);
+ }
+ else
+ {
+ m_cpuCount = 1;
+ m_cpuModel = "Unknown";
+ }
+
+#endif
+ StringUtils::Replace(m_cpuModel, '\r', ' ');
+ StringUtils::Replace(m_cpuModel, '\n', ' ');
+ StringUtils::RemoveDuplicatedSpacesAndTabs(m_cpuModel);
+ StringUtils::Trim(m_cpuModel);
+
+ /* Set some default for empty string variables */
+ if (m_cpuBogoMips.empty())
+ m_cpuBogoMips = "N/A";
+ if (m_cpuHardware.empty())
+ m_cpuHardware = "N/A";
+ if (m_cpuRevision.empty())
+ m_cpuRevision = "N/A";
+ if (m_cpuSerial.empty())
+ m_cpuSerial = "N/A";
+
+ readProcStat(m_userTicks, m_niceTicks, m_systemTicks, m_idleTicks, m_ioTicks);
+ m_nextUsedReadTime.Set(MINIMUM_TIME_BETWEEN_READS);
+
+ ReadCPUFeatures();
+
+ // Set MMX2 when SSE is present as SSE is a superset of MMX2 and Intel doesn't set the MMX2 cap
+ if (m_cpuFeatures & CPU_FEATURE_SSE)
+ m_cpuFeatures |= CPU_FEATURE_MMX2;
+
+ if (HasNeon())
+ m_cpuFeatures |= CPU_FEATURE_NEON;
+
+}
+
+CCPUInfo::~CCPUInfo()
+{
+#ifdef TARGET_POSIX
+ if (m_fProcStat != NULL)
+ fclose(m_fProcStat);
+
+ if (m_fProcTemperature != NULL)
+ fclose(m_fProcTemperature);
+
+ if (m_fCPUFreq != NULL)
+ fclose(m_fCPUFreq);
+#elif defined(TARGET_WINDOWS)
+ if (m_cpuQueryFreq)
+ PdhCloseQuery(m_cpuQueryFreq);
+
+ if (m_cpuQueryLoad)
+ PdhCloseQuery(m_cpuQueryLoad);
+#endif
+}
+
+int CCPUInfo::getUsedPercentage()
+{
+ if (!m_nextUsedReadTime.IsTimePast())
+ return m_lastUsedPercentage;
+
+ unsigned long long userTicks;
+ unsigned long long niceTicks;
+ unsigned long long systemTicks;
+ unsigned long long idleTicks;
+ unsigned long long ioTicks;
+
+ if (!readProcStat(userTicks, niceTicks, systemTicks, idleTicks, ioTicks))
+ return m_lastUsedPercentage;
+
+ userTicks -= m_userTicks;
+ niceTicks -= m_niceTicks;
+ systemTicks -= m_systemTicks;
+ idleTicks -= m_idleTicks;
+ ioTicks -= m_ioTicks;
+
+ if(userTicks + niceTicks + systemTicks + idleTicks + ioTicks == 0)
+ return m_lastUsedPercentage;
+ int result = (int) (double(userTicks + niceTicks + systemTicks) * 100.0 / double(userTicks + niceTicks + systemTicks + idleTicks + ioTicks) + 0.5);
+
+ m_userTicks += userTicks;
+ m_niceTicks += niceTicks;
+ m_systemTicks += systemTicks;
+ m_idleTicks += idleTicks;
+ m_ioTicks += ioTicks;
+
+ m_lastUsedPercentage = result;
+ m_nextUsedReadTime.Set(MINIMUM_TIME_BETWEEN_READS);
+
+ return result;
+}
+
+float CCPUInfo::getCPUFrequency()
+{
+ // Get CPU frequency, scaled to MHz.
+#if defined(TARGET_DARWIN)
+ long long hz = 0;
+ size_t len = sizeof(hz);
+ if (sysctlbyname("hw.cpufrequency", &hz, &len, NULL, 0) == -1)
+ return 0.f;
+ return hz / 1000000.0;
+#elif defined TARGET_WINDOWS
+ if (m_cpuFreqCounter && PdhCollectQueryData(m_cpuQueryFreq) == ERROR_SUCCESS)
+ {
+ PDH_RAW_COUNTER cnt;
+ DWORD cntType;
+ if (PdhGetRawCounterValue(m_cpuFreqCounter, &cntType, &cnt) == ERROR_SUCCESS &&
+ (cnt.CStatus == PDH_CSTATUS_VALID_DATA || cnt.CStatus == PDH_CSTATUS_NEW_DATA))
+ {
+ return float(cnt.FirstValue);
+ }
+ }
+
+ if (!m_cores.empty())
+ return float(m_cores.begin()->second.m_fSpeed);
+ else
+ return 0.f;
+#elif defined(TARGET_FREEBSD)
+ int hz = 0;
+ size_t len = sizeof(hz);
+ if (sysctlbyname("dev.cpu.0.freq", &hz, &len, NULL, 0) != 0)
+ hz = 0;
+ return (float)hz;
+#else
+ int value = 0;
+ if (m_fCPUFreq && !m_cpuInfoForFreq)
+ {
+ rewind(m_fCPUFreq);
+ fflush(m_fCPUFreq);
+ fscanf(m_fCPUFreq, "%d", &value);
+ value /= 1000.0;
+ }
+ if (m_fCPUFreq && m_cpuInfoForFreq)
+ {
+ rewind(m_fCPUFreq);
+ fflush(m_fCPUFreq);
+ float mhz, avg=0.0;
+ int n, cpus=0;
+ while(EOF!=(n=fscanf(m_fCPUFreq," MHz : %f ", &mhz)))
+ {
+ if (n>0) {
+ cpus++;
+ avg += mhz;
+ }
+ fscanf(m_fCPUFreq,"%*s");
+ }
+
+ if (cpus > 0)
+ value = avg/cpus;
+ }
+ return value;
+#endif
+}
+
+bool CCPUInfo::getTemperature(CTemperature& temperature)
+{
+ int value = 0;
+ char scale = 0;
+
+#ifdef TARGET_POSIX
+#if defined(TARGET_DARWIN_OSX)
+ value = SMCGetTemperature(SMC_KEY_CPU_TEMP);
+ scale = 'c';
+#else
+ int ret = 0;
+ FILE *p = NULL;
+ CStdString cmd = g_advancedSettings.m_cpuTempCmd;
+
+ temperature.SetState(CTemperature::invalid);
+
+ if (cmd.empty() && m_fProcTemperature == NULL)
+ return false;
+
+ if (!cmd.empty())
+ {
+ p = popen (cmd.c_str(), "r");
+ if (p)
+ {
+ ret = fscanf(p, "%d %c", &value, &scale);
+ pclose(p);
+ }
+ }
+ else
+ {
+ // procfs is deprecated in the linux kernel, we should move away from
+ // using it for temperature data. It doesn't seem that sysfs has a
+ // general enough interface to bother implementing ATM.
+
+ rewind(m_fProcTemperature);
+ fflush(m_fProcTemperature);
+ ret = fscanf(m_fProcTemperature, "temperature: %d %c", &value, &scale);
+
+ // read from the temperature file of the new kernels
+ if (!ret)
+ {
+ ret = fscanf(m_fProcTemperature, "%d", &value);
+ value = value / 1000;
+ scale = 'c';
+ ret++;
+ }
+ }
+
+ if (ret != 2)
+ return false;
+#endif
+#endif // TARGET_POSIX
+
+ if (scale == 'C' || scale == 'c')
+ temperature = CTemperature::CreateFromCelsius(value);
+ else if (scale == 'F' || scale == 'f')
+ temperature = CTemperature::CreateFromFahrenheit(value);
+ else
+ return false;
+
+ return true;
+}
+
+bool CCPUInfo::HasCoreId(int nCoreId) const
+{
+ map<int, CoreInfo>::const_iterator iter = m_cores.find(nCoreId);
+ if (iter != m_cores.end())
+ return true;
+ return false;
+}
+
+const CoreInfo &CCPUInfo::GetCoreInfo(int nCoreId)
+{
+ map<int, CoreInfo>::iterator iter = m_cores.find(nCoreId);
+ if (iter != m_cores.end())
+ return iter->second;
+
+ static CoreInfo dummy;
+ return dummy;
+}
+
+bool CCPUInfo::readProcStat(unsigned long long& user, unsigned long long& nice,
+ unsigned long long& system, unsigned long long& idle, unsigned long long& io)
+{
+
+#ifdef TARGET_WINDOWS
+ FILETIME idleTime;
+ FILETIME kernelTime;
+ FILETIME userTime;
+ if (GetSystemTimes(&idleTime, &kernelTime, &userTime) == 0)
+ return false;
+
+ idle = (uint64_t(idleTime.dwHighDateTime) << 32) + uint64_t(idleTime.dwLowDateTime);
+ // returned "kernelTime" includes "idleTime"
+ system = (uint64_t(kernelTime.dwHighDateTime) << 32) + uint64_t(kernelTime.dwLowDateTime) - idle;
+ user = (uint64_t(userTime.dwHighDateTime) << 32) + uint64_t(userTime.dwLowDateTime);
+ nice = 0;
+ io = 0;
+
+ if (m_cpuFreqCounter && PdhCollectQueryData(m_cpuQueryLoad) == ERROR_SUCCESS)
+ {
+ for (std::map<int, CoreInfo>::iterator it = m_cores.begin(); it != m_cores.end(); ++it)
+ {
+ CoreInfo& curCore = it->second; // simplify usage
+ PDH_RAW_COUNTER cnt;
+ DWORD cntType;
+ if (curCore.m_coreCounter && PdhGetRawCounterValue(curCore.m_coreCounter, &cntType, &cnt) == ERROR_SUCCESS &&
+ (cnt.CStatus == PDH_CSTATUS_VALID_DATA || cnt.CStatus == PDH_CSTATUS_NEW_DATA))
+ {
+ const LONGLONG coreTotal = cnt.SecondValue,
+ coreIdle = cnt.FirstValue;
+ const LONGLONG deltaTotal = coreTotal - curCore.m_total,
+ deltaIdle = coreIdle - curCore.m_idle;
+ const double load = (double(deltaTotal - deltaIdle) * 100.0) / double(deltaTotal);
+
+ // win32 has some problems with calculation of load if load close to zero
+ curCore.m_fPct = (load < 0) ? 0 : load;
+ if (load >= 0 || deltaTotal > 5 * 10 * 1000 * 1000) // do not update (smooth) values for 5 seconds on negative loads
+ {
+ curCore.m_total = coreTotal;
+ curCore.m_idle = coreIdle;
+ }
+ }
+ else
+ curCore.m_fPct = double(m_lastUsedPercentage); // use CPU average as fallback
+ }
+ }
+ else
+ for (std::map<int, CoreInfo>::iterator it = m_cores.begin(); it != m_cores.end(); ++it)
+ it->second.m_fPct = double(m_lastUsedPercentage); // use CPU average as fallback
+#elif defined(TARGET_FREEBSD)
+ long *cptimes;
+ size_t len;
+ int i;
+
+ len = sizeof(long) * 32 * CPUSTATES;
+ if (sysctlbyname("kern.cp_times", NULL, &len, NULL, 0) != 0)
+ return false;
+ cptimes = (long*)malloc(len);
+ if (cptimes == NULL)
+ return false;
+ if (sysctlbyname("kern.cp_times", cptimes, &len, NULL, 0) != 0)
+ {
+ free(cptimes);
+ return false;
+ }
+ user = 0;
+ nice = 0;
+ system = 0;
+ idle = 0;
+ io = 0;
+ for (i = 0; i < m_cpuCount; i++)
+ {
+ long coreUser, coreNice, coreSystem, coreIdle, coreIO;
+ double total;
+
+ coreUser = cptimes[i * CPUSTATES + CP_USER];
+ coreNice = cptimes[i * CPUSTATES + CP_NICE];
+ coreSystem = cptimes[i * CPUSTATES + CP_SYS];
+ coreIO = cptimes[i * CPUSTATES + CP_INTR];
+ coreIdle = cptimes[i * CPUSTATES + CP_IDLE];
+
+ map<int, CoreInfo>::iterator iter = m_cores.find(i);
+ if (iter != m_cores.end())
+ {
+ coreUser -= iter->second.m_user;
+ coreNice -= iter->second.m_nice;
+ coreSystem -= iter->second.m_system;
+ coreIdle -= iter->second.m_idle;
+ coreIO -= iter->second.m_io;
+
+ total = (double)(coreUser + coreNice + coreSystem + coreIdle + coreIO);
+ if(total != 0.0f)
+ iter->second.m_fPct = ((double)(coreUser + coreNice + coreSystem) * 100.0) / total;
+
+ iter->second.m_user += coreUser;
+ iter->second.m_nice += coreNice;
+ iter->second.m_system += coreSystem;
+ iter->second.m_idle += coreIdle;
+ iter->second.m_io += coreIO;
+
+ user += coreUser;
+ nice += coreNice;
+ system += coreSystem;
+ idle += coreIdle;
+ io += coreIO;
+ }
+ }
+ free(cptimes);
+#else
+ if (m_fProcStat == NULL)
+ return false;
+
+#ifdef TARGET_ANDROID
+ // Just another (vanilla) NDK quirk:
+ // rewind + fflush do not actually flush the buffers,
+ // the same initial content is returned rather than re-read
+ fclose(m_fProcStat);
+ m_fProcStat = fopen("/proc/stat", "r");
+#else
+ rewind(m_fProcStat);
+ fflush(m_fProcStat);
+#endif
+
+ char buf[256];
+ if (!fgets(buf, sizeof(buf), m_fProcStat))
+ return false;
+
+ int num = sscanf(buf, "cpu %llu %llu %llu %llu %llu %*s\n", &user, &nice, &system, &idle, &io);
+ if (num < 5)
+ io = 0;
+
+ while (fgets(buf, sizeof(buf), m_fProcStat) && num >= 4)
+ {
+ unsigned long long coreUser, coreNice, coreSystem, coreIdle, coreIO;
+ int nCpu=0;
+ num = sscanf(buf, "cpu%d %llu %llu %llu %llu %llu %*s\n", &nCpu, &coreUser, &coreNice, &coreSystem, &coreIdle, &coreIO);
+ if (num < 6)
+ coreIO = 0;
+
+ map<int, CoreInfo>::iterator iter = m_cores.find(nCpu);
+ if (num > 4 && iter != m_cores.end())
+ {
+ coreUser -= iter->second.m_user;
+ coreNice -= iter->second.m_nice;
+ coreSystem -= iter->second.m_system;
+ coreIdle -= iter->second.m_idle;
+ coreIO -= iter->second.m_io;
+
+ double total = (double)(coreUser + coreNice + coreSystem + coreIdle + coreIO);
+ if(total == 0.0f)
+ iter->second.m_fPct = 0.0f;
+ else
+ iter->second.m_fPct = ((double)(coreUser + coreNice + coreSystem) * 100.0) / total;
+
+ iter->second.m_user += coreUser;
+ iter->second.m_nice += coreNice;
+ iter->second.m_system += coreSystem;
+ iter->second.m_idle += coreIdle;
+ iter->second.m_io += coreIO;
+ }
+ }
+#endif
+
+ return true;
+}
+
+std::string CCPUInfo::GetCoresUsageString() const
+{
+ std::string strCores;
+ for (std::map<int, CoreInfo>::const_iterator it = m_cores.begin(); it != m_cores.end(); ++it)
+ {
+ if (!strCores.empty())
+ strCores += ' ';
+ if (it->second.m_fPct < 10.0)
+ strCores += StringUtils::Format("CPU%d: %1.1f%%", it->first, it->second.m_fPct);
+ else
+ strCores += StringUtils::Format("CPU%d: %3.0f%%", it->first, it->second.m_fPct);
+ }
+
+ return strCores;
+}
+
+void CCPUInfo::ReadCPUFeatures()
+{
+#ifdef TARGET_WINDOWS
+
+ int CPUInfo[4]; // receives EAX, EBX, ECD and EDX in that order
+
+ __cpuid(CPUInfo, 0);
+ int MaxStdInfoType = CPUInfo[0];
+
+ if (MaxStdInfoType >= CPUID_INFOTYPE_STANDARD)
+ {
+ __cpuid(CPUInfo, CPUID_INFOTYPE_STANDARD);
+ if (CPUInfo[CPUINFO_EDX] & CPUID_00000001_EDX_MMX)
+ m_cpuFeatures |= CPU_FEATURE_MMX;
+ if (CPUInfo[CPUINFO_EDX] & CPUID_00000001_EDX_SSE)
+ m_cpuFeatures |= CPU_FEATURE_SSE;
+ if (CPUInfo[CPUINFO_EDX] & CPUID_00000001_EDX_SSE2)
+ m_cpuFeatures |= CPU_FEATURE_SSE2;
+ if (CPUInfo[CPUINFO_ECX] & CPUID_00000001_ECX_SSE3)
+ m_cpuFeatures |= CPU_FEATURE_SSE3;
+ if (CPUInfo[CPUINFO_ECX] & CPUID_00000001_ECX_SSSE3)
+ m_cpuFeatures |= CPU_FEATURE_SSSE3;
+ if (CPUInfo[CPUINFO_ECX] & CPUID_00000001_ECX_SSE4)
+ m_cpuFeatures |= CPU_FEATURE_SSE4;
+ if (CPUInfo[CPUINFO_ECX] & CPUID_00000001_ECX_SSE42)
+ m_cpuFeatures |= CPU_FEATURE_SSE42;
+ }
+
+ __cpuid(CPUInfo, 0x80000000);
+ int MaxExtInfoType = CPUInfo[0];
+
+ if (MaxExtInfoType >= CPUID_INFOTYPE_EXTENDED)
+ {
+ __cpuid(CPUInfo, CPUID_INFOTYPE_EXTENDED);
+
+ if (CPUInfo[CPUINFO_EDX] & CPUID_80000001_EDX_MMX)
+ m_cpuFeatures |= CPU_FEATURE_MMX;
+ if (CPUInfo[CPUINFO_EDX] & CPUID_80000001_EDX_MMX2)
+ m_cpuFeatures |= CPU_FEATURE_MMX2;
+ if (CPUInfo[CPUINFO_EDX] & CPUID_80000001_EDX_3DNOW)
+ m_cpuFeatures |= CPU_FEATURE_3DNOW;
+ if (CPUInfo[CPUINFO_EDX] & CPUID_80000001_EDX_3DNOWEXT)
+ m_cpuFeatures |= CPU_FEATURE_3DNOWEXT;
+ }
+
+#elif defined(TARGET_DARWIN)
+ #if defined(__ppc__)
+ m_cpuFeatures |= CPU_FEATURE_ALTIVEC;
+ #elif defined(TARGET_DARWIN_IOS)
+ #else
+ size_t len = 512 - 1; // '-1' for trailing space
+ char buffer[512] ={0};
+
+ if (sysctlbyname("machdep.cpu.features", &buffer, &len, NULL, 0) == 0)
+ {
+ strcat(buffer, " ");
+ if (strstr(buffer,"MMX "))
+ m_cpuFeatures |= CPU_FEATURE_MMX;
+ if (strstr(buffer,"MMXEXT "))
+ m_cpuFeatures |= CPU_FEATURE_MMX2;
+ if (strstr(buffer,"SSE "))
+ m_cpuFeatures |= CPU_FEATURE_SSE;
+ if (strstr(buffer,"SSE2 "))
+ m_cpuFeatures |= CPU_FEATURE_SSE2;
+ if (strstr(buffer,"SSE3 "))
+ m_cpuFeatures |= CPU_FEATURE_SSE3;
+ if (strstr(buffer,"SSSE3 "))
+ m_cpuFeatures |= CPU_FEATURE_SSSE3;
+ if (strstr(buffer,"SSE4.1 "))
+ m_cpuFeatures |= CPU_FEATURE_SSE4;
+ if (strstr(buffer,"SSE4.2 "))
+ m_cpuFeatures |= CPU_FEATURE_SSE42;
+ if (strstr(buffer,"3DNOW "))
+ m_cpuFeatures |= CPU_FEATURE_3DNOW;
+ if (strstr(buffer,"3DNOWEXT "))
+ m_cpuFeatures |= CPU_FEATURE_3DNOWEXT;
+ }
+ else
+ m_cpuFeatures |= CPU_FEATURE_MMX;
+ #endif
+#elif defined(LINUX)
+// empty on purpose, the implementation is in the constructor
+#elif !defined(__powerpc__) && !defined(__ppc__) && !defined(__arm__)
+ m_cpuFeatures |= CPU_FEATURE_MMX;
+#elif defined(__powerpc__) || defined(__ppc__)
+ m_cpuFeatures |= CPU_FEATURE_ALTIVEC;
+#endif
+}
+
+bool CCPUInfo::HasNeon()
+{
+ static int has_neon = -1;
+#if defined (TARGET_ANDROID)
+ if (has_neon == -1)
+ has_neon = (CAndroidFeatures::HasNeon()) ? 1 : 0;
+
+#elif defined(TARGET_DARWIN_IOS)
+ has_neon = 1;
+
+#elif defined(TARGET_LINUX) && defined(__ARM_NEON__)
+ if (has_neon == -1)
+ {
+ has_neon = 0;
+ // why are we not looking at the Features in
+ // /proc/cpuinfo for neon ?
+ int fd = open("/proc/self/auxv", O_RDONLY);
+ if (fd >= 0)
+ {
+ Elf32_auxv_t auxv;
+ while (read(fd, &auxv, sizeof(Elf32_auxv_t)) == sizeof(Elf32_auxv_t))
+ {
+ if (auxv.a_type == AT_HWCAP)
+ {
+ has_neon = (auxv.a_un.a_val & HWCAP_NEON) ? 1 : 0;
+ break;
+ }
+ }
+ close(fd);
+ }
+ }
+
+#endif
+
+ return has_neon == 1;
+}
+
+CCPUInfo g_cpuInfo;
diff --git a/src/utils/CPUInfo.h b/src/utils/CPUInfo.h
new file mode 100644
index 0000000000..baf87514e9
--- /dev/null
+++ b/src/utils/CPUInfo.h
@@ -0,0 +1,142 @@
+#ifndef CPUINFO_H
+#define CPUINFO_H
+
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <time.h>
+#include <string>
+#include <map>
+#include "threads/SystemClock.h"
+
+#ifdef TARGET_WINDOWS
+// avoid inclusion of <windows.h> and others
+typedef void* HANDLE;
+typedef HANDLE PDH_HQUERY;
+typedef HANDLE PDH_HCOUNTER;
+#endif
+class CTemperature;
+
+#define CPU_FEATURE_MMX 1 << 0
+#define CPU_FEATURE_MMX2 1 << 1
+#define CPU_FEATURE_SSE 1 << 2
+#define CPU_FEATURE_SSE2 1 << 3
+#define CPU_FEATURE_SSE3 1 << 4
+#define CPU_FEATURE_SSSE3 1 << 5
+#define CPU_FEATURE_SSE4 1 << 6
+#define CPU_FEATURE_SSE42 1 << 7
+#define CPU_FEATURE_3DNOW 1 << 8
+#define CPU_FEATURE_3DNOWEXT 1 << 9
+#define CPU_FEATURE_ALTIVEC 1 << 10
+#define CPU_FEATURE_NEON 1 << 11
+
+struct CoreInfo
+{
+ int m_id;
+ double m_fSpeed;
+ double m_fPct;
+#ifdef TARGET_POSIX
+ unsigned long long m_user;
+ unsigned long long m_nice;
+ unsigned long long m_system;
+ unsigned long long m_io;
+#elif defined(TARGET_WINDOWS)
+ PDH_HCOUNTER m_coreCounter;
+ unsigned long long m_total;
+#endif
+ unsigned long long m_idle;
+ std::string m_strVendor;
+ std::string m_strModel;
+ std::string m_strBogoMips;
+ std::string m_strHardware;
+ std::string m_strRevision;
+ std::string m_strSerial;
+#ifdef TARGET_POSIX
+ CoreInfo() : m_id(0), m_fSpeed(.0), m_fPct(.0), m_user(0LL), m_nice(0LL), m_system(0LL), m_io(0LL), m_idle(0LL) {}
+#elif defined(TARGET_WINDOWS)
+ CoreInfo() : m_id(0), m_fSpeed(.0), m_fPct(.0), m_coreCounter(NULL), m_total(0LL), m_idle(0LL) {}
+#endif
+ bool operator<(const CoreInfo& other) const { return m_id < other.m_id; }
+};
+
+class CCPUInfo
+{
+public:
+ CCPUInfo(void);
+ ~CCPUInfo();
+
+ int getUsedPercentage();
+ int getCPUCount() const { return m_cpuCount; }
+ float getCPUFrequency();
+ bool getTemperature(CTemperature& temperature);
+ std::string& getCPUModel() { return m_cpuModel; }
+ std::string& getCPUBogoMips() { return m_cpuBogoMips; }
+ std::string& getCPUHardware() { return m_cpuHardware; }
+ std::string& getCPURevision() { return m_cpuRevision; }
+ std::string& getCPUSerial() { return m_cpuSerial; }
+
+ const CoreInfo &GetCoreInfo(int nCoreId);
+ bool HasCoreId(int nCoreId) const;
+
+ std::string GetCoresUsageString() const;
+
+ unsigned int GetCPUFeatures() const { return m_cpuFeatures; }
+
+private:
+ bool readProcStat(unsigned long long& user, unsigned long long& nice, unsigned long long& system,
+ unsigned long long& idle, unsigned long long& io);
+ void ReadCPUFeatures();
+ static bool HasNeon();
+
+#ifdef TARGET_POSIX
+ FILE* m_fProcStat;
+ FILE* m_fProcTemperature;
+ FILE* m_fCPUFreq;
+ bool m_cpuInfoForFreq;
+#elif defined(TARGET_WINDOWS)
+ PDH_HQUERY m_cpuQueryFreq;
+ PDH_HQUERY m_cpuQueryLoad;
+ PDH_HCOUNTER m_cpuFreqCounter;
+#endif
+
+ unsigned long long m_userTicks;
+ unsigned long long m_niceTicks;
+ unsigned long long m_systemTicks;
+ unsigned long long m_idleTicks;
+ unsigned long long m_ioTicks;
+
+ int m_lastUsedPercentage;
+ XbmcThreads::EndTime m_nextUsedReadTime;
+ std::string m_cpuModel;
+ std::string m_cpuBogoMips;
+ std::string m_cpuHardware;
+ std::string m_cpuRevision;
+ std::string m_cpuSerial;
+ int m_cpuCount;
+ unsigned int m_cpuFeatures;
+
+ std::map<int, CoreInfo> m_cores;
+
+};
+
+extern CCPUInfo g_cpuInfo;
+
+#endif
diff --git a/src/utils/CharsetConverter.cpp b/src/utils/CharsetConverter.cpp
new file mode 100644
index 0000000000..23bf0822f0
--- /dev/null
+++ b/src/utils/CharsetConverter.cpp
@@ -0,0 +1,892 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "CharsetConverter.h"
+#include "Util.h"
+#include "utils/StringUtils.h"
+#include <fribidi/fribidi.h>
+#include "LangInfo.h"
+#include "guilib/LocalizeStrings.h"
+#include "settings/lib/Setting.h"
+#include "settings/Settings.h"
+#include "threads/SingleLock.h"
+#include "utils/Utf8Utils.h"
+#include "log.h"
+
+#include <errno.h>
+#include <iconv.h>
+
+#if !defined(TARGET_WINDOWS) && defined(HAVE_CONFIG_H)
+ #include "config.h"
+#endif
+
+#ifdef WORDS_BIGENDIAN
+ #define ENDIAN_SUFFIX "BE"
+#else
+ #define ENDIAN_SUFFIX "LE"
+#endif
+
+#if defined(TARGET_DARWIN)
+ #define WCHAR_IS_UCS_4 1
+ #define UTF16_CHARSET "UTF-16" ENDIAN_SUFFIX
+ #define UTF32_CHARSET "UTF-32" ENDIAN_SUFFIX
+ #define UTF8_SOURCE "UTF-8-MAC"
+ #define WCHAR_CHARSET UTF32_CHARSET
+#elif defined(TARGET_WINDOWS)
+ #define WCHAR_IS_UTF16 1
+ #define UTF16_CHARSET "UTF-16" ENDIAN_SUFFIX
+ #define UTF32_CHARSET "UTF-32" ENDIAN_SUFFIX
+ #define UTF8_SOURCE "UTF-8"
+ #define WCHAR_CHARSET UTF16_CHARSET
+ #pragma comment(lib, "libfribidi.lib")
+ #pragma comment(lib, "libiconv.lib")
+#elif defined(TARGET_ANDROID)
+ #define WCHAR_IS_UCS_4 1
+ #define UTF16_CHARSET "UTF-16" ENDIAN_SUFFIX
+ #define UTF32_CHARSET "UTF-32" ENDIAN_SUFFIX
+ #define UTF8_SOURCE "UTF-8"
+ #define WCHAR_CHARSET UTF32_CHARSET
+#else
+ #define UTF16_CHARSET "UTF-16" ENDIAN_SUFFIX
+ #define UTF32_CHARSET "UTF-32" ENDIAN_SUFFIX
+ #define UTF8_SOURCE "UTF-8"
+ #define WCHAR_CHARSET "WCHAR_T"
+ #if __STDC_ISO_10646__
+ #ifdef SIZEOF_WCHAR_T
+ #if SIZEOF_WCHAR_T == 4
+ #define WCHAR_IS_UCS_4 1
+ #elif SIZEOF_WCHAR_T == 2
+ #define WCHAR_IS_UCS_2 1
+ #endif
+ #endif
+ #endif
+#endif
+
+#define NO_ICONV ((iconv_t)-1)
+
+enum SpecialCharset
+{
+ NotSpecialCharset = 0,
+ SystemCharset,
+ UserCharset /* locale.charset */,
+ SubtitleCharset /* subtitles.charset */,
+ KaraokeCharset /* karaoke.charset */
+};
+
+
+class CConverterType : public CCriticalSection
+{
+public:
+ CConverterType(const std::string& sourceCharset, const std::string& targetCharset, unsigned int targetSingleCharMaxLen = 1);
+ CConverterType(enum SpecialCharset sourceSpecialCharset, const std::string& targetCharset, unsigned int targetSingleCharMaxLen = 1);
+ CConverterType(const std::string& sourceCharset, enum SpecialCharset targetSpecialCharset, unsigned int targetSingleCharMaxLen = 1);
+ CConverterType(enum SpecialCharset sourceSpecialCharset, enum SpecialCharset targetSpecialCharset, unsigned int targetSingleCharMaxLen = 1);
+ CConverterType(const CConverterType& other);
+ ~CConverterType();
+
+ iconv_t GetConverter(CSingleLock& converterLock);
+
+ void Reset(void);
+ void ReinitTo(const std::string& sourceCharset, const std::string& targetCharset, unsigned int targetSingleCharMaxLen = 1);
+ std::string GetSourceCharset(void) const { return m_sourceCharset; }
+ std::string GetTargetCharset(void) const { return m_targetCharset; }
+ unsigned int GetTargetSingleCharMaxLen(void) const { return m_targetSingleCharMaxLen; }
+
+private:
+ static std::string ResolveSpecialCharset(enum SpecialCharset charset);
+
+ enum SpecialCharset m_sourceSpecialCharset;
+ std::string m_sourceCharset;
+ enum SpecialCharset m_targetSpecialCharset;
+ std::string m_targetCharset;
+ iconv_t m_iconv;
+ unsigned int m_targetSingleCharMaxLen;
+};
+
+CConverterType::CConverterType(const std::string& sourceCharset, const std::string& targetCharset, unsigned int targetSingleCharMaxLen /*= 1*/) : CCriticalSection(),
+ m_sourceSpecialCharset(NotSpecialCharset),
+ m_sourceCharset(sourceCharset),
+ m_targetSpecialCharset(NotSpecialCharset),
+ m_targetCharset(targetCharset),
+ m_iconv(NO_ICONV),
+ m_targetSingleCharMaxLen(targetSingleCharMaxLen)
+{
+}
+
+CConverterType::CConverterType(enum SpecialCharset sourceSpecialCharset, const std::string& targetCharset, unsigned int targetSingleCharMaxLen /*= 1*/) : CCriticalSection(),
+ m_sourceSpecialCharset(sourceSpecialCharset),
+ m_sourceCharset(),
+ m_targetSpecialCharset(NotSpecialCharset),
+ m_targetCharset(targetCharset),
+ m_iconv(NO_ICONV),
+ m_targetSingleCharMaxLen(targetSingleCharMaxLen)
+{
+}
+
+CConverterType::CConverterType(const std::string& sourceCharset, enum SpecialCharset targetSpecialCharset, unsigned int targetSingleCharMaxLen /*= 1*/) : CCriticalSection(),
+ m_sourceSpecialCharset(NotSpecialCharset),
+ m_sourceCharset(sourceCharset),
+ m_targetSpecialCharset(targetSpecialCharset),
+ m_targetCharset(),
+ m_iconv(NO_ICONV),
+ m_targetSingleCharMaxLen(targetSingleCharMaxLen)
+{
+}
+
+CConverterType::CConverterType(enum SpecialCharset sourceSpecialCharset, enum SpecialCharset targetSpecialCharset, unsigned int targetSingleCharMaxLen /*= 1*/) : CCriticalSection(),
+ m_sourceSpecialCharset(sourceSpecialCharset),
+ m_sourceCharset(),
+ m_targetSpecialCharset(targetSpecialCharset),
+ m_targetCharset(),
+ m_iconv(NO_ICONV),
+ m_targetSingleCharMaxLen(targetSingleCharMaxLen)
+{
+}
+
+CConverterType::CConverterType(const CConverterType& other) : CCriticalSection(),
+ m_sourceSpecialCharset(other.m_sourceSpecialCharset),
+ m_sourceCharset(other.m_sourceCharset),
+ m_targetSpecialCharset(other.m_targetSpecialCharset),
+ m_targetCharset(other.m_targetCharset),
+ m_iconv(NO_ICONV),
+ m_targetSingleCharMaxLen(other.m_targetSingleCharMaxLen)
+{
+}
+
+
+CConverterType::~CConverterType()
+{
+ CSingleLock lock(*this);
+ if (m_iconv != NO_ICONV)
+ iconv_close(m_iconv);
+ lock.Leave(); // ensure unlocking before final destruction
+}
+
+
+iconv_t CConverterType::GetConverter(CSingleLock& converterLock)
+{
+ // ensure that this unique instance is locked externally
+ if (&converterLock.get_underlying() != this)
+ return NO_ICONV;
+
+ if (m_iconv == NO_ICONV)
+ {
+ if (m_sourceSpecialCharset)
+ m_sourceCharset = ResolveSpecialCharset(m_sourceSpecialCharset);
+ if (m_targetSpecialCharset)
+ m_targetCharset = ResolveSpecialCharset(m_targetSpecialCharset);
+
+ m_iconv = iconv_open(m_targetCharset.c_str(), m_sourceCharset.c_str());
+
+ if (m_iconv == NO_ICONV)
+ CLog::Log(LOGERROR, "%s: iconv_open() for \"%s\" -> \"%s\" failed, errno = %d (%s)",
+ __FUNCTION__, m_sourceCharset.c_str(), m_targetCharset.c_str(), errno, strerror(errno));
+ }
+
+ return m_iconv;
+}
+
+
+void CConverterType::Reset(void)
+{
+ CSingleLock lock(*this);
+ if (m_iconv != NO_ICONV)
+ {
+ iconv_close(m_iconv);
+ m_iconv = NO_ICONV;
+ }
+
+ if (m_sourceSpecialCharset)
+ m_sourceCharset.clear();
+ if (m_targetSpecialCharset)
+ m_targetCharset.clear();
+
+}
+
+void CConverterType::ReinitTo(const std::string& sourceCharset, const std::string& targetCharset, unsigned int targetSingleCharMaxLen /*= 1*/)
+{
+ CSingleLock lock(*this);
+ if (sourceCharset != m_sourceCharset || targetCharset != m_targetCharset)
+ {
+ if (m_iconv != NO_ICONV)
+ {
+ iconv_close(m_iconv);
+ m_iconv = NO_ICONV;
+ }
+
+ m_sourceSpecialCharset = NotSpecialCharset;
+ m_sourceCharset = sourceCharset;
+ m_targetSpecialCharset = NotSpecialCharset;
+ m_targetCharset = targetCharset;
+ m_targetSingleCharMaxLen = targetSingleCharMaxLen;
+ }
+}
+
+std::string CConverterType::ResolveSpecialCharset(enum SpecialCharset charset)
+{
+ switch (charset)
+ {
+ case SystemCharset:
+ return "";
+ case UserCharset:
+ return g_langInfo.GetGuiCharSet();
+ case SubtitleCharset:
+ return g_langInfo.GetSubtitleCharSet();
+ case KaraokeCharset:
+ {
+ CSetting* karaokeSetting = CSettings::Get().GetSetting("karaoke.charset");
+ if (karaokeSetting == NULL || ((CSettingString*)karaokeSetting)->GetValue() == "DEFAULT")
+ return g_langInfo.GetGuiCharSet();
+
+ return ((CSettingString*)karaokeSetting)->GetValue();
+ }
+ case NotSpecialCharset:
+ default:
+ return "UTF-8"; /* dummy value */
+ }
+}
+
+
+enum StdConversionType /* Keep it in sync with CCharsetConverter::CInnerConverter::m_stdConversion */
+{
+ NoConversion = -1,
+ Utf8ToUtf32 = 0,
+ Utf32ToUtf8,
+ Utf32ToW,
+ WToUtf32,
+ SubtitleCharsetToUtf8,
+ Utf8ToUserCharset,
+ UserCharsetToUtf8,
+ Utf32ToUserCharset,
+ WtoUtf8,
+ Utf16LEtoW,
+ Utf16BEtoUtf8,
+ Utf16LEtoUtf8,
+ Utf8toW,
+ Utf8ToSystem,
+ SystemToUtf8,
+ Ucs2CharsetToUtf8,
+ NumberOfStdConversionTypes /* Dummy sentinel entry */
+};
+
+
+/* We don't want to pollute header file with many additional includes and definitions, so put
+ here all staff that require usage of types defined in this file or in additional headers */
+class CCharsetConverter::CInnerConverter
+{
+public:
+ static bool logicalToVisualBiDi(const std::u32string& stringSrc, std::u32string& stringDst, FriBidiCharType base = FRIBIDI_TYPE_LTR, const bool failOnBadString = false);
+
+ template<class INPUT,class OUTPUT>
+ static bool stdConvert(StdConversionType convertType, const INPUT& strSource, OUTPUT& strDest, bool failOnInvalidChar = false);
+ template<class INPUT,class OUTPUT>
+ static bool customConvert(const std::string& sourceCharset, const std::string& targetCharset, const INPUT& strSource, OUTPUT& strDest, bool failOnInvalidChar = false);
+
+ template<class INPUT,class OUTPUT>
+ static bool convert(iconv_t type, int multiplier, const INPUT& strSource, OUTPUT& strDest, bool failOnInvalidChar = false);
+
+ static CConverterType m_stdConversion[NumberOfStdConversionTypes];
+ static CCriticalSection m_critSectionFriBiDi;
+};
+
+/* single symbol sizes in chars */
+const int CCharsetConverter::m_Utf8CharMinSize = 1;
+const int CCharsetConverter::m_Utf8CharMaxSize = 4;
+
+CConverterType CCharsetConverter::CInnerConverter::m_stdConversion[NumberOfStdConversionTypes] = /* keep it in sync with enum StdConversionType */
+{
+ /* Utf8ToUtf32 */ CConverterType(UTF8_SOURCE, UTF32_CHARSET),
+ /* Utf32ToUtf8 */ CConverterType(UTF32_CHARSET, "UTF-8", CCharsetConverter::m_Utf8CharMaxSize),
+ /* Utf32ToW */ CConverterType(UTF32_CHARSET, WCHAR_CHARSET),
+ /* WToUtf32 */ CConverterType(WCHAR_CHARSET, UTF32_CHARSET),
+ /* SubtitleCharsetToUtf8*/CConverterType(SubtitleCharset, "UTF-8", CCharsetConverter::m_Utf8CharMaxSize),
+ /* Utf8ToUserCharset */ CConverterType(UTF8_SOURCE, UserCharset),
+ /* UserCharsetToUtf8 */ CConverterType(UserCharset, "UTF-8", CCharsetConverter::m_Utf8CharMaxSize),
+ /* Utf32ToUserCharset */ CConverterType(UTF32_CHARSET, UserCharset),
+ /* WtoUtf8 */ CConverterType(WCHAR_CHARSET, "UTF-8", CCharsetConverter::m_Utf8CharMaxSize),
+ /* Utf16LEtoW */ CConverterType("UTF-16LE", WCHAR_CHARSET),
+ /* Utf16BEtoUtf8 */ CConverterType("UTF-16BE", "UTF-8", CCharsetConverter::m_Utf8CharMaxSize),
+ /* Utf16LEtoUtf8 */ CConverterType("UTF-16LE", "UTF-8", CCharsetConverter::m_Utf8CharMaxSize),
+ /* Utf8toW */ CConverterType(UTF8_SOURCE, WCHAR_CHARSET),
+ /* Utf8ToSystem */ CConverterType(UTF8_SOURCE, SystemCharset),
+ /* SystemToUtf8 */ CConverterType(SystemCharset, UTF8_SOURCE),
+ /* Ucs2CharsetToUtf8 */ CConverterType("UCS-2LE", "UTF-8", CCharsetConverter::m_Utf8CharMaxSize)
+};
+
+CCriticalSection CCharsetConverter::CInnerConverter::m_critSectionFriBiDi;
+
+
+
+template<class INPUT,class OUTPUT>
+bool CCharsetConverter::CInnerConverter::stdConvert(StdConversionType convertType, const INPUT& strSource, OUTPUT& strDest, bool failOnInvalidChar /*= false*/)
+{
+ strDest.clear();
+ if (strSource.empty())
+ return true;
+
+ if (convertType < 0 || convertType >= NumberOfStdConversionTypes)
+ return false;
+
+ CConverterType& convType = m_stdConversion[convertType];
+ CSingleLock converterLock(convType);
+
+ return convert(convType.GetConverter(converterLock), convType.GetTargetSingleCharMaxLen(), strSource, strDest, failOnInvalidChar);
+}
+
+template<class INPUT,class OUTPUT>
+bool CCharsetConverter::CInnerConverter::customConvert(const std::string& sourceCharset, const std::string& targetCharset, const INPUT& strSource, OUTPUT& strDest, bool failOnInvalidChar /*= false*/)
+{
+ strDest.clear();
+ if (strSource.empty())
+ return true;
+
+ iconv_t conv = iconv_open(targetCharset.c_str(), sourceCharset.c_str());
+ if (conv == NO_ICONV)
+ {
+ CLog::Log(LOGERROR, "%s: iconv_open() for \"%s\" -> \"%s\" failed, errno = %d (%s)",
+ __FUNCTION__, sourceCharset.c_str(), targetCharset.c_str(), errno, strerror(errno));
+ return false;
+ }
+ const int dstMultp = (targetCharset.compare(0, 5, "UTF-8") == 0) ? CCharsetConverter::m_Utf8CharMaxSize : 1;
+ const bool result = convert(conv, dstMultp, strSource, strDest, failOnInvalidChar);
+ iconv_close(conv);
+
+ return result;
+}
+
+
+/* iconv may declare inbuf to be char** rather than const char** depending on platform and version,
+ so provide a wrapper that handles both */
+struct charPtrPtrAdapter
+{
+ const char** pointer;
+ charPtrPtrAdapter(const char** p) :
+ pointer(p) { }
+ operator char**()
+ { return const_cast<char**>(pointer); }
+ operator const char**()
+ { return pointer; }
+};
+
+template<class INPUT,class OUTPUT>
+bool CCharsetConverter::CInnerConverter::convert(iconv_t type, int multiplier, const INPUT& strSource, OUTPUT& strDest, bool failOnInvalidChar /*= false*/)
+{
+ if (type == NO_ICONV)
+ return false;
+
+ //input buffer for iconv() is the buffer from strSource
+ size_t inBufSize = (strSource.length() + 1) * sizeof(typename INPUT::value_type);
+ const char* inBuf = (const char*)strSource.c_str();
+
+ //allocate output buffer for iconv()
+ size_t outBufSize = (strSource.length() + 1) * sizeof(typename OUTPUT::value_type) * multiplier;
+ char* outBuf = (char*)malloc(outBufSize);
+ if (outBuf == NULL)
+ {
+ CLog::Log(LOGSEVERE, "%s: malloc failed", __FUNCTION__);
+ return false;
+ }
+
+ size_t inBytesAvail = inBufSize; //how many bytes iconv() can read
+ size_t outBytesAvail = outBufSize; //how many bytes iconv() can write
+ const char* inBufStart = inBuf; //where in our input buffer iconv() should start reading
+ char* outBufStart = outBuf; //where in out output buffer iconv() should start writing
+
+ size_t returnV;
+ while(1)
+ {
+ //iconv() will update inBufStart, inBytesAvail, outBufStart and outBytesAvail
+ returnV = iconv(type, charPtrPtrAdapter(&inBufStart), &inBytesAvail, &outBufStart, &outBytesAvail);
+
+ if (returnV == (size_t)-1)
+ {
+ if (errno == E2BIG) //output buffer is not big enough
+ {
+ //save where iconv() ended converting, realloc might make outBufStart invalid
+ size_t bytesConverted = outBufSize - outBytesAvail;
+
+ //make buffer twice as big
+ outBufSize *= 2;
+ char* newBuf = (char*)realloc(outBuf, outBufSize);
+ if (!newBuf)
+ {
+ CLog::Log(LOGSEVERE, "%s realloc failed with errno=%d(%s)",
+ __FUNCTION__, errno, strerror(errno));
+ break;
+ }
+ outBuf = newBuf;
+
+ //update the buffer pointer and counter
+ outBufStart = outBuf + bytesConverted;
+ outBytesAvail = outBufSize - bytesConverted;
+
+ //continue in the loop and convert the rest
+ continue;
+ }
+ else if (errno == EILSEQ) //An invalid multibyte sequence has been encountered in the input
+ {
+ if (failOnInvalidChar)
+ break;
+
+ //skip invalid byte
+ inBufStart++;
+ inBytesAvail--;
+ //continue in the loop and convert the rest
+ continue;
+ }
+ else if (errno == EINVAL) /* Invalid sequence at the end of input buffer */
+ {
+ if (!failOnInvalidChar)
+ returnV = 0; /* reset error status to use converted part */
+
+ break;
+ }
+ else //iconv() had some other error
+ {
+ CLog::Log(LOGERROR, "%s: iconv() failed, errno=%d (%s)",
+ __FUNCTION__, errno, strerror(errno));
+ }
+ }
+ break;
+ }
+
+ //complete the conversion (reset buffers), otherwise the current data will prefix the data on the next call
+ if (iconv(type, NULL, NULL, &outBufStart, &outBytesAvail) == (size_t)-1)
+ CLog::Log(LOGERROR, "%s failed cleanup errno=%d(%s)", __FUNCTION__, errno, strerror(errno));
+
+ if (returnV == (size_t)-1)
+ {
+ free(outBuf);
+ return false;
+ }
+ //we're done
+
+ const typename OUTPUT::size_type sizeInChars = (typename OUTPUT::size_type) (outBufSize - outBytesAvail) / sizeof(typename OUTPUT::value_type);
+ typename OUTPUT::const_pointer strPtr = (typename OUTPUT::const_pointer) outBuf;
+ /* Make sure that all buffer is assigned and string is stopped at end of buffer */
+ if (strPtr[sizeInChars-1] == 0 && strSource[strSource.length()-1] != 0)
+ strDest.assign(strPtr, sizeInChars-1);
+ else
+ strDest.assign(strPtr, sizeInChars);
+
+ free(outBuf);
+
+ return true;
+}
+
+bool CCharsetConverter::CInnerConverter::logicalToVisualBiDi(const std::u32string& stringSrc, std::u32string& stringDst, FriBidiCharType base /*= FRIBIDI_TYPE_LTR*/, const bool failOnBadString /*= false*/)
+{
+ stringDst.clear();
+
+ const size_t srcLen = stringSrc.length();
+ if (srcLen == 0)
+ return true;
+
+ stringDst.reserve(srcLen);
+ size_t lineStart = 0;
+
+ // libfribidi is not threadsafe, so make sure we make it so
+ CSingleLock lock(m_critSectionFriBiDi);
+ do
+ {
+ size_t lineEnd = stringSrc.find('\n', lineStart);
+ if (lineEnd >= srcLen) // equal to 'lineEnd == std::string::npos'
+ lineEnd = srcLen;
+ else
+ lineEnd++; // include '\n'
+
+ const size_t lineLen = lineEnd - lineStart;
+
+ FriBidiChar* visual = (FriBidiChar*) malloc((lineLen + 1) * sizeof(FriBidiChar));
+ if (visual == NULL)
+ {
+ free(visual);
+ CLog::Log(LOGSEVERE, "%s: can't allocate memory", __FUNCTION__);
+ return false;
+ }
+
+ bool bidiFailed = false;
+ FriBidiCharType baseCopy = base; // preserve same value for all lines, required because fribidi_log2vis will modify parameter value
+ if (fribidi_log2vis((const FriBidiChar*)(stringSrc.c_str() + lineStart), lineLen, &baseCopy, visual, NULL, NULL, NULL))
+ {
+ // Removes bidirectional marks
+ const int newLen = fribidi_remove_bidi_marks(visual, lineLen, NULL, NULL, NULL);
+ if (newLen > 0)
+ stringDst.append((const char32_t*)visual, (size_t)newLen);
+ else if (newLen < 0)
+ bidiFailed = failOnBadString;
+ }
+ else
+ bidiFailed = failOnBadString;
+
+ free(visual);
+
+ if (bidiFailed)
+ return false;
+
+ lineStart = lineEnd;
+ } while (lineStart < srcLen);
+
+ return !stringDst.empty();
+}
+
+
+static struct SCharsetMapping
+{
+ const char* charset;
+ const char* caption;
+} g_charsets[] = {
+ { "ISO-8859-1", "Western Europe (ISO)" }
+ , { "ISO-8859-2", "Central Europe (ISO)" }
+ , { "ISO-8859-3", "South Europe (ISO)" }
+ , { "ISO-8859-4", "Baltic (ISO)" }
+ , { "ISO-8859-5", "Cyrillic (ISO)" }
+ , { "ISO-8859-6", "Arabic (ISO)" }
+ , { "ISO-8859-7", "Greek (ISO)" }
+ , { "ISO-8859-8", "Hebrew (ISO)" }
+ , { "ISO-8859-9", "Turkish (ISO)" }
+ , { "CP1250", "Central Europe (Windows)" }
+ , { "CP1251", "Cyrillic (Windows)" }
+ , { "CP1252", "Western Europe (Windows)" }
+ , { "CP1253", "Greek (Windows)" }
+ , { "CP1254", "Turkish (Windows)" }
+ , { "CP1255", "Hebrew (Windows)" }
+ , { "CP1256", "Arabic (Windows)" }
+ , { "CP1257", "Baltic (Windows)" }
+ , { "CP1258", "Vietnamesse (Windows)" }
+ , { "CP874", "Thai (Windows)" }
+ , { "BIG5", "Chinese Traditional (Big5)" }
+ , { "GBK", "Chinese Simplified (GBK)" }
+ , { "SHIFT_JIS", "Japanese (Shift-JIS)" }
+ , { "CP949", "Korean" }
+ , { "BIG5-HKSCS", "Hong Kong (Big5-HKSCS)" }
+ , { NULL, NULL }
+};
+
+
+CCharsetConverter::CCharsetConverter()
+{
+}
+
+void CCharsetConverter::OnSettingChanged(const CSetting* setting)
+{
+ if (setting == NULL)
+ return;
+
+ const std::string& settingId = setting->GetId();
+ if (settingId == "locale.charset")
+ resetUserCharset();
+ else if (settingId == "subtitles.charset")
+ resetSubtitleCharset();
+ else if (settingId == "karaoke.charset")
+ resetKaraokeCharset();
+}
+
+void CCharsetConverter::clear()
+{
+}
+
+std::vector<std::string> CCharsetConverter::getCharsetLabels()
+{
+ std::vector<std::string> lab;
+ for(SCharsetMapping* c = g_charsets; c->charset; c++)
+ lab.push_back(c->caption);
+
+ return lab;
+}
+
+std::string CCharsetConverter::getCharsetLabelByName(const std::string& charsetName)
+{
+ for(SCharsetMapping* c = g_charsets; c->charset; c++)
+ {
+ if (StringUtils::EqualsNoCase(charsetName,c->charset))
+ return c->caption;
+ }
+
+ return "";
+}
+
+std::string CCharsetConverter::getCharsetNameByLabel(const std::string& charsetLabel)
+{
+ for(SCharsetMapping* c = g_charsets; c->charset; c++)
+ {
+ if (StringUtils::EqualsNoCase(charsetLabel, c->caption))
+ return c->charset;
+ }
+
+ return "";
+}
+
+void CCharsetConverter::reset(void)
+{
+ for (int i = 0; i < NumberOfStdConversionTypes; i++)
+ CInnerConverter::m_stdConversion[i].Reset();
+}
+
+void CCharsetConverter::resetSystemCharset(void)
+{
+ CInnerConverter::m_stdConversion[Utf8ToSystem].Reset();
+ CInnerConverter::m_stdConversion[SystemToUtf8].Reset();
+}
+
+void CCharsetConverter::resetUserCharset(void)
+{
+ CInnerConverter::m_stdConversion[UserCharsetToUtf8].Reset();
+ CInnerConverter::m_stdConversion[UserCharsetToUtf8].Reset();
+ CInnerConverter::m_stdConversion[Utf32ToUserCharset].Reset();
+ resetSubtitleCharset();
+ resetKaraokeCharset();
+}
+
+void CCharsetConverter::resetSubtitleCharset(void)
+{
+ CInnerConverter::m_stdConversion[SubtitleCharsetToUtf8].Reset();
+}
+
+void CCharsetConverter::resetKaraokeCharset(void)
+{
+}
+
+void CCharsetConverter::reinitCharsetsFromSettings(void)
+{
+ resetUserCharset(); // this will also reinit Subtitle and Karaoke charsets
+}
+
+bool CCharsetConverter::utf8ToUtf32(const std::string& utf8StringSrc, std::u32string& utf32StringDst, bool failOnBadChar /*= true*/)
+{
+ return CInnerConverter::stdConvert(Utf8ToUtf32, utf8StringSrc, utf32StringDst, failOnBadChar);
+}
+
+std::u32string CCharsetConverter::utf8ToUtf32(const std::string& utf8StringSrc, bool failOnBadChar /*= true*/)
+{
+ std::u32string converted;
+ utf8ToUtf32(utf8StringSrc, converted, failOnBadChar);
+ return converted;
+}
+
+bool CCharsetConverter::utf8ToUtf32Visual(const std::string& utf8StringSrc, std::u32string& utf32StringDst, bool bVisualBiDiFlip /*= false*/, bool forceLTRReadingOrder /*= false*/, bool failOnBadChar /*= false*/)
+{
+ if (bVisualBiDiFlip)
+ {
+ std::u32string converted;
+ if (!CInnerConverter::stdConvert(Utf8ToUtf32, utf8StringSrc, converted, failOnBadChar))
+ return false;
+
+ return CInnerConverter::logicalToVisualBiDi(converted, utf32StringDst, forceLTRReadingOrder ? FRIBIDI_TYPE_LTR : FRIBIDI_TYPE_PDF, failOnBadChar);
+ }
+ return CInnerConverter::stdConvert(Utf8ToUtf32, utf8StringSrc, utf32StringDst, failOnBadChar);
+}
+
+bool CCharsetConverter::utf32ToUtf8(const std::u32string& utf32StringSrc, std::string& utf8StringDst, bool failOnBadChar /*= true*/)
+{
+ return CInnerConverter::stdConvert(Utf32ToUtf8, utf32StringSrc, utf8StringDst, failOnBadChar);
+}
+
+std::string CCharsetConverter::utf32ToUtf8(const std::u32string& utf32StringSrc, bool failOnBadChar /*= false*/)
+{
+ std::string converted;
+ utf32ToUtf8(utf32StringSrc, converted, failOnBadChar);
+ return converted;
+}
+
+bool CCharsetConverter::utf32ToW(const std::u32string& utf32StringSrc, std::wstring& wStringDst, bool failOnBadChar /*= true*/)
+{
+#ifdef WCHAR_IS_UCS_4
+ wStringDst.assign((const wchar_t*)utf32StringSrc.c_str(), utf32StringSrc.length());
+ return true;
+#else // !WCHAR_IS_UCS_4
+ return CInnerConverter::stdConvert(Utf32ToW, utf32StringSrc, wStringDst, failOnBadChar);
+#endif // !WCHAR_IS_UCS_4
+}
+
+bool CCharsetConverter::utf32logicalToVisualBiDi(const std::u32string& logicalStringSrc, std::u32string& visualStringDst, bool forceLTRReadingOrder /*= false*/, bool failOnBadString /*= false*/)
+{
+ return CInnerConverter::logicalToVisualBiDi(logicalStringSrc, visualStringDst, forceLTRReadingOrder ? FRIBIDI_TYPE_LTR : FRIBIDI_TYPE_PDF, failOnBadString);
+}
+
+bool CCharsetConverter::wToUtf32(const std::wstring& wStringSrc, std::u32string& utf32StringDst, bool failOnBadChar /*= true*/)
+{
+#ifdef WCHAR_IS_UCS_4
+ /* UCS-4 is almost equal to UTF-32, but UTF-32 has strict limits on possible values, while UCS-4 is usually unchecked.
+ * With this "conversion" we ensure that output will be valid UTF-32 string. */
+#endif
+ return CInnerConverter::stdConvert(WToUtf32, wStringSrc, utf32StringDst, failOnBadChar);
+}
+
+// The bVisualBiDiFlip forces a flip of characters for hebrew/arabic languages, only set to false if the flipping
+// of the string is already made or the string is not displayed in the GUI
+bool CCharsetConverter::utf8ToW(const std::string& utf8StringSrc, std::wstring& wStringDst, bool bVisualBiDiFlip /*= true*/,
+ bool forceLTRReadingOrder /*= false*/, bool failOnBadChar /*= false*/)
+{
+ // Try to flip hebrew/arabic characters, if any
+ if (bVisualBiDiFlip)
+ {
+ wStringDst.clear();
+ std::u32string utf32str;
+ if (!CInnerConverter::stdConvert(Utf8ToUtf32, utf8StringSrc, utf32str, failOnBadChar))
+ return false;
+
+ std::u32string utf32flipped;
+ const bool bidiResult = CInnerConverter::logicalToVisualBiDi(utf32str, utf32flipped, forceLTRReadingOrder ? FRIBIDI_TYPE_LTR : FRIBIDI_TYPE_PDF, failOnBadChar);
+
+ return CInnerConverter::stdConvert(Utf32ToW, utf32flipped, wStringDst, failOnBadChar) && bidiResult;
+ }
+
+ return CInnerConverter::stdConvert(Utf8toW, utf8StringSrc, wStringDst, failOnBadChar);
+}
+
+bool CCharsetConverter::subtitleCharsetToUtf8(const std::string& stringSrc, std::string& utf8StringDst)
+{
+ return CInnerConverter::stdConvert(SubtitleCharsetToUtf8, stringSrc, utf8StringDst, false);
+}
+
+bool CCharsetConverter::fromW(const std::wstring& wStringSrc,
+ std::string& stringDst, const std::string& enc)
+{
+ return CInnerConverter::customConvert(WCHAR_CHARSET, enc, wStringSrc, stringDst);
+}
+
+bool CCharsetConverter::toW(const std::string& stringSrc,
+ std::wstring& wStringDst, const std::string& enc)
+{
+ return CInnerConverter::customConvert(enc, WCHAR_CHARSET, stringSrc, wStringDst);
+}
+
+bool CCharsetConverter::utf8ToStringCharset(const std::string& utf8StringSrc, std::string& stringDst)
+{
+ return CInnerConverter::stdConvert(Utf8ToUserCharset, utf8StringSrc, stringDst);
+}
+
+bool CCharsetConverter::utf8ToStringCharset(std::string& stringSrcDst)
+{
+ std::string strSrc(stringSrcDst);
+ return utf8ToStringCharset(strSrc, stringSrcDst);
+}
+
+bool CCharsetConverter::ToUtf8(const std::string& strSourceCharset, const std::string& stringSrc, std::string& utf8StringDst, bool failOnBadChar /*= false*/)
+{
+ if (strSourceCharset == "UTF-8")
+ { // simple case - no conversion necessary
+ utf8StringDst = stringSrc;
+ return true;
+ }
+
+ return CInnerConverter::customConvert(strSourceCharset, "UTF-8", stringSrc, utf8StringDst, failOnBadChar);
+}
+
+bool CCharsetConverter::utf8To(const std::string& strDestCharset, const std::string& utf8StringSrc, std::string& stringDst)
+{
+ if (strDestCharset == "UTF-8")
+ { // simple case - no conversion necessary
+ stringDst = utf8StringSrc;
+ return true;
+ }
+
+ return CInnerConverter::customConvert(UTF8_SOURCE, strDestCharset, utf8StringSrc, stringDst);
+}
+
+bool CCharsetConverter::utf8To(const std::string& strDestCharset, const std::string& utf8StringSrc, std::u16string& utf16StringDst)
+{
+ return CInnerConverter::customConvert(UTF8_SOURCE, strDestCharset, utf8StringSrc, utf16StringDst);
+}
+
+bool CCharsetConverter::utf8To(const std::string& strDestCharset, const std::string& utf8StringSrc, std::u32string& utf32StringDst)
+{
+ return CInnerConverter::customConvert(UTF8_SOURCE, strDestCharset, utf8StringSrc, utf32StringDst);
+}
+
+bool CCharsetConverter::unknownToUTF8(std::string& stringSrcDst)
+{
+ std::string source(stringSrcDst);
+ return unknownToUTF8(source, stringSrcDst);
+}
+
+bool CCharsetConverter::unknownToUTF8(const std::string& stringSrc, std::string& utf8StringDst, bool failOnBadChar /*= false*/)
+{
+ // checks whether it's utf8 already, and if not converts using the sourceCharset if given, else the string charset
+ if (CUtf8Utils::isValidUtf8(stringSrc))
+ {
+ utf8StringDst = stringSrc;
+ return true;
+ }
+ return CInnerConverter::stdConvert(UserCharsetToUtf8, stringSrc, utf8StringDst, failOnBadChar);
+}
+
+bool CCharsetConverter::wToUTF8(const std::wstring& wStringSrc, std::string& utf8StringDst, bool failOnBadChar /*= false*/)
+{
+ return CInnerConverter::stdConvert(WtoUtf8, wStringSrc, utf8StringDst, failOnBadChar);
+}
+
+bool CCharsetConverter::utf16BEtoUTF8(const std::u16string& utf16StringSrc, std::string& utf8StringDst)
+{
+ return CInnerConverter::stdConvert(Utf16BEtoUtf8, utf16StringSrc, utf8StringDst);
+}
+
+bool CCharsetConverter::utf16LEtoUTF8(const std::u16string& utf16StringSrc,
+ std::string& utf8StringDst)
+{
+ return CInnerConverter::stdConvert(Utf16LEtoUtf8, utf16StringSrc, utf8StringDst);
+}
+
+bool CCharsetConverter::ucs2ToUTF8(const std::u16string& ucs2StringSrc, std::string& utf8StringDst)
+{
+ return CInnerConverter::stdConvert(Ucs2CharsetToUtf8, ucs2StringSrc,utf8StringDst);
+}
+
+bool CCharsetConverter::utf16LEtoW(const std::u16string& utf16String, std::wstring& wString)
+{
+ return CInnerConverter::stdConvert(Utf16LEtoW, utf16String, wString);
+}
+
+bool CCharsetConverter::utf32ToStringCharset(const std::u32string& utf32StringSrc, std::string& stringDst)
+{
+ return CInnerConverter::stdConvert(Utf32ToUserCharset, utf32StringSrc, stringDst);
+}
+
+bool CCharsetConverter::utf8ToSystem(std::string& stringSrcDst, bool failOnBadChar /*= false*/)
+{
+ std::string strSrc(stringSrcDst);
+ return CInnerConverter::stdConvert(Utf8ToSystem, strSrc, stringSrcDst, failOnBadChar);
+}
+
+bool CCharsetConverter::systemToUtf8(const std::string& sysStringSrc, std::string& utf8StringDst, bool failOnBadChar /*= false*/)
+{
+ return CInnerConverter::stdConvert(SystemToUtf8, sysStringSrc, utf8StringDst, failOnBadChar);
+}
+
+bool CCharsetConverter::utf8logicalToVisualBiDi(const std::string& utf8StringSrc, std::string& utf8StringDst, bool failOnBadString /*= false*/)
+{
+ utf8StringDst.clear();
+ std::u32string utf32flipped;
+ if (!utf8ToUtf32Visual(utf8StringSrc, utf32flipped, true, true, failOnBadString))
+ return false;
+
+ return CInnerConverter::stdConvert(Utf32ToUtf8, utf32flipped, utf8StringDst, failOnBadString);
+}
+
+void CCharsetConverter::SettingOptionsCharsetsFiller(const CSetting* setting, std::vector< std::pair<std::string, std::string> >& list, std::string& current, void *data)
+{
+ std::vector<std::string> vecCharsets = g_charsetConverter.getCharsetLabels();
+ sort(vecCharsets.begin(), vecCharsets.end(), sortstringbyname());
+
+ list.push_back(make_pair(g_localizeStrings.Get(13278), "DEFAULT")); // "Default"
+ for (int i = 0; i < (int) vecCharsets.size(); ++i)
+ list.push_back(make_pair(vecCharsets[i], g_charsetConverter.getCharsetNameByLabel(vecCharsets[i])));
+}
diff --git a/src/utils/CharsetConverter.h b/src/utils/CharsetConverter.h
new file mode 100644
index 0000000000..b3453c101f
--- /dev/null
+++ b/src/utils/CharsetConverter.h
@@ -0,0 +1,179 @@
+#ifndef CCHARSET_CONVERTER
+#define CCHARSET_CONVERTER
+
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "settings/lib/ISettingCallback.h"
+#include "threads/CriticalSection.h"
+#include "utils/GlobalsHandling.h"
+#include "utils/uXstrings.h"
+
+#include <string>
+#include <vector>
+
+class CSetting;
+
+class CCharsetConverter : public ISettingCallback
+{
+public:
+ CCharsetConverter();
+
+ virtual void OnSettingChanged(const CSetting* setting);
+
+ static void reset();
+ static void resetSystemCharset();
+ static void reinitCharsetsFromSettings(void);
+
+ static void clear();
+
+ /**
+ * Convert UTF-8 string to UTF-32 string.
+ * No RTL logical-visual transformation is performed.
+ * @param utf8StringSrc is source UTF-8 string to convert
+ * @param utf32StringDst is output UTF-32 string, empty on any error
+ * @param failOnBadChar if set to true function will fail on invalid character,
+ * otherwise invalid character will be skipped
+ * @return true on successful conversion, false on any error
+ */
+ static bool utf8ToUtf32(const std::string& utf8StringSrc, std::u32string& utf32StringDst, bool failOnBadChar = true);
+ /**
+ * Convert UTF-8 string to UTF-32 string.
+ * No RTL logical-visual transformation is performed.
+ * @param utf8StringSrc is source UTF-8 string to convert
+ * @param failOnBadChar if set to true function will fail on invalid character,
+ * otherwise invalid character will be skipped
+ * @return converted string on successful conversion, empty string on any error
+ */
+ static std::u32string utf8ToUtf32(const std::string& utf8StringSrc, bool failOnBadChar = true);
+ /**
+ * Convert UTF-8 string to UTF-32 string.
+ * RTL logical-visual transformation is optionally performed.
+ * Use it for readable text, GUI strings etc.
+ * @param utf8StringSrc is source UTF-8 string to convert
+ * @param utf32StringDst is output UTF-32 string, empty on any error
+ * @param bVisualBiDiFlip allow RTL visual-logical transformation if set to true, must be set
+ * to false is logical-visual transformation is already done
+ * @param forceLTRReadingOrder force LTR reading order
+ * @param failOnBadChar if set to true function will fail on invalid character,
+ * otherwise invalid character will be skipped
+ * @return true on successful conversion, false on any error
+ */
+ static bool utf8ToUtf32Visual(const std::string& utf8StringSrc, std::u32string& utf32StringDst, bool bVisualBiDiFlip = false, bool forceLTRReadingOrder = false, bool failOnBadChar = false);
+ /**
+ * Convert UTF-32 string to UTF-8 string.
+ * No RTL visual-logical transformation is performed.
+ * @param utf32StringSrc is source UTF-32 string to convert
+ * @param utf8StringDst is output UTF-8 string, empty on any error
+ * @param failOnBadChar if set to true function will fail on invalid character,
+ * otherwise invalid character will be skipped
+ * @return true on successful conversion, false on any error
+ */
+ static bool utf32ToUtf8(const std::u32string& utf32StringSrc, std::string& utf8StringDst, bool failOnBadChar = false);
+ /**
+ * Convert UTF-32 string to UTF-8 string.
+ * No RTL visual-logical transformation is performed.
+ * @param utf32StringSrc is source UTF-32 string to convert
+ * @param failOnBadChar if set to true function will fail on invalid character,
+ * otherwise invalid character will be skipped
+ * @return converted string on successful conversion, empty string on any error
+ */
+ static std::string utf32ToUtf8(const std::u32string& utf32StringSrc, bool failOnBadChar = false);
+ /**
+ * Convert UTF-32 string to wchar_t string (wstring).
+ * No RTL visual-logical transformation is performed.
+ * @param utf32StringSrc is source UTF-32 string to convert
+ * @param wStringDst is output wchar_t string, empty on any error
+ * @param failOnBadChar if set to true function will fail on invalid character,
+ * otherwise invalid character will be skipped
+ * @return true on successful conversion, false on any error
+ */
+ static bool utf32ToW(const std::u32string& utf32StringSrc, std::wstring& wStringDst, bool failOnBadChar = false);
+ /**
+ * Perform logical to visual flip.
+ * @param logicalStringSrc is source string with logical characters order
+ * @param visualStringDst is output string with visual characters order, empty on any error
+ * @param forceLTRReadingOrder force LTR reading order
+ * @return true on success, false otherwise
+ */
+ static bool utf32logicalToVisualBiDi(const std::u32string& logicalStringSrc, std::u32string& visualStringDst, bool forceLTRReadingOrder = false, bool failOnBadString = false);
+ /**
+ * Strictly convert wchar_t string (wstring) to UTF-32 string.
+ * No RTL visual-logical transformation is performed.
+ * @param wStringSrc is source wchar_t string to convert
+ * @param utf32StringDst is output UTF-32 string, empty on any error
+ * @param failOnBadChar if set to true function will fail on invalid character,
+ * otherwise invalid character will be skipped
+ * @return true on successful conversion, false on any error
+ */
+ static bool wToUtf32(const std::wstring& wStringSrc, std::u32string& utf32StringDst, bool failOnBadChar = false);
+
+ static bool utf8ToW(const std::string& utf8StringSrc, std::wstring& wStringDst,
+ bool bVisualBiDiFlip = true, bool forceLTRReadingOrder = false,
+ bool failOnBadChar = false);
+
+ static bool utf16LEtoW(const std::u16string& utf16String, std::wstring& wString);
+
+ static bool subtitleCharsetToUtf8(const std::string& stringSrc, std::string& utf8StringDst);
+
+ static bool utf8ToStringCharset(const std::string& utf8StringSrc, std::string& stringDst);
+
+ static bool utf8ToStringCharset(std::string& stringSrcDst);
+ static bool utf8ToSystem(std::string& stringSrcDst, bool failOnBadChar = false);
+ static bool systemToUtf8(const std::string& sysStringSrc, std::string& utf8StringDst, bool failOnBadChar = false);
+
+ static bool utf8To(const std::string& strDestCharset, const std::string& utf8StringSrc, std::string& stringDst);
+ static bool utf8To(const std::string& strDestCharset, const std::string& utf8StringSrc, std::u16string& utf16StringDst);
+ static bool utf8To(const std::string& strDestCharset, const std::string& utf8StringSrc, std::u32string& utf32StringDst);
+
+ static bool ToUtf8(const std::string& strSourceCharset, const std::string& stringSrc, std::string& utf8StringDst, bool failOnBadChar = false);
+
+ static bool wToUTF8(const std::wstring& wStringSrc, std::string& utf8StringDst, bool failOnBadChar = false);
+ static bool utf16BEtoUTF8(const std::u16string& utf16StringSrc, std::string& utf8StringDst);
+ static bool utf16LEtoUTF8(const std::u16string& utf16StringSrc, std::string& utf8StringDst);
+ static bool ucs2ToUTF8(const std::u16string& ucs2StringSrc, std::string& utf8StringDst);
+
+ static bool utf8logicalToVisualBiDi(const std::string& utf8StringSrc, std::string& utf8StringDst, bool failOnBadString = false);
+
+ static bool utf32ToStringCharset(const std::u32string& utf32StringSrc, std::string& stringDst);
+
+ static std::vector<std::string> getCharsetLabels();
+ static std::string getCharsetLabelByName(const std::string& charsetName);
+ static std::string getCharsetNameByLabel(const std::string& charsetLabel);
+
+ static bool unknownToUTF8(std::string& stringSrcDst);
+ static bool unknownToUTF8(const std::string& stringSrc, std::string& utf8StringDst, bool failOnBadChar = false);
+
+ static bool toW(const std::string& stringSrc, std::wstring& wStringDst, const std::string& enc);
+ static bool fromW(const std::wstring& wStringSrc, std::string& stringDst, const std::string& enc);
+
+ static void SettingOptionsCharsetsFiller(const CSetting* setting, std::vector< std::pair<std::string, std::string> >& list, std::string& current, void *data);
+private:
+ static void resetUserCharset(void);
+ static void resetSubtitleCharset(void);
+ static void resetKaraokeCharset(void);
+
+ static const int m_Utf8CharMinSize, m_Utf8CharMaxSize;
+ class CInnerConverter;
+};
+
+XBMC_GLOBAL(CCharsetConverter,g_charsetConverter);
+
+#endif
diff --git a/src/utils/CharsetDetection.cpp b/src/utils/CharsetDetection.cpp
new file mode 100644
index 0000000000..810b01f07f
--- /dev/null
+++ b/src/utils/CharsetDetection.cpp
@@ -0,0 +1,649 @@
+/*
+* Copyright (C) 2013 Team XBMC
+* http://xbmc.org
+*
+* This Program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2, or (at your option)
+* any later version.
+*
+* This Program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with XBMC; see the file COPYING. If not, see
+* <http://www.gnu.org/licenses/>.
+*
+*/
+
+#include <algorithm>
+#include "CharsetDetection.h"
+#include "utils/CharsetConverter.h"
+#include "utils/StringUtils.h"
+#include "utils/Utf8Utils.h"
+#include "LangInfo.h"
+#include "utils/log.h"
+
+/* XML declaration can be virtually any size (with many-many whitespaces)
+ * but for in real world we don't need to process megabytes of data
+ * so limit search for XML declaration to reasonable value */
+const size_t CCharsetDetection::m_XmlDeclarationMaxLength = 250;
+
+/* According to http://www.w3.org/TR/2013/CR-html5-20130806/single-page.html#charset
+ * encoding must be placed in first 1024 bytes of document */
+const size_t CCharsetDetection::m_HtmlCharsetEndSearchPos = 1024;
+
+/* According to http://www.w3.org/TR/2013/CR-html5-20130806/single-page.html#space-character
+ * tab, LF, FF, CR or space can be used as whitespace */
+const std::string CCharsetDetection::m_HtmlWhitespaceChars("\x09\x0A\x0C\x0D\x20"); // tab, LF, FF, CR and space
+
+std::string CCharsetDetection::GetBomEncoding(const char* const content, const size_t contentLength)
+{
+ if (contentLength < 2)
+ return "";
+ if (content[0] == (char)0xFE && content[1] == (char)0xFF)
+ return "UTF-16BE";
+ if (contentLength >= 4 && content[0] == (char)0xFF && content[1] == (char)0xFE && content[2] == (char)0x00 && content[3] == (char)0x00)
+ return "UTF-32LE"; /* first two bytes are same for UTF-16LE and UTF-32LE, so first check for full UTF-32LE mark */
+ if (content[0] == (char)0xFF && content[1] == (char)0xFE)
+ return "UTF-16LE";
+ if (contentLength < 3)
+ return "";
+ if (content[0] == (char)0xEF && content[1] == (char)0xBB && content[2] == (char)0xBF)
+ return "UTF-8";
+ if (contentLength < 4)
+ return "";
+ if (content[0] == (char)0x00 && content[1] == (char)0x00 && content[2] == (char)0xFE && content[3] == (char)0xFF)
+ return "UTF-32BE";
+ if (contentLength >= 5 && content[0] == (char)0x2B && content[1] == (char)0x2F && content[2] == (char)0x76 &&
+ (content[4] == (char)0x32 || content[4] == (char)0x39 || content[4] == (char)0x2B || content[4] == (char)0x2F))
+ return "UTF-7";
+ if (content[0] == (char)0x84 && content[1] == (char)0x31 && content[2] == (char)0x95 && content[3] == (char)0x33)
+ return "GB18030";
+
+ return "";
+}
+
+bool CCharsetDetection::DetectXmlEncoding(const char* const xmlContent, const size_t contentLength, std::string& detectedEncoding)
+{
+ detectedEncoding.clear();
+
+ if (contentLength < 2)
+ return false; // too short for any detection
+
+ /* Byte Order Mark has priority over "encoding=" parameter */
+ detectedEncoding = GetBomEncoding(xmlContent, contentLength);
+ if (!detectedEncoding.empty())
+ return true;
+
+ /* try to read encoding from XML declaration */
+ if (GetXmlEncodingFromDeclaration(xmlContent, contentLength, detectedEncoding))
+ {
+ StringUtils::ToUpper(detectedEncoding);
+
+ /* make some safety checks */
+ if (detectedEncoding == "UTF-8")
+ return true; // fast track for most common case
+
+ if (StringUtils::StartsWith(detectedEncoding, "UCS-") || StringUtils::StartsWith(detectedEncoding, "UTF-"))
+ {
+ if (detectedEncoding == "UTF-7")
+ return true;
+
+ /* XML declaration was detected in UTF-8 mode (by 'GetXmlEncodingFromDeclaration') so we know
+ * that text in single byte encoding, but declaration itself wrongly specify multibyte encoding */
+ detectedEncoding.clear();
+ return false;
+ }
+ return true;
+ }
+
+ /* try to detect basic encoding */
+ std::string guessedEncoding;
+ if (!GuessXmlEncoding(xmlContent, contentLength, guessedEncoding))
+ return false; /* can't detect any encoding */
+
+ /* have some guessed encoding, try to use it */
+ std::string convertedXml;
+ /* use 'm_XmlDeclarationMaxLength * 4' below for UTF-32-like encodings */
+ if (!g_charsetConverter.ToUtf8(guessedEncoding, std::string(xmlContent, std::min(contentLength, m_XmlDeclarationMaxLength * 4)), convertedXml)
+ || convertedXml.empty())
+ return false; /* can't convert, guessed encoding is wrong */
+
+ /* text converted, hopefully at least XML declaration is in UTF-8 now */
+ std::string declaredEncoding;
+ /* try to read real encoding from converted XML declaration */
+ if (!GetXmlEncodingFromDeclaration(convertedXml.c_str(), convertedXml.length(), declaredEncoding))
+ { /* did not find real encoding in XML declaration, use guessed encoding */
+ detectedEncoding = guessedEncoding;
+ return true;
+ }
+
+ /* found encoding in converted XML declaration, we know correct endianness and number of bytes per char */
+ /* make some safety checks */
+ StringUtils::ToUpper(declaredEncoding);
+ if (declaredEncoding == guessedEncoding)
+ return true;
+
+ if (StringUtils::StartsWith(guessedEncoding, "UCS-4"))
+ {
+ if (declaredEncoding.length() < 5 ||
+ (!StringUtils::StartsWith(declaredEncoding, "UTF-32") && !StringUtils::StartsWith(declaredEncoding, "UCS-4")))
+ { /* Guessed encoding was correct because we can convert and read XML declaration, but declaration itself is wrong (not 4-bytes encoding) */
+ detectedEncoding = guessedEncoding;
+ return true;
+ }
+ }
+ else if (StringUtils::StartsWith(guessedEncoding, "UTF-16"))
+ {
+ if (declaredEncoding.length() < 5 ||
+ (!StringUtils::StartsWith(declaredEncoding, "UTF-16") && !StringUtils::StartsWith(declaredEncoding, "UCS-2")))
+ { /* Guessed encoding was correct because we can read XML declaration, but declaration is wrong (not 2-bytes encoding) */
+ detectedEncoding = guessedEncoding;
+ return true;
+ }
+ }
+
+ if (StringUtils::StartsWith(guessedEncoding, "UCS-4") || StringUtils::StartsWith(guessedEncoding, "UTF-16"))
+ {
+ /* Check endianness in declared encoding. We already know correct endianness as XML declaration was detected after conversion. */
+ /* Guessed UTF/UCS encoding always ends with endianness */
+ std::string guessedEndianness(guessedEncoding, guessedEncoding.length() - 2);
+
+ if (!StringUtils::EndsWith(declaredEncoding, "BE") && !StringUtils::EndsWith(declaredEncoding, "LE")) /* Declared encoding without endianness */
+ detectedEncoding = declaredEncoding + guessedEndianness; /* add guessed endianness */
+ else if (!StringUtils::EndsWith(declaredEncoding, guessedEndianness)) /* Wrong endianness in declared encoding */
+ detectedEncoding = declaredEncoding.substr(0, declaredEncoding.length() - 2) + guessedEndianness; /* replace endianness by guessed endianness */
+ else
+ detectedEncoding = declaredEncoding; /* declared encoding with correct endianness */
+
+ return true;
+ }
+ else if (StringUtils::StartsWith(guessedEncoding, "EBCDIC"))
+ {
+ if (declaredEncoding.find("EBCDIC") != std::string::npos)
+ detectedEncoding = declaredEncoding; /* Declared encoding is some specific EBCDIC encoding */
+ else
+ detectedEncoding = guessedEncoding;
+
+ return true;
+ }
+
+ /* should be unreachable */
+ return false;
+}
+
+bool CCharsetDetection::GetXmlEncodingFromDeclaration(const char* const xmlContent, const size_t contentLength, std::string& declaredEncoding)
+{
+ // following code is std::string-processing analog of regular expression-processing
+ // regular expression: "<\\?xml([ \n\r\t]+[^ \n\t\r>]+)*[ \n\r\t]+encoding[ \n\r\t]*=[ \n\r\t]*('[^ \n\t\r>']+'|\"[^ \n\t\r>\"]+\")"
+ // on win32 x86 machine regular expression is slower that std::string 20-40 times and can slowdown XML processing for several times
+ // seems that this regular expression is too slow due to many variable length parts, regexp for '&amp;'-fixing is much faster
+
+ declaredEncoding.clear();
+
+ // avoid extra large search
+ std::string strXml(xmlContent, std::min(contentLength, m_XmlDeclarationMaxLength));
+
+ size_t pos = strXml.find("<?xml");
+ if (pos == std::string::npos || pos + 6 > strXml.length() || pos > strXml.find('<'))
+ return false; // no "<?xml" declaration, "<?xml" is not first element or "<?xml" is incomplete
+
+ pos += 5; // 5 is length of "<?xml"
+
+ const size_t declLength = std::min(std::min(m_XmlDeclarationMaxLength, contentLength - pos), strXml.find('>', pos) - pos);
+ const std::string xmlDecl(xmlContent + pos, declLength);
+ const char* const xmlDeclC = xmlDecl.c_str(); // for faster processing of [] and for null-termination
+
+ static const char* const whiteSpaceChars = " \n\r\t"; // according to W3C Recommendation for XML, any of them can be used as separator
+ pos = 0;
+
+ while (pos + 12 <= declLength) // 12 is minimal length of "encoding='x'"
+ {
+ pos = xmlDecl.find_first_of(whiteSpaceChars, pos);
+ if (pos == std::string::npos)
+ return false; // no " encoding=" in declaration
+
+ pos = xmlDecl.find_first_not_of(whiteSpaceChars, pos);
+ if (pos == std::string::npos)
+ return false; // no "encoding=" in declaration
+
+ if (xmlDecl.compare(pos, 8, "encoding", 8) != 0)
+ continue; // not "encoding" parameter
+ pos += 8; // length of "encoding"
+
+ if (xmlDeclC[pos] == ' ' || xmlDeclC[pos] == '\n' || xmlDeclC[pos] == '\r' || xmlDeclC[pos] == '\t') // no buffer overrun as string is null-terminated
+ {
+ pos = xmlDecl.find_first_not_of(whiteSpaceChars, pos);
+ if (pos == std::string::npos)
+ return false; // this " encoding" is incomplete, only whitespace chars remains
+ }
+ if (xmlDeclC[pos] != '=')
+ { // "encoding" without "=", try to find other
+ pos--; // step back to whitespace
+ continue;
+ }
+
+ pos++; // skip '='
+ if (xmlDeclC[pos] == ' ' || xmlDeclC[pos] == '\n' || xmlDeclC[pos] == '\r' || xmlDeclC[pos] == '\t') // no buffer overrun as string is null-terminated
+ {
+ pos = xmlDecl.find_first_not_of(whiteSpaceChars, pos);
+ if (pos == std::string::npos)
+ return false; // this " encoding" is incomplete, only whitespace chars remains
+ }
+ size_t encNameEndPos;
+ if (xmlDeclC[pos] == '"')
+ encNameEndPos = xmlDecl.find('"', ++pos);
+ else if (xmlDeclC[pos] == '\'')
+ encNameEndPos = xmlDecl.find('\'', ++pos);
+ else
+ continue; // no quote or double quote after 'encoding=', try to find other
+
+ if (encNameEndPos != std::string::npos)
+ {
+ declaredEncoding.assign(xmlDecl, pos, encNameEndPos - pos);
+ return true;
+ }
+ // no closing quote or double quote after 'encoding="x', try to find other
+ }
+
+ return false;
+}
+
+bool CCharsetDetection::GuessXmlEncoding(const char* const xmlContent, const size_t contentLength, std::string& supposedEncoding)
+{
+ supposedEncoding.clear();
+ if (contentLength < 4)
+ return false; // too little data to guess
+
+ if (xmlContent[0] == 0 && xmlContent[1] == 0 && xmlContent[2] == 0 && xmlContent[3] == (char)0x3C) // '<' == '00 00 00 3C' in UCS-4 (UTF-32) big-endian
+ supposedEncoding = "UCS-4BE"; // use UCS-4 according to W3C recommendation
+ else if (xmlContent[0] == (char)0x3C && xmlContent[1] == 0 && xmlContent[2] == 0 && xmlContent[3] == 0) // '<' == '3C 00 00 00' in UCS-4 (UTF-32) little-endian
+ supposedEncoding = "UCS-4LE"; // use UCS-4 according to W3C recommendation
+ else if (xmlContent[0] == 0 && xmlContent[1] == (char)0x3C && xmlContent[2] == 0 && xmlContent[3] == (char)0x3F) // "<?" == "00 3C 00 3F" in UTF-16 (UCS-2) big-endian
+ supposedEncoding = "UTF-16BE";
+ else if (xmlContent[0] == (char)0x3C && xmlContent[1] == 0 && xmlContent[2] == (char)0x3F && xmlContent[3] == 0) // "<?" == "3C 00 3F 00" in UTF-16 (UCS-2) little-endian
+ supposedEncoding = "UTF-16LE";
+ else if (xmlContent[0] == (char)0x4C && xmlContent[1] == (char)0x6F && xmlContent[2] == (char)0xA7 && xmlContent[3] == (char)0x94) // "<?xm" == "4C 6F A7 94" in most EBCDIC encodings
+ supposedEncoding = "EBCDIC-CP-US"; // guessed value, real value must be read from declaration
+ else
+ return false;
+
+ return true;
+}
+
+bool CCharsetDetection::ConvertHtmlToUtf8(const std::string& htmlContent, std::string& converted, const std::string& serverReportedCharset, std::string& usedHtmlCharset)
+{
+ converted.clear();
+ usedHtmlCharset.clear();
+ if (htmlContent.empty())
+ {
+ usedHtmlCharset = "UTF-8"; // any charset can be used for empty content, use UTF-8 as default
+ return false;
+ }
+
+ // this is relaxed implementation of http://www.w3.org/TR/2013/CR-html5-20130806/single-page.html#determining-the-character-encoding
+
+ // try to get charset from Byte Order Mark
+ std::string bomCharset(GetBomEncoding(htmlContent));
+ if (checkConversion(bomCharset, htmlContent, converted))
+ {
+ usedHtmlCharset = bomCharset;
+ return true;
+ }
+
+ // try charset from HTTP header (or from other out-of-band source)
+ if (checkConversion(serverReportedCharset, htmlContent, converted))
+ {
+ usedHtmlCharset = serverReportedCharset;
+ return true;
+ }
+
+ // try to find charset in HTML
+ std::string declaredCharset(GetHtmlEncodingFromHead(htmlContent));
+ if (!declaredCharset.empty())
+ {
+ if (declaredCharset.compare(0, 3, "UTF", 3) == 0)
+ declaredCharset = "UTF-8"; // charset string was found in singlebyte mode, charset can't be multibyte encoding
+ if (checkConversion(declaredCharset, htmlContent, converted))
+ {
+ usedHtmlCharset = declaredCharset;
+ return true;
+ }
+ }
+
+ // try UTF-8 if not tried before
+ if (bomCharset != "UTF-8" && serverReportedCharset != "UTF-8" && declaredCharset != "UTF-8" && checkConversion("UTF-8", htmlContent, converted))
+ {
+ usedHtmlCharset = "UTF-8";
+ return false; // only guessed value
+ }
+
+ // try user charset
+ std::string userCharset(g_langInfo.GetGuiCharSet());
+ if (checkConversion(userCharset, htmlContent, converted))
+ {
+ usedHtmlCharset = userCharset;
+ return false; // only guessed value
+ }
+
+ // try WINDOWS-1252
+ if (checkConversion("WINDOWS-1252", htmlContent, converted))
+ {
+ usedHtmlCharset = "WINDOWS-1252";
+ return false; // only guessed value
+ }
+
+ // can't find exact charset
+ // use one of detected as fallback
+ if (!bomCharset.empty())
+ usedHtmlCharset = bomCharset;
+ else if (!serverReportedCharset.empty())
+ usedHtmlCharset = serverReportedCharset;
+ else if (!declaredCharset.empty())
+ usedHtmlCharset = declaredCharset;
+ else if (!userCharset.empty())
+ usedHtmlCharset = userCharset;
+ else
+ usedHtmlCharset = "WINDOWS-1252";
+
+ CLog::Log(LOGWARNING, "%s: Can't correctly convert to UTF-8 charset, converting as \"%s\"", __FUNCTION__, usedHtmlCharset.c_str());
+ g_charsetConverter.ToUtf8(usedHtmlCharset, htmlContent, converted, false);
+
+ return false;
+}
+
+bool CCharsetDetection::ConvertPlainTextToUtf8(const std::string& textContent, std::string& converted, const std::string& serverReportedCharset, std::string& usedCharset)
+{
+ converted.clear();
+ usedCharset.clear();
+ if (textContent.empty())
+ {
+ usedCharset = "UTF-8"; // any charset can be used for empty content, use UTF-8 as default
+ return true;
+ }
+
+ // try to get charset from Byte Order Mark
+ std::string bomCharset(GetBomEncoding(textContent));
+ if (checkConversion(bomCharset, textContent, converted))
+ {
+ usedCharset = bomCharset;
+ return true;
+ }
+
+ // try charset from HTTP header (or from other out-of-band source)
+ if (checkConversion(serverReportedCharset, textContent, converted))
+ {
+ usedCharset = serverReportedCharset;
+ return true;
+ }
+
+ // try UTF-8 if not tried before
+ if (bomCharset != "UTF-8" && serverReportedCharset != "UTF-8" && checkConversion("UTF-8", textContent, converted))
+ {
+ usedCharset = "UTF-8";
+ return true;
+ }
+
+ // try user charset
+ std::string userCharset(g_langInfo.GetGuiCharSet());
+ if (checkConversion(userCharset, textContent, converted))
+ {
+ usedCharset = userCharset;
+ return true;
+ }
+
+ // try system default charset
+ if (g_charsetConverter.systemToUtf8(textContent, converted, true))
+ {
+ usedCharset = "char"; // synonym to system charset
+ return true;
+ }
+
+ // try WINDOWS-1252
+ if (checkConversion("WINDOWS-1252", textContent, converted))
+ {
+ usedCharset = "WINDOWS-1252";
+ return true;
+ }
+
+ // can't find correct charset
+ // use one of detected as fallback
+ if (!serverReportedCharset.empty())
+ usedCharset = serverReportedCharset;
+ else if (!bomCharset.empty())
+ usedCharset = bomCharset;
+ else if (!userCharset.empty())
+ usedCharset = userCharset;
+ else
+ usedCharset = "WINDOWS-1252";
+
+ CLog::Log(LOGWARNING, "%s: Can't correctly convert to UTF-8 charset, converting as \"%s\"", __FUNCTION__, usedCharset.c_str());
+ g_charsetConverter.ToUtf8(usedCharset, textContent, converted, false);
+
+ return false;
+}
+
+
+bool CCharsetDetection::checkConversion(const std::string& srcCharset, const std::string& src, std::string& dst)
+{
+ if (srcCharset.empty())
+ return false;
+
+ if (srcCharset != "UTF-8")
+ {
+ if (g_charsetConverter.ToUtf8(srcCharset, src, dst, true))
+ return true;
+ }
+ else if (CUtf8Utils::isValidUtf8(src))
+ {
+ dst = src;
+ return true;
+ }
+
+ return false;
+}
+
+std::string CCharsetDetection::GetHtmlEncodingFromHead(const std::string& htmlContent)
+{
+ std::string smallerHtmlContent;
+ if (htmlContent.length() > 2 * m_HtmlCharsetEndSearchPos)
+ smallerHtmlContent.assign(htmlContent, 0, 2 * m_HtmlCharsetEndSearchPos); // use twice more bytes to search for charset for safety
+
+ const std::string& html = smallerHtmlContent.empty() ? htmlContent : smallerHtmlContent; // limit search
+ const char* const htmlC = html.c_str(); // for null-termination
+ const size_t len = html.length();
+
+ // this is an implementation of http://www.w3.org/TR/2013/CR-html5-20130806/single-page.html#prescan-a-byte-stream-to-determine-its-encoding
+ // labels in comments correspond to the labels in HTML5 standard
+ // note: opposite to standard, everything is converted to uppercase instead of lower case
+ size_t pos = 0;
+ while (pos < len) // "loop" label
+ {
+ if (html.compare(pos, 4, "<!--", 4) == 0)
+ {
+ pos = html.find("-->", pos + 2);
+ if (pos == std::string::npos)
+ return "";
+ pos += 2;
+ }
+ else if (htmlC[pos] == '<' && (htmlC[pos + 1] == 'm' || htmlC[pos + 1] == 'M') && (htmlC[pos + 2] == 'e' || htmlC[pos + 2] == 'E')
+ && (htmlC[pos + 3] == 't' || htmlC[pos + 3] == 'T') && (htmlC[pos + 4] == 'a' || htmlC[pos + 4] == 'A')
+ && (htmlC[pos + 5] == 0x09 || htmlC[pos + 5] == 0x0A || htmlC[pos + 5] == 0x0C || htmlC[pos + 5] == 0x0D || htmlC[pos + 5] == 0x20 || htmlC[pos + 5] == 0x2F))
+ { // this is case insensitive "<meta" and one of tab, LF, FF, CR, space or slash
+ pos += 5; // "pos" points to symbol after "<meta"
+ std::string attrName, attrValue;
+ bool gotPragma = false;
+ std::string contentCharset;
+ do // "attributes" label
+ {
+ pos = GetHtmlAttribute(html, pos, attrName, attrValue);
+ if (attrName == "HTTP-EQUIV" && attrValue == "CONTENT-TYPE")
+ gotPragma = true;
+ else if (attrName == "CONTENT")
+ contentCharset = ExtractEncodingFromHtmlMeta(attrValue);
+ else if (attrName == "CHARSET")
+ {
+ StringUtils::Trim(attrValue, m_HtmlWhitespaceChars.c_str()); // tab, LF, FF, CR, space
+ if (!attrValue.empty())
+ return attrValue;
+ }
+ } while (!attrName.empty() && pos < len);
+
+ // "processing" label
+ if (gotPragma && !contentCharset.empty())
+ return contentCharset;
+ }
+ else if (htmlC[pos] == '<' && ((htmlC[pos + 1] >= 'A' && htmlC[pos + 1] <= 'Z') || (htmlC[pos + 1] >= 'a' && htmlC[pos + 1] <= 'z')))
+ {
+ pos = html.find_first_of("\x09\x0A\x0C\x0D >", pos); // tab, LF, FF, CR, space or '>'
+ std::string attrName, attrValue;
+ do
+ {
+ pos = GetHtmlAttribute(html, pos, attrName, attrValue);
+ } while (pos < len && !attrName.empty());
+ }
+ else if (html.compare(pos, 2, "<!", 2) == 0 || html.compare(pos, 2, "</", 2) == 0 || html.compare(pos, 2, "<?", 2) == 0)
+ pos = html.find('>', pos);
+
+ if (pos == std::string::npos)
+ return "";
+
+ // "next byte" label
+ pos++;
+ }
+
+ return ""; // no charset was found
+}
+
+size_t CCharsetDetection::GetHtmlAttribute(const std::string& htmlContent, size_t pos, std::string& attrName, std::string& attrValue)
+{
+ attrName.clear();
+ attrValue.clear();
+ static const char* const htmlWhitespaceSlash = "\x09\x0A\x0C\x0D\x20\x2F"; // tab, LF, FF, CR, space or slash
+ const char* const htmlC = htmlContent.c_str();
+ const size_t len = htmlContent.length();
+
+ // this is an implementation of http://www.w3.org/TR/2013/CR-html5-20130806/single-page.html#concept-get-attributes-when-sniffing
+ // labels in comments correspond to the labels in HTML5 standard
+ // note: opposite to standard, everything is converted to uppercase instead of lower case
+ pos = htmlContent.find_first_not_of(htmlWhitespaceSlash, pos);
+ if (pos == std::string::npos || htmlC[pos] == '>')
+ return pos; // only white spaces or slashes up to the end of the htmlContent or no more attributes
+
+ while (pos < len && htmlC[pos] != '=')
+ {
+ const char chr = htmlC[pos];
+ if (chr == '/' || chr == '>')
+ return pos; // no attributes or empty attribute value
+ else if (m_HtmlWhitespaceChars.find(chr) != std::string::npos) // chr is one of whitespaces
+ {
+ pos = htmlContent.find_first_not_of(m_HtmlWhitespaceChars, pos); // "spaces" label
+ if (pos == std::string::npos || htmlC[pos] != '=')
+ return pos; // only white spaces up to the end or no attribute value
+ break;
+ }
+ else
+ appendCharAsAsciiUpperCase(attrName, chr);
+
+ pos++;
+ }
+
+ if (pos >= len)
+ return std::string::npos; // no '=', '/' or '>' were found up to the end of htmlContent
+
+ pos++; // advance pos to character after '='
+
+ pos = htmlContent.find_first_not_of(m_HtmlWhitespaceChars, pos); // "value" label
+ if (pos == std::string::npos)
+ return pos; // only white spaces remain in htmlContent
+
+ if (htmlC[pos] == '>')
+ return pos; // empty attribute value
+ else if (htmlC[pos] == '"' || htmlC[pos] == '\'')
+ {
+ const char qChr = htmlC[pos];
+ // "quote loop" label
+ while (++pos < len)
+ {
+ const char chr = htmlC[pos];
+ if (chr == qChr)
+ return pos + 1;
+ else
+ appendCharAsAsciiUpperCase(attrValue, chr);
+ }
+ return std::string::npos; // no closing quote is found
+ }
+
+ appendCharAsAsciiUpperCase(attrValue, htmlC[pos]);
+ pos++;
+
+ while (pos < len)
+ {
+ const char chr = htmlC[pos];
+ if (m_HtmlWhitespaceChars.find(chr) != std::string::npos || chr == '>')
+ return pos;
+ else
+ appendCharAsAsciiUpperCase(attrValue, chr);
+
+ pos++;
+ }
+
+ return std::string::npos; // rest of htmlContent was attribute value
+}
+
+std::string CCharsetDetection::ExtractEncodingFromHtmlMeta(std::string metaContent, size_t pos /*= 0*/)
+{
+ size_t len = metaContent.length();
+ if (pos >= len)
+ return "";
+
+ const char* const metaContentC = metaContent.c_str();
+
+ // this is an implementation of http://www.w3.org/TR/2013/CR-html5-20130806/single-page.html#algorithm-for-extracting-a-character-encoding-from-a-meta-element
+ // labels in comments correspond to the labels in HTML5 standard
+ // note: opposite to standard, case sensitive match is used as argument is always in uppercase
+ std::string charset;
+ do
+ {
+ // "loop" label
+ pos = metaContent.find("CHARSET", pos);
+ if (pos == std::string::npos)
+ return "";
+
+ pos = metaContent.find_first_not_of(m_HtmlWhitespaceChars, pos + 7); // '7' is the length of 'CHARSET'
+ if (pos != std::string::npos && metaContentC[pos] == '=')
+ {
+ pos = metaContent.find_first_not_of(m_HtmlWhitespaceChars, pos + 1);
+ if (pos != std::string::npos)
+ {
+ if (metaContentC[pos] == '\'' || metaContentC[pos] == '"')
+ {
+ const char qChr = metaContentC[pos];
+ pos++;
+ const size_t closeQpos = metaContent.find(qChr, pos);
+ if (closeQpos != std::string::npos)
+ charset.assign(metaContent, pos, closeQpos - pos);
+ }
+ else
+ charset.assign(metaContent, pos, metaContent.find("\x09\x0A\x0C\x0D ;", pos) - pos); // assign content up to the next tab, LF, FF, CR, space, semicolon or end of string
+ }
+ break;
+ }
+ } while (pos < len);
+
+ static const char* const htmlWhitespaceCharsC = m_HtmlWhitespaceChars.c_str();
+ StringUtils::Trim(charset, htmlWhitespaceCharsC);
+
+ return charset;
+}
+
+inline void CCharsetDetection::appendCharAsAsciiUpperCase(std::string& str, const char chr)
+{
+ if (chr >= 'a' && chr <= 'z')
+ str.push_back(chr - ('a' - 'A')); // convert to upper case
+ else
+ str.push_back(chr);
+}
diff --git a/src/utils/CharsetDetection.h b/src/utils/CharsetDetection.h
new file mode 100644
index 0000000000..171b460053
--- /dev/null
+++ b/src/utils/CharsetDetection.h
@@ -0,0 +1,106 @@
+#pragma once
+
+/*
+* Copyright (C) 2013 Team XBMC
+* http://xbmc.org
+*
+* This Program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2, or (at your option)
+* any later version.
+*
+* This Program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with XBMC; see the file COPYING. If not, see
+* <http://www.gnu.org/licenses/>.
+*
+*/
+
+#include <string>
+
+
+class CCharsetDetection
+{
+public:
+ /**
+ * Detect text encoding by Byte Order Mark
+ * Multibyte encodings (UTF-16/32) always ends with explicit endianness (LE/BE)
+ * @param content pointer to text to analyze
+ * @param contentLength length of text
+ * @return detected encoding or empty string if BOM not detected
+ */
+ static std::string GetBomEncoding(const char* const content, const size_t contentLength);
+ /**
+ * Detect text encoding by Byte Order Mark
+ * Multibyte encodings (UTF-16/32) always ends with explicit endianness (LE/BE)
+ * @param content the text to analyze
+ * @return detected encoding or empty string if BOM not detected
+ */
+ static inline std::string GetBomEncoding(const std::string& content)
+ { return GetBomEncoding(content.c_str(), content.length()); }
+
+ static inline bool DetectXmlEncoding(const std::string& xmlContent, std::string& detectedEncoding)
+ { return DetectXmlEncoding(xmlContent.c_str(), xmlContent.length(), detectedEncoding); }
+
+ static bool DetectXmlEncoding(const char* const xmlContent, const size_t contentLength, std::string& detectedEncoding);
+
+ /**
+ * Detect HTML charset and HTML convert to UTF-8
+ * @param htmlContent content of HTML file
+ * @param converted receive result of conversion
+ * @param serverReportedCharset charset from HTTP header or from other out-of-band source, empty if unknown or unset
+ * @return true if charset is properly detected and HTML is correctly converted, false if charset is only guessed
+ */
+ static inline bool ConvertHtmlToUtf8(const std::string& htmlContent, std::string& converted, const std::string& serverReportedCharset = "")
+ {
+ std::string usedHtmlCharset;
+ return ConvertHtmlToUtf8(htmlContent, converted, serverReportedCharset, usedHtmlCharset);
+ }
+ /**
+ * Detect HTML charset and HTML convert to UTF-8
+ * @param htmlContent content of HTML file
+ * @param converted receive result of conversion
+ * @param serverReportedCharset charset from HTTP header or from other out-of-band source, empty if unknown or unset
+ * @param usedHtmlCharset receive charset used for conversion
+ * @return true if charset is properly detected and HTML is correctly converted, false if charset is only guessed
+ */
+ static bool ConvertHtmlToUtf8(const std::string& htmlContent, std::string& converted, const std::string& serverReportedCharset, std::string& usedHtmlCharset);
+
+ /**
+ * Try to convert plain text to UTF-8 using best suitable charset
+ * @param textContent text to convert
+ * @param converted receive result of conversion
+ * @param serverReportedCharset charset from HTTP header or from other out-of-band source, empty if unknown or unset
+ * @param usedCharset receive charset used for conversion
+ * @return true if converted without errors, false otherwise
+ */
+ static bool ConvertPlainTextToUtf8(const std::string& textContent, std::string& converted, const std::string& serverReportedCharset, std::string& usedCharset);
+
+private:
+ static bool GetXmlEncodingFromDeclaration(const char* const xmlContent, const size_t contentLength, std::string& declaredEncoding);
+ /**
+ * Try to guess text encoding by searching for '<?xml' mark in different encodings
+ * Multibyte encodings (UTF/UCS) always ends with explicit endianness (LE/BE)
+ * @param content pointer to text to analyze
+ * @param contentLength length of text
+ * @param detectedEncoding reference to variable that receive supposed encoding
+ * @return true if any encoding supposed, false otherwise
+ */
+ static bool GuessXmlEncoding(const char* const xmlContent, const size_t contentLength, std::string& supposedEncoding);
+
+ static std::string GetHtmlEncodingFromHead(const std::string& htmlContent);
+ static size_t GetHtmlAttribute(const std::string& htmlContent, size_t pos, std::string& atrName, std::string& strValue);
+ static std::string ExtractEncodingFromHtmlMeta(std::string metaContent, size_t pos = 0);
+
+ static bool checkConversion(const std::string& srcCharset, const std::string& src, std::string& dst);
+ static void appendCharAsAsciiUpperCase(std::string& str, const char chr);
+
+ static const size_t m_XmlDeclarationMaxLength;
+ static const size_t m_HtmlCharsetEndSearchPos;
+
+ static const std::string m_HtmlWhitespaceChars;
+};
diff --git a/src/utils/Crc32.cpp b/src/utils/Crc32.cpp
new file mode 100644
index 0000000000..2607b4088a
--- /dev/null
+++ b/src/utils/Crc32.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "Crc32.h"
+#include "utils/StringUtils.h"
+
+uint32_t crc_tab[256] =
+{
+ 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L,
+ 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L,
+ 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L,
+ 0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL,
+ 0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L,
+ 0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L,
+ 0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L,
+ 0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL,
+ 0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L,
+ 0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L,
+ 0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L,
+ 0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL,
+ 0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L,
+ 0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L,
+ 0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L,
+ 0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL,
+ 0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL,
+ 0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L,
+ 0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L,
+ 0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL,
+ 0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL,
+ 0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L,
+ 0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L,
+ 0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL,
+ 0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL,
+ 0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L,
+ 0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L,
+ 0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL,
+ 0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL,
+ 0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L,
+ 0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L,
+ 0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL,
+ 0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L,
+ 0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL,
+ 0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL,
+ 0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L,
+ 0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L,
+ 0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL,
+ 0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL,
+ 0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L,
+ 0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L,
+ 0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL,
+ 0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL,
+ 0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L,
+ 0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L,
+ 0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL,
+ 0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL,
+ 0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L,
+ 0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L,
+ 0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL,
+ 0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L,
+ 0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L,
+ 0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L,
+ 0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL,
+ 0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L,
+ 0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L,
+ 0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L,
+ 0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL,
+ 0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L,
+ 0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L,
+ 0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L,
+ 0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL,
+ 0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L,
+ 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L
+};
+
+Crc32::Crc32()
+{
+ Reset();
+}
+
+void Crc32::Reset()
+{
+ m_crc = 0xFFFFFFFF;
+}
+
+void Crc32::Compute(const char* buffer, size_t count)
+{
+ while (count--)
+ m_crc = (m_crc << 8) ^ crc_tab[((m_crc >> 24) ^ *buffer++) & 0xFF];
+}
+
+void Crc32::Compute(const std::string& strValue)
+{
+ Compute(strValue.c_str(), strValue.size());
+}
+
+void Crc32::ComputeFromLowerCase(const std::string& strValue)
+{
+ std::string strLower = strValue;
+ StringUtils::ToLower(strLower);
+ Compute(strLower.c_str(), strLower.size());
+}
+
diff --git a/src/utils/Crc32.h b/src/utils/Crc32.h
new file mode 100644
index 0000000000..3d30c8fac7
--- /dev/null
+++ b/src/utils/Crc32.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#include <string>
+#include <stdint.h>
+
+class Crc32
+{
+public:
+ Crc32();
+ void Reset();
+ void Compute(const char* buffer, size_t count);
+ void Compute(const std::string& strValue);
+ void ComputeFromLowerCase(const std::string& strValue);
+
+ operator uint32_t () const
+ {
+ return m_crc;
+ }
+
+private:
+ uint32_t m_crc;
+};
+
diff --git a/src/utils/CryptThreading.cpp b/src/utils/CryptThreading.cpp
new file mode 100644
index 0000000000..49a24e557f
--- /dev/null
+++ b/src/utils/CryptThreading.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef TARGET_WINDOWS
+#error "The threading options for the cryptography libraries don't need to be and shouldn't be set on Windows. Do not include CryptThreading in your windows project."
+#endif
+
+#include "CryptThreading.h"
+#include "threads/Thread.h"
+#include "utils/log.h"
+
+#if (defined HAVE_CONFIG_H) && (!defined TARGET_WINDOWS)
+ #include "config.h"
+#else
+#define HAVE_OPENSSL
+#endif
+
+#ifdef HAVE_OPENSSL
+#include <openssl/crypto.h>
+#endif
+
+#ifdef HAVE_GCRYPT
+#include <gcrypt.h>
+#include <errno.h>
+
+GCRY_THREAD_OPTION_PTHREAD_IMPL;
+#endif
+
+/* ========================================================================= */
+/* openssl locking implementation for curl */
+static CCriticalSection* getlock(int index)
+{
+ return g_cryptThreadingInitializer.get_lock(index);
+}
+
+static void lock_callback(int mode, int type, const char* file, int line)
+{
+ if (mode & 0x01 /* CRYPTO_LOCK from openssl/crypto.h */ )
+ getlock(type)->lock();
+ else
+ getlock(type)->unlock();
+}
+
+static unsigned long thread_id()
+{
+ return (unsigned long)CThread::GetCurrentThreadId();
+}
+/* ========================================================================= */
+
+CryptThreadingInitializer::CryptThreadingInitializer()
+{
+ bool attemptedToSetSSLMTHook = false;
+#ifdef HAVE_OPENSSL
+ // set up OpenSSL
+ numlocks = CRYPTO_num_locks();
+ CRYPTO_set_id_callback(thread_id);
+ CRYPTO_set_locking_callback(lock_callback);
+ attemptedToSetSSLMTHook = true;
+#else
+ numlocks = 1;
+#endif
+
+ locks = new CCriticalSection*[numlocks];
+ for (int i = 0; i < numlocks; i++)
+ locks[i] = NULL;
+
+#ifdef HAVE_GCRYPT
+ // set up gcrypt
+ gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
+ attemptedToSetSSLMTHook = true;
+#endif
+
+ if (!attemptedToSetSSLMTHook)
+ CLog::Log(LOGWARNING, "Could not determine the libcurl security library to set the locking scheme. This may cause problem with multithreaded use of ssl or libraries that depend on it (libcurl).");
+
+}
+
+CryptThreadingInitializer::~CryptThreadingInitializer()
+{
+ CSingleLock l(locksLock);
+#ifdef HAVE_OPENSSL
+ CRYPTO_set_locking_callback(NULL);
+#endif
+
+ for (int i = 0; i < numlocks; i++)
+ delete locks[i]; // I always forget ... delete is NULL safe.
+
+ delete [] locks;
+}
+
+CCriticalSection* CryptThreadingInitializer::get_lock(int index)
+{
+ CSingleLock l(locksLock);
+ CCriticalSection* curlock = locks[index];
+ if (curlock == NULL)
+ {
+ curlock = new CCriticalSection();
+ locks[index] = curlock;
+ }
+
+ return curlock;
+}
+
+
+
+
diff --git a/src/utils/CryptThreading.h b/src/utils/CryptThreading.h
new file mode 100644
index 0000000000..32a4c0743b
--- /dev/null
+++ b/src/utils/CryptThreading.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#include "utils/GlobalsHandling.h"
+#include "threads/CriticalSection.h"
+
+class CryptThreadingInitializer
+{
+ CCriticalSection** locks;
+ int numlocks;
+ CCriticalSection locksLock;
+
+public:
+ CryptThreadingInitializer();
+ ~CryptThreadingInitializer();
+
+ CCriticalSection* get_lock(int index);
+
+private:
+ CryptThreadingInitializer(const CryptThreadingInitializer &rhs);
+ CryptThreadingInitializer& operator=(const CryptThreadingInitializer&);
+};
+
+XBMC_GLOBAL_REF(CryptThreadingInitializer,g_cryptThreadingInitializer);
+#define g_cryptThreadingInitializer XBMC_GLOBAL_USE(CryptThreadingInitializer)
diff --git a/src/utils/DatabaseUtils.cpp b/src/utils/DatabaseUtils.cpp
new file mode 100644
index 0000000000..f5b7ca6f10
--- /dev/null
+++ b/src/utils/DatabaseUtils.cpp
@@ -0,0 +1,658 @@
+/*
+ * Copyright (C) 2012-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sstream>
+
+#include "DatabaseUtils.h"
+#include "dbwrappers/dataset.h"
+#include "music/MusicDatabase.h"
+#include "utils/log.h"
+#include "utils/Variant.h"
+#include "utils/StringUtils.h"
+#include "video/VideoDatabase.h"
+
+MediaType DatabaseUtils::MediaTypeFromVideoContentType(int videoContentType)
+{
+ VIDEODB_CONTENT_TYPE type = (VIDEODB_CONTENT_TYPE)videoContentType;
+ switch (type)
+ {
+ case VIDEODB_CONTENT_MOVIES:
+ return MediaTypeMovie;
+
+ case VIDEODB_CONTENT_MOVIE_SETS:
+ return MediaTypeVideoCollection;
+
+ case VIDEODB_CONTENT_TVSHOWS:
+ return MediaTypeTvShow;
+
+ case VIDEODB_CONTENT_EPISODES:
+ return MediaTypeEpisode;
+
+ case VIDEODB_CONTENT_MUSICVIDEOS:
+ return MediaTypeMusicVideo;
+
+ default:
+ break;
+ }
+
+ return MediaTypeNone;
+}
+
+std::string DatabaseUtils::GetField(Field field, const MediaType &mediaType, DatabaseQueryPart queryPart)
+{
+ if (field == FieldNone || mediaType == MediaTypeNone)
+ return "";
+
+ if (mediaType == MediaTypeAlbum)
+ {
+ if (field == FieldId) return "albumview.idAlbum";
+ else if (field == FieldAlbum) return "albumview.strAlbum";
+ else if (field == FieldArtist || field == FieldAlbumArtist) return "albumview.strArtists";
+ else if (field == FieldGenre) return "albumview.strGenre";
+ else if (field == FieldYear) return "albumview.iYear";
+ else if (field == FieldMoods) return "albumview.strMoods";
+ else if (field == FieldStyles) return "albumview.strStyles";
+ else if (field == FieldThemes) return "albumview.strThemes";
+ else if (field == FieldReview) return "albumview.strReview";
+ else if (field == FieldMusicLabel) return "albumview.strLabel";
+ else if (field == FieldAlbumType) return "albumview.strType";
+ else if (field == FieldRating) return "albumview.iRating";
+ else if (field == FieldDateAdded && queryPart == DatabaseQueryPartOrderBy) return "albumview.idalbum"; // only used for order clauses
+ else if (field == FieldPlaycount) return "albumview.iTimesPlayed";
+ }
+ else if (mediaType == MediaTypeSong)
+ {
+ if (field == FieldId) return "songview.idSong";
+ else if (field == FieldTitle) return "songview.strTitle";
+ else if (field == FieldTrackNumber) return "songview.iTrack";
+ else if (field == FieldTime) return "songview.iDuration";
+ else if (field == FieldYear) return "songview.iYear";
+ else if (field == FieldFilename) return "songview.strFilename";
+ else if (field == FieldPlaycount) return "songview.iTimesPlayed";
+ else if (field == FieldStartOffset) return "songview.iStartOffset";
+ else if (field == FieldEndOffset) return "songview.iEndOffset";
+ else if (field == FieldLastPlayed) return "songview.lastPlayed";
+ else if (field == FieldRating) return "songview.rating";
+ else if (field == FieldComment) return "songview.comment";
+ else if (field == FieldAlbum) return "songview.strAlbum";
+ else if (field == FieldPath) return "songview.strPath";
+ else if (field == FieldArtist || field == FieldAlbumArtist) return "songview.strArtists";
+ else if (field == FieldGenre) return "songview.strGenre";
+ else if (field == FieldDateAdded && queryPart == DatabaseQueryPartOrderBy) return "songview.idSong"; // only used for order clauses
+ }
+ else if (mediaType == MediaTypeArtist)
+ {
+ if (field == FieldId) return "artistview.idArtist";
+ else if (field == FieldArtist) return "artistview.strArtist";
+ else if (field == FieldGenre) return "artistview.strGenres";
+ else if (field == FieldMoods) return "artistview.strMoods";
+ else if (field == FieldStyles) return "artistview.strStyles";
+ else if (field == FieldInstruments) return "artistview.strInstruments";
+ else if (field == FieldBiography) return "artistview.strBiography";
+ else if (field == FieldBorn) return "artistview.strBorn";
+ else if (field == FieldBandFormed) return "artistview.strFormed";
+ else if (field == FieldDisbanded) return "artistview.strDisbanded";
+ else if (field == FieldDied) return "artistview.strDied";
+ }
+ else if (mediaType == MediaTypeMusicVideo)
+ {
+ CStdString result;
+ if (field == FieldId) return "musicvideoview.idMVideo";
+ else if (field == FieldTitle) result = StringUtils::Format("musicvideoview.c%02d",VIDEODB_ID_MUSICVIDEO_TITLE);
+ else if (field == FieldTime) result = StringUtils::Format("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_RUNTIME);
+ else if (field == FieldDirector) result = StringUtils::Format("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_DIRECTOR);
+ else if (field == FieldStudio) result = StringUtils::Format("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_STUDIOS);
+ else if (field == FieldYear) result = StringUtils::Format("musicvideoview.c%02d",VIDEODB_ID_MUSICVIDEO_YEAR);
+ else if (field == FieldPlot) result = StringUtils::Format("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_PLOT);
+ else if (field == FieldAlbum) result = StringUtils::Format("musicvideoview.c%02d",VIDEODB_ID_MUSICVIDEO_ALBUM);
+ else if (field == FieldArtist) result = StringUtils::Format("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_ARTIST);
+ else if (field == FieldGenre) result = StringUtils::Format("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_GENRE);
+ else if (field == FieldTrackNumber) result = StringUtils::Format("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_TRACK);
+ else if (field == FieldFilename) return "musicvideoview.strFilename";
+ else if (field == FieldPath) return "musicvideoview.strPath";
+ else if (field == FieldPlaycount) return "musicvideoview.playCount";
+ else if (field == FieldLastPlayed) return "musicvideoview.lastPlayed";
+ else if (field == FieldDateAdded) return "musicvideoview.dateAdded";
+
+ if (!result.empty())
+ return result;
+ }
+ else if (mediaType == MediaTypeMovie)
+ {
+ CStdString result;
+ if (field == FieldId) return "movieview.idMovie";
+ else if (field == FieldTitle)
+ {
+ // We need some extra logic to get the title value if sorttitle isn't set
+ if (queryPart == DatabaseQueryPartOrderBy)
+ result = StringUtils::Format("CASE WHEN length(movieview.c%02d) > 0 THEN movieview.c%02d ELSE movieview.c%02d END", VIDEODB_ID_SORTTITLE, VIDEODB_ID_SORTTITLE, VIDEODB_ID_TITLE);
+ else
+ result = StringUtils::Format("movieview.c%02d", VIDEODB_ID_TITLE);
+ }
+ else if (field == FieldPlot) result = StringUtils::Format("movieview.c%02d", VIDEODB_ID_PLOT);
+ else if (field == FieldPlotOutline) result = StringUtils::Format("movieview.c%02d", VIDEODB_ID_PLOTOUTLINE);
+ else if (field == FieldTagline) result = StringUtils::Format("movieview.c%02d", VIDEODB_ID_TAGLINE);
+ else if (field == FieldVotes) result = StringUtils::Format("movieview.c%02d", VIDEODB_ID_VOTES);
+ else if (field == FieldRating)
+ {
+ if (queryPart == DatabaseQueryPartOrderBy)
+ result = StringUtils::Format("CAST(movieview.c%02d as DECIMAL(5,3))", VIDEODB_ID_RATING);
+ else
+ result = StringUtils::Format("movieview.c%02d", VIDEODB_ID_RATING);
+ }
+ else if (field == FieldWriter) result = StringUtils::Format("movieview.c%02d", VIDEODB_ID_CREDITS);
+ else if (field == FieldYear) result = StringUtils::Format("movieview.c%02d", VIDEODB_ID_YEAR);
+ else if (field == FieldSortTitle) result = StringUtils::Format("movieview.c%02d", VIDEODB_ID_SORTTITLE);
+ else if (field == FieldTime) result = StringUtils::Format("movieview.c%02d", VIDEODB_ID_RUNTIME);
+ else if (field == FieldMPAA) result = StringUtils::Format("movieview.c%02d", VIDEODB_ID_MPAA);
+ else if (field == FieldTop250) result = StringUtils::Format("movieview.c%02d", VIDEODB_ID_TOP250);
+ else if (field == FieldSet) return "movieview.strSet";
+ else if (field == FieldGenre) result = StringUtils::Format("movieview.c%02d", VIDEODB_ID_GENRE);
+ else if (field == FieldDirector) result = StringUtils::Format("movieview.c%02d", VIDEODB_ID_DIRECTOR);
+ else if (field == FieldStudio) result = StringUtils::Format("movieview.c%02d", VIDEODB_ID_STUDIOS);
+ else if (field == FieldTrailer) result = StringUtils::Format("movieview.c%02d", VIDEODB_ID_TRAILER);
+ else if (field == FieldCountry) result = StringUtils::Format("movieview.c%02d", VIDEODB_ID_COUNTRY);
+ else if (field == FieldFilename) return "movieview.strFilename";
+ else if (field == FieldPath) return "movieview.strPath";
+ else if (field == FieldPlaycount) return "movieview.playCount";
+ else if (field == FieldLastPlayed) return "movieview.lastPlayed";
+ else if (field == FieldDateAdded) return "movieview.dateAdded";
+
+ if (!result.empty())
+ return result;
+ }
+ else if (mediaType == MediaTypeTvShow)
+ {
+ CStdString result;
+ if (field == FieldId) return "tvshowview.idShow";
+ else if (field == FieldTitle)
+ {
+ // We need some extra logic to get the title value if sorttitle isn't set
+ if (queryPart == DatabaseQueryPartOrderBy)
+ result = StringUtils::Format("CASE WHEN length(tvshowview.c%02d) > 0 THEN tvshowview.c%02d ELSE tvshowview.c%02d END", VIDEODB_ID_TV_SORTTITLE, VIDEODB_ID_TV_SORTTITLE, VIDEODB_ID_TV_TITLE);
+ else
+ result = StringUtils::Format("tvshowview.c%02d", VIDEODB_ID_TV_TITLE);
+ }
+ else if (field == FieldPlot) result = StringUtils::Format("tvshowview.c%02d", VIDEODB_ID_TV_PLOT);
+ else if (field == FieldTvShowStatus) result = StringUtils::Format("tvshowview.c%02d", VIDEODB_ID_TV_STATUS);
+ else if (field == FieldVotes) result = StringUtils::Format("tvshowview.c%02d", VIDEODB_ID_TV_VOTES);
+ else if (field == FieldRating) result = StringUtils::Format("tvshowview.c%02d", VIDEODB_ID_TV_RATING);
+ else if (field == FieldYear) result = StringUtils::Format("tvshowview.c%02d", VIDEODB_ID_TV_PREMIERED);
+ else if (field == FieldGenre) result = StringUtils::Format("tvshowview.c%02d", VIDEODB_ID_TV_GENRE);
+ else if (field == FieldMPAA) result = StringUtils::Format("tvshowview.c%02d", VIDEODB_ID_TV_MPAA);
+ else if (field == FieldStudio) result = StringUtils::Format("tvshowview.c%02d", VIDEODB_ID_TV_STUDIOS);
+ else if (field == FieldSortTitle) result = StringUtils::Format("tvshowview.c%02d", VIDEODB_ID_TV_SORTTITLE);
+ else if (field == FieldPath) return "tvshowview.strPath";
+ else if (field == FieldDateAdded) return "tvshowview.dateAdded";
+ else if (field == FieldLastPlayed) return "tvshowview.lastPlayed";
+ else if (field == FieldSeason) return "tvshowview.totalSeasons";
+ else if (field == FieldNumberOfEpisodes) return "tvshowview.totalCount";
+ else if (field == FieldNumberOfWatchedEpisodes) return "tvshowview.watchedcount";
+
+ if (!result.empty())
+ return result;
+ }
+ else if (mediaType == MediaTypeEpisode)
+ {
+ CStdString result;
+ if (field == FieldId) return "episodeview.idEpisode";
+ else if (field == FieldTitle) result = StringUtils::Format("episodeview.c%02d", VIDEODB_ID_EPISODE_TITLE);
+ else if (field == FieldPlot) result = StringUtils::Format("episodeview.c%02d", VIDEODB_ID_EPISODE_PLOT);
+ else if (field == FieldVotes) result = StringUtils::Format("episodeview.c%02d", VIDEODB_ID_EPISODE_VOTES);
+ else if (field == FieldRating) result = StringUtils::Format("episodeview.c%02d", VIDEODB_ID_EPISODE_RATING);
+ else if (field == FieldWriter) result = StringUtils::Format("episodeview.c%02d", VIDEODB_ID_EPISODE_CREDITS);
+ else if (field == FieldAirDate) result = StringUtils::Format("episodeview.c%02d", VIDEODB_ID_EPISODE_AIRED);
+ else if (field == FieldTime) result = StringUtils::Format("episodeview.c%02d", VIDEODB_ID_EPISODE_RUNTIME);
+ else if (field == FieldDirector) result = StringUtils::Format("episodeview.c%02d", VIDEODB_ID_EPISODE_DIRECTOR);
+ else if (field == FieldSeason) result = StringUtils::Format("episodeview.c%02d", VIDEODB_ID_EPISODE_SEASON);
+ else if (field == FieldEpisodeNumber) result = StringUtils::Format("episodeview.c%02d", VIDEODB_ID_EPISODE_EPISODE);
+ else if (field == FieldUniqueId) result = StringUtils::Format("episodeview.c%02d", VIDEODB_ID_EPISODE_UNIQUEID);
+ else if (field == FieldEpisodeNumberSpecialSort) result = StringUtils::Format("episodeview.c%02d", VIDEODB_ID_EPISODE_SORTEPISODE);
+ else if (field == FieldSeasonSpecialSort) result = StringUtils::Format("episodeview.c%02d", VIDEODB_ID_EPISODE_SORTSEASON);
+ else if (field == FieldFilename) return "episodeview.strFilename";
+ else if (field == FieldPath) return "episodeview.strPath";
+ else if (field == FieldPlaycount) return "episodeview.playCount";
+ else if (field == FieldLastPlayed) return "episodeview.lastPlayed";
+ else if (field == FieldDateAdded) return "episodeview.dateAdded";
+ else if (field == FieldTvShowTitle) return "episodeview.strTitle";
+ else if (field == FieldYear) return "episodeview.premiered";
+ else if (field == FieldMPAA) return "episodeview.mpaa";
+ else if (field == FieldStudio) return "episodeview.strStudio";
+
+ if (!result.empty())
+ return result;
+ }
+
+ if (field == FieldRandom && queryPart == DatabaseQueryPartOrderBy)
+ return "RANDOM()";
+
+ return "";
+}
+
+int DatabaseUtils::GetField(Field field, const MediaType &mediaType)
+{
+ if (field == FieldNone || mediaType == MediaTypeNone)
+ return -1;
+
+ return GetField(field, mediaType, false);
+}
+
+int DatabaseUtils::GetFieldIndex(Field field, const MediaType &mediaType)
+{
+ if (field == FieldNone || mediaType == MediaTypeNone)
+ return -1;
+
+ return GetField(field, mediaType, true);
+}
+
+bool DatabaseUtils::GetSelectFields(const Fields &fields, const MediaType &mediaType, FieldList &selectFields)
+{
+ if (mediaType == MediaTypeNone || fields.empty())
+ return false;
+
+ Fields sortFields = fields;
+
+ // add necessary fields to create the label
+ if (mediaType == MediaTypeSong || mediaType == MediaTypeVideo || mediaType == MediaTypeVideoCollection ||
+ mediaType == MediaTypeMusicVideo || mediaType == MediaTypeMovie || mediaType == MediaTypeTvShow || mediaType == MediaTypeEpisode)
+ sortFields.insert(FieldTitle);
+ if (mediaType == MediaTypeEpisode)
+ {
+ sortFields.insert(FieldSeason);
+ sortFields.insert(FieldEpisodeNumber);
+ }
+ else if (mediaType == MediaTypeAlbum)
+ sortFields.insert(FieldAlbum);
+ else if (mediaType == MediaTypeSong)
+ sortFields.insert(FieldTrackNumber);
+ else if (mediaType == MediaTypeArtist)
+ sortFields.insert(FieldArtist);
+
+ selectFields.clear();
+ for (Fields::const_iterator it = sortFields.begin(); it != sortFields.end(); ++it)
+ {
+ // ignore FieldLabel because it needs special handling (see further up)
+ if (*it == FieldLabel)
+ continue;
+
+ if (GetField(*it, mediaType, DatabaseQueryPartSelect).empty())
+ {
+ CLog::Log(LOGDEBUG, "DatabaseUtils::GetSortFieldList: unknown field %d", *it);
+ continue;
+ }
+ selectFields.push_back(*it);
+ }
+
+ return !selectFields.empty();
+}
+
+bool DatabaseUtils::GetFieldValue(const dbiplus::field_value &fieldValue, CVariant &variantValue)
+{
+ if (fieldValue.get_isNull())
+ {
+ variantValue = CVariant::ConstNullVariant;
+ return true;
+ }
+
+ switch (fieldValue.get_fType())
+ {
+ case dbiplus::ft_String:
+ case dbiplus::ft_WideString:
+ case dbiplus::ft_Object:
+ variantValue = fieldValue.get_asString();
+ return true;
+ case dbiplus::ft_Char:
+ case dbiplus::ft_WChar:
+ variantValue = fieldValue.get_asChar();
+ return true;
+ case dbiplus::ft_Boolean:
+ variantValue = fieldValue.get_asBool();
+ return true;
+ case dbiplus::ft_Short:
+ variantValue = fieldValue.get_asShort();
+ return true;
+ case dbiplus::ft_UShort:
+ variantValue = fieldValue.get_asShort();
+ return true;
+ case dbiplus::ft_Int:
+ variantValue = fieldValue.get_asInt();
+ return true;
+ case dbiplus::ft_UInt:
+ variantValue = fieldValue.get_asUInt();
+ return true;
+ case dbiplus::ft_Float:
+ variantValue = fieldValue.get_asFloat();
+ return true;
+ case dbiplus::ft_Double:
+ case dbiplus::ft_LongDouble:
+ variantValue = fieldValue.get_asDouble();
+ return true;
+ case dbiplus::ft_Int64:
+ variantValue = fieldValue.get_asInt64();
+ return true;
+ }
+
+ return false;
+}
+
+bool DatabaseUtils::GetDatabaseResults(const MediaType &mediaType, const FieldList &fields, const std::auto_ptr<dbiplus::Dataset> &dataset, DatabaseResults &results)
+{
+ if (dataset->num_rows() == 0)
+ return true;
+
+ const dbiplus::result_set &resultSet = dataset->get_result_set();
+ unsigned int offset = results.size();
+
+ if (fields.empty())
+ {
+ DatabaseResult result;
+ for (unsigned int index = 0; index < resultSet.records.size(); index++)
+ {
+ result[FieldRow] = index + offset;
+ results.push_back(result);
+ }
+
+ return true;
+ }
+
+ if (resultSet.record_header.size() < fields.size())
+ return false;
+
+ std::vector<int> fieldIndexLookup;
+ fieldIndexLookup.reserve(fields.size());
+ for (FieldList::const_iterator it = fields.begin(); it != fields.end(); ++it)
+ fieldIndexLookup.push_back(GetFieldIndex(*it, mediaType));
+
+ results.reserve(resultSet.records.size() + offset);
+ for (unsigned int index = 0; index < resultSet.records.size(); index++)
+ {
+ DatabaseResult result;
+ result[FieldRow] = index + offset;
+
+ unsigned int lookupIndex = 0;
+ for (FieldList::const_iterator it = fields.begin(); it != fields.end(); ++it)
+ {
+ int fieldIndex = fieldIndexLookup[lookupIndex++];
+ if (fieldIndex < 0)
+ return false;
+
+ std::pair<Field, CVariant> value;
+ value.first = *it;
+ if (!GetFieldValue(resultSet.records[index]->at(fieldIndex), value.second))
+ CLog::Log(LOGWARNING, "GetDatabaseResults: unable to retrieve value of field %s", resultSet.record_header[fieldIndex].name.c_str());
+
+ if (value.first == FieldYear &&
+ (mediaType == MediaTypeTvShow || mediaType == MediaTypeEpisode))
+ {
+ CDateTime dateTime;
+ dateTime.SetFromDBDate(value.second.asString());
+ if (dateTime.IsValid())
+ {
+ value.second.clear();
+ value.second = dateTime.GetYear();
+ }
+ }
+
+ result.insert(value);
+ }
+
+ result[FieldMediaType] = mediaType;
+ if (mediaType == MediaTypeMovie || mediaType == MediaTypeVideoCollection ||
+ mediaType == MediaTypeTvShow || mediaType == MediaTypeMusicVideo)
+ result[FieldLabel] = result.at(FieldTitle).asString();
+ else if (mediaType == MediaTypeEpisode)
+ {
+ std::ostringstream label;
+ label << (int)(result.at(FieldSeason).asInteger() * 100 + result.at(FieldEpisodeNumber).asInteger());
+ label << ". ";
+ label << result.at(FieldTitle).asString();
+ result[FieldLabel] = label.str();
+ }
+ else if (mediaType == MediaTypeAlbum)
+ result[FieldLabel] = result.at(FieldAlbum).asString();
+ else if (mediaType == MediaTypeSong)
+ {
+ std::ostringstream label;
+ label << (int)result.at(FieldTrackNumber).asInteger();
+ label << ". ";
+ label << result.at(FieldTitle).asString();
+ result[FieldLabel] = label.str();
+ }
+ else if (mediaType == MediaTypeArtist)
+ result[FieldLabel] = result.at(FieldArtist).asString();
+
+ results.push_back(result);
+ }
+
+ return true;
+}
+
+std::string DatabaseUtils::BuildLimitClause(int end, int start /* = 0 */)
+{
+ std::ostringstream sql;
+ sql << " LIMIT ";
+ if (start > 0)
+ {
+ if (end > 0)
+ {
+ end = end - start;
+ if (end < 0)
+ end = 0;
+ }
+
+ sql << start << "," << end;
+ }
+ else
+ sql << end;
+
+ return sql.str();
+}
+
+int DatabaseUtils::GetField(Field field, const MediaType &mediaType, bool asIndex)
+{
+ if (field == FieldNone || mediaType == MediaTypeNone)
+ return -1;
+
+ int index = -1;
+
+ if (mediaType == MediaTypeAlbum)
+ {
+ if (field == FieldId) return CMusicDatabase::album_idAlbum;
+ else if (field == FieldAlbum) return CMusicDatabase::album_strAlbum;
+ else if (field == FieldArtist || field == FieldAlbumArtist) return CMusicDatabase::album_strArtists;
+ else if (field == FieldGenre) return CMusicDatabase::album_strGenres;
+ else if (field == FieldYear) return CMusicDatabase::album_iYear;
+ else if (field == FieldMoods) return CMusicDatabase::album_strMoods;
+ else if (field == FieldStyles) return CMusicDatabase::album_strStyles;
+ else if (field == FieldThemes) return CMusicDatabase::album_strThemes;
+ else if (field == FieldReview) return CMusicDatabase::album_strReview;
+ else if (field == FieldMusicLabel) return CMusicDatabase::album_strLabel;
+ else if (field == FieldAlbumType) return CMusicDatabase::album_strType;
+ else if (field == FieldRating) return CMusicDatabase::album_iRating;
+ else if (field == FieldPlaycount) return CMusicDatabase::album_iTimesPlayed;
+ }
+ else if (mediaType == MediaTypeSong)
+ {
+ if (field == FieldId) return CMusicDatabase::song_idSong;
+ else if (field == FieldTitle) return CMusicDatabase::song_strTitle;
+ else if (field == FieldTrackNumber) return CMusicDatabase::song_iTrack;
+ else if (field == FieldTime) return CMusicDatabase::song_iDuration;
+ else if (field == FieldYear) return CMusicDatabase::song_iYear;
+ else if (field == FieldFilename) return CMusicDatabase::song_strFileName;
+ else if (field == FieldPlaycount) return CMusicDatabase::song_iTimesPlayed;
+ else if (field == FieldStartOffset) return CMusicDatabase::song_iStartOffset;
+ else if (field == FieldEndOffset) return CMusicDatabase::song_iEndOffset;
+ else if (field == FieldLastPlayed) return CMusicDatabase::song_lastplayed;
+ else if (field == FieldRating) return CMusicDatabase::song_rating;
+ else if (field == FieldComment) return CMusicDatabase::song_comment;
+ else if (field == FieldAlbum) return CMusicDatabase::song_strAlbum;
+ else if (field == FieldPath) return CMusicDatabase::song_strPath;
+ else if (field == FieldGenre) return CMusicDatabase::song_strGenres;
+ else if (field == FieldArtist || field == FieldAlbumArtist) return CMusicDatabase::song_strArtists;
+ }
+ else if (mediaType == MediaTypeArtist)
+ {
+ if (field == FieldId) return CMusicDatabase::artist_idArtist;
+ else if (field == FieldArtist) return CMusicDatabase::artist_strArtist;
+ else if (field == FieldGenre) return CMusicDatabase::artist_strGenres;
+ else if (field == FieldMoods) return CMusicDatabase::artist_strMoods;
+ else if (field == FieldStyles) return CMusicDatabase::artist_strStyles;
+ else if (field == FieldInstruments) return CMusicDatabase::artist_strInstruments;
+ else if (field == FieldBiography) return CMusicDatabase::artist_strBiography;
+ else if (field == FieldBorn) return CMusicDatabase::artist_strBorn;
+ else if (field == FieldBandFormed) return CMusicDatabase::artist_strFormed;
+ else if (field == FieldDisbanded) return CMusicDatabase::artist_strDisbanded;
+ else if (field == FieldDied) return CMusicDatabase::artist_strDied;
+ }
+ else if (mediaType == MediaTypeMusicVideo)
+ {
+ if (field == FieldId) return 0;
+ else if (field == FieldTitle) index = VIDEODB_ID_MUSICVIDEO_TITLE;
+ else if (field == FieldTime) index = VIDEODB_ID_MUSICVIDEO_RUNTIME;
+ else if (field == FieldDirector) index = VIDEODB_ID_MUSICVIDEO_DIRECTOR;
+ else if (field == FieldStudio) index = VIDEODB_ID_MUSICVIDEO_STUDIOS;
+ else if (field == FieldYear) index = VIDEODB_ID_MUSICVIDEO_YEAR;
+ else if (field == FieldPlot) index = VIDEODB_ID_MUSICVIDEO_PLOT;
+ else if (field == FieldAlbum) index = VIDEODB_ID_MUSICVIDEO_ALBUM;
+ else if (field == FieldArtist) index = VIDEODB_ID_MUSICVIDEO_ARTIST;
+ else if (field == FieldGenre) index = VIDEODB_ID_MUSICVIDEO_GENRE;
+ else if (field == FieldTrackNumber) index = VIDEODB_ID_MUSICVIDEO_TRACK;
+ else if (field == FieldFilename) return VIDEODB_DETAILS_MUSICVIDEO_FILE;
+ else if (field == FieldPath) return VIDEODB_DETAILS_MUSICVIDEO_PATH;
+ else if (field == FieldPlaycount) return VIDEODB_DETAILS_MUSICVIDEO_PLAYCOUNT;
+ else if (field == FieldLastPlayed) return VIDEODB_DETAILS_MUSICVIDEO_LASTPLAYED;
+ else if (field == FieldDateAdded) return VIDEODB_DETAILS_MUSICVIDEO_DATEADDED;
+
+ if (index < 0)
+ return index;
+
+ if (asIndex)
+ {
+ // see VideoDatabase.h
+ // the first field is the item's ID and the second is the item's file ID
+ index += 2;
+ }
+ }
+ else if (mediaType == MediaTypeMovie)
+ {
+ if (field == FieldId) return 0;
+ else if (field == FieldTitle) index = VIDEODB_ID_TITLE;
+ else if (field == FieldSortTitle) index = VIDEODB_ID_SORTTITLE;
+ else if (field == FieldPlot) index = VIDEODB_ID_PLOT;
+ else if (field == FieldPlotOutline) index = VIDEODB_ID_PLOTOUTLINE;
+ else if (field == FieldTagline) index = VIDEODB_ID_TAGLINE;
+ else if (field == FieldVotes) index = VIDEODB_ID_VOTES;
+ else if (field == FieldRating) index = VIDEODB_ID_RATING;
+ else if (field == FieldWriter) index = VIDEODB_ID_CREDITS;
+ else if (field == FieldYear) index = VIDEODB_ID_YEAR;
+ else if (field == FieldTime) index = VIDEODB_ID_RUNTIME;
+ else if (field == FieldMPAA) index = VIDEODB_ID_MPAA;
+ else if (field == FieldTop250) index = VIDEODB_ID_TOP250;
+ else if (field == FieldSet) return VIDEODB_DETAILS_MOVIE_SET_NAME;
+ else if (field == FieldGenre) index = VIDEODB_ID_GENRE;
+ else if (field == FieldDirector) index = VIDEODB_ID_DIRECTOR;
+ else if (field == FieldStudio) index = VIDEODB_ID_STUDIOS;
+ else if (field == FieldTrailer) index = VIDEODB_ID_TRAILER;
+ else if (field == FieldCountry) index = VIDEODB_ID_COUNTRY;
+ else if (field == FieldFilename) index = VIDEODB_DETAILS_MOVIE_FILE;
+ else if (field == FieldPath) return VIDEODB_DETAILS_MOVIE_PATH;
+ else if (field == FieldPlaycount) return VIDEODB_DETAILS_MOVIE_PLAYCOUNT;
+ else if (field == FieldLastPlayed) return VIDEODB_DETAILS_MOVIE_LASTPLAYED;
+ else if (field == FieldDateAdded) return VIDEODB_DETAILS_MOVIE_DATEADDED;
+
+ if (index < 0)
+ return index;
+
+ if (asIndex)
+ {
+ // see VideoDatabase.h
+ // the first field is the item's ID and the second is the item's file ID
+ index += 2;
+ }
+ }
+ else if (mediaType == MediaTypeTvShow)
+ {
+ if (field == FieldId) return 0;
+ else if (field == FieldTitle) index = VIDEODB_ID_TV_TITLE;
+ else if (field == FieldSortTitle) index = VIDEODB_ID_TV_SORTTITLE;
+ else if (field == FieldPlot) index = VIDEODB_ID_TV_PLOT;
+ else if (field == FieldTvShowStatus) index = VIDEODB_ID_TV_STATUS;
+ else if (field == FieldVotes) index = VIDEODB_ID_TV_VOTES;
+ else if (field == FieldRating) index = VIDEODB_ID_TV_RATING;
+ else if (field == FieldYear) index = VIDEODB_ID_TV_PREMIERED;
+ else if (field == FieldGenre) index = VIDEODB_ID_TV_GENRE;
+ else if (field == FieldMPAA) index = VIDEODB_ID_TV_MPAA;
+ else if (field == FieldStudio) index = VIDEODB_ID_TV_STUDIOS;
+ else if (field == FieldPath) return VIDEODB_DETAILS_TVSHOW_PATH;
+ else if (field == FieldDateAdded) return VIDEODB_DETAILS_TVSHOW_DATEADDED;
+ else if (field == FieldLastPlayed) return VIDEODB_DETAILS_TVSHOW_LASTPLAYED;
+ else if (field == FieldNumberOfEpisodes) return VIDEODB_DETAILS_TVSHOW_NUM_EPISODES;
+ else if (field == FieldNumberOfWatchedEpisodes) return VIDEODB_DETAILS_TVSHOW_NUM_WATCHED;
+ else if (field == FieldSeason) return VIDEODB_DETAILS_TVSHOW_NUM_SEASONS;
+
+ if (index < 0)
+ return index;
+
+ if (asIndex)
+ {
+ // see VideoDatabase.h
+ // the first field is the item's ID
+ index += 1;
+ }
+ }
+ else if (mediaType == MediaTypeEpisode)
+ {
+ if (field == FieldId) return 0;
+ else if (field == FieldTitle) index = VIDEODB_ID_EPISODE_TITLE;
+ else if (field == FieldPlot) index = VIDEODB_ID_EPISODE_PLOT;
+ else if (field == FieldVotes) index = VIDEODB_ID_EPISODE_VOTES;
+ else if (field == FieldRating) index = VIDEODB_ID_EPISODE_RATING;
+ else if (field == FieldWriter) index = VIDEODB_ID_EPISODE_CREDITS;
+ else if (field == FieldAirDate) index = VIDEODB_ID_EPISODE_AIRED;
+ else if (field == FieldTime) index = VIDEODB_ID_EPISODE_RUNTIME;
+ else if (field == FieldDirector) index = VIDEODB_ID_EPISODE_DIRECTOR;
+ else if (field == FieldSeason) index = VIDEODB_ID_EPISODE_SEASON;
+ else if (field == FieldEpisodeNumber) index = VIDEODB_ID_EPISODE_EPISODE;
+ else if (field == FieldUniqueId) index = VIDEODB_ID_EPISODE_UNIQUEID;
+ else if (field == FieldEpisodeNumberSpecialSort) index = VIDEODB_ID_EPISODE_SORTEPISODE;
+ else if (field == FieldSeasonSpecialSort) index = VIDEODB_ID_EPISODE_SORTSEASON;
+ else if (field == FieldFilename) return VIDEODB_DETAILS_EPISODE_FILE;
+ else if (field == FieldPath) return VIDEODB_DETAILS_EPISODE_PATH;
+ else if (field == FieldPlaycount) return VIDEODB_DETAILS_EPISODE_PLAYCOUNT;
+ else if (field == FieldLastPlayed) return VIDEODB_DETAILS_EPISODE_LASTPLAYED;
+ else if (field == FieldDateAdded) return VIDEODB_DETAILS_EPISODE_DATEADDED;
+ else if (field == FieldTvShowTitle) return VIDEODB_DETAILS_EPISODE_TVSHOW_NAME;
+ else if (field == FieldStudio) return VIDEODB_DETAILS_EPISODE_TVSHOW_STUDIO;
+ else if (field == FieldYear) return VIDEODB_DETAILS_EPISODE_TVSHOW_AIRED;
+ else if (field == FieldMPAA) return VIDEODB_DETAILS_EPISODE_TVSHOW_MPAA;
+
+ if (index < 0)
+ return index;
+
+ if (asIndex)
+ {
+ // see VideoDatabase.h
+ // the first field is the item's ID and the second is the item's file ID
+ index += 2;
+ }
+ }
+
+ return index;
+}
diff --git a/src/utils/DatabaseUtils.h b/src/utils/DatabaseUtils.h
new file mode 100644
index 0000000000..3ac9f36183
--- /dev/null
+++ b/src/utils/DatabaseUtils.h
@@ -0,0 +1,161 @@
+#pragma once
+/*
+ * Copyright (C) 2012-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "media/MediaType.h"
+
+class CVariant;
+
+namespace dbiplus
+{
+ class Dataset;
+ class field_value;
+}
+
+typedef enum {
+ // special fields used during sorting
+ FieldUnknown = -1,
+ FieldNone = 0,
+ FieldSort, // used to store the string to use for sorting
+ FieldSortSpecial, // whether the item needs special handling (0 = no, 1 = sort on top, 2 = sort on bottom)
+ FieldLabel,
+ FieldFolder,
+ FieldMediaType,
+ FieldRow, // the row number in a dataset
+
+ // special fields not retrieved from the database
+ FieldSize,
+ FieldDate,
+ FieldDriveType,
+ FieldStartOffset,
+ FieldEndOffset,
+ FieldProgramCount,
+ FieldBitrate,
+ FieldListeners,
+ FieldPlaylist,
+ FieldVirtualFolder,
+ FieldRandom,
+ FieldDateTaken,
+
+ // fields retrievable from the database
+ FieldId,
+ FieldGenre,
+ FieldAlbum,
+ FieldArtist,
+ FieldAlbumArtist,
+ FieldTitle,
+ FieldSortTitle,
+ FieldYear,
+ FieldTime,
+ FieldTrackNumber,
+ FieldFilename,
+ FieldPath,
+ FieldPlaycount,
+ FieldLastPlayed,
+ FieldInProgress,
+ FieldRating,
+ FieldComment,
+ FieldDateAdded,
+ FieldTvShowTitle,
+ FieldPlot,
+ FieldPlotOutline,
+ FieldTagline,
+ FieldTvShowStatus,
+ FieldVotes,
+ FieldDirector,
+ FieldActor,
+ FieldStudio,
+ FieldCountry,
+ FieldMPAA,
+ FieldTop250,
+ FieldSet,
+ FieldNumberOfEpisodes,
+ FieldNumberOfWatchedEpisodes,
+ FieldWriter,
+ FieldAirDate,
+ FieldEpisodeNumber,
+ FieldUniqueId,
+ FieldSeason,
+ FieldEpisodeNumberSpecialSort,
+ FieldSeasonSpecialSort,
+ FieldReview,
+ FieldThemes,
+ FieldMoods,
+ FieldStyles,
+ FieldAlbumType,
+ FieldMusicLabel,
+ FieldTrailer,
+ FieldVideoResolution,
+ FieldVideoAspectRatio,
+ FieldVideoCodec,
+ FieldAudioChannels,
+ FieldAudioCodec,
+ FieldAudioLanguage,
+ FieldSubtitleLanguage,
+ FieldProductionCode,
+ FieldTag,
+ FieldChannelName,
+ FieldChannelNumber,
+ FieldInstruments,
+ FieldBiography,
+ FieldBorn,
+ FieldBandFormed,
+ FieldDisbanded,
+ FieldDied,
+ FieldStereoMode,
+ FieldMax
+} Field;
+
+typedef std::set<Field> Fields;
+typedef std::vector<Field> FieldList;
+
+typedef enum {
+ DatabaseQueryPartSelect,
+ DatabaseQueryPartWhere,
+ DatabaseQueryPartOrderBy,
+} DatabaseQueryPart;
+
+typedef std::map<Field, CVariant> DatabaseResult;
+typedef std::vector<DatabaseResult> DatabaseResults;
+
+class DatabaseUtils
+{
+public:
+ static MediaType MediaTypeFromVideoContentType(int videoContentType);
+
+ static std::string GetField(Field field, const MediaType &mediaType, DatabaseQueryPart queryPart);
+ static int GetField(Field field, const MediaType &mediaType);
+ static int GetFieldIndex(Field field, const MediaType &mediaType);
+ static bool GetSelectFields(const Fields &fields, const MediaType &mediaType, FieldList &selectFields);
+
+ static bool GetFieldValue(const dbiplus::field_value &fieldValue, CVariant &variantValue);
+ static bool GetDatabaseResults(const MediaType &mediaType, const FieldList &fields, const std::auto_ptr<dbiplus::Dataset> &dataset, DatabaseResults &results);
+
+ static std::string BuildLimitClause(int end, int start = 0);
+
+private:
+ static int GetField(Field field, const MediaType &mediaType, bool asIndex);
+};
diff --git a/src/utils/EdenVideoArtUpdater.cpp b/src/utils/EdenVideoArtUpdater.cpp
new file mode 100644
index 0000000000..448f390860
--- /dev/null
+++ b/src/utils/EdenVideoArtUpdater.cpp
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2012-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "EdenVideoArtUpdater.h"
+#include "video/VideoDatabase.h"
+#include "video/VideoInfoScanner.h"
+#include "FileItem.h"
+#include "utils/log.h"
+#include "utils/Crc32.h"
+#include "utils/URIUtils.h"
+#include "utils/ScraperUrl.h"
+#include "utils/StringUtils.h"
+#include "TextureCache.h"
+#include "TextureCacheJob.h"
+#include "pictures/Picture.h"
+#include "profiles/ProfilesManager.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/Settings.h"
+#include "guilib/Texture.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "filesystem/File.h"
+#include "filesystem/StackDirectory.h"
+#include "dialogs/GUIDialogExtendedProgressBar.h"
+#include "interfaces/AnnouncementManager.h"
+
+using namespace std;
+using namespace VIDEO;
+using namespace XFILE;
+
+CEdenVideoArtUpdater::CEdenVideoArtUpdater() : CThread("VideoArtUpdater")
+{
+ m_textureDB.Open();
+}
+
+CEdenVideoArtUpdater::~CEdenVideoArtUpdater()
+{
+ m_textureDB.Close();
+}
+
+void CEdenVideoArtUpdater::Start()
+{
+ CEdenVideoArtUpdater *updater = new CEdenVideoArtUpdater();
+ updater->Create(true); // autodelete
+}
+
+void CEdenVideoArtUpdater::Process()
+{
+ // grab all movies...
+ CVideoDatabase db;
+ if (!db.Open())
+ return;
+
+ CFileItemList items;
+
+ CGUIDialogExtendedProgressBar* dialog =
+ (CGUIDialogExtendedProgressBar*)g_windowManager.GetWindow(WINDOW_DIALOG_EXT_PROGRESS);
+
+ CGUIDialogProgressBarHandle *handle = dialog->GetHandle(g_localizeStrings.Get(314));
+ handle->SetTitle(g_localizeStrings.Get(12349));
+
+ // movies
+ db.GetMoviesByWhere("videodb://movies/titles/", CDatabase::Filter(), items);
+ for (int i = 0; i < items.Size(); i++)
+ {
+ CFileItemPtr item = items[i];
+ handle->SetProgress(i, items.Size());
+ handle->SetText(StringUtils::Format(g_localizeStrings.Get(12350).c_str(), item->GetLabel().c_str()));
+ string cachedThumb = GetCachedVideoThumb(*item);
+ string cachedFanart = GetCachedFanart(*item);
+
+ item->SetPath(item->GetVideoInfoTag()->m_strFileNameAndPath);
+ item->GetVideoInfoTag()->m_fanart.Unpack();
+ item->GetVideoInfoTag()->m_strPictureURL.Parse();
+
+ map<string, string> artwork;
+ if (!db.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork)
+ || (artwork.size() == 1 && artwork.find("thumb") != artwork.end()))
+ {
+ CStdString art = CVideoInfoScanner::GetImage(item.get(), true, item->GetVideoInfoTag()->m_basePath != item->GetPath(), "thumb");
+ std::string type;
+ if (CacheTexture(art, cachedThumb, item->GetLabel(), type))
+ artwork.insert(make_pair(type, art));
+
+ art = CVideoInfoScanner::GetFanart(item.get(), true);
+ if (CacheTexture(art, cachedFanart, item->GetLabel()))
+ artwork.insert(make_pair("fanart", art));
+
+ if (artwork.empty())
+ artwork.insert(make_pair("thumb", ""));
+ db.SetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork);
+ }
+ }
+ items.Clear();
+
+ // music videos
+ db.GetMusicVideosNav("videodb://musicvideos/titles/", items, false);
+ for (int i = 0; i < items.Size(); i++)
+ {
+ CFileItemPtr item = items[i];
+ handle->SetProgress(i, items.Size());
+ handle->SetText(StringUtils::Format(g_localizeStrings.Get(12350).c_str(), item->GetLabel().c_str()));
+ string cachedThumb = GetCachedVideoThumb(*item);
+ string cachedFanart = GetCachedFanart(*item);
+
+ item->SetPath(item->GetVideoInfoTag()->m_strFileNameAndPath);
+ item->GetVideoInfoTag()->m_fanart.Unpack();
+ item->GetVideoInfoTag()->m_strPictureURL.Parse();
+
+ map<string, string> artwork;
+ if (!db.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork)
+ || (artwork.size() == 1 && artwork.find("thumb") != artwork.end()))
+ {
+ CStdString art = CVideoInfoScanner::GetImage(item.get(), true, item->GetVideoInfoTag()->m_basePath != item->GetPath(), "thumb");
+ std::string type;
+ if (CacheTexture(art, cachedThumb, item->GetLabel(), type))
+ artwork.insert(make_pair(type, art));
+
+ art = CVideoInfoScanner::GetFanart(item.get(), true);
+ if (CacheTexture(art, cachedFanart, item->GetLabel()))
+ artwork.insert(make_pair("fanart", art));
+
+ if (artwork.empty())
+ artwork.insert(make_pair("thumb", ""));
+ db.SetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork);
+ }
+ }
+ items.Clear();
+
+ // tvshows
+ // count the number of episodes
+ db.GetTvShowsNav("videodb://tvshows/titles/", items);
+ for (int i = 0; i < items.Size(); i++)
+ {
+ CFileItemPtr item = items[i];
+ handle->SetText(StringUtils::Format(g_localizeStrings.Get(12350).c_str(), item->GetLabel().c_str()));
+ string cachedThumb = GetCachedVideoThumb(*item);
+ string cachedFanart = GetCachedFanart(*item);
+
+ item->SetPath(item->GetVideoInfoTag()->m_strPath);
+ item->GetVideoInfoTag()->m_fanart.Unpack();
+ item->GetVideoInfoTag()->m_strPictureURL.Parse();
+
+ map<string, string> artwork;
+ if (!db.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork)
+ || (artwork.size() == 1 && artwork.find("thumb") != artwork.end()))
+ {
+ CStdString art = CVideoInfoScanner::GetImage(item.get(), true, false, "thumb");
+ std::string type;
+ if (CacheTexture(art, cachedThumb, item->GetLabel(), type))
+ artwork.insert(make_pair(type, art));
+
+ art = CVideoInfoScanner::GetFanart(item.get(), true);
+ if (CacheTexture(art, cachedFanart, item->GetLabel()))
+ artwork.insert(make_pair("fanart", art));
+
+ if (artwork.empty())
+ artwork.insert(make_pair("thumb", ""));
+ db.SetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork);
+ }
+
+ // now season art...
+ map<int, map<string, string> > seasons;
+ vector<string> artTypes; artTypes.push_back("thumb");
+ CVideoInfoScanner::GetSeasonThumbs(*item->GetVideoInfoTag(), seasons, artTypes, true);
+ for (map<int, map<string, string> >::const_iterator j = seasons.begin(); j != seasons.end(); ++j)
+ {
+ if (j->second.empty())
+ continue;
+ int idSeason = db.AddSeason(item->GetVideoInfoTag()->m_iDbId, j->first);
+ map<string, string> seasonArt;
+ if (idSeason > -1 && !db.GetArtForItem(idSeason, MediaTypeSeason, seasonArt))
+ {
+ std::string cachedSeason = GetCachedSeasonThumb(j->first, item->GetVideoInfoTag()->m_strPath);
+ std::string type;
+ std::string originalUrl = j->second.begin()->second;
+ if (CacheTexture(originalUrl, cachedSeason, "", type))
+ db.SetArtForItem(idSeason, MediaTypeSeason, type, originalUrl);
+ }
+ }
+
+ // now episodes...
+ CFileItemList items2;
+ db.GetEpisodesByWhere("videodb://tvshows/titles/-1/-1/", db.PrepareSQL("episodeview.idShow=%d", item->GetVideoInfoTag()->m_iDbId), items2);
+ for (int j = 0; j < items2.Size(); j++)
+ {
+ handle->SetProgress(j, items2.Size());
+ CFileItemPtr episode = items2[j];
+ string cachedThumb = GetCachedEpisodeThumb(*episode);
+ if (!CFile::Exists(cachedThumb))
+ cachedThumb = GetCachedVideoThumb(*episode);
+ episode->SetPath(episode->GetVideoInfoTag()->m_strFileNameAndPath);
+ episode->GetVideoInfoTag()->m_strPictureURL.Parse();
+
+ map<string, string> artwork;
+ if (!db.GetArtForItem(episode->GetVideoInfoTag()->m_iDbId, episode->GetVideoInfoTag()->m_type, artwork)
+ || (artwork.size() == 1 && artwork.find("thumb") != artwork.end()))
+ {
+ CStdString art = CVideoInfoScanner::GetImage(episode.get(), true, episode->GetVideoInfoTag()->m_basePath != episode->GetPath(), "thumb");
+ if (CacheTexture(art, cachedThumb, episode->GetLabel()))
+ artwork.insert(make_pair("thumb", art));
+ else
+ artwork.insert(make_pair("thumb", ""));
+ db.SetArtForItem(episode->GetVideoInfoTag()->m_iDbId, episode->GetVideoInfoTag()->m_type, artwork);
+ }
+ }
+ }
+ items.Clear();
+
+ // now sets
+ db.GetSetsNav("videodb://movies/sets/", items, VIDEODB_CONTENT_MOVIES);
+ for (int i = 0; i < items.Size(); i++)
+ {
+ CFileItemPtr item = items[i];
+ handle->SetProgress(i, items.Size());
+ handle->SetText(StringUtils::Format(g_localizeStrings.Get(12350).c_str(), item->GetLabel().c_str()));
+ map<string, string> artwork;
+ if (!db.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork))
+ { // grab the first movie from this set
+ CFileItemList items2;
+ db.GetMoviesNav("videodb://movies/titles/", items2, -1, -1, -1, -1, -1, -1, item->GetVideoInfoTag()->m_iDbId);
+ if (items2.Size() > 1)
+ {
+ if (db.GetArtForItem(items2[0]->GetVideoInfoTag()->m_iDbId, items2[0]->GetVideoInfoTag()->m_type, artwork))
+ db.SetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork);
+ }
+ }
+ }
+ items.Clear();
+
+ // now actors
+ if (CSettings::Get().GetBool("videolibrary.actorthumbs"))
+ {
+ db.GetActorsNav("videodb://movies/titles/", items, VIDEODB_CONTENT_MOVIES);
+ db.GetActorsNav("videodb://tvshows/titles/", items, VIDEODB_CONTENT_TVSHOWS);
+ db.GetActorsNav("videodb://tvshows/titles/", items, VIDEODB_CONTENT_EPISODES);
+ db.GetActorsNav("videodb://musicvideos/titles/", items, VIDEODB_CONTENT_MUSICVIDEOS);
+ for (int i = 0; i < items.Size(); i++)
+ {
+ CFileItemPtr item = items[i];
+ handle->SetProgress(i, items.Size());
+ handle->SetText(StringUtils::Format(g_localizeStrings.Get(12350).c_str(), item->GetLabel().c_str()));
+ map<string, string> artwork;
+ if (!db.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork))
+ {
+ item->GetVideoInfoTag()->m_strPictureURL.Parse();
+ string cachedThumb = GetCachedActorThumb(*item);
+
+ string art = CScraperUrl::GetThumbURL(item->GetVideoInfoTag()->m_strPictureURL.GetFirstThumb());
+ if (CacheTexture(art, cachedThumb, item->GetLabel()))
+ artwork.insert(make_pair("thumb", art));
+ else
+ artwork.insert(make_pair("thumb", ""));
+ db.SetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork);
+ }
+ }
+ }
+ handle->MarkFinished();
+
+ ANNOUNCEMENT::CAnnouncementManager::Get().Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnScanFinished");
+
+ items.Clear();
+}
+
+bool CEdenVideoArtUpdater::CacheTexture(std::string &originalUrl, const std::string &cachedFile, const std::string &label)
+{
+ std::string type;
+ return CacheTexture(originalUrl, cachedFile, label, type);
+}
+
+bool CEdenVideoArtUpdater::CacheTexture(std::string &originalUrl, const std::string &cachedFile, const std::string &label, std::string &type)
+{
+ if (!CFile::Exists(cachedFile))
+ {
+ CLog::Log(LOGERROR, "%s No cached art for item %s (should be %s)", __FUNCTION__, label.c_str(), cachedFile.c_str());
+ return false;
+ }
+ if (originalUrl.empty())
+ {
+ originalUrl = GetThumb(cachedFile, "http://unknown/video/", true);
+ CLog::Log(LOGERROR, "%s No original url for item %s, but cached art exists, using %s", __FUNCTION__, label.c_str(), originalUrl.c_str());
+ }
+
+ CTextureDetails details;
+ details.updateable = false;
+ details.hash = "NOHASH";
+ type = "thumb"; // unknown art type
+
+ CBaseTexture *texture = CTextureCacheJob::LoadImage(cachedFile, 0, 0, "");
+ if (texture)
+ {
+ if (texture->HasAlpha())
+ details.file = CTextureCache::GetCacheFile(originalUrl) + ".png";
+ else
+ details.file = CTextureCache::GetCacheFile(originalUrl) + ".jpg";
+
+ CLog::Log(LOGDEBUG, "Caching image '%s' ('%s') to '%s' for item '%s'", originalUrl.c_str(), cachedFile.c_str(), details.file.c_str(), label.c_str());
+
+ uint32_t width = 0, height = 0;
+ if (CPicture::CacheTexture(texture, width, height, CTextureCache::GetCachedPath(details.file)))
+ {
+ details.width = width;
+ details.height = height;
+ type = CVideoInfoScanner::GetArtTypeFromSize(details.width, details.height);
+ delete texture;
+ m_textureDB.AddCachedTexture(originalUrl, details);
+ return true;
+ }
+ }
+ CLog::Log(LOGERROR, "Can't cache image '%s' ('%s') for item '%s'", originalUrl.c_str(), cachedFile.c_str(), label.c_str());
+ return false;
+}
+
+CStdString CEdenVideoArtUpdater::GetCachedActorThumb(const CFileItem &item)
+{
+ return GetThumb("actor" + item.GetLabel(), CProfilesManager::Get().GetVideoThumbFolder(), true);
+}
+
+CStdString CEdenVideoArtUpdater::GetCachedSeasonThumb(int season, const CStdString &path)
+{
+ CStdString label;
+ if (season == -1)
+ label = g_localizeStrings.Get(20366);
+ else if (season == 0)
+ label = g_localizeStrings.Get(20381);
+ else
+ label = StringUtils::Format(g_localizeStrings.Get(20358).c_str(), season);
+ return GetThumb("season" + path + label, CProfilesManager::Get().GetVideoThumbFolder(), true);
+}
+
+CStdString CEdenVideoArtUpdater::GetCachedEpisodeThumb(const CFileItem &item)
+{
+ // get the locally cached thumb
+ CStdString strCRC = StringUtils::Format("%sepisode%i",
+ item.GetVideoInfoTag()->m_strFileNameAndPath.c_str(),
+ item.GetVideoInfoTag()->m_iEpisode);
+ return GetThumb(strCRC, CProfilesManager::Get().GetVideoThumbFolder(), true);
+}
+
+CStdString CEdenVideoArtUpdater::GetCachedVideoThumb(const CFileItem &item)
+{
+ if (item.m_bIsFolder && !item.GetVideoInfoTag()->m_strPath.empty())
+ return GetThumb(item.GetVideoInfoTag()->m_strPath, CProfilesManager::Get().GetVideoThumbFolder(), true);
+ else if (!item.GetVideoInfoTag()->m_strFileNameAndPath.empty())
+ {
+ CStdString path = item.GetVideoInfoTag()->m_strFileNameAndPath;
+ if (URIUtils::IsStack(path))
+ path = CStackDirectory::GetFirstStackedFile(path);
+ return GetThumb(path, CProfilesManager::Get().GetVideoThumbFolder(), true);
+ }
+ return GetThumb(item.GetPath(), CProfilesManager::Get().GetVideoThumbFolder(), true);
+}
+
+CStdString CEdenVideoArtUpdater::GetCachedFanart(const CFileItem &item)
+{
+ if (!item.GetVideoInfoTag()->m_artist.empty())
+ return GetThumb(StringUtils::Join(item.GetVideoInfoTag()->m_artist, g_advancedSettings.m_videoItemSeparator), URIUtils::AddFileToFolder(CProfilesManager::Get().GetThumbnailsFolder(), "Music/Fanart/"), false);
+ CStdString path = item.GetVideoInfoTag()->GetPath();
+ if (path.empty())
+ return "";
+ return GetThumb(path, URIUtils::AddFileToFolder(CProfilesManager::Get().GetVideoThumbFolder(), "Fanart/"), false);
+}
+
+CStdString CEdenVideoArtUpdater::GetThumb(const CStdString &path, const CStdString &path2, bool split)
+{
+ // get the locally cached thumb
+ Crc32 crc;
+ crc.ComputeFromLowerCase(path);
+
+ CStdString thumb;
+ if (split)
+ {
+ CStdString hex = StringUtils::Format("%08x", (__int32)crc);
+ thumb = StringUtils::Format("%c\\%08x.tbn", hex[0], (unsigned __int32)crc);
+ }
+ else
+ thumb = StringUtils::Format("%08x.tbn", (unsigned __int32)crc);
+
+ return URIUtils::AddFileToFolder(path2, thumb);
+}
diff --git a/src/utils/EdenVideoArtUpdater.h b/src/utils/EdenVideoArtUpdater.h
new file mode 100644
index 0000000000..312ff593b3
--- /dev/null
+++ b/src/utils/EdenVideoArtUpdater.h
@@ -0,0 +1,56 @@
+#pragma once
+/*
+ * Copyright (C) 2012-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string>
+#include "threads/Thread.h"
+#include "TextureDatabase.h"
+
+class CFileItem;
+
+class CEdenVideoArtUpdater : CThread
+{
+public:
+ CEdenVideoArtUpdater();
+ ~CEdenVideoArtUpdater();
+
+ static void Start();
+
+ virtual void Process();
+
+private:
+ /*! \brief Caches the texture from oldCachedFile as if it came from originalUrl into the texture cache.
+ \param originalUrl [in/out] the url that we think the oldCachedFile came from. May be set if it's empty and an oldCachedFile exists.
+ \param oldCachedFile the old cached file
+ \param label the label of the item for logging
+ \param type [out] the type of art (poster/banner/thumb)
+ */
+ bool CacheTexture(std::string &originalUrl, const std::string &cachedFile, const std::string &label, std::string &type);
+ bool CacheTexture(std::string &originalUrl, const std::string &oldCachedFile, const std::string &label);
+
+ CStdString GetCachedActorThumb(const CFileItem &item);
+ CStdString GetCachedSeasonThumb(int season, const CStdString &path);
+ CStdString GetCachedEpisodeThumb(const CFileItem &item);
+ CStdString GetCachedVideoThumb(const CFileItem &item);
+ CStdString GetCachedFanart(const CFileItem &item);
+ CStdString GetThumb(const CStdString &path, const CStdString &path2, bool split /* = false */);
+
+ CTextureDatabase m_textureDB;
+};
diff --git a/src/utils/EndianSwap.cpp b/src/utils/EndianSwap.cpp
new file mode 100644
index 0000000000..7f6b1b6c99
--- /dev/null
+++ b/src/utils/EndianSwap.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2012-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "EndianSwap.h"
+
+/* based on libavformat/spdif.c */
+void Endian_Swap16_buf(uint16_t *dst, uint16_t *src, int w)
+{
+ int i;
+
+ for (i = 0; i + 8 <= w; i += 8) {
+ dst[i + 0] = Endian_Swap16(src[i + 0]);
+ dst[i + 1] = Endian_Swap16(src[i + 1]);
+ dst[i + 2] = Endian_Swap16(src[i + 2]);
+ dst[i + 3] = Endian_Swap16(src[i + 3]);
+ dst[i + 4] = Endian_Swap16(src[i + 4]);
+ dst[i + 5] = Endian_Swap16(src[i + 5]);
+ dst[i + 6] = Endian_Swap16(src[i + 6]);
+ dst[i + 7] = Endian_Swap16(src[i + 7]);
+ }
+
+ for (; i < w; i++)
+ dst[i + 0] = Endian_Swap16(src[i + 0]);
+}
+
diff --git a/src/utils/EndianSwap.h b/src/utils/EndianSwap.h
new file mode 100644
index 0000000000..8ab6398a1c
--- /dev/null
+++ b/src/utils/EndianSwap.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+ /* Endian_SwapXX functions taken from SDL (SDL_endian.h) */
+
+#ifndef __ENDIAN_SWAP_H__
+#define __ENDIAN_SWAP_H__
+
+/* Include config.h to define (or not) WORDS_BIGENDIAN
+ File created by configure */
+#if defined(TARGET_POSIX)
+#include "config.h"
+#include <inttypes.h>
+#endif
+#ifdef TARGET_WINDOWS
+#define __inline__ __inline
+#include <stdint.h>
+#endif
+
+
+/* Set up for C function definitions, even when using C++ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(__GNUC__) && (defined(__powerpc__) || defined(__ppc__))
+static __inline__ uint16_t Endian_Swap16(uint16_t x)
+{
+ uint16_t result;
+
+ __asm__("rlwimi %0,%2,8,16,23" : "=&r" (result) : "0" (x >> 8), "r" (x));
+ return result;
+}
+
+static __inline__ uint32_t Endian_Swap32(uint32_t x)
+{
+ uint32_t result;
+
+ __asm__("rlwimi %0,%2,24,16,23" : "=&r" (result) : "0" (x>>24), "r" (x));
+ __asm__("rlwimi %0,%2,8,8,15" : "=&r" (result) : "0" (result), "r" (x));
+ __asm__("rlwimi %0,%2,24,0,7" : "=&r" (result) : "0" (result), "r" (x));
+ return result;
+}
+#else
+static __inline__ uint16_t Endian_Swap16(uint16_t x) {
+ return((x<<8)|(x>>8));
+}
+
+static __inline__ uint32_t Endian_Swap32(uint32_t x) {
+ return((x<<24)|((x<<8)&0x00FF0000)|((x>>8)&0x0000FF00)|(x>>24));
+}
+#endif
+
+static __inline__ uint64_t Endian_Swap64(uint64_t x) {
+ uint32_t hi, lo;
+
+ /* Separate into high and low 32-bit values and swap them */
+ lo = (uint32_t)(x&0xFFFFFFFF);
+ x >>= 32;
+ hi = (uint32_t)(x&0xFFFFFFFF);
+ x = Endian_Swap32(lo);
+ x <<= 32;
+ x |= Endian_Swap32(hi);
+ return(x);
+
+}
+
+void Endian_Swap16_buf(uint16_t *dst, uint16_t *src, int w);
+
+#ifndef WORDS_BIGENDIAN
+#define Endian_SwapLE16(X) (X)
+#define Endian_SwapLE32(X) (X)
+#define Endian_SwapLE64(X) (X)
+#define Endian_SwapBE16(X) Endian_Swap16(X)
+#define Endian_SwapBE32(X) Endian_Swap32(X)
+#define Endian_SwapBE64(X) Endian_Swap64(X)
+#else
+#define Endian_SwapLE16(X) Endian_Swap16(X)
+#define Endian_SwapLE32(X) Endian_Swap32(X)
+#define Endian_SwapLE64(X) Endian_Swap64(X)
+#define Endian_SwapBE16(X) (X)
+#define Endian_SwapBE32(X) (X)
+#define Endian_SwapBE64(X) (X)
+#endif
+
+/* Ends C function definitions when using C++ */
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ENDIAN_SWAP_H__ */
diff --git a/src/utils/Environment.cpp b/src/utils/Environment.cpp
new file mode 100644
index 0000000000..149b399e8c
--- /dev/null
+++ b/src/utils/Environment.cpp
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/**
+ * \file utils\Environment.cpp
+ * \brief Implements CEnvironment class functions.
+ *
+ * Some ideas were inspired by PostgreSQL's pgwin32_putenv function.
+ * Refined, updated, enhanced and modified for XBMC by Karlson2k.
+ */
+
+#include "Environment.h"
+#include <stdlib.h>
+#ifdef TARGET_WINDOWS
+#include <Windows.h>
+#include "utils\SystemInfo.h"
+#endif
+
+// --------------------- Helper Functions ---------------------
+
+#ifdef TARGET_WINDOWS
+
+std::wstring CEnvironment::win32ConvertUtf8ToW(const std::string &text, bool *resultSuccessful /* = NULL*/)
+{
+ if (text.empty())
+ {
+ if (resultSuccessful != NULL)
+ *resultSuccessful = true;
+ return L"";
+ }
+ if (resultSuccessful != NULL)
+ *resultSuccessful = false;
+
+ int bufSize = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, text.c_str(), -1, NULL, 0);
+ if (bufSize == 0)
+ return L"";
+ wchar_t *converted = new wchar_t[bufSize];
+ if (MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, text.c_str(), -1, converted, bufSize) != bufSize)
+ {
+ delete[] converted;
+ return L"";
+ }
+
+ std::wstring Wret (converted);
+ delete[] converted;
+
+ if (resultSuccessful != NULL)
+ *resultSuccessful = true;
+ return Wret;
+}
+
+std::string CEnvironment::win32ConvertWToUtf8(const std::wstring &text, bool *resultSuccessful /*= NULL*/)
+{
+ if (text.empty())
+ {
+ if (resultSuccessful != NULL)
+ *resultSuccessful = true;
+ return "";
+ }
+ if (resultSuccessful != NULL)
+ *resultSuccessful = false;
+
+ int bufSize = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, text.c_str(), -1, NULL, 0, NULL, NULL);
+ if (bufSize == 0)
+ return "";
+ char * converted = new char[bufSize];
+ if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, text.c_str(), -1, converted, bufSize, NULL, NULL) != bufSize)
+ {
+ delete[] converted;
+ return "";
+ }
+
+ std::string ret(converted);
+ delete[] converted;
+
+ if (resultSuccessful != NULL)
+ *resultSuccessful = true;
+ return ret;
+}
+
+// --------------------- Internal Function ---------------------
+
+typedef int (_cdecl * wputenvPtr) (const wchar_t *envstring);
+
+/**
+ * \fn int CEnvironment::win32_setenv(const std::wstring &name, const std::wstring &value = L"",
+ * updateAction action = autoDetect)
+ * \brief Internal function used to manipulate with environment variables on win32.
+ *
+ * This function make all dirty work with setting, deleting and modifying environment variables.
+ *
+ * \param name The environment variable name.
+ * \param value (optional) the new value of environment variable.
+ * \param action (optional) the action.
+ * \return Zero on success, 2 if at least one external runtime update failed, 4 if process
+ * environment update failed, 8 if our runtime environment update failed or, in case of
+ * several errors, sum of all errors values; non-zero in case of other errors.
+ */
+int CEnvironment::win32_setenv(const std::string &name, const std::string &value /* = "" */, enum updateAction action /* = autoDetect */)
+{
+ std::wstring Wname (win32ConvertUtf8ToW(name));
+ if (Wname.empty() || name.find('=') != std::wstring::npos)
+ return -1;
+ if ( (action == addOnly || action == addOrUpdateOnly) && value.empty() )
+ return -1;
+ if (action == addOnly && !(getenv(name).empty()) )
+ return 0;
+
+ bool convIsOK;
+ std::wstring Wvalue (win32ConvertUtf8ToW(value,&convIsOK));
+ if (!convIsOK)
+ return -1;
+
+ int retValue = 0;
+ std::wstring EnvString;
+ if (action == deleteVariable)
+ EnvString = Wname + L"=";
+ else
+ EnvString = Wname + L"=" + Wvalue;
+
+ static const wchar_t *modulesList[] =
+ {
+ /*{ L"msvcrt20.dll" }, // Visual C++ 2.0 / 2.1 / 2.2
+ { L"msvcrt40.dll" }, // Visual C++ 4.0 / 4.1 */ // too old and no UNICODE support - ignoring
+ { L"msvcrt.dll" }, // Visual Studio 6.0 / MinGW[-w64]
+ { L"msvcr70.dll" }, // Visual Studio 2002
+ { L"msvcr71.dll" }, // Visual Studio 2003
+ { L"msvcr80.dll" }, // Visual Studio 2005
+ { L"msvcr90.dll" }, // Visual Studio 2008
+ { L"msvcr100.dll" }, // Visual Studio 2010
+#ifdef _DEBUG
+ { L"msvcr100d.dll" },// Visual Studio 2010 (debug)
+#endif
+ { L"msvcr110.dll" }, // Visual Studio 2012
+#ifdef _DEBUG
+ { L"msvcr110d.dll" },// Visual Studio 2012 (debug)
+#endif
+ { L"msvcr120.dll" }, // Visual Studio 2013
+#ifdef _DEBUG
+ { L"msvcr120d.dll" },// Visual Studio 2013 (debug)
+#endif
+ { NULL } // Terminating NULL for list
+ };
+
+ // Check all modules each function run, because modules can be loaded/unloaded at runtime
+ for (int i = 0; modulesList[i]; i++)
+ {
+ HMODULE hModule;
+ if (!GetModuleHandleExW(0, modulesList[i], &hModule) || hModule == NULL) // Flag 0 ensures that module will be kept loaded until it'll be freed
+ continue; // Module not loaded
+
+ wputenvPtr wputenvFunc = (wputenvPtr) GetProcAddress(hModule, "_wputenv");
+ if (wputenvFunc != NULL && wputenvFunc(EnvString.c_str()) != 0)
+ retValue |= 2; // At lest one external runtime library Environment update failed
+ FreeLibrary(hModule);
+ }
+
+ // Update process Environment used for current process and for future new child processes
+ if (action == deleteVariable || value.empty())
+ retValue += SetEnvironmentVariableW(Wname.c_str(), NULL) ? 0 : 4; // 4 if failed
+ else
+ retValue += SetEnvironmentVariableW(Wname.c_str(), Wvalue.c_str()) ? 0 : 4; // 4 if failed
+
+ // Finally update our runtime Environment
+ retValue += (::_wputenv(EnvString.c_str()) == 0) ? 0 : 8; // 8 if failed
+
+ return retValue;
+}
+#endif
+
+// --------------------- Main Functions ---------------------
+
+int CEnvironment::setenv(const std::string &name, const std::string &value, int overwrite /*= 1*/)
+{
+#ifdef TARGET_WINDOWS
+ return (win32_setenv(name, value, overwrite ? autoDetect : addOnly)==0) ? 0 : -1;
+#else
+ if (value.empty() && overwrite != 0)
+ return ::unsetenv(name.c_str());
+ return ::setenv(name.c_str(), value.c_str(), overwrite);
+#endif
+}
+
+std::string CEnvironment::getenv(const std::string &name)
+{
+#ifdef TARGET_WINDOWS
+ std::wstring Wname (win32ConvertUtf8ToW(name));
+ if (Wname.empty())
+ return "";
+
+ wchar_t * wStr = ::_wgetenv(Wname.c_str());
+ if (wStr != NULL)
+ return win32ConvertWToUtf8(wStr);
+
+ // Not found in Environment of runtime library
+ // Try Environment of process as fallback
+ unsigned int varSize = GetEnvironmentVariableW(Wname.c_str(), NULL, 0);
+ if (varSize == 0)
+ return ""; // Not found
+ wchar_t * valBuf = new wchar_t[varSize];
+ if (GetEnvironmentVariableW(Wname.c_str(), valBuf, varSize) != varSize-1)
+ {
+ delete[] valBuf;
+ return "";
+ }
+ std::wstring Wvalue (valBuf);
+ delete[] valBuf;
+
+ return win32ConvertWToUtf8(Wvalue);
+#else
+ char * str = ::getenv(name.c_str());
+ if (str == NULL)
+ return "";
+ return str;
+#endif
+}
+
+int CEnvironment::unsetenv(const std::string &name)
+{
+#ifdef TARGET_WINDOWS
+ return (win32_setenv(name, "", deleteVariable)) == 0 ? 0 : -1;
+#else
+ return ::unsetenv(name.c_str());
+#endif
+}
+
+int CEnvironment::putenv(const std::string &envstring)
+{
+ if (envstring.empty())
+ return 0;
+ size_t pos = envstring.find('=');
+ if (pos == 0) // '=' is the first character
+ return -1;
+ if (pos == std::string::npos)
+ return unsetenv(envstring);
+ if (pos == envstring.length()-1) // '=' is in last position
+ {
+ std::string name(envstring);
+ name.erase(name.length()-1, 1);
+ return unsetenv(name);
+ }
+ std::string name(envstring, 0, pos), value(envstring, pos+1);
+
+ return setenv(name, value);
+}
+
diff --git a/src/utils/Environment.h b/src/utils/Environment.h
new file mode 100644
index 0000000000..30aea82e66
--- /dev/null
+++ b/src/utils/Environment.h
@@ -0,0 +1,106 @@
+#pragma once
+#ifndef XBMC_SETENV_H
+#define XBMC_SETENV_H
+/*
+ * Copyright (C) 2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/**
+ * \file utils\Environment.h
+ * \brief Declares CEnvironment class for platform-independent environment variables manipulations.
+ *
+ */
+#include <string>
+
+/**
+ * @class CEnvironment
+ *
+ * @brief Platform-independent environment variables manipulations.
+ *
+ * Provide analog for POSIX functions:
+ * + setenv
+ * + unsetenv
+ * + putenv
+ * + getenv
+ *
+ * You can generally use the functions as you would normally in POSIX-style.
+ * The differences below are just to make things more convenient through use of std::string (2,3),
+ * and to also allow the Win32-style of unsetting variables (4,5) if wanted.
+ * 1. CEnvironment::setenv parameter 'overwrite' is optional, set by default to 1 (allow overwrite).
+ * 2. CEnvironment::putenv uses copy of provided string (rather than string itself) to change environment,
+ * so you can free parameter variable right after call of function.
+ * 3. CEnvironment::getenv returns a copy of environment variable value instead of pointer to value.
+ * 4. CEnvironment::setenv can be used to unset variables. Just pass empty string for 'value' parameter.
+ * 5. CEnvironment::putenv can be used to unset variables. Set parameter to 'var=' (Windows style) or
+ * just 'var' (POSIX style), and 'var' will be unset.
+ *
+ * All 'std::string' types are supposed to be in UTF-8 encoding.
+ * All functions work on all platforms. Special care is taken on Windows platform where Environment is changed for process itself,
+ * for process runtime library and for all runtime libraries (MSVCRT) loaded by third-party modules.
+ * Functions internally make all necessary UTF-8 <-> wide conversions.*
+ */
+
+class CEnvironment
+{
+public:
+ /**
+ * \fn static int CEnvironment::setenv(const std::string &name, const std::string &value,
+ * int overwrite = 1);
+ * \brief Sets or unsets environment variable.
+ * \param name The environment variable name to add/modify/delete.
+ * \param value The environment variable new value. If set to empty string, variable will be
+ * deleted from the environment.
+ * \param overwrite (optional) If set to non-zero, existing variable will be overwritten. If set to zero and
+ * variable is already present, then variable will be unchanged and function returns success.
+ * \return Zero on success, non-zero on error.
+ */
+ static int setenv(const std::string &name, const std::string &value, int overwrite = 1);
+ /**
+ * \fn static int CEnvironment::unsetenv(const std::string &name);
+ * \brief Deletes environment variable.
+ * \param name The environment variable name to delete.
+ * \return Zero on success, non-zero on error.
+ */
+ static int unsetenv(const std::string &name);
+
+ /**
+ * \fn static int CEnvironment:putenv(const std::string &envstring);
+ * \brief Adds/modifies/deletes environment variable.
+ * \param envstring The variable-value string in form 'var=value'. If set to 'var=' or 'var', then variable
+ * will be deleted from the environment.
+ * \return Zero on success, non-zero on error.
+ */
+ static int putenv(const std::string &envstring);
+ /**
+ * \fn static std::string CEnvironment::getenv(const std::string &name);
+ * \brief Gets value of environment variable in UTF-8 encoding.
+ * \param name The name of environment variable.
+ * \return Copy of of environment variable value or empty string if variable in not present in environment.
+ * \sa xbmc_getenvUtf8, xbmc_getenvW
+ */
+ static std::string getenv(const std::string &name);
+#ifdef TARGET_WINDOWS
+private:
+ static std::wstring win32ConvertUtf8ToW(const std::string &text, bool *resultSuccessful = NULL);
+ static std::string win32ConvertWToUtf8(const std::wstring &text, bool *resultSuccessful = NULL);
+ enum updateAction:int {addOrUpdateOnly = -2, deleteVariable = -1, addOnly = 0, autoDetect = 1};
+ static int win32_setenv(const std::string &name, const std::string &value = "", updateAction action = autoDetect);
+#endif // TARGET_WINDOWS
+};
+#endif
diff --git a/src/utils/Fanart.cpp b/src/utils/Fanart.cpp
new file mode 100644
index 0000000000..9a86bd052a
--- /dev/null
+++ b/src/utils/Fanart.cpp
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "Fanart.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/XMLUtils.h"
+#include "URIUtils.h"
+#include "StringUtils.h"
+
+const unsigned int CFanart::max_fanart_colors=3;
+
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/// CFanart Functions
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+CFanart::CFanart()
+{
+}
+
+void CFanart::Pack()
+{
+ // Take our data and pack it into the m_xml string
+ m_xml.clear();
+ TiXmlElement fanart("fanart");
+ for (std::vector<SFanartData>::const_iterator it = m_fanart.begin(); it != m_fanart.end(); ++it)
+ {
+ TiXmlElement thumb("thumb");
+ thumb.SetAttribute("dim", it->strResolution.c_str());
+ thumb.SetAttribute("colors", it->strColors.c_str());
+ thumb.SetAttribute("preview", it->strPreview.c_str());
+ TiXmlText text(it->strImage);
+ thumb.InsertEndChild(text);
+ fanart.InsertEndChild(thumb);
+ }
+ m_xml << fanart;
+}
+
+bool CFanart::Unpack()
+{
+ CXBMCTinyXML doc;
+ doc.Parse(m_xml);
+
+ m_fanart.clear();
+
+ TiXmlElement *fanart = doc.FirstChildElement("fanart");
+ while (fanart)
+ {
+ std::string url = XMLUtils::GetAttribute(fanart, "url");
+ TiXmlElement *fanartThumb = fanart->FirstChildElement("thumb");
+ while (fanartThumb)
+ {
+ if (!fanartThumb->NoChildren())
+ {
+ SFanartData data;
+ if (url.empty())
+ {
+ data.strImage = fanartThumb->FirstChild()->ValueStr();
+ data.strPreview = XMLUtils::GetAttribute(fanartThumb, "preview");
+ }
+ else
+ {
+ data.strImage = URIUtils::AddFileToFolder(url, fanartThumb->FirstChild()->ValueStr());
+ if (fanartThumb->Attribute("preview"))
+ data.strPreview = URIUtils::AddFileToFolder(url, fanartThumb->Attribute("preview"));
+ }
+ data.strResolution = XMLUtils::GetAttribute(fanartThumb, "dim");
+ ParseColors(XMLUtils::GetAttribute(fanartThumb, "colors"), data.strColors);
+ m_fanart.push_back(data);
+ }
+ fanartThumb = fanartThumb->NextSiblingElement("thumb");
+ }
+ fanart = fanart->NextSiblingElement("fanart");
+ }
+ return true;
+}
+
+std::string CFanart::GetImageURL(unsigned int index) const
+{
+ if (index >= m_fanart.size())
+ return "";
+
+ return m_fanart[index].strImage;
+}
+
+std::string CFanart::GetPreviewURL(unsigned int index) const
+{
+ if (index >= m_fanart.size())
+ return "";
+
+ return m_fanart[index].strPreview.empty() ? m_fanart[index].strImage : m_fanart[index].strPreview;
+}
+
+const std::string CFanart::GetColor(unsigned int index) const
+{
+ if (index >= max_fanart_colors || m_fanart.size() == 0 ||
+ m_fanart[0].strColors.size() < index*9+8)
+ return "FFFFFFFF";
+
+ // format is AARRGGBB,AARRGGBB etc.
+ return m_fanart[0].strColors.substr(index*9, 8);
+}
+
+bool CFanart::SetPrimaryFanart(unsigned int index)
+{
+ if (index >= m_fanart.size())
+ return false;
+
+ std::iter_swap(m_fanart.begin()+index, m_fanart.begin());
+
+ // repack our data
+ Pack();
+
+ return true;
+}
+
+unsigned int CFanart::GetNumFanarts() const
+{
+ return m_fanart.size();
+}
+
+bool CFanart::ParseColors(const std::string &colorsIn, std::string &colorsOut)
+{
+ // Formats:
+ // 0: XBMC ARGB Hexadecimal string comma seperated "FFFFFFFF,DDDDDDDD,AAAAAAAA"
+ // 1: The TVDB RGB Int Triplets, pipe seperate with leading/trailing pipes "|68,69,59|69,70,58|78,78,68|"
+
+ // Essentially we read the colors in using the proper format, and store them in our own fixed temporary format (3 DWORDS), and then
+ // write them back in in the specified format.
+
+ if (colorsIn.empty())
+ return false;
+
+ // check for the TVDB RGB triplets "|68,69,59|69,70,58|78,78,68|"
+ if (colorsIn[0] == '|')
+ { // need conversion
+ colorsOut.clear();
+ std::vector<std::string> strColors = StringUtils::Split(colorsIn, "|");
+ for (int i = 0; i < std::min((int)strColors.size()-1, (int)max_fanart_colors); i++)
+ { // split up each color
+ std::vector<std::string> strTriplets = StringUtils::Split(strColors[i+1], ",");
+ if (strTriplets.size() == 3)
+ { // convert
+ if (colorsOut.size())
+ colorsOut += ",";
+ colorsOut += StringUtils::Format("FF%2lx%2lx%2lx", atol(strTriplets[0].c_str()), atol(strTriplets[1].c_str()), atol(strTriplets[2].c_str()));
+ }
+ }
+ }
+ else
+ { // assume is our format
+ colorsOut = colorsIn;
+ }
+ return true;
+}
diff --git a/src/utils/Fanart.h b/src/utils/Fanart.h
new file mode 100644
index 0000000000..53464e7074
--- /dev/null
+++ b/src/utils/Fanart.h
@@ -0,0 +1,120 @@
+// Fanart.h
+//////////////////////////////////////////////////////////////////////
+
+#if !defined(FANART_H_)
+#define FANART_H_
+
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string>
+#include <vector>
+
+#pragma once
+
+///
+/// /brief CFanart is the core of fanart support and contains all fanart data for a specific show
+///
+/// CFanart stores all data related to all available fanarts for a given TV show and provides
+/// functions required to manipulate and access that data.
+/// In order to provide an interface between the fanart data and the XBMC database, all data
+/// is stored internally it its own form, as well as packed into an XML formatted string
+/// stored in the member variable m_xml.
+/// Information on multiple fanarts for a given show is stored, but XBMC only cares about the
+/// very first fanart stored. These interfaces provide a means to access the data in that first
+/// fanart record, as well as to set which fanart is the first record. Externally, all XBMC needs
+/// to care about is getting and setting that first record. Everything else is maintained internally
+/// by CFanart. This point is key to using the interface properly.
+class CFanart
+{
+public:
+ ///
+ /// Standard constructor doesn't need to do anything
+ CFanart();
+ ///
+ /// Takes the internal fanart data and packs it into an XML formatted string in m_xml
+ /// \sa m_xml
+ void Pack();
+ ///
+ /// Takes the XML formatted string m_xml and unpacks the fanart data contained into the internal data
+ /// \return A boolean indicating success or failure
+ /// \sa m_xml
+ bool Unpack();
+ ///
+ /// Retrieves the fanart full res image URL
+ /// \param index - index of image to retrieve (defaults to 0)
+ /// \return A string containing the full URL to the full resolution fanart image
+ std::string GetImageURL(unsigned int index = 0) const;
+ ///
+ /// Retrieves the fanart preview image URL, or full res image URL if that doesn't exist
+ /// \param index - index of image to retrieve (defaults to 0)
+ /// \return A string containing the full URL to the full resolution fanart image
+ std::string GetPreviewURL(unsigned int index = 0) const;
+ ///
+ /// Used to return a specified fanart theme color value
+ /// \param index: 0 based index of the color to retrieve. A fanart theme contains 3 colors, indices 0-2, arranged from darkest to lightest.
+ const std::string GetColor(unsigned int index) const;
+ ///
+ /// Sets a particular fanart to be the "primary" fanart, or in other words, sets which fanart is actually used by XBMC
+ ///
+ /// This is the one of the only instances in the public interface where there is any hint that more than one fanart exists, but its by neccesity.
+ /// \param index: 0 based index of which fanart to set as the primary fanart
+ /// \return A boolean value indicating success or failure. This should only return false if the specified index is not a valid fanart
+ bool SetPrimaryFanart(unsigned int index);
+ ///
+ /// Returns how many fanarts are stored
+ /// \return An integer indicating how many fanarts are stored in the class. Fanart indices are 0 to (GetNumFanarts() - 1)
+ unsigned int GetNumFanarts() const;
+ ///
+ /// m_xml contains an XML formatted string which is all fanart packed into one string.
+ ///
+ /// This string is the "interface" as it were to the XBMC database, and MUST be kept in sync with the rest of the class. Therefore
+ /// anytime this string is changed, the change should be followed up by a call to CFanart::UnPack(). This XML formaytted string is
+ /// also the interface used to pass the fanart data from the scraper to CFanart.
+ std::string m_xml;
+private:
+ static const unsigned int max_fanart_colors;
+ ///
+ /// Parse various color formats as returned by the sites scraped into a format we recognize
+ ///
+ /// Supported Formats:
+ ///
+ /// * The TVDB RGB Int Triplets, pipe seperate with leading/trailing pipes "|68,69,59|69,70,58|78,78,68|"
+ /// * XBMC ARGB Hexadecimal string comma seperated "FFFFFFFF,DDDDDDDD,AAAAAAAA"
+ ///
+ /// \param colorsIn: string containing colors in some format to be converted
+ /// \param colorsOut: XBMC ARGB Hexadecimal string comma seperated "FFFFFFFF,DDDDDDDD,AAAAAAAA"
+ /// \return boolean indicating success or failure.
+ static bool ParseColors(const std::string&colorsIn, std::string&colorsOut);
+
+ struct SFanartData
+ {
+ std::string strImage;
+ std::string strResolution;
+ std::string strColors;
+ std::string strPreview;
+ };
+
+ ///
+ /// std::vector that stores all our fanart data
+ std::vector<SFanartData> m_fanart;
+};
+
+#endif
diff --git a/src/utils/FileOperationJob.cpp b/src/utils/FileOperationJob.cpp
new file mode 100644
index 0000000000..099ae8fb42
--- /dev/null
+++ b/src/utils/FileOperationJob.cpp
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "FileOperationJob.h"
+#include "filesystem/File.h"
+#include "filesystem/Directory.h"
+#include "filesystem/ZipManager.h"
+#include "filesystem/FileDirectoryFactory.h"
+#include "filesystem/MultiPathDirectory.h"
+#include "filesystem/SpecialProtocol.h"
+#include "log.h"
+#include "Util.h"
+#include "URIUtils.h"
+#include "utils/StringUtils.h"
+#include "URL.h"
+#include "guilib/LocalizeStrings.h"
+#include "guilib/GUIWindowManager.h"
+#include "dialogs/GUIDialogExtendedProgressBar.h"
+
+#ifdef HAS_FILESYSTEM_RAR
+#include "filesystem/RarManager.h"
+#endif
+
+using namespace std;
+using namespace XFILE;
+
+CFileOperationJob::CFileOperationJob()
+{
+ m_action = ActionCopy;
+ m_heading = 0;
+ m_line = 0;
+ m_handle = NULL;
+ m_displayProgress = false;
+}
+
+CFileOperationJob::CFileOperationJob(FileAction action, CFileItemList & items,
+ const CStdString& strDestFile,
+ bool displayProgress,
+ int heading, int line)
+{
+ m_handle = NULL;
+ m_displayProgress = displayProgress;
+ m_heading = heading;
+ m_line = line;
+ SetFileOperation(action, items, strDestFile);
+}
+
+void CFileOperationJob::SetFileOperation(FileAction action, CFileItemList &items, const CStdString &strDestFile)
+{
+ m_action = action;
+ m_strDestFile = strDestFile;
+
+ m_items.Clear();
+ for (int i = 0; i < items.Size(); i++)
+ m_items.Add(CFileItemPtr(new CFileItem(*items[i])));
+}
+
+bool CFileOperationJob::DoWork()
+{
+ FileOperationList ops;
+ double totalTime = 0.0;
+
+ if (m_displayProgress)
+ {
+ CGUIDialogExtendedProgressBar* dialog =
+ (CGUIDialogExtendedProgressBar*)g_windowManager.GetWindow(WINDOW_DIALOG_EXT_PROGRESS);
+ m_handle = dialog->GetHandle(GetActionString(m_action));
+ }
+
+ bool success = DoProcess(m_action, m_items, m_strDestFile, ops, totalTime);
+
+ unsigned int size = ops.size();
+
+ double opWeight = 100.0 / totalTime;
+ double current = 0.0;
+
+ for (unsigned int i = 0; i < size && success; i++)
+ success &= ops[i].ExecuteOperation(this, current, opWeight);
+
+ if (m_handle)
+ m_handle->MarkFinished();
+
+ return success;
+}
+
+bool CFileOperationJob::DoProcessFile(FileAction action, const CStdString& strFileA, const CStdString& strFileB, FileOperationList &fileOperations, double &totalTime)
+{
+ int64_t time = 1;
+
+ if (action == ActionCopy || action == ActionReplace || (action == ActionMove && !CanBeRenamed(strFileA, strFileB)))
+ {
+ struct __stat64 data;
+ if(CFile::Stat(strFileA, &data) == 0)
+ time += data.st_size;
+ }
+
+ fileOperations.push_back(CFileOperation(action, strFileA, strFileB, time));
+
+ totalTime += time;
+
+ return true;
+}
+
+bool CFileOperationJob::DoProcessFolder(FileAction action, const CStdString& strPath, const CStdString& strDestFile, FileOperationList &fileOperations, double &totalTime)
+{
+ // check whether this folder is a filedirectory - if so, we don't process it's contents
+ CFileItem item(strPath, false);
+ IFileDirectory *file = CFileDirectoryFactory::Create(item.GetURL(), &item);
+ if (file)
+ {
+ delete file;
+ return true;
+ }
+ CLog::Log(LOGDEBUG,"FileManager, processing folder: %s",strPath.c_str());
+ CFileItemList items;
+ //m_rootDir.GetDirectory(strPath, items);
+ CDirectory::GetDirectory(strPath, items, "", DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_GET_HIDDEN);
+ for (int i = 0; i < items.Size(); i++)
+ {
+ CFileItemPtr pItem = items[i];
+ pItem->Select(true);
+ CLog::Log(LOGDEBUG," -- %s",pItem->GetPath().c_str());
+ }
+
+ if (!DoProcess(action, items, strDestFile, fileOperations, totalTime)) return false;
+
+ if (action == ActionMove)
+ {
+ fileOperations.push_back(CFileOperation(ActionDeleteFolder, strPath, "", 1));
+ totalTime += 1.0;
+ }
+
+ return true;
+}
+
+bool CFileOperationJob::DoProcess(FileAction action, CFileItemList & items, const CStdString& strDestFile, FileOperationList &fileOperations, double &totalTime)
+{
+ for (int iItem = 0; iItem < items.Size(); ++iItem)
+ {
+ CFileItemPtr pItem = items[iItem];
+ if (pItem->IsSelected())
+ {
+ CStdString strNoSlash = pItem->GetPath();
+ URIUtils::RemoveSlashAtEnd(strNoSlash);
+ CStdString strFileName = URIUtils::GetFileName(strNoSlash);
+
+ // special case for upnp
+ if (URIUtils::IsUPnP(items.GetPath()) || URIUtils::IsUPnP(pItem->GetPath()))
+ {
+ // get filename from label instead of path
+ strFileName = pItem->GetLabel();
+
+ if(!pItem->m_bIsFolder && !URIUtils::HasExtension(strFileName))
+ {
+ // FIXME: for now we only work well if the url has the extension
+ // we should map the content type to the extension otherwise
+ strFileName += URIUtils::GetExtension(pItem->GetPath());
+ }
+
+ strFileName = CUtil::MakeLegalFileName(strFileName);
+ }
+
+ CStdString strnewDestFile;
+ if(!strDestFile.empty()) // only do this if we have a destination
+ strnewDestFile = URIUtils::ChangeBasePath(pItem->GetPath(), strFileName, strDestFile); // Convert (URL) encoding + slashes (if source / target differ)
+
+ if (pItem->m_bIsFolder)
+ {
+ // in ActionReplace mode all subdirectories will be removed by the below
+ // DoProcessFolder(ActionDelete) call as well, so ActionCopy is enough when
+ // processing those
+ FileAction subdirAction = (action == ActionReplace) ? ActionCopy : action;
+ // create folder on dest. drive
+ if (action != ActionDelete && action != ActionDeleteFolder)
+ DoProcessFile(ActionCreateFolder, strnewDestFile, "", fileOperations, totalTime);
+ if (action == ActionReplace && CDirectory::Exists(strnewDestFile))
+ DoProcessFolder(ActionDelete, strnewDestFile, "", fileOperations, totalTime);
+ if (!DoProcessFolder(subdirAction, pItem->GetPath(), strnewDestFile, fileOperations, totalTime))
+ return false;
+ if (action == ActionDelete || action == ActionDeleteFolder)
+ DoProcessFile(ActionDeleteFolder, pItem->GetPath(), "", fileOperations, totalTime);
+ }
+ else
+ DoProcessFile(action, pItem->GetPath(), strnewDestFile, fileOperations, totalTime);
+ }
+ }
+ return true;
+}
+
+CFileOperationJob::CFileOperation::CFileOperation(FileAction action, const CStdString &strFileA, const CStdString &strFileB, int64_t time) : m_action(action), m_strFileA(strFileA), m_strFileB(strFileB), m_time(time)
+{
+}
+
+struct DataHolder
+{
+ CFileOperationJob *base;
+ double current;
+ double opWeight;
+};
+
+CStdString CFileOperationJob::GetActionString(FileAction action)
+{
+ CStdString result;
+ switch (action)
+ {
+ case ActionCopy:
+ case ActionReplace:
+ result = g_localizeStrings.Get(115);
+ break;
+ case ActionMove:
+ result = g_localizeStrings.Get(116);
+ break;
+ case ActionDelete:
+ case ActionDeleteFolder:
+ result = g_localizeStrings.Get(117);
+ break;
+ case ActionCreateFolder:
+ result = g_localizeStrings.Get(119);
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
+
+bool CFileOperationJob::CFileOperation::ExecuteOperation(CFileOperationJob *base, double &current, double opWeight)
+{
+ bool bResult = true;
+
+ base->m_currentFile = CURL(m_strFileA).GetFileNameWithoutPath();
+ base->m_currentOperation = GetActionString(m_action);
+
+ if (base->ShouldCancel((unsigned)current, 100))
+ return false;
+
+ if (base->m_handle)
+ {
+ base->m_handle->SetText(base->GetCurrentFile());
+ base->m_handle->SetPercentage((float)current);
+ }
+
+ DataHolder data = {base, current, opWeight};
+
+ switch (m_action)
+ {
+ case ActionCopy:
+ case ActionReplace:
+ {
+ CLog::Log(LOGDEBUG,"FileManager: copy %s -> %s\n", m_strFileA.c_str(), m_strFileB.c_str());
+
+ bResult = CFile::Copy(m_strFileA, m_strFileB, this, &data);
+ }
+ break;
+ case ActionMove:
+ {
+ CLog::Log(LOGDEBUG,"FileManager: move %s -> %s\n", m_strFileA.c_str(), m_strFileB.c_str());
+
+ if (CanBeRenamed(m_strFileA, m_strFileB))
+ bResult = CFile::Rename(m_strFileA, m_strFileB);
+ else if (CFile::Copy(m_strFileA, m_strFileB, this, &data))
+ bResult = CFile::Delete(m_strFileA);
+ else
+ bResult = false;
+ }
+ break;
+ case ActionDelete:
+ {
+ CLog::Log(LOGDEBUG,"FileManager: delete %s\n", m_strFileA.c_str());
+
+ bResult = CFile::Delete(m_strFileA);
+ }
+ break;
+ case ActionDeleteFolder:
+ {
+ CLog::Log(LOGDEBUG,"FileManager: delete folder %s\n", m_strFileA.c_str());
+
+ bResult = CDirectory::Remove(m_strFileA);
+ }
+ break;
+ case ActionCreateFolder:
+ {
+ CLog::Log(LOGDEBUG,"FileManager: create folder %s\n", m_strFileA.c_str());
+
+ bResult = CDirectory::Create(m_strFileA);
+ }
+ break;
+ }
+
+ current += (double)m_time * opWeight;
+
+ return bResult;
+}
+
+inline bool CFileOperationJob::CanBeRenamed(const CStdString &strFileA, const CStdString &strFileB)
+{
+#ifndef TARGET_POSIX
+ if (strFileA[1] == ':' && strFileA[0] == strFileB[0])
+ return true;
+#else
+ if (URIUtils::IsHD(strFileA) && URIUtils::IsHD(strFileB))
+ return true;
+#endif
+ return false;
+}
+
+void CFileOperationJob::CFileOperation::Debug()
+{
+ printf("%i | %s > %s\n", m_action, m_strFileA.c_str(), m_strFileB.c_str());
+}
+
+bool CFileOperationJob::CFileOperation::OnFileCallback(void* pContext, int ipercent, float avgSpeed)
+{
+ DataHolder *data = (DataHolder *)pContext;
+ double current = data->current + ((double)ipercent * data->opWeight * (double)m_time)/ 100.0;
+
+ if (avgSpeed > 1000000.0f)
+ data->base->m_avgSpeed = StringUtils::Format("%.1f MB/s", avgSpeed / 1000000.0f);
+ else
+ data->base->m_avgSpeed = StringUtils::Format("%.1f KB/s", avgSpeed / 1000.0f);
+
+ if (data->base->m_handle)
+ {
+ CStdString line;
+ line = StringUtils::Format("%s (%s)",
+ data->base->GetCurrentFile().c_str(),
+ data->base->GetAverageSpeed().c_str());
+ data->base->m_handle->SetText(line);
+ data->base->m_handle->SetPercentage((float)current);
+ }
+
+ return !data->base->ShouldCancel((unsigned)current, 100);
+}
+
+bool CFileOperationJob::operator==(const CJob* job) const
+{
+ if (strcmp(job->GetType(),GetType()) == 0)
+ {
+ const CFileOperationJob* rjob = dynamic_cast<const CFileOperationJob*>(job);
+ if (rjob)
+ {
+ if (GetAction() == rjob->GetAction() &&
+ m_strDestFile == rjob->m_strDestFile &&
+ m_items.Size() == rjob->m_items.Size())
+ {
+ for (int i=0;i<m_items.Size();++i)
+ {
+ if (m_items[i]->GetPath() != rjob->m_items[i]->GetPath() ||
+ m_items[i]->IsSelected() != rjob->m_items[i]->IsSelected())
+ return false;
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+}
diff --git a/src/utils/FileOperationJob.h b/src/utils/FileOperationJob.h
new file mode 100644
index 0000000000..908da10329
--- /dev/null
+++ b/src/utils/FileOperationJob.h
@@ -0,0 +1,93 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "system.h"
+#include "FileItem.h"
+#include "Job.h"
+#include "filesystem/File.h"
+
+class CGUIDialogProgressBarHandle;
+
+class CFileOperationJob : public CJob
+{
+public:
+ enum FileAction
+ {
+ ActionCopy = 1,
+ ActionMove,
+ ActionDelete,
+ ActionReplace, ///< Copy, emptying any existing destination directories first
+ ActionCreateFolder,
+ ActionDeleteFolder,
+ };
+
+ CFileOperationJob();
+ CFileOperationJob(FileAction action, CFileItemList & items,
+ const CStdString& strDestFile,
+ bool displayProgress=false,
+ int errorHeading=0, int errorLine=0);
+
+ void SetFileOperation(FileAction action, CFileItemList &items, const CStdString &strDestFile);
+
+ virtual bool operator==(const CJob *job) const;
+
+ static CStdString GetActionString(FileAction action);
+
+ const char* GetType() const { return m_displayProgress?"filemanager":""; }
+
+ virtual bool DoWork();
+ const CStdString &GetAverageSpeed() const { return m_avgSpeed; }
+ const CStdString &GetCurrentOperation() const { return m_currentOperation; }
+ const CStdString &GetCurrentFile() const { return m_currentFile; }
+ const CFileItemList &GetItems() const { return m_items; }
+ FileAction GetAction() const { return m_action; }
+ int GetHeading() const { return m_heading; }
+ int GetLine() const { return m_line; }
+private:
+ class CFileOperation : public XFILE::IFileCallback
+ {
+ public:
+ CFileOperation(FileAction action, const CStdString &strFileA, const CStdString &strFileB, int64_t time);
+ bool ExecuteOperation(CFileOperationJob *base, double &current, double opWeight);
+ void Debug();
+ virtual bool OnFileCallback(void* pContext, int ipercent, float avgSpeed);
+ private:
+ FileAction m_action;
+ CStdString m_strFileA, m_strFileB;
+ int64_t m_time;
+ };
+ friend class CFileOperation;
+ typedef std::vector<CFileOperation> FileOperationList;
+ bool DoProcess(FileAction action, CFileItemList & items, const CStdString& strDestFile, FileOperationList &fileOperations, double &totalTime);
+ bool DoProcessFolder(FileAction action, const CStdString& strPath, const CStdString& strDestFile, FileOperationList &fileOperations, double &totalTime);
+ bool DoProcessFile(FileAction action, const CStdString& strFileA, const CStdString& strFileB, FileOperationList &fileOperations, double &totalTime);
+
+ static inline bool CanBeRenamed(const CStdString &strFileA, const CStdString &strFileB);
+
+ FileAction m_action;
+ CFileItemList m_items;
+ CStdString m_strDestFile;
+ CStdString m_avgSpeed, m_currentOperation, m_currentFile;
+ CGUIDialogProgressBarHandle* m_handle;
+ bool m_displayProgress;
+ int m_heading;
+ int m_line;
+};
diff --git a/src/utils/FileUtils.cpp b/src/utils/FileUtils.cpp
new file mode 100644
index 0000000000..2b54f363f3
--- /dev/null
+++ b/src/utils/FileUtils.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2010-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#include "FileUtils.h"
+#include "guilib/GUIWindowManager.h"
+#include "dialogs/GUIDialogYesNo.h"
+#include "guilib/GUIKeyboardFactory.h"
+#include "utils/log.h"
+#include "guilib/LocalizeStrings.h"
+#include "JobManager.h"
+#include "FileOperationJob.h"
+#include "URIUtils.h"
+#include "filesystem/MultiPathDirectory.h"
+#include <vector>
+#include "settings/MediaSourceSettings.h"
+#include "Util.h"
+#include "StringUtils.h"
+#include "URL.h"
+#include "settings/Settings.h"
+
+using namespace XFILE;
+using namespace std;
+
+bool CFileUtils::DeleteItem(const CStdString &strPath, bool force)
+{
+ CFileItemPtr item(new CFileItem(strPath));
+ item->SetPath(strPath);
+ item->m_bIsFolder = URIUtils::HasSlashAtEnd(strPath);
+ item->Select(true);
+ return DeleteItem(item, force);
+}
+
+bool CFileUtils::DeleteItem(const CFileItemPtr &item, bool force)
+{
+ if (!item || item->IsParentFolder())
+ return false;
+
+ CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
+ if (!force && pDialog)
+ {
+ pDialog->SetHeading(122);
+ pDialog->SetLine(0, 125);
+ pDialog->SetLine(1, CURL(item->GetPath()).GetWithoutUserDetails());
+ pDialog->SetLine(2, "");
+ pDialog->DoModal();
+ if (!pDialog->IsConfirmed()) return false;
+ }
+
+ // Create a temporary item list containing the file/folder for deletion
+ CFileItemPtr pItemTemp(new CFileItem(*item));
+ pItemTemp->Select(true);
+ CFileItemList items;
+ items.Add(pItemTemp);
+
+ // grab the real filemanager window, set up the progress bar,
+ // and process the delete action
+ CFileOperationJob op(CFileOperationJob::ActionDelete, items, "");
+
+ return op.DoWork();
+}
+
+bool CFileUtils::RenameFile(const CStdString &strFile)
+{
+ CStdString strFileAndPath(strFile);
+ URIUtils::RemoveSlashAtEnd(strFileAndPath);
+ CStdString strFileName = URIUtils::GetFileName(strFileAndPath);
+ CStdString strPath = URIUtils::GetDirectory(strFileAndPath);
+ if (CGUIKeyboardFactory::ShowAndGetInput(strFileName, g_localizeStrings.Get(16013), false))
+ {
+ strPath = URIUtils::AddFileToFolder(strPath, strFileName);
+ CLog::Log(LOGINFO,"FileUtils: rename %s->%s\n", strFileAndPath.c_str(), strPath.c_str());
+ if (URIUtils::IsMultiPath(strFileAndPath))
+ { // special case for multipath renames - rename all the paths.
+ vector<std::string> paths;
+ CMultiPathDirectory::GetPaths(strFileAndPath, paths);
+ bool success = false;
+ for (unsigned int i = 0; i < paths.size(); ++i)
+ {
+ CStdString filePath(paths[i]);
+ URIUtils::RemoveSlashAtEnd(filePath);
+ filePath = URIUtils::GetDirectory(filePath);
+ filePath = URIUtils::AddFileToFolder(filePath, strFileName);
+ if (CFile::Rename(paths[i], filePath))
+ success = true;
+ }
+ return success;
+ }
+ return CFile::Rename(strFileAndPath, strPath);
+ }
+ return false;
+}
+
+bool CFileUtils::RemoteAccessAllowed(const CStdString &strPath)
+{
+ const unsigned int SourcesSize = 5;
+ CStdString SourceNames[] = { "programs", "files", "video", "music", "pictures" };
+
+ string realPath = URIUtils::GetRealPath(strPath);
+ // for rar:// and zip:// paths we need to extract the path to the archive
+ // instead of using the VFS path
+ while (URIUtils::IsInArchive(realPath))
+ realPath = CURL(realPath).GetHostName();
+
+ if (StringUtils::StartsWithNoCase(realPath, "virtualpath://upnproot/"))
+ return true;
+ else if (StringUtils::StartsWithNoCase(realPath, "musicdb://"))
+ return true;
+ else if (StringUtils::StartsWithNoCase(realPath, "videodb://"))
+ return true;
+ else if (StringUtils::StartsWithNoCase(realPath, "library://video"))
+ return true;
+ else if (StringUtils::StartsWithNoCase(realPath, "sources://video"))
+ return true;
+ else if (StringUtils::StartsWithNoCase(realPath, "special://musicplaylists"))
+ return true;
+ else if (StringUtils::StartsWithNoCase(realPath, "special://profile/playlists"))
+ return true;
+ else if (StringUtils::StartsWithNoCase(realPath, "special://videoplaylists"))
+ return true;
+ else if (StringUtils::StartsWithNoCase(realPath, "special://skin"))
+ return true;
+ else if (StringUtils::StartsWithNoCase(realPath, "special://profile/addon_data"))
+ return true;
+ else if (StringUtils::StartsWithNoCase(realPath, "addons://sources"))
+ return true;
+ else if (StringUtils::StartsWithNoCase(realPath, "upnp://"))
+ return true;
+ else if (StringUtils::StartsWithNoCase(realPath, "plugin://"))
+ return true;
+ else
+ {
+ std::string strPlaylistsPath = CSettings::Get().GetString("system.playlistspath");
+ URIUtils::RemoveSlashAtEnd(strPlaylistsPath);
+ if (StringUtils::StartsWithNoCase(realPath, strPlaylistsPath))
+ return true;
+ }
+ bool isSource;
+ for (unsigned int index = 0; index < SourcesSize; index++)
+ {
+ VECSOURCES* sources = CMediaSourceSettings::Get().GetSources(SourceNames[index]);
+ int sourceIndex = CUtil::GetMatchingSource(realPath, *sources, isSource);
+ if (sourceIndex >= 0 && sourceIndex < (int)sources->size() && sources->at(sourceIndex).m_iHasLock != 2 && sources->at(sourceIndex).m_allowSharing)
+ return true;
+ }
+ return false;
+}
diff --git a/src/utils/FileUtils.h b/src/utils/FileUtils.h
new file mode 100644
index 0000000000..a73ab62401
--- /dev/null
+++ b/src/utils/FileUtils.h
@@ -0,0 +1,31 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <string>
+#include "FileItem.h"
+
+class CFileUtils
+{
+public:
+ static bool DeleteItem(const CFileItemPtr &item, bool force=false);
+ static bool DeleteItem(const CStdString &strPath, bool force=false);
+ static bool RenameFile(const CStdString &strFile);
+ static bool RemoteAccessAllowed(const CStdString &strPath);
+};
diff --git a/src/utils/GLUtils.cpp b/src/utils/GLUtils.cpp
new file mode 100644
index 0000000000..83d47d1663
--- /dev/null
+++ b/src/utils/GLUtils.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "system.h"
+
+#include "GLUtils.h"
+#include "log.h"
+#include "settings/AdvancedSettings.h"
+#include "windowing/WindowingFactory.h"
+
+void _VerifyGLState(const char* szfile, const char* szfunction, int lineno){
+#if defined(HAS_GL) && defined(_DEBUG)
+#define printMatrix(matrix) \
+ { \
+ for (int ixx = 0 ; ixx<4 ; ixx++) \
+ { \
+ CLog::Log(LOGDEBUG, "% 3.3f % 3.3f % 3.3f % 3.3f ", \
+ matrix[ixx*4], matrix[ixx*4+1], matrix[ixx*4+2], \
+ matrix[ixx*4+3]); \
+ } \
+ }
+ if (g_advancedSettings.m_logLevel < LOG_LEVEL_DEBUG_FREEMEM)
+ return;
+ GLenum err = glGetError();
+ if (err==GL_NO_ERROR)
+ return;
+ CLog::Log(LOGERROR, "GL ERROR: %s\n", gluErrorString(err));
+ if (szfile && szfunction)
+ CLog::Log(LOGERROR, "In file:%s function:%s line:%d", szfile, szfunction, lineno);
+ GLboolean bools[16];
+ GLfloat matrix[16];
+ glGetFloatv(GL_SCISSOR_BOX, matrix);
+ CLog::Log(LOGDEBUG, "Scissor box: %f, %f, %f, %f", matrix[0], matrix[1], matrix[2], matrix[3]);
+ glGetBooleanv(GL_SCISSOR_TEST, bools);
+ CLog::Log(LOGDEBUG, "Scissor test enabled: %d", (int)bools[0]);
+ glGetFloatv(GL_VIEWPORT, matrix);
+ CLog::Log(LOGDEBUG, "Viewport: %f, %f, %f, %f", matrix[0], matrix[1], matrix[2], matrix[3]);
+ glGetFloatv(GL_PROJECTION_MATRIX, matrix);
+ CLog::Log(LOGDEBUG, "Projection Matrix:");
+ printMatrix(matrix);
+ glGetFloatv(GL_MODELVIEW_MATRIX, matrix);
+ CLog::Log(LOGDEBUG, "Modelview Matrix:");
+ printMatrix(matrix);
+// abort();
+#endif
+}
+
+void LogGraphicsInfo()
+{
+#if defined(HAS_GL) || defined(HAS_GLES)
+ const GLubyte *s;
+
+ s = glGetString(GL_VENDOR);
+ if (s)
+ CLog::Log(LOGNOTICE, "GL_VENDOR = %s", s);
+ else
+ CLog::Log(LOGNOTICE, "GL_VENDOR = NULL");
+
+ s = glGetString(GL_RENDERER);
+ if (s)
+ CLog::Log(LOGNOTICE, "GL_RENDERER = %s", s);
+ else
+ CLog::Log(LOGNOTICE, "GL_RENDERER = NULL");
+
+ s = glGetString(GL_VERSION);
+ if (s)
+ CLog::Log(LOGNOTICE, "GL_VERSION = %s", s);
+ else
+ CLog::Log(LOGNOTICE, "GL_VERSION = NULL");
+
+ s = glGetString(GL_SHADING_LANGUAGE_VERSION);
+ if (s)
+ CLog::Log(LOGNOTICE, "GL_SHADING_LANGUAGE_VERSION = %s", s);
+ else
+ CLog::Log(LOGNOTICE, "GL_SHADING_LANGUAGE_VERSION = NULL");
+
+ //GL_NVX_gpu_memory_info extension
+#define GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX 0x9047
+#define GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX 0x9048
+#define GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX 0x9049
+#define GL_GPU_MEMORY_INFO_EVICTION_COUNT_NVX 0x904A
+#define GL_GPU_MEMORY_INFO_EVICTED_MEMORY_NVX 0x904B
+
+ if (g_Windowing.IsExtSupported("GL_NVX_gpu_memory_info"))
+ {
+ GLint mem = 0;
+
+ glGetIntegerv(GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX, &mem);
+ CLog::Log(LOGNOTICE, "GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX = %i", mem);
+
+ //this seems to be the amount of ram on the videocard
+ glGetIntegerv(GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, &mem);
+ CLog::Log(LOGNOTICE, "GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX = %i", mem);
+ }
+
+ s = glGetString(GL_EXTENSIONS);
+ if (s)
+ CLog::Log(LOGNOTICE, "GL_EXTENSIONS = %s", s);
+ else
+ CLog::Log(LOGNOTICE, "GL_EXTENSIONS = NULL");
+
+#else /* !HAS_GL */
+ CLog::Log(LOGNOTICE,
+ "Please define LogGraphicsInfo for your chosen graphics libary");
+#endif /* !HAS_GL */
+}
+
+int glFormatElementByteCount(GLenum format)
+{
+ switch (format)
+ {
+#ifndef HAS_GLES
+ case GL_BGRA:
+#endif
+ case GL_RGBA:
+ return 4;
+#ifndef HAS_GLES
+ case GL_BGR:
+#endif
+ case GL_RGB:
+ return 3;
+ case GL_LUMINANCE_ALPHA:
+ return 2;
+ case GL_LUMINANCE:
+ case GL_ALPHA:
+ return 1;
+ default:
+ CLog::Log(LOGERROR, "glFormatElementByteCount - Unknown format %u", format);
+ return 1;
+ }
+}
diff --git a/src/utils/GLUtils.h b/src/utils/GLUtils.h
new file mode 100644
index 0000000000..7ea50643e8
--- /dev/null
+++ b/src/utils/GLUtils.h
@@ -0,0 +1,46 @@
+#pragma once
+
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+// GL Error checking macro
+// this function is useful for tracking down GL errors, which otherwise
+// just result in undefined behavior and can be difficult to track down.
+//
+// Just call it 'VerifyGLState()' after a sequence of GL calls
+//
+// if GL_DEBUGGING and HAS_GL are defined, the function checks
+// for GL errors and prints the current state of the various matrices;
+// if not it's just an empty inline stub, and thus won't affect performance
+// and will be optimized out.
+
+#include "system.h"
+#include "system_gl.h"
+
+void _VerifyGLState(const char* szfile, const char* szfunction, int lineno);
+#if defined(GL_DEBUGGING) && (defined(HAS_GL) || defined(HAS_GLES))
+#define VerifyGLState() _VerifyGLState(__FILE__, __FUNCTION__, __LINE__)
+#else
+#define VerifyGLState()
+#endif
+
+void LogGraphicsInfo();
+
+int glFormatElementByteCount(GLenum format);
diff --git a/src/utils/GlobalsHandling.h b/src/utils/GlobalsHandling.h
new file mode 100644
index 0000000000..c27f76eb46
--- /dev/null
+++ b/src/utils/GlobalsHandling.h
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+/**
+ * This file contains the pattern for moving "globals" from the BSS Segment to the heap.
+ * A note on usage of this pattern for globals replacement:
+ *
+ * This pattern uses a singleton pattern and some compiler/C preprocessor sugar to allow
+ * "global" variables to be lazy instantiated and initialized and moved from the BSS segment
+ * to the heap (that is, they are instantiated on the heap when they are first used rather
+ * than relying on the startup code to initialize the BSS segment). This eliminates the
+ * problem associated with global variable dependencies across compilation units.
+ *
+ * Reference counting from the BSS segment is used to destruct these globals at the time the
+ * last compilation unit that knows about it is finalized by the post-main shutdown. The book
+ * keeping is done by smuggling a smart pointer into every file that references a particular
+ * "global class" through the use of a 'static' declaration of an instance of that smart
+ * pointer in the header file of the global class (did you ever think you'd see a file scope
+ * 'static' variable in a header file - on purpose?)
+ *
+ * There are two different ways to use this pattern when replacing global variables.
+ * The selection of which one to use depends on whether or not there is a possiblity
+ * that the code in the .cpp file for the global can be executed from a static method
+ * somewhere. This may take some explanation.
+ *
+ * The (at least) two ways to do this:
+ *
+ * 1) You can use the reference object boost::shared_ptr to access the global variable.
+ *
+ * This would be the preferred means since it is (very slightly) more efficent than
+ * the alternative. To use this pattern you replace standard static references to
+ * the global with access through the reference. If you use the C preprocessor to
+ * do this for you can put the following code in the header file where the global's
+ * class is declared:
+ *
+ * static boost::shared_ptr<GlobalVariableClass> g_globalVariableRef(xbmcutil::GlobalsSingleton<GlobalVariableClass>::getInstance());
+ * #define g_globalVariable (*(g_globalVariableRef.get()))
+ *
+ * Note what this does. In every file that includes this header there will be a *static*
+ * instance of the boost::shared_ptr<GlobalVariableClass> smart pointer. This effectively
+ * reference counts the singleton from every compilation unit (ie, object code file that
+ * results from a compilation of a .c/.cpp file) that references this global directly.
+ *
+ * There is a problem with this, however. Keep in mind that the instance of the smart pointer
+ * (being in the BSS segment of the compilation unit) is ITSELF an object that depends on
+ * the BSS segment initialization in order to be initialized with an instance from the
+ * singleton. That means, depending on the code structure, it is possible to get into a
+ * circumstance where the above #define could be exercised PRIOR TO the setting of the
+ * value of the smart pointer.
+ *
+ * Some reflection on this should lead you to the conclusion that the only way for this to
+ * happen is if access to this global can take place through a static/global method, directly
+ * or indirectly (ie, the static/global method can call another method that uses the
+ * reference), where that static is called from initialization code exercised prior to
+ * the start of 'main.'
+ *
+ * Because of the "indirectly" in the above statement, this situation can be difficult to
+ * determine beforehand.
+ *
+ * 2) Alternatively, when you KNOW that the global variable can suffer from the above described
+ * problem, you can restrict all access to the variable to the singleton by changing
+ * the #define to:
+ *
+ * #define g_globalVariable (*(xbmcutil::Singleton<GlobalVariableClass>::getInstance()))
+ *
+ * A few things to note about this. First, this separates the reference counting aspect
+ * from the access aspect of this solution. The smart pointers are no longer used for
+ * access, only for reference counting. Secondly, all access is through the singleton directly
+ * so there is no reliance on the state of the BSS segment for the code to operate
+ * correctly.
+ *
+ * This solution is required for g_Windowing because it's accessed (both directly and
+ * indirectly) from the static methods of CLog which are called repeatedly from
+ * code exercised during the initialization of the BSS segment.
+ */
+
+namespace xbmcutil
+{
+ /**
+ * This class is an implementation detail of the macros defined below and
+ * is NOT meant to be used as a general purpose utility. IOW, DO NOT USE THIS
+ * CLASS to support a general singleton design pattern, it's specialized
+ * for solving the initialization/finalization order/dependency problem
+ * with global variables and should only be used via the macros below.
+ *
+ * Currently THIS IS NOT THREAD SAFE! Why not just add a lock you ask?
+ * Because this singleton is used to initialize global variables and
+ * there is an issue with having the lock used prior to its
+ * initialization. No matter what, if this class is used as a replacement
+ * for global variables there's going to be a race condition if it's used
+ * anywhere else. So currently this is the only prescribed use.
+ *
+ * Therefore this hack depends on the fact that compilation unit global/static
+ * initialization is done in a single thread.
+ */
+ template <class T> class GlobalsSingleton
+ {
+ /**
+ * This thing just deletes the shared_ptr when the 'instance'
+ * goes out of scope (when the bss segment of the compilation unit
+ * that 'instance' is sitting in is deinitialized). See the comment
+ * on 'instance' for more information.
+ */
+ template <class K> class Deleter
+ {
+ public:
+ K* guarded;
+ ~Deleter() { if (guarded) delete guarded; }
+ };
+
+ /**
+ * Is it possible that getInstance can be called prior to the shared_ptr 'instance'
+ * being initialized as a global? If so, then the shared_ptr constructor would
+ * effectively 'reset' the shared pointer after it had been set by the prior
+ * getInstance call, and a second instance would be created. We really don't
+ * want this to happen so 'instance' is a pointer to a smart pointer so that
+ * we can deterministally handle its construction. It is guarded by the
+ * Deleter class above so that when the bss segment that this static is
+ * sitting in is deinitialized, the shared_ptr pointer will be cleaned up.
+ */
+ static Deleter<boost::shared_ptr<T> > instance;
+
+ /**
+ * See 'getQuick' below.
+ */
+ static T* quick;
+ public:
+
+ /**
+ * Retrieve an instance of the singleton using a shared pointer for
+ * referenece counting.
+ */
+ inline static boost::shared_ptr<T> getInstance()
+ {
+ if (!instance.guarded)
+ {
+ if (!quick)
+ quick = new T;
+ instance.guarded = new boost::shared_ptr<T>(quick);
+ }
+ return *(instance.guarded);
+ }
+
+ /**
+ * This is for quick access when using form (2) of the pattern. Before 'mdd' points
+ * it out, this might be a case of 'solving problems we don't have' but this access
+ * is used frequently within the event loop so any help here should benefit the
+ * overall performance and there is nothing complicated or tricky here and not
+ * a lot of code to maintain.
+ */
+ inline static T* getQuick()
+ {
+ if (!quick)
+ quick = new T;
+
+ return quick;
+ }
+
+ };
+
+ template <class T> typename GlobalsSingleton<T>::template Deleter<boost::shared_ptr<T> > GlobalsSingleton<T>::instance;
+ template <class T> T* GlobalsSingleton<T>::quick;
+
+ /**
+ * This is another bit of hackery that will act as a flag for
+ * whether or not a global/static has been initialized yet. An instance
+ * should be placed in the cpp file after the static/global it's meant to
+ * monitor.
+ */
+ class InitFlag { public: InitFlag(bool& flag) { flag = true; } };
+}
+
+/**
+ * For pattern (2) above, you can use the following macro. This pattern is safe to
+ * use in all cases but may be very slightly less efficient.
+ *
+ * Also, you must also use a #define to replace the actual global variable since
+ * there's no way to use a macro to add a #define. An example would be:
+ *
+ * XBMC_GLOBAL_REF(CWinSystemWin32DX, g_Windowing);
+ * #define g_Windowing XBMC_GLOBAL_USE(CWinSystemWin32DX)
+ *
+ */
+#define XBMC_GLOBAL_REF(classname,g_variable) \
+ static boost::shared_ptr<classname> g_variable##Ref(xbmcutil::GlobalsSingleton<classname>::getInstance())
+
+/**
+ * This declares the actual use of the variable. It needs to be used in another #define
+ * of the form:
+ *
+ * #define g_variable XBMC_GLOBAL_USE(classname)
+ */
+#define XBMC_GLOBAL_USE(classname) (*(xbmcutil::GlobalsSingleton<classname>::getQuick()))
+
+/**
+ * For pattern (1) above, you can use the following macro. WARNING: This should only
+ * be used when the global in question is never accessed, directly or indirectly, from
+ * a static method called (again, directly or indirectly) during startup or shutdown.
+ */
+#define XBMC_GLOBAL(classname,g_variable) \
+ XBMC_GLOBAL_REF(classname,g_variable); \
+ static classname & g_variable = (*(g_variable##Ref.get()))
+
diff --git a/src/utils/GroupUtils.cpp b/src/utils/GroupUtils.cpp
new file mode 100644
index 0000000000..208403eb18
--- /dev/null
+++ b/src/utils/GroupUtils.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2012-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <map>
+#include <set>
+
+#include "GroupUtils.h"
+#include "FileItem.h"
+#include "utils/StringUtils.h"
+#include "utils/Variant.h"
+#include "video/VideoDbUrl.h"
+#include "video/VideoInfoTag.h"
+#include "utils/URIUtils.h"
+#include "filesystem/MultiPathDirectory.h"
+
+using namespace std;
+
+typedef map<int, set<CFileItemPtr> > SetMap;
+
+bool GroupUtils::Group(GroupBy groupBy, const std::string &baseDir, const CFileItemList &items, CFileItemList &groupedItems, GroupAttribute groupAttributes /* = GroupAttributeNone */)
+{
+ if (groupBy == GroupByNone)
+ return false;
+
+ // nothing to do if there are no items to group
+ if (items.Size() <= 0)
+ return true;
+
+ SetMap setMap;
+ for (int index = 0; index < items.Size(); index++)
+ {
+ bool add = true;
+ const CFileItemPtr item = items.Get(index);
+
+ // group by sets
+ if ((groupBy & GroupBySet) &&
+ item->HasVideoInfoTag() && item->GetVideoInfoTag()->m_iSetId > 0)
+ {
+ add = false;
+ setMap[item->GetVideoInfoTag()->m_iSetId].insert(item);
+ }
+
+ if (add)
+ groupedItems.Add(item);
+ }
+
+ if ((groupBy & GroupBySet) && !setMap.empty())
+ {
+ CVideoDbUrl itemsUrl;
+ if (!itemsUrl.FromString(baseDir))
+ return false;
+
+ for (SetMap::const_iterator set = setMap.begin(); set != setMap.end(); ++set)
+ {
+ // only one item in the set, so just re-add it
+ if (set->second.size() == 1 && (groupAttributes & GroupAttributeIgnoreSingleItems))
+ {
+ groupedItems.Add(*set->second.begin());
+ continue;
+ }
+
+ CFileItemPtr pItem(new CFileItem((*set->second.begin())->GetVideoInfoTag()->m_strSet));
+ pItem->GetVideoInfoTag()->m_iDbId = set->first;
+ pItem->GetVideoInfoTag()->m_type = MediaTypeVideoCollection;
+
+ std::string basePath = StringUtils::Format("videodb://movies/sets/%i/", set->first);
+ CVideoDbUrl videoUrl;
+ if (!videoUrl.FromString(basePath))
+ pItem->SetPath(basePath);
+ else
+ {
+ videoUrl.AddOptions(itemsUrl.GetOptionsString());
+ pItem->SetPath(videoUrl.ToString());
+ }
+ pItem->m_bIsFolder = true;
+
+ CVideoInfoTag* setInfo = pItem->GetVideoInfoTag();
+ setInfo->m_strPath = pItem->GetPath();
+ setInfo->m_strTitle = pItem->GetLabel();
+
+ int ratings = 0;
+ int iWatched = 0; // have all the movies been played at least once?
+ std::set<std::string> pathSet;
+ for (std::set<CFileItemPtr>::const_iterator movie = set->second.begin(); movie != set->second.end(); ++movie)
+ {
+ CVideoInfoTag* movieInfo = (*movie)->GetVideoInfoTag();
+ // handle rating
+ if (movieInfo->m_fRating > 0.0f)
+ {
+ ratings++;
+ setInfo->m_fRating += movieInfo->m_fRating;
+ }
+
+ // handle year
+ if (movieInfo->m_iYear > setInfo->m_iYear)
+ setInfo->m_iYear = movieInfo->m_iYear;
+
+ // handle lastplayed
+ if (movieInfo->m_lastPlayed.IsValid() && movieInfo->m_lastPlayed > setInfo->m_lastPlayed)
+ setInfo->m_lastPlayed = movieInfo->m_lastPlayed;
+
+ // handle dateadded
+ if (movieInfo->m_dateAdded.IsValid() && movieInfo->m_dateAdded > setInfo->m_dateAdded)
+ setInfo->m_dateAdded = movieInfo->m_dateAdded;
+
+ // handle playcount/watched
+ setInfo->m_playCount += movieInfo->m_playCount;
+ if (movieInfo->m_playCount > 0)
+ iWatched++;
+
+ //accumulate the path for a multipath construction
+ CFileItem video(movieInfo->m_basePath, false);
+ if (video.IsVideo())
+ pathSet.insert(URIUtils::GetParentPath(movieInfo->m_basePath));
+ else
+ pathSet.insert(movieInfo->m_basePath);
+ }
+ setInfo->m_basePath = XFILE::CMultiPathDirectory::ConstructMultiPath(pathSet);
+
+ if (ratings > 1)
+ pItem->GetVideoInfoTag()->m_fRating /= ratings;
+
+ setInfo->m_playCount = iWatched >= (int)set->second.size() ? (setInfo->m_playCount / set->second.size()) : 0;
+ pItem->SetProperty("total", (int)set->second.size());
+ pItem->SetProperty("watched", iWatched);
+ pItem->SetProperty("unwatched", (int)set->second.size() - iWatched);
+ pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, setInfo->m_playCount > 0);
+
+ groupedItems.Add(pItem);
+ }
+ }
+
+ return true;
+}
+
+bool GroupUtils::GroupAndSort(GroupBy groupBy, const std::string &baseDir, const CFileItemList &items, const SortDescription &sortDescription, CFileItemList &groupedItems, GroupAttribute groupAttributes /* = GroupAttributeNone */)
+{
+ if (!Group(groupBy, baseDir, items, groupedItems, groupAttributes))
+ return false;
+
+ groupedItems.Sort(sortDescription);
+ return true;
+}
diff --git a/src/utils/GroupUtils.h b/src/utils/GroupUtils.h
new file mode 100644
index 0000000000..99fefac900
--- /dev/null
+++ b/src/utils/GroupUtils.h
@@ -0,0 +1,42 @@
+#pragma once
+/*
+ * Copyright (C) 2012-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "SortUtils.h"
+
+class CFileItemList;
+
+// can be used as a flag
+typedef enum {
+ GroupByNone = 0x0,
+ GroupBySet = 0x1
+} GroupBy;
+
+typedef enum {
+ GroupAttributeNone = 0x0,
+ GroupAttributeIgnoreSingleItems = 0x1
+} GroupAttribute;
+
+class GroupUtils
+{
+public:
+ static bool Group(GroupBy groupBy, const std::string &baseDir, const CFileItemList &items, CFileItemList &groupedItems, GroupAttribute groupAttributes = GroupAttributeNone);
+ static bool GroupAndSort(GroupBy groupBy, const std::string &baseDir, const CFileItemList &items, const SortDescription &sortDescription, CFileItemList &groupedItems, GroupAttribute groupAttributes = GroupAttributeNone);
+};
diff --git a/src/utils/HTMLTable.cpp b/src/utils/HTMLTable.cpp
new file mode 100644
index 0000000000..1cd7e9dd4b
--- /dev/null
+++ b/src/utils/HTMLTable.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "HTMLTable.h"
+#include "HTMLUtil.h"
+
+
+using namespace HTML;
+
+CHTMLRow::CHTMLRow(void)
+{}
+
+CHTMLRow::~CHTMLRow(void)
+{}
+
+int CHTMLRow::GetColumns() const
+{
+ return (int)m_vecColums.size();
+}
+
+const std::string& CHTMLRow::GetColumValue(int iColumn) const
+{
+ return m_vecColums[iColumn];
+}
+
+void CHTMLRow::Parse(const std::string& strTable)
+{
+ CHTMLUtil util;
+ std::string strTag;
+ int iTableRowStart = 0;
+ do
+ {
+ iTableRowStart = util.FindTag(strTable, "<td", strTag, iTableRowStart);
+ if (iTableRowStart >= 0)
+ {
+ iTableRowStart += (int)strTag.size();
+ int iTableRowEnd = util.FindClosingTag(strTable, "td", strTag, iTableRowStart) - 1;
+ if (iTableRowEnd < -1)
+ break;
+
+ std::string strRow = strTable.substr(iTableRowStart, 1 + iTableRowEnd - iTableRowStart);
+ m_vecColums.push_back(strRow);
+
+ iTableRowStart = iTableRowEnd + 1;
+
+ }
+ }
+ while (iTableRowStart >= 0);
+}
+//------------------------------------------------------------------------------
+CHTMLTable::CHTMLTable(void)
+{}
+
+CHTMLTable::~CHTMLTable(void)
+{}
+
+int CHTMLTable::GetRows() const
+{
+ return (int)m_vecRows.size();
+}
+
+const CHTMLRow& CHTMLTable::GetRow(int iRow) const
+{
+ return m_vecRows[iRow];
+}
+
+void CHTMLTable::Parse(const std::string& strHTML)
+{
+ m_vecRows.erase(m_vecRows.begin(), m_vecRows.end());
+ CHTMLUtil util;
+ std::string strTag;
+ int iPosStart = util.FindTag(strHTML, "<table", strTag);
+ if (iPosStart >= 0)
+ {
+ iPosStart += (int)strTag.size();
+ int iPosEnd = util.FindClosingTag(strHTML, "table", strTag, iPosStart) - 1;
+ if (iPosEnd < 0)
+ {
+ iPosEnd = (int)strHTML.size();
+ }
+
+ std::string strTable = strHTML.substr(iPosStart, 1 + iPosEnd - iPosStart);
+ int iTableRowStart = 0;
+ do
+ {
+ iTableRowStart = util.FindTag(strTable, "<tr", strTag, iTableRowStart);
+ if (iTableRowStart >= 0)
+ {
+ iTableRowStart += (int)strTag.size();
+ int iTableRowEnd = util.FindClosingTag(strTable, "tr", strTag, iTableRowStart) - 1;
+ if (iTableRowEnd < 0)
+ break;
+
+ std::string strRow = strTable.substr(iTableRowStart, 1 + iTableRowEnd - iTableRowStart);
+ CHTMLRow row;
+ row.Parse(strRow);
+ m_vecRows.push_back(row);
+ iTableRowStart = iTableRowEnd + 1;
+ }
+ }
+ while (iTableRowStart >= 0);
+ }
+}
+
diff --git a/src/utils/HTMLTable.h b/src/utils/HTMLTable.h
new file mode 100644
index 0000000000..d9705ba706
--- /dev/null
+++ b/src/utils/HTMLTable.h
@@ -0,0 +1,52 @@
+#pragma once
+
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string>
+#include <vector>
+
+namespace HTML
+{
+class CHTMLRow
+{
+public:
+ CHTMLRow(void);
+ virtual ~CHTMLRow(void);
+ int GetColumns() const;
+ const std::string& GetColumValue(int iColumn) const;
+ void Parse(const std::string& strTableRow);
+
+protected:
+ std::vector<std::string> m_vecColums;
+};
+
+class CHTMLTable
+{
+public:
+ CHTMLTable(void);
+ virtual ~CHTMLTable(void);
+ void Parse(const std::string& strHTML);
+ int GetRows() const;
+ const CHTMLRow& GetRow(int iRow) const;
+protected:
+ std::vector<CHTMLRow> m_vecRows;
+};
+}
diff --git a/src/utils/HTMLUtil.cpp b/src/utils/HTMLUtil.cpp
new file mode 100644
index 0000000000..feb9edcf19
--- /dev/null
+++ b/src/utils/HTMLUtil.cpp
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "HTMLUtil.h"
+#include "utils/StringUtils.h"
+#include <wctype.h>
+
+using namespace std;
+using namespace HTML;
+
+
+CHTMLUtil::CHTMLUtil(void)
+{}
+
+CHTMLUtil::~CHTMLUtil(void)
+{}
+
+int CHTMLUtil::FindTag(const std::string& strHTML, const std::string& strTag, std::string& strtagFound, int iPos)
+{
+ std::string strHTMLLow = strHTML;
+ std::string strTagLow = strTag;
+ StringUtils::ToLower(strHTMLLow);
+ StringUtils::ToLower(strTagLow);
+ strtagFound = "";
+
+ size_t iStart = strHTMLLow.find(strTag, iPos);
+ if (iStart == std::string::npos)
+ return -1;
+
+ size_t iEnd = strHTMLLow.find(">", iStart);
+ if (iEnd == std::string::npos)
+ iEnd = strHTMLLow.size();
+
+ strtagFound = strHTMLLow.substr(iStart, (iEnd + 1) - iStart);
+ return iStart;
+}
+
+int CHTMLUtil::FindClosingTag(const std::string& strHTML, const std::string& strTag, std::string& strtagFound, int iPos)
+{
+ std::string strHTMLLow = strHTML;
+ std::string strTagLow = strTag;
+ StringUtils::ToLower(strHTMLLow);
+ StringUtils::ToLower(strTagLow);
+ strtagFound = "";
+
+ size_t iStart = strHTMLLow.find("</" + strTag, iPos);
+ if (iStart == std::string::npos)
+ return -1;
+
+ size_t iOpenStart = strHTMLLow.find("<" + strTag, iPos);
+ while (iOpenStart < iStart && iOpenStart != std::string::npos)
+ {
+ iStart = strHTMLLow.find("</" + strTag, iStart + 1);
+ iOpenStart = strHTMLLow.find("<" + strTag, iOpenStart + 1);
+ }
+
+ size_t iEnd = strHTMLLow.find(">", iStart);
+ if (iEnd == std::string::npos)
+ iEnd = strHTMLLow.size();
+
+ strtagFound = strHTMLLow.substr(iStart, (iEnd + 1) - iStart);
+ return iStart;
+}
+
+void CHTMLUtil::getValueOfTag(const std::string& strTagAndValue, std::string& strValue)
+{
+ // strTagAndValue contains:
+ // like <a href=blablabla.....>value</a>
+ strValue = strTagAndValue;
+ size_t iStart = strTagAndValue.find(">");
+ size_t iEnd = strTagAndValue.find("<", iStart + 1);
+ if (iStart != std::string::npos &&
+ iEnd != std::string::npos)
+ {
+ iStart++;
+ strValue = strTagAndValue.substr(iStart, iEnd - iStart);
+ }
+}
+
+void CHTMLUtil::getAttributeOfTag(const std::string& strTagAndValue, const std::string& strTag, std::string& strValue)
+{
+ // strTagAndValue contains:
+ // like <a href=""value".....
+ strValue = strTagAndValue;
+ size_t iStart = strTagAndValue.find(strTag);
+ if (iStart == std::string::npos)
+ return ;
+
+ iStart += strTag.size();
+
+ while (strTagAndValue[iStart + 1] == 0x20 ||
+ strTagAndValue[iStart + 1] == 0x27 ||
+ strTagAndValue[iStart + 1] == 34)
+ iStart++;
+
+ size_t iEnd = iStart + 1;
+ while (strTagAndValue[iEnd] != 0x27 &&
+ strTagAndValue[iEnd] != 0x20 &&
+ strTagAndValue[iEnd] != 34 &&
+ strTagAndValue[iEnd] != '>')
+ iEnd++;
+
+ if (iStart != std::string::npos && iEnd != std::string::npos)
+ {
+ strValue = strTagAndValue.substr(iStart, iEnd - iStart);
+ }
+}
+
+void CHTMLUtil::RemoveTags(std::string& strHTML)
+{
+ int iNested = 0;
+ std::string strReturn = "";
+ for (int i = 0; i < (int) strHTML.size(); ++i)
+ {
+ if (strHTML[i] == '<') iNested++;
+ else if (strHTML[i] == '>') iNested--;
+ else
+ {
+ if (!iNested)
+ {
+ strReturn += strHTML[i];
+ }
+ }
+ }
+
+ strHTML = strReturn;
+}
+
+typedef struct
+{
+ const wchar_t* html;
+ const wchar_t w;
+} HTMLMapping;
+
+static const HTMLMapping mappings[] =
+ {{L"&amp;", 0x0026},
+ {L"&apos;", 0x0027},
+ {L"&acute;", 0x00B4},
+ {L"&agrave;", 0x00E0},
+ {L"&aacute;", 0x00E1},
+ {L"&acirc;", 0x00E2},
+ {L"&atilde;", 0x00E3},
+ {L"&auml;", 0x00E4},
+ {L"&aring;", 0x00E5},
+ {L"&aelig;", 0x00E6},
+ {L"&Agrave;", 0x00C0},
+ {L"&Aacute;", 0x00C1},
+ {L"&Acirc;", 0x00C2},
+ {L"&Atilde;", 0x00C3},
+ {L"&Auml;", 0x00C4},
+ {L"&Aring;", 0x00C5},
+ {L"&AElig;", 0x00C6},
+ {L"&bdquo;", 0x201E},
+ {L"&brvbar;", 0x00A6},
+ {L"&bull;", 0x2022},
+ {L"&bullet;", 0x2022},
+ {L"&cent;", 0x00A2},
+ {L"&circ;", 0x02C6},
+ {L"&curren;", 0x00A4},
+ {L"&copy;", 0x00A9},
+ {L"&cedil;", 0x00B8},
+ {L"&Ccedil;", 0x00C7},
+ {L"&ccedil;", 0x00E7},
+ {L"&dagger;", 0x2020},
+ {L"&deg;", 0x00B0},
+ {L"&divide;", 0x00F7},
+ {L"&Dagger;", 0x2021},
+ {L"&egrave;", 0x00E8},
+ {L"&eacute;", 0x00E9},
+ {L"&ecirc;", 0x00EA},
+ {L"&emsp;", 0x2003},
+ {L"&ensp;", 0x2002},
+ {L"&euml;", 0x00EB},
+ {L"&eth;", 0x00F0},
+ {L"&euro;", 0x20AC},
+ {L"&Egrave;", 0x00C8},
+ {L"&Eacute;", 0x00C9},
+ {L"&Ecirc;", 0x00CA},
+ {L"&Euml;", 0x00CB},
+ {L"&ETH;", 0x00D0},
+ {L"&quot;", 0x0022},
+ {L"&frasl;", 0x2044},
+ {L"&frac14;", 0x00BC},
+ {L"&frac12;", 0x00BD},
+ {L"&frac34;", 0x00BE},
+ {L"&gt;", 0x003E},
+ {L"&hellip;", 0x2026},
+ {L"&iexcl;", 0x00A1},
+ {L"&iquest;", 0x00BF},
+ {L"&igrave;", 0x00EC},
+ {L"&iacute;", 0x00ED},
+ {L"&icirc;", 0x00EE},
+ {L"&iuml;", 0x00EF},
+ {L"&Igrave;", 0x00CC},
+ {L"&Iacute;", 0x00CD},
+ {L"&Icirc;", 0x00CE},
+ {L"&Iuml;", 0x00CF},
+ {L"&lrm;", 0x200E},
+ {L"&lt;", 0x003C},
+ {L"&laquo;", 0x00AB},
+ {L"&ldquo;", 0x201C},
+ {L"&lsaquo;", 0x2039},
+ {L"&lsquo;", 0x2018},
+ {L"&macr;", 0x00AF},
+ {L"&micro;", 0x00B5},
+ {L"&middot;", 0x00B7},
+ {L"&mdash;", 0x2014},
+ {L"&nbsp;", 0x00A0},
+ {L"&ndash;", 0x2013},
+ {L"&ntilde;", 0x00F1},
+ {L"&not;", 0x00AC},
+ {L"&Ntilde;", 0x00D1},
+ {L"&ordf;", 0x00AA},
+ {L"&ordm;", 0x00BA},
+ {L"&oelig;", 0x0153},
+ {L"&ograve;", 0x00F2},
+ {L"&oacute;", 0x00F3},
+ {L"&ocirc;", 0x00F4},
+ {L"&otilde;", 0x00F5},
+ {L"&ouml;", 0x00F6},
+ {L"&oslash;", 0x00F8},
+ {L"&OElig;", 0x0152},
+ {L"&Ograve;", 0x00D2},
+ {L"&Oacute;", 0x00D3},
+ {L"&Ocirc;", 0x00D4},
+ {L"&Otilde;", 0x00D5},
+ {L"&Ouml;", 0x00D6},
+ {L"&Oslash;", 0x00D8},
+ {L"&para;", 0x00B6},
+ {L"&permil;", 0x2030},
+ {L"&plusmn;", 0x00B1},
+ {L"&pound;", 0x00A3},
+ {L"&raquo;", 0x00BB},
+ {L"&rdquo;", 0x201D},
+ {L"&reg;", 0x00AE},
+ {L"&rlm;", 0x200F},
+ {L"&rsaquo;", 0x203A},
+ {L"&rsquo;", 0x2019},
+ {L"&sbquo;", 0x201A},
+ {L"&scaron;", 0x0161},
+ {L"&sect;", 0x00A7},
+ {L"&shy;", 0x00AD},
+ {L"&sup1;", 0x00B9},
+ {L"&sup2;", 0x00B2},
+ {L"&sup3;", 0x00B3},
+ {L"&szlig;", 0x00DF},
+ {L"&Scaron;", 0x0160},
+ {L"&thinsp;", 0x2009},
+ {L"&thorn;", 0x00FE},
+ {L"&tilde;", 0x02DC},
+ {L"&times;", 0x00D7},
+ {L"&trade;", 0x2122},
+ {L"&THORN;", 0x00DE},
+ {L"&uml;", 0x00A8},
+ {L"&ugrave;", 0x00F9},
+ {L"&uacute;", 0x00FA},
+ {L"&ucirc;", 0x00FB},
+ {L"&uuml;", 0x00FC},
+ {L"&Ugrave;", 0x00D9},
+ {L"&Uacute;", 0x00DA},
+ {L"&Ucirc;", 0x00DB},
+ {L"&Uuml;", 0x00DC},
+ {L"&yen;", 0x00A5},
+ {L"&yuml;", 0x00FF},
+ {L"&yacute;", 0x00FD},
+ {L"&Yacute;", 0x00DD},
+ {L"&Yuml;", 0x0178},
+ {L"&zwj;", 0x200D},
+ {L"&zwnj;", 0x200C},
+ {NULL, L'\0'}};
+
+void CHTMLUtil::ConvertHTMLToW(const std::wstring& strHTML, std::wstring& strStripped)
+{
+ /* TODO:STRING_CLEANUP */
+ if (strHTML.size() == 0)
+ {
+ strStripped.clear();
+ return ;
+ }
+ size_t iPos = 0;
+ strStripped = strHTML;
+ while (mappings[iPos].html)
+ {
+ StringUtils::Replace(strStripped, mappings[iPos].html,std::wstring(1, mappings[iPos].w));
+ iPos++;
+ }
+
+ iPos = strStripped.find(L"&#");
+ while (iPos > 0 && iPos < strStripped.size() - 4)
+ {
+ size_t iStart = iPos + 1;
+ iPos += 2;
+ std::wstring num;
+ int base = 10;
+ if (strStripped[iPos] == L'x')
+ {
+ base = 16;
+ iPos++;
+ }
+
+ size_t i = iPos;
+ while (iPos < strStripped.size() &&
+ (base == 16 ? iswxdigit(strStripped[iPos]) : iswdigit(strStripped[iPos])))
+ iPos++;
+
+ num = strStripped.substr(i, iPos-i);
+ wchar_t val = (wchar_t)wcstol(num.c_str(),NULL,base);
+ if (base == 10)
+ num = StringUtils::Format(L"&#%ls;", num.c_str());
+ else
+ num = StringUtils::Format(L"&#x%ls;", num.c_str());
+
+ StringUtils::Replace(strStripped, num,std::wstring(1,val));
+ iPos = strStripped.find(L"&#", iStart);
+ }
+}
+
diff --git a/src/utils/HTMLUtil.h b/src/utils/HTMLUtil.h
new file mode 100644
index 0000000000..7d67634e20
--- /dev/null
+++ b/src/utils/HTMLUtil.h
@@ -0,0 +1,39 @@
+#pragma once
+
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string>
+
+namespace HTML
+{
+class CHTMLUtil
+{
+public:
+ CHTMLUtil(void);
+ virtual ~CHTMLUtil(void);
+ static int FindTag(const std::string& strHTML, const std::string& strTag, std::string& strtagFound, int iPos = 0);
+ static int FindClosingTag(const std::string& strHTML, const std::string& strTag, std::string& strtagFound, int iPos);
+ static void getValueOfTag(const std::string& strTagAndValue, std::string& strValue);
+ static void getAttributeOfTag(const std::string& strTagAndValue, const std::string& strTag, std::string& strValue);
+ static void RemoveTags(std::string& strHTML);
+ static void ConvertHTMLToW(const std::wstring& strHTML, std::wstring& strStripped);
+};
+}
diff --git a/src/utils/HttpHeader.cpp b/src/utils/HttpHeader.cpp
new file mode 100644
index 0000000000..3e80789f44
--- /dev/null
+++ b/src/utils/HttpHeader.cpp
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "HttpHeader.h"
+#include "utils/StringUtils.h"
+
+// header white space characters according to RFC 2616
+const char* const CHttpHeader::m_whitespaceChars = " \t";
+
+
+CHttpHeader::CHttpHeader()
+{
+ m_headerdone = false;
+}
+
+CHttpHeader::~CHttpHeader()
+{
+}
+
+void CHttpHeader::Parse(const std::string& strData)
+{
+ size_t pos = 0;
+ const size_t len = strData.length();
+ const char* const strDataC = strData.c_str();
+
+ // According to RFC 2616 any header line can have continuation on next line, if next line is started from whitespace char
+ // This code at first checks for whitespace char at the begging of the line, and if found, then current line is appended to m_lastHeaderLine
+ // If current line is NOT started from whitespace char, then previously stored (and completed) m_lastHeaderLine is parsed and current line is assigned to m_lastHeaderLine (to be parsed later)
+ while (pos < len)
+ {
+ size_t lineEnd = strData.find('\x0a', pos); // use '\x0a' instead of '\n' to be platform independent
+
+ if (lineEnd == std::string::npos)
+ return; // error: expected only complete lines
+
+ const size_t nextLine = lineEnd + 1;
+ if (lineEnd > pos && strDataC[lineEnd - 1] == '\x0d') // use '\x0d' instead of '\r' to be platform independent
+ lineEnd--;
+
+ if (m_headerdone)
+ Clear(); // clear previous header and process new one
+
+ if (strDataC[pos] == ' ' || strDataC[pos] == '\t') // same chars as in CHttpHeader::m_whitespaceChars
+ { // line is started from whitespace char: this is continuation of previous line
+ pos = strData.find_first_not_of(m_whitespaceChars, pos);
+
+ m_lastHeaderLine.push_back(' '); // replace all whitespace chars at start of the line with single space
+ m_lastHeaderLine.append(strData, pos, lineEnd - pos); // append current line
+ }
+ else
+ { // this line is NOT continuation, this line is new header line
+ if (!m_lastHeaderLine.empty())
+ ParseLine(m_lastHeaderLine); // process previously stored completed line (if any)
+
+ m_lastHeaderLine.assign(strData, pos, lineEnd - pos); // store current line to (possibly) complete later. Will be parsed on next turns.
+
+ if (pos == lineEnd)
+ m_headerdone = true; // current line is bare "\r\n" (or "\n"), means end of header; no need to process current m_lastHeaderLine
+ }
+
+ pos = nextLine; // go to next line (if any)
+ }
+}
+
+bool CHttpHeader::ParseLine(const std::string& headerLine)
+{
+ const size_t valueStart = headerLine.find(':');
+
+ if (valueStart != std::string::npos)
+ {
+ std::string strParam(headerLine, 0, valueStart);
+ std::string strValue(headerLine, valueStart + 1);
+
+ StringUtils::Trim(strParam, m_whitespaceChars);
+ StringUtils::ToLower(strParam);
+
+ StringUtils::Trim(strValue, m_whitespaceChars);
+
+ if (!strParam.empty() && !strValue.empty())
+ m_params.push_back(HeaderParams::value_type(strParam, strValue));
+ else
+ return false;
+ }
+ else if (m_protoLine.empty())
+ m_protoLine = headerLine;
+
+ return true;
+}
+
+void CHttpHeader::AddParam(const std::string& param, const std::string& value, const bool overwrite /*= false*/)
+{
+ std::string paramLower(param);
+ StringUtils::ToLower(paramLower);
+ StringUtils::Trim(paramLower, m_whitespaceChars);
+ if (paramLower.empty())
+ return;
+
+ if (overwrite)
+ { // delete ALL parameters with the same name
+ // note: 'GetValue' always returns last added parameter,
+ // so you probably don't need to overwrite
+ for (size_t i = 0; i < m_params.size();)
+ {
+ if (m_params[i].first == paramLower)
+ m_params.erase(m_params.begin() + i);
+ else
+ ++i;
+ }
+ }
+
+ std::string valueTrim(value);
+ StringUtils::Trim(valueTrim, m_whitespaceChars);
+ if (valueTrim.empty())
+ return;
+
+ m_params.push_back(HeaderParams::value_type(paramLower, valueTrim));
+}
+
+std::string CHttpHeader::GetValue(const std::string& strParam) const
+{
+ std::string paramLower(strParam);
+ StringUtils::ToLower(paramLower);
+
+ return GetValueRaw(paramLower);
+}
+
+std::string CHttpHeader::GetValueRaw(const std::string& strParam) const
+{
+ // look in reverse to find last parameter (probably most important)
+ for (HeaderParams::const_reverse_iterator iter = m_params.rbegin(); iter != m_params.rend(); ++iter)
+ {
+ if (iter->first == strParam)
+ return iter->second;
+ }
+
+ return "";
+}
+
+std::vector<std::string> CHttpHeader::GetValues(std::string strParam) const
+{
+ StringUtils::ToLower(strParam);
+ std::vector<std::string> values;
+
+ for (HeaderParams::const_iterator iter = m_params.begin(); iter != m_params.end(); ++iter)
+ {
+ if (iter->first == strParam)
+ values.push_back(iter->second);
+ }
+
+ return values;
+}
+
+std::string CHttpHeader::GetHeader(void) const
+{
+ if (m_protoLine.empty() && m_params.empty())
+ return "";
+
+ std::string strHeader(m_protoLine + "\r\n");
+
+ for (HeaderParams::const_iterator iter = m_params.begin(); iter != m_params.end(); ++iter)
+ strHeader += ((*iter).first + ": " + (*iter).second + "\r\n");
+
+ strHeader += "\r\n";
+ return strHeader;
+}
+
+std::string CHttpHeader::GetMimeType(void) const
+{
+ std::string strValue(GetValueRaw("content-type"));
+
+ std::string mimeType(strValue, 0, strValue.find(';'));
+ StringUtils::TrimRight(mimeType, m_whitespaceChars);
+
+ return mimeType;
+}
+
+std::string CHttpHeader::GetCharset(void) const
+{
+ std::string strValue(GetValueRaw("content-type"));
+ if (strValue.empty())
+ return strValue;
+
+ StringUtils::ToUpper(strValue);
+ const size_t len = strValue.length();
+
+ // extract charset value from 'contenttype/contentsubtype;pram1=param1Val ; charset=XXXX\t;param2=param2Val'
+ // most common form: 'text/html; charset=XXXX'
+ // charset value can be in double quotes: 'text/xml; charset="XXX XX"'
+
+ size_t pos = strValue.find(';');
+ while (pos < len)
+ {
+ // move to the next non-whitespace character
+ pos = strValue.find_first_not_of(m_whitespaceChars, pos + 1);
+
+ if (pos != std::string::npos)
+ {
+ if (strValue.compare(pos, 8, "CHARSET=", 8) == 0)
+ {
+ pos += 8; // move position to char after 'CHARSET='
+ int len = strValue.find(';', pos);
+ if (len != std::string::npos)
+ len -= pos;
+ std::string charset(strValue, pos, len); // intentionally ignoring possible ';' inside quoted string
+ // as we don't support any charset with ';' in name
+ StringUtils::Trim(charset, m_whitespaceChars);
+ if (!charset.empty())
+ {
+ if (charset[0] != '"')
+ return charset;
+ else
+ { // charset contains quoted string (allowed according to RFC 2616)
+ StringUtils::Replace(charset, "\\", ""); // unescape chars, ignoring possible '\"' and '\\'
+ const size_t closingQ = charset.find('"', 1);
+ if (closingQ == std::string::npos)
+ return ""; // no closing quote
+
+ return charset.substr(1, closingQ - 1);
+ }
+ }
+ }
+ pos = strValue.find(';', pos); // find next parameter
+ }
+ }
+
+ return ""; // no charset is detected
+}
+
+void CHttpHeader::Clear()
+{
+ m_params.clear();
+ m_protoLine.clear();
+ m_headerdone = false;
+ m_lastHeaderLine.clear();
+}
diff --git a/src/utils/HttpHeader.h b/src/utils/HttpHeader.h
new file mode 100644
index 0000000000..38875240b0
--- /dev/null
+++ b/src/utils/HttpHeader.h
@@ -0,0 +1,65 @@
+#pragma once
+
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <utility>
+#include <vector>
+#include <string>
+
+class CHttpHeader
+{
+public:
+ typedef std::pair<std::string, std::string> HeaderParamValue;
+ typedef std::vector<HeaderParamValue> HeaderParams;
+ typedef HeaderParams::iterator HeaderParamsIter;
+
+ CHttpHeader();
+ ~CHttpHeader();
+
+ void Parse(const std::string& strData);
+ void AddParam(const std::string& param, const std::string& value, const bool overwrite = false);
+
+ std::string GetValue(const std::string& strParam) const;
+ std::vector<std::string> GetValues(std::string strParam) const;
+
+ std::string GetHeader(void) const;
+
+ std::string GetMimeType(void) const;
+ std::string GetCharset(void) const;
+ inline std::string GetProtoLine() const
+ { return m_protoLine; }
+
+ inline bool IsHeaderDone(void) const
+ { return m_headerdone; }
+
+ void Clear();
+
+protected:
+ std::string GetValueRaw(const std::string& strParam) const;
+ bool ParseLine(const std::string& headerLine);
+
+ HeaderParams m_params;
+ std::string m_protoLine;
+ bool m_headerdone;
+ std::string m_lastHeaderLine;
+ static const char* const m_whitespaceChars;
+};
+
diff --git a/src/utils/HttpParser.cpp b/src/utils/HttpParser.cpp
new file mode 100644
index 0000000000..deab1a5e2c
--- /dev/null
+++ b/src/utils/HttpParser.cpp
@@ -0,0 +1,266 @@
+/*
+ * This code implements parsing of HTTP requests.
+ * This code was written by Steve Hanov in 2009, no copyright is claimed.
+ * This code is in the public domain.
+ * Code was taken from http://refactormycode.com/codes/778-an-efficient-http-parser
+ *
+ * Copyright (C) 2011-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "HttpParser.h"
+
+HttpParser::HttpParser() :
+ _headerStart(0),
+ _bodyStart(0),
+ _parsedTo( 0 ),
+ _state( 0 ),
+ _keyIndex(0),
+ _valueIndex(0),
+ _contentLength(0),
+ _contentStart(0),
+ _uriIndex(0),
+ _status( Incomplete )
+{
+
+}
+
+HttpParser::~HttpParser()
+{
+
+}
+
+void
+HttpParser::parseHeader()
+{
+ // run the fsm.
+ const int CR = 13;
+ const int LF = 10;
+ const int ANY = 256;
+
+ enum Action {
+ // make lower case
+ LOWER = 0x1,
+
+ // convert current character to null.
+ NULLIFY = 0x2,
+
+ // set the header index to the current position
+ SET_HEADER_START = 0x4,
+
+ // set the key index to the current position
+ SET_KEY = 0x8,
+
+ // set value index to the current position.
+ SET_VALUE = 0x10,
+
+ // store current key/value pair.
+ STORE_KEY_VALUE = 0x20,
+
+ // sets content start to current position + 1
+ SET_CONTENT_START = 0x40
+ };
+
+ static const struct FSM {
+ State curState;
+ int c;
+ State nextState;
+ unsigned actions;
+ } fsm[] = {
+ { p_request_line, CR, p_request_line_cr, NULLIFY },
+ { p_request_line, ANY, p_request_line, 0 },
+ { p_request_line_cr, LF, p_request_line_crlf, 0 },
+ { p_request_line_crlf, CR, p_request_line_crlfcr, 0 },
+ { p_request_line_crlf, ANY, p_key, SET_HEADER_START | SET_KEY | LOWER },
+ { p_request_line_crlfcr, LF, p_content, SET_CONTENT_START },
+ { p_key, ':', p_key_colon, NULLIFY },
+ { p_key, ANY, p_key, LOWER },
+ { p_key_colon, ' ', p_key_colon_sp, 0 },
+ { p_key_colon_sp, ANY, p_value, SET_VALUE },
+ { p_value, CR, p_value_cr, NULLIFY | STORE_KEY_VALUE },
+ { p_value, ANY, p_value, 0 },
+ { p_value_cr, LF, p_value_crlf, 0 },
+ { p_value_crlf, CR, p_value_crlfcr, 0 },
+ { p_value_crlf, ANY, p_key, SET_KEY | LOWER },
+ { p_value_crlfcr, LF, p_content, SET_CONTENT_START },
+ { p_error, ANY, p_error, 0 }
+ };
+
+ for( unsigned i = _parsedTo; i < _data.length(); ++i) {
+ char c = _data[i];
+ State nextState = p_error;
+
+ for ( unsigned d = 0; d < sizeof(fsm) / sizeof(FSM); ++d ) {
+ if ( fsm[d].curState == _state &&
+ ( c == fsm[d].c || fsm[d].c == ANY ) ) {
+
+ nextState = fsm[d].nextState;
+
+ if ( fsm[d].actions & LOWER ) {
+ _data[i] = tolower( _data[i] );
+ }
+
+ if ( fsm[d].actions & NULLIFY ) {
+ _data[i] = 0;
+ }
+
+ if ( fsm[d].actions & SET_HEADER_START ) {
+ _headerStart = i;
+ }
+
+ if ( fsm[d].actions & SET_KEY ) {
+ _keyIndex = i;
+ }
+
+ if ( fsm[d].actions & SET_VALUE ) {
+ _valueIndex = i;
+ }
+
+ if ( fsm[d].actions & SET_CONTENT_START ) {
+ _contentStart = i + 1;
+ }
+
+ if ( fsm[d].actions & STORE_KEY_VALUE ) {
+ // store position of first character of key.
+ _keys.push_back( _keyIndex );
+ }
+
+ break;
+ }
+ }
+
+ _state = nextState;
+
+ if ( _state == p_content ) {
+ const char* str = getValue("content-length");
+ if ( str ) {
+ _contentLength = atoi( str );
+ }
+ break;
+ }
+ }
+
+ _parsedTo = _data.length();
+
+}
+
+bool
+HttpParser::parseRequestLine()
+{
+ size_t sp1;
+ size_t sp2;
+
+ sp1 = _data.find( ' ', 0 );
+ if ( sp1 == std::string::npos ) return false;
+ sp2 = _data.find( ' ', sp1 + 1 );
+ if ( sp2 == std::string::npos ) return false;
+
+ _data[sp1] = 0;
+ _data[sp2] = 0;
+ _uriIndex = sp1 + 1;
+ return true;
+}
+
+HttpParser::status_t
+HttpParser::addBytes( const char* bytes, unsigned len )
+{
+ if ( _status != Incomplete ) {
+ return _status;
+ }
+
+ // append the bytes to data.
+ _data.append( bytes, len );
+
+ if ( _state < p_content ) {
+ parseHeader();
+ }
+
+ if ( _state == p_error ) {
+ _status = Error;
+ } else if ( _state == p_content ) {
+ if ( _contentLength == 0 || _data.length() - _contentStart >= _contentLength ) {
+ if ( parseRequestLine() ) {
+ _status = Done;
+ } else {
+ _status = Error;
+ }
+ }
+ }
+
+ return _status;
+}
+
+const char*
+HttpParser::getMethod() const
+{
+ return &_data[0];
+}
+
+const char*
+HttpParser::getUri() const
+{
+ return &_data[_uriIndex];
+}
+
+const char*
+HttpParser::getQueryString() const
+{
+ const char* pos = getUri();
+ while( *pos ) {
+ if ( *pos == '?' ) {
+ pos++;
+ break;
+ }
+ pos++;
+ }
+ return pos;
+}
+
+const char*
+HttpParser::getBody() const
+{
+ if ( _contentLength > 0 ) {
+ return &_data[_contentStart];
+ } else {
+ return NULL;
+ }
+}
+
+// key should be in lower case.
+const char*
+HttpParser::getValue( const char* key ) const
+{
+ for( IntArray::const_iterator iter = _keys.begin();
+ iter != _keys.end(); ++iter )
+ {
+ unsigned index = *iter;
+ if ( strcmp( &_data[index], key ) == 0 ) {
+ return &_data[index + strlen(key) + 2];
+ }
+
+ }
+
+ return NULL;
+}
+
+unsigned
+HttpParser::getContentLength() const
+{
+ return _contentLength;
+}
+
diff --git a/src/utils/HttpParser.h b/src/utils/HttpParser.h
new file mode 100644
index 0000000000..0e91ad26fe
--- /dev/null
+++ b/src/utils/HttpParser.h
@@ -0,0 +1,112 @@
+/*
+ * This code implements parsing of HTTP requests.
+ * This code was written by Steve Hanov in 2009, no copyright is claimed.
+ * This code is in the public domain.
+ * Code was taken from http://refactormycode.com/codes/778-an-efficient-http-parser
+ *
+ * Copyright (C) 2011-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef HTTPPARSER_H_
+#define HTTPPARSER_H_
+#include <stdlib.h>
+#include <vector>
+#include <string>
+#include <string.h>
+
+// A class to incrementally parse an HTTP header as it comes in. It
+// lets you know when it has received all required bytes, as specified
+// by the content-length header (if present). If there is no content-length,
+// it will stop reading after the final "\n\r".
+//
+// Example usage:
+//
+// HttpParser parser;
+// HttpParser::status_t status;
+//
+// for( ;; ) {
+// // read bytes from socket into buffer, break on error
+// status = parser.addBytes( buffer, length );
+// if ( status != HttpParser::Incomplete ) break;
+// }
+//
+// if ( status == HttpParser::Done ) {
+// // parse fully formed http message.
+// }
+
+
+class HttpParser
+{
+public:
+ HttpParser();
+ ~HttpParser();
+
+ enum status_t {
+ Done,
+ Error,
+ Incomplete
+ };
+
+ status_t addBytes( const char* bytes, unsigned len );
+
+ const char* getMethod() const;
+ const char* getUri() const;
+ const char* getQueryString() const;
+ const char* getBody() const;
+ // key should be in lower case when looking up.
+ const char* getValue( const char* key ) const;
+ unsigned getContentLength() const;
+
+private:
+ void parseHeader();
+ bool parseRequestLine();
+
+ std::string _data;
+ unsigned _headerStart;
+ unsigned _bodyStart;
+ unsigned _parsedTo;
+ int _state;
+ unsigned _keyIndex;
+ unsigned _valueIndex;
+ unsigned _contentLength;
+ unsigned _contentStart;
+ unsigned _uriIndex;
+
+ typedef std::vector<unsigned> IntArray;
+ IntArray _keys;
+
+ enum State {
+ p_request_line=0,
+ p_request_line_cr=1,
+ p_request_line_crlf=2,
+ p_request_line_crlfcr=3,
+ p_key=4,
+ p_key_colon=5,
+ p_key_colon_sp=6,
+ p_value=7,
+ p_value_cr=8,
+ p_value_crlf=9,
+ p_value_crlfcr=10,
+ p_content=11, // here we are done parsing the header.
+ p_error=12 // here an error has occurred and the parse failed.
+ };
+
+ status_t _status;
+};
+#endif//HTTPPARSER_H_
diff --git a/src/utils/HttpResponse.cpp b/src/utils/HttpResponse.cpp
new file mode 100644
index 0000000000..9d741c2f9f
--- /dev/null
+++ b/src/utils/HttpResponse.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2011-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdio.h>
+
+#include "HttpResponse.h"
+
+#define SPACE " "
+#define SEPARATOR ": "
+#define LINEBREAK "\r\n"
+
+#define HEADER_CONTENT_LENGTH "Content-Length"
+
+std::map<HTTP::StatusCode, std::string> CHttpResponse::m_statusCodeText = CHttpResponse::createStatusCodes();
+
+CHttpResponse::CHttpResponse(HTTP::Method method, HTTP::StatusCode status, HTTP::Version version /* = HTTPVersion1_1 */)
+{
+ m_method = method;
+ m_status = status;
+ m_version = version;
+
+ m_content = NULL;
+ m_contentLength = 0;
+}
+
+void CHttpResponse::AddHeader(const std::string &field, const std::string &value)
+{
+ if (field.empty())
+ return;
+
+ m_headers.push_back(std::pair<std::string, std::string>(field, value));
+}
+
+void CHttpResponse::SetContent(const char* data, unsigned int length)
+{
+ m_content = data;
+
+ if (m_content == NULL)
+ m_contentLength = 0;
+ else
+ m_contentLength = length;
+}
+
+unsigned int CHttpResponse::Create(char *&response)
+{
+ m_buffer.clear();
+
+ m_buffer.append("HTTP/");
+ switch (m_version)
+ {
+ case HTTP::Version1_0:
+ m_buffer.append("1.0");
+ break;
+
+ case HTTP::Version1_1:
+ m_buffer.append("1.1");
+ break;
+
+ default:
+ return 0;
+ }
+
+ char statusBuffer[4];
+ sprintf(statusBuffer, "%d", (int)m_status);
+ m_buffer.append(SPACE);
+ m_buffer.append(statusBuffer);
+
+ m_buffer.append(SPACE);
+ m_buffer.append(m_statusCodeText.find(m_status)->second);
+ m_buffer.append(LINEBREAK);
+
+ bool hasContentLengthHeader = false;
+ for (unsigned int index = 0; index < m_headers.size(); index++)
+ {
+ m_buffer.append(m_headers[index].first);
+ m_buffer.append(SEPARATOR);
+ m_buffer.append(m_headers[index].second);
+ m_buffer.append(LINEBREAK);
+
+ if (m_headers[index].first.compare(HEADER_CONTENT_LENGTH) == 0)
+ hasContentLengthHeader = true;
+ }
+
+ if (!hasContentLengthHeader && m_content != NULL && m_contentLength > 0)
+ {
+ m_buffer.append(HEADER_CONTENT_LENGTH);
+ m_buffer.append(SEPARATOR);
+ char lengthBuffer[11];
+ sprintf(lengthBuffer, "%u", m_contentLength);
+ m_buffer.append(lengthBuffer);
+ m_buffer.append(LINEBREAK);
+ }
+
+ m_buffer.append(LINEBREAK);
+ if (m_content != NULL && m_contentLength > 0)
+ m_buffer.append(m_content, m_contentLength);
+
+ response = (char *)m_buffer.c_str();
+ return m_buffer.size();
+}
+
+std::map<HTTP::StatusCode, std::string> CHttpResponse::createStatusCodes()
+{
+ std::map<HTTP::StatusCode, std::string> map;
+ map[HTTP::Continue] = "Continue";
+ map[HTTP::SwitchingProtocols] = "Switching Protocols";
+ map[HTTP::Processing] = "Processing";
+ map[HTTP::ConnectionTimedOut] = "Connection timed out";
+ map[HTTP::OK] = "OK";
+ map[HTTP::Created] = "Created";
+ map[HTTP::Accepted] = "Accepted";
+ map[HTTP::NonAuthoritativeInformation] = "Non-Authoritative Information";
+ map[HTTP::NoContent] = "No Content";
+ map[HTTP::ResetContent] = "Reset Content";
+ map[HTTP::PartialContent] = "Partial Content";
+ map[HTTP::MultiStatus] = "Multi-Status";
+ map[HTTP::MultipleChoices] = "Multiple Choices";
+ map[HTTP::MovedPermanently] = "Moved Permanently";
+ map[HTTP::Found] = "Found";
+ map[HTTP::SeeOther] = "See Other";
+ map[HTTP::NotModified] = "Not Modified";
+ map[HTTP::UseProxy] = "Use Proxy";
+ //map[HTTP::SwitchProxy] = "Switch Proxy";
+ map[HTTP::TemporaryRedirect] = "Temporary Redirect";
+ map[HTTP::BadRequest] = "Bad Request";
+ map[HTTP::Unauthorized] = "Unauthorized";
+ map[HTTP::PaymentRequired] = "Payment Required";
+ map[HTTP::Forbidden] = "Forbidden";
+ map[HTTP::NotFound] = "Not Found";
+ map[HTTP::MethodNotAllowed] = "Method Not Allowed";
+ map[HTTP::NotAcceptable] = "Not Acceptable";
+ map[HTTP::ProxyAuthenticationRequired] = "Proxy Authentication Required";
+ map[HTTP::RequestTimeout] = "Request Time-out";
+ map[HTTP::Conflict] = "Conflict";
+ map[HTTP::Gone] = "Gone";
+ map[HTTP::LengthRequired] = "Length Required";
+ map[HTTP::PreconditionFailed] = "Precondition Failed";
+ map[HTTP::RequestEntityTooLarge] = "Request Entity Too Large";
+ map[HTTP::RequestURITooLong] = "Request-URI Too Long";
+ map[HTTP::UnsupportedMediaType] = "Unsupported Media Type";
+ map[HTTP::RequestedRangeNotSatisfiable] = "Requested range not satisfiable";
+ map[HTTP::ExpectationFailed] = "Expectation Failed";
+ map[HTTP::ImATeapot] = "I'm a Teapot";
+ map[HTTP::TooManyConnections] = "There are too many connections from your internet address";
+ map[HTTP::UnprocessableEntity] = "Unprocessable Entity";
+ map[HTTP::Locked] = "Locked";
+ map[HTTP::FailedDependency] = "Failed Dependency";
+ map[HTTP::UnorderedCollection] = "UnorderedCollection";
+ map[HTTP::UpgradeRequired] = "Upgrade Required";
+ map[HTTP::InternalServerError] = "Internal Server Error";
+ map[HTTP::NotImplemented] = "Not Implemented";
+ map[HTTP::BadGateway] = "Bad Gateway";
+ map[HTTP::ServiceUnavailable] = "Service Unavailable";
+ map[HTTP::GatewayTimeout] = "Gateway Time-out";
+ map[HTTP::HTTPVersionNotSupported] = "HTTP Version not supported";
+ map[HTTP::VariantAlsoNegotiates] = "Variant Also Negotiates";
+ map[HTTP::InsufficientStorage] = "Insufficient Storage";
+ map[HTTP::BandwidthLimitExceeded] = "Bandwidth Limit Exceeded";
+ map[HTTP::NotExtended] = "Not Extended";
+
+ return map;
+}
diff --git a/src/utils/HttpResponse.h b/src/utils/HttpResponse.h
new file mode 100644
index 0000000000..a2705b7b1e
--- /dev/null
+++ b/src/utils/HttpResponse.h
@@ -0,0 +1,135 @@
+#pragma once
+/*
+ * Copyright (C) 2011-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string>
+#include <vector>
+#include <map>
+
+namespace HTTP
+{
+ enum Version
+ {
+ Version1_0,
+ Version1_1
+ };
+
+ enum Method
+ {
+ Get,
+ Head,
+ POST,
+ PUT,
+ Delete,
+ Trace,
+ Connect
+ };
+
+ enum StatusCode
+ {
+ // Information 1xx
+ Continue = 100,
+ SwitchingProtocols = 101,
+ Processing = 102,
+ ConnectionTimedOut = 103,
+
+ // Success 2xx
+ OK = 200,
+ Created = 201,
+ Accepted = 202,
+ NonAuthoritativeInformation = 203,
+ NoContent = 204,
+ ResetContent = 205,
+ PartialContent = 206,
+ MultiStatus = 207,
+
+ // Redirects 3xx
+ MultipleChoices = 300,
+ MovedPermanently = 301,
+ Found = 302,
+ SeeOther = 303,
+ NotModified = 304,
+ UseProxy = 305,
+ //SwitchProxy = 306,
+ TemporaryRedirect = 307,
+
+ // Client errors 4xx
+ BadRequest = 400,
+ Unauthorized = 401,
+ PaymentRequired = 402,
+ Forbidden = 403,
+ NotFound = 404,
+ MethodNotAllowed = 405,
+ NotAcceptable = 406,
+ ProxyAuthenticationRequired = 407,
+ RequestTimeout = 408,
+ Conflict = 409,
+ Gone = 410,
+ LengthRequired = 411,
+ PreconditionFailed = 412,
+ RequestEntityTooLarge = 413,
+ RequestURITooLong = 414,
+ UnsupportedMediaType = 415,
+ RequestedRangeNotSatisfiable = 416,
+ ExpectationFailed = 417,
+ ImATeapot = 418,
+ TooManyConnections = 421,
+ UnprocessableEntity = 422,
+ Locked = 423,
+ FailedDependency = 424,
+ UnorderedCollection = 425,
+ UpgradeRequired = 426,
+
+ // Server errors 5xx
+ InternalServerError = 500,
+ NotImplemented = 501,
+ BadGateway = 502,
+ ServiceUnavailable = 503,
+ GatewayTimeout = 504,
+ HTTPVersionNotSupported = 505,
+ VariantAlsoNegotiates = 506,
+ InsufficientStorage = 507,
+ BandwidthLimitExceeded = 509,
+ NotExtended = 510
+ };
+}
+
+class CHttpResponse
+{
+public:
+ CHttpResponse(HTTP::Method method, HTTP::StatusCode status, HTTP::Version version = HTTP::Version1_1);
+
+ void AddHeader(const std::string &field, const std::string &value);
+ void SetContent(const char* data, unsigned int length);
+
+ unsigned int Create(char *&response);
+
+private:
+ HTTP::Method m_method;
+ HTTP::StatusCode m_status;
+ HTTP::Version m_version;
+ std::vector< std::pair<std::string, std::string> > m_headers;
+ const char* m_content;
+ unsigned int m_contentLength;
+ std::string m_buffer;
+
+ static std::map<HTTP::StatusCode, std::string> m_statusCodeText;
+ static std::map<HTTP::StatusCode, std::string> createStatusCodes();
+};
diff --git a/src/utils/IArchivable.h b/src/utils/IArchivable.h
new file mode 100644
index 0000000000..3738edacb8
--- /dev/null
+++ b/src/utils/IArchivable.h
@@ -0,0 +1,31 @@
+#pragma once
+
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+class CArchive;
+
+class IArchivable
+{
+public:
+ virtual void Archive(CArchive& ar) = 0;
+ virtual ~IArchivable() {}
+};
+
diff --git a/src/utils/IRssObserver.h b/src/utils/IRssObserver.h
new file mode 100644
index 0000000000..eb930b6a60
--- /dev/null
+++ b/src/utils/IRssObserver.h
@@ -0,0 +1,32 @@
+#pragma once
+/*
+ * Copyright (C) 2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+typedef uint32_t character_t;
+typedef std::vector<character_t> vecText;
+
+class IRssObserver
+{
+public:
+ virtual ~IRssObserver() {}
+
+ virtual void OnFeedUpdate(const vecText &feed) = 0;
+ virtual void OnFeedRelease() = 0;
+}; \ No newline at end of file
diff --git a/src/utils/ISerializable.h b/src/utils/ISerializable.h
new file mode 100644
index 0000000000..2dab5ce4d2
--- /dev/null
+++ b/src/utils/ISerializable.h
@@ -0,0 +1,29 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+class CVariant;
+
+class ISerializable
+{
+public:
+ virtual void Serialize(CVariant& value) const = 0;
+ virtual ~ISerializable() {}
+};
diff --git a/src/utils/ISortable.h b/src/utils/ISortable.h
new file mode 100644
index 0000000000..21bc712b59
--- /dev/null
+++ b/src/utils/ISortable.h
@@ -0,0 +1,31 @@
+#pragma once
+/*
+ * Copyright (C) 2012-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <map>
+
+#include "SortUtils.h"
+
+class ISortable
+{
+public:
+ virtual ~ISortable() { }
+ virtual void ToSortable(SortItem& sortable, Field field) const = 0;
+};
diff --git a/src/utils/IXmlDeserializable.h b/src/utils/IXmlDeserializable.h
new file mode 100644
index 0000000000..866a84ec67
--- /dev/null
+++ b/src/utils/IXmlDeserializable.h
@@ -0,0 +1,30 @@
+#pragma once
+/*
+ * Copyright (C) 2012-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+class TiXmlNode;
+
+class IXmlDeserializable
+{
+public:
+ virtual ~IXmlDeserializable() { }
+
+ virtual bool Deserialize(const TiXmlNode *node) = 0;
+};
diff --git a/src/utils/InfoLoader.cpp b/src/utils/InfoLoader.cpp
new file mode 100644
index 0000000000..429964a273
--- /dev/null
+++ b/src/utils/InfoLoader.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "InfoLoader.h"
+#include "guilib/LocalizeStrings.h"
+#include "JobManager.h"
+#include "TimeUtils.h"
+
+CInfoLoader::CInfoLoader(unsigned int timeToRefresh)
+{
+ m_refreshTime = 0;
+ m_timeToRefresh = timeToRefresh;
+ m_busy = false;
+}
+
+CInfoLoader::~CInfoLoader()
+{
+}
+
+void CInfoLoader::OnJobComplete(unsigned int jobID, bool success, CJob *job)
+{
+ m_refreshTime = CTimeUtils::GetFrameTime() + m_timeToRefresh;
+ m_busy = false;
+}
+
+std::string CInfoLoader::GetInfo(int info)
+{
+ // Refresh if need be
+ if (m_refreshTime < CTimeUtils::GetFrameTime() && !m_busy)
+ { // queue up the job
+ m_busy = true;
+ CJobManager::GetInstance().AddJob(GetJob(), this);
+ }
+ if (m_busy && CTimeUtils::GetFrameTime() - m_refreshTime > 1000)
+ {
+ return BusyInfo(info);
+ }
+ return TranslateInfo(info);
+}
+
+std::string CInfoLoader::BusyInfo(int info) const
+{
+ return g_localizeStrings.Get(503);
+}
+
+std::string CInfoLoader::TranslateInfo(int info) const
+{
+ return "";
+}
+
+void CInfoLoader::Refresh()
+{
+ m_refreshTime = CTimeUtils::GetFrameTime();
+}
+
diff --git a/src/utils/InfoLoader.h b/src/utils/InfoLoader.h
new file mode 100644
index 0000000000..d72d5e12ab
--- /dev/null
+++ b/src/utils/InfoLoader.h
@@ -0,0 +1,44 @@
+#pragma once
+
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "Job.h"
+#include <string>
+
+class CInfoLoader : public IJobCallback
+{
+public:
+ CInfoLoader(unsigned int timeToRefresh = 5 * 60 * 1000);
+ virtual ~CInfoLoader();
+
+ std::string GetInfo(int info);
+ void Refresh();
+
+ virtual void OnJobComplete(unsigned int jobID, bool success, CJob *job);
+protected:
+ virtual CJob *GetJob() const=0;
+ virtual std::string TranslateInfo(int info) const;
+ virtual std::string BusyInfo(int info) const;
+private:
+ unsigned int m_refreshTime;
+ unsigned int m_timeToRefresh;
+ bool m_busy;
+};
diff --git a/src/utils/JSONVariantParser.cpp b/src/utils/JSONVariantParser.cpp
new file mode 100644
index 0000000000..db08849e49
--- /dev/null
+++ b/src/utils/JSONVariantParser.cpp
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "JSONVariantParser.h"
+
+yajl_callbacks CJSONVariantParser::callbacks = {
+ CJSONVariantParser::ParseNull,
+ CJSONVariantParser::ParseBoolean,
+ CJSONVariantParser::ParseInteger,
+ CJSONVariantParser::ParseDouble,
+ NULL,
+ CJSONVariantParser::ParseString,
+ CJSONVariantParser::ParseMapStart,
+ CJSONVariantParser::ParseMapKey,
+ CJSONVariantParser::ParseMapEnd,
+ CJSONVariantParser::ParseArrayStart,
+ CJSONVariantParser::ParseArrayEnd
+};
+
+CJSONVariantParser::CJSONVariantParser(IParseCallback *callback)
+{
+ m_callback = callback;
+
+#if YAJL_MAJOR == 2
+ m_handler = yajl_alloc(&callbacks, NULL, this);
+
+ yajl_config(m_handler, yajl_allow_comments, 1);
+ yajl_config(m_handler, yajl_dont_validate_strings, 0);
+#else
+ yajl_parser_config cfg = { 1, 1 };
+
+ m_handler = yajl_alloc(&callbacks, &cfg, NULL, this);
+#endif
+
+ m_status = ParseVariable;
+}
+
+CJSONVariantParser::~CJSONVariantParser()
+{
+#if YAJL_MAJOR == 2
+ yajl_complete_parse(m_handler);
+#else
+ yajl_parse_complete(m_handler);
+#endif
+ yajl_free(m_handler);
+}
+
+void CJSONVariantParser::push_buffer(const unsigned char *buffer, unsigned int length)
+{
+ yajl_parse(m_handler, buffer, length);
+}
+
+CVariant CJSONVariantParser::Parse(const unsigned char *json, unsigned int length)
+{
+ CSimpleParseCallback callback;
+ CJSONVariantParser parser(&callback);
+
+ parser.push_buffer(json, length);
+
+ return callback.GetOutput();
+}
+
+int CJSONVariantParser::ParseNull(void * ctx)
+{
+ CJSONVariantParser *parser = (CJSONVariantParser *)ctx;
+
+ parser->PushObject(CVariant::VariantTypeNull);
+ parser->PopObject();
+
+ return 1;
+}
+
+int CJSONVariantParser::ParseBoolean(void * ctx, int boolean)
+{
+ CJSONVariantParser *parser = (CJSONVariantParser *)ctx;
+
+ parser->PushObject(CVariant(boolean != 0));
+ parser->PopObject();
+
+ return 1;
+}
+
+#if YAJL_MAJOR ==2
+int CJSONVariantParser::ParseInteger(void * ctx, long long integerVal)
+#else
+int CJSONVariantParser::ParseInteger(void * ctx, long integerVal)
+#endif
+{
+ CJSONVariantParser *parser = (CJSONVariantParser *)ctx;
+
+ parser->PushObject(CVariant((int64_t)integerVal));
+ parser->PopObject();
+
+ return 1;
+}
+
+int CJSONVariantParser::ParseDouble(void * ctx, double doubleVal)
+{
+ CJSONVariantParser *parser = (CJSONVariantParser *)ctx;
+
+ parser->PushObject(CVariant((float)doubleVal));
+ parser->PopObject();
+
+ return 1;
+}
+
+#if YAJL_MAJOR == 2
+int CJSONVariantParser::ParseString(void * ctx, const unsigned char * stringVal, size_t stringLen)
+#else
+int CJSONVariantParser::ParseString(void * ctx, const unsigned char * stringVal, unsigned int stringLen)
+#endif
+{
+ CJSONVariantParser *parser = (CJSONVariantParser *)ctx;
+
+ parser->PushObject(CVariant((const char *)stringVal, stringLen));
+ parser->PopObject();
+
+ return 1;
+}
+
+int CJSONVariantParser::ParseMapStart(void * ctx)
+{
+ CJSONVariantParser *parser = (CJSONVariantParser *)ctx;
+
+ parser->PushObject(CVariant::VariantTypeObject);
+
+ return 1;
+}
+
+#if YAJL_MAJOR == 2
+int CJSONVariantParser::ParseMapKey(void * ctx, const unsigned char * stringVal, size_t stringLen)
+#else
+int CJSONVariantParser::ParseMapKey(void * ctx, const unsigned char * stringVal, unsigned int stringLen)
+#endif
+{
+ CJSONVariantParser *parser = (CJSONVariantParser *)ctx;
+
+ parser->m_key = std::string((const char *)stringVal, 0, stringLen);
+
+ return 1;
+}
+
+int CJSONVariantParser::ParseMapEnd(void * ctx)
+{
+ CJSONVariantParser *parser = (CJSONVariantParser *)ctx;
+
+ parser->PopObject();
+
+ return 1;
+}
+
+int CJSONVariantParser::ParseArrayStart(void * ctx)
+{
+ CJSONVariantParser *parser = (CJSONVariantParser *)ctx;
+
+ parser->PushObject(CVariant::VariantTypeArray);
+
+ return 1;
+}
+
+int CJSONVariantParser::ParseArrayEnd(void * ctx)
+{
+ CJSONVariantParser *parser = (CJSONVariantParser *)ctx;
+
+ parser->PopObject();
+
+ return 1;
+}
+
+void CJSONVariantParser::PushObject(CVariant variant)
+{
+ if (m_status == ParseObject)
+ {
+ (*m_parse[m_parse.size() - 1])[m_key] = variant;
+ m_parse.push_back(&(*m_parse[m_parse.size() - 1])[m_key]);
+ }
+ else if (m_status == ParseArray)
+ {
+ CVariant *temp = m_parse[m_parse.size() - 1];
+ temp->push_back(variant);
+ m_parse.push_back(&(*temp)[temp->size() - 1]);
+ }
+ else if (m_parse.size() == 0)
+ {
+ m_parse.push_back(new CVariant(variant));
+ }
+
+ if (variant.isObject())
+ m_status = ParseObject;
+ else if (variant.isArray())
+ m_status = ParseArray;
+ else
+ m_status = ParseVariable;
+}
+
+void CJSONVariantParser::PopObject()
+{
+ CVariant *variant = m_parse[m_parse.size() - 1];
+ m_parse.pop_back();
+
+ if (m_parse.size())
+ {
+ variant = m_parse[m_parse.size() - 1];
+ if (variant->isObject())
+ m_status = ParseObject;
+ else if (variant->isArray())
+ m_status = ParseArray;
+ else
+ m_status = ParseVariable;
+ }
+ else if (m_callback)
+ {
+ m_callback->onParsed(variant);
+ delete variant;
+
+ m_parse.clear();
+ m_status = ParseVariable;
+ }
+}
diff --git a/src/utils/JSONVariantParser.h b/src/utils/JSONVariantParser.h
new file mode 100644
index 0000000000..38f88a9ad6
--- /dev/null
+++ b/src/utils/JSONVariantParser.h
@@ -0,0 +1,102 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "system.h"
+#include "Variant.h"
+
+#include <yajl/yajl_parse.h>
+#include <yajl/yajl_gen.h>
+#ifdef HAVE_YAJL_YAJL_VERSION_H
+#include <yajl/yajl_version.h>
+#endif
+
+class IParseCallback
+{
+public:
+ virtual ~IParseCallback() { }
+
+ virtual void onParsed(CVariant *variant) = 0;
+};
+
+class CSimpleParseCallback : public IParseCallback
+{
+public:
+ virtual void onParsed(CVariant *variant) { m_parsed = *variant; }
+ CVariant &GetOutput() { return m_parsed; }
+
+private:
+ CVariant m_parsed;
+};
+
+class CJSONVariantParser
+{
+public:
+ CJSONVariantParser(IParseCallback *callback);
+ ~CJSONVariantParser();
+
+ void push_buffer(const unsigned char *buffer, unsigned int length);
+
+ static CVariant Parse(const unsigned char *json, unsigned int length);
+
+private:
+ static int ParseNull(void * ctx);
+ static int ParseBoolean(void * ctx, int boolean);
+#if YAJL_MAJOR == 2
+ static int ParseInteger(void * ctx, long long integerVal);
+#else
+ static int ParseInteger(void * ctx, long integerVal);
+#endif
+ static int ParseDouble(void * ctx, double doubleVal);
+#if YAJL_MAJOR == 2
+ static int ParseString(void * ctx, const unsigned char * stringVal, size_t stringLen);
+#else
+ static int ParseString(void * ctx, const unsigned char * stringVal, unsigned int stringLen);
+#endif
+ static int ParseMapStart(void * ctx);
+#if YAJL_MAJOR == 2
+ static int ParseMapKey(void * ctx, const unsigned char * stringVal, size_t stringLen);
+#else
+ static int ParseMapKey(void * ctx, const unsigned char * stringVal, unsigned int stringLen);
+#endif
+ static int ParseMapEnd(void * ctx);
+ static int ParseArrayStart(void * ctx);
+ static int ParseArrayEnd(void * ctx);
+
+ void PushObject(CVariant variant);
+ void PopObject();
+
+ static yajl_callbacks callbacks;
+
+ IParseCallback *m_callback;
+ yajl_handle m_handler;
+
+ CVariant m_parsedObject;
+ std::vector<CVariant *> m_parse;
+ std::string m_key;
+
+ enum PARSE_STATUS
+ {
+ ParseArray = 1,
+ ParseObject = 2,
+ ParseVariable = 0
+ };
+ PARSE_STATUS m_status;
+};
diff --git a/src/utils/JSONVariantWriter.cpp b/src/utils/JSONVariantWriter.cpp
new file mode 100644
index 0000000000..54309992be
--- /dev/null
+++ b/src/utils/JSONVariantWriter.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <locale>
+
+#include "JSONVariantWriter.h"
+
+using namespace std;
+
+string CJSONVariantWriter::Write(const CVariant &value, bool compact)
+{
+ string output;
+
+#if YAJL_MAJOR == 2
+ yajl_gen g = yajl_gen_alloc(NULL);
+ yajl_gen_config(g, yajl_gen_beautify, compact ? 0 : 1);
+ yajl_gen_config(g, yajl_gen_indent_string, "\t");
+#else
+ yajl_gen_config conf = { compact ? 0 : 1, "\t" };
+ yajl_gen g = yajl_gen_alloc(&conf, NULL);
+#endif
+
+ // Set locale to classic ("C") to ensure valid JSON numbers
+ const char *currentLocale = setlocale(LC_NUMERIC, NULL);
+ std::string backupLocale;
+ if (currentLocale != NULL)
+ {
+ backupLocale = currentLocale;
+ setlocale(LC_NUMERIC, "C");
+ }
+
+ if (InternalWrite(g, value))
+ {
+ const unsigned char * buffer;
+
+#if YAJL_MAJOR == 2
+ size_t length;
+ yajl_gen_get_buf(g, &buffer, &length);
+#else
+ unsigned int length;
+ yajl_gen_get_buf(g, &buffer, &length);
+#endif
+ output = string((const char *)buffer, length);
+ }
+
+ // Re-set locale to what it was before using yajl
+ if (!backupLocale.empty())
+ setlocale(LC_NUMERIC, backupLocale.c_str());
+
+ yajl_gen_clear(g);
+ yajl_gen_free(g);
+
+ return output;
+}
+
+bool CJSONVariantWriter::InternalWrite(yajl_gen g, const CVariant &value)
+{
+ bool success = false;
+
+ switch (value.type())
+ {
+ case CVariant::VariantTypeInteger:
+#if YAJL_MAJOR == 2
+ success = yajl_gen_status_ok == yajl_gen_integer(g, (long long int)value.asInteger());
+#else
+ success = yajl_gen_status_ok == yajl_gen_integer(g, (long int)value.asInteger());
+#endif
+ break;
+ case CVariant::VariantTypeUnsignedInteger:
+#if YAJL_MAJOR == 2
+ success = yajl_gen_status_ok == yajl_gen_integer(g, (long long int)value.asUnsignedInteger());
+#else
+ success = yajl_gen_status_ok == yajl_gen_integer(g, (long int)value.asUnsignedInteger());
+#endif
+ break;
+ case CVariant::VariantTypeDouble:
+ success = yajl_gen_status_ok == yajl_gen_double(g, value.asDouble());
+ break;
+ case CVariant::VariantTypeBoolean:
+ success = yajl_gen_status_ok == yajl_gen_bool(g, value.asBoolean() ? 1 : 0);
+ break;
+ case CVariant::VariantTypeString:
+#if YAJL_MAJOR == 2
+ success = yajl_gen_status_ok == yajl_gen_string(g, (const unsigned char*)value.c_str(), (size_t)value.size());
+#else
+ success = yajl_gen_status_ok == yajl_gen_string(g, (const unsigned char*)value.c_str(), value.size());
+#endif
+ break;
+ case CVariant::VariantTypeArray:
+ success = yajl_gen_status_ok == yajl_gen_array_open(g);
+
+ for (CVariant::const_iterator_array itr = value.begin_array(); itr != value.end_array() && success; ++itr)
+ success &= InternalWrite(g, *itr);
+
+ if (success)
+ success = yajl_gen_status_ok == yajl_gen_array_close(g);
+
+ break;
+ case CVariant::VariantTypeObject:
+ success = yajl_gen_status_ok == yajl_gen_map_open(g);
+
+ for (CVariant::const_iterator_map itr = value.begin_map(); itr != value.end_map() && success; ++itr)
+ {
+#if YAJL_MAJOR == 2
+ success &= yajl_gen_status_ok == yajl_gen_string(g, (const unsigned char*)itr->first.c_str(), (size_t)itr->first.length());
+#else
+ success &= yajl_gen_status_ok == yajl_gen_string(g, (const unsigned char*)itr->first.c_str(), itr->first.length());
+#endif
+ if (success)
+ success &= InternalWrite(g, itr->second);
+ }
+
+ if (success)
+ success &= yajl_gen_status_ok == yajl_gen_map_close(g);
+
+ break;
+ case CVariant::VariantTypeConstNull:
+ case CVariant::VariantTypeNull:
+ default:
+ success = yajl_gen_status_ok == yajl_gen_null(g);
+ break;
+ }
+
+ return success;
+}
diff --git a/src/utils/JSONVariantWriter.h b/src/utils/JSONVariantWriter.h
new file mode 100644
index 0000000000..877df81afc
--- /dev/null
+++ b/src/utils/JSONVariantWriter.h
@@ -0,0 +1,35 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "system.h"
+#include "Variant.h"
+#include <yajl/yajl_gen.h>
+#ifdef HAVE_YAJL_YAJL_VERSION_H
+#include <yajl/yajl_version.h>
+#endif
+
+class CJSONVariantWriter
+{
+public:
+ static std::string Write(const CVariant &value, bool compact);
+private:
+ static bool InternalWrite(yajl_gen g, const CVariant &value);
+};
diff --git a/src/utils/Job.h b/src/utils/Job.h
new file mode 100644
index 0000000000..61c5220fa1
--- /dev/null
+++ b/src/utils/Job.h
@@ -0,0 +1,170 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+class CJob;
+
+#include <stddef.h>
+
+#define kJobTypeMediaFlags "mediaflags"
+#define kJobTypeCacheImage "cacheimage"
+#define kJobTypeDDSCompress "ddscompress"
+
+/*!
+ \ingroup jobs
+ \brief Callback interface for asynchronous jobs.
+
+ Used by clients of the CJobManager to receive progress and completion notification of jobs.
+ Clients of small jobs wishing to perform actions on job completion should implement the
+ IJobCallback::OnJobComplete() function. Clients of larger jobs may choose to implement the
+ IJobCallback::OnJobProgress() function in order to be kept informed of progress.
+
+ \sa CJobManager and CJob
+ */
+class IJobCallback
+{
+public:
+ /*!
+ \brief Destructor for job call back objects.
+
+ \sa CJobManager and CJob
+ */
+ virtual ~IJobCallback() {};
+
+ /*!
+ \brief The callback used when a job completes.
+
+ OnJobComplete is called at the completion of the job's DoWork() function, and is used
+ to return information to the caller on the result of the job. On returning form this function
+ the CJobManager will destroy this job.
+
+ \param jobID the unique id of the job (as retrieved from CJobManager::AddJob)
+ \param success the result from the DoWork call
+ \param job the job that has been processed. The job will be destroyed after this function returns
+ \sa CJobManager and CJob
+ */
+ virtual void OnJobComplete(unsigned int jobID, bool success, CJob *job)=0;
+
+ /*!
+ \brief An optional callback function that a job may call while processing.
+
+ OnJobProgress may be called periodically by a job during it's DoWork() function. It is used
+ by the job to report on progress.
+
+ \param jobID the unique id of the job (as retrieved from CJobManager::AddJob)
+ \param progress the current progress of the job, out of total.
+ \param total the total amount of work to be processed.
+ \param job the job that has been processed.
+ \sa CJobManager and CJob
+ */
+ virtual void OnJobProgress(unsigned int jobID, unsigned int progress, unsigned int total, const CJob *job) {};
+};
+
+class CJobManager;
+
+/*!
+ \ingroup jobs
+ \brief Base class for jobs that are executed asyncronously.
+
+ Clients of the CJobManager should subclass CJob and provide the DoWork() function. Data should be
+ passed to the job on creation, and any data sharing between the job and the client should be kept to within
+ the callback functions if possible, and guarded with critical sections as appropriate.
+
+ Jobs typically fall into two groups: small jobs that perform a single function, and larger jobs that perform a
+ sequence of functions. Clients with small jobs should implement the IJobCallback::OnJobComplete() callback to receive results.
+ Clients with larger jobs may wish to implement both the IJobCallback::OnJobComplete() and IJobCallback::OnJobProgress()
+ callbacks to receive updates. Jobs may be cancelled at any point by the client via CJobManager::CancelJob(), however
+ effort should be taken to ensure that any callbacks and cancellation is suitably guarded against simultaneous thread access.
+
+ Handling cancellation of jobs within the OnJobProgress callback is a threadsafe operation, as all execution is
+ then in the Job thread.
+
+ \sa CJobManager and IJobCallback
+ */
+class CJob
+{
+public:
+ /*!
+ \brief Priority levels for jobs, specified by clients when adding jobs to the CJobManager.
+ \sa CJobManager
+ */
+ enum PRIORITY {
+ PRIORITY_LOW_PAUSABLE = 0,
+ PRIORITY_LOW,
+ PRIORITY_NORMAL,
+ PRIORITY_HIGH
+ };
+ CJob() { m_callback = NULL; };
+
+ /*!
+ \brief Destructor for job objects.
+
+ Jobs are destroyed by the CJobManager after the OnJobComplete() callback is complete.
+ CJob subclasses should therefore supply a virtual destructor to cleanup any memory allocated by
+ complete or cancelled jobs.
+
+ \sa CJobManager
+ */
+ virtual ~CJob() {};
+
+ /*!
+ \brief Main workhorse function of CJob instances
+
+ All CJob subclasses must implement this function, performing all processing. Once this function
+ is complete, the OnJobComplete() callback is called, and the job is then destroyed.
+
+ \sa CJobManager, IJobCallback::OnJobComplete()
+ */
+ virtual bool DoWork() = 0; // function to do the work
+
+ /*!
+ \brief Function that returns the type of job.
+
+ CJob subclasses may optionally implement this function to specify the type of job.
+ This is useful for the CJobManager::AddLIFOJob() routine, which preempts similar jobs
+ with the new job.
+
+ \return a unique character string describing the job.
+ \sa CJobManager
+ */
+ virtual const char *GetType() const { return ""; };
+
+ virtual bool operator==(const CJob* job) const
+ {
+ return false;
+ }
+
+ /*!
+ \brief Function for longer jobs to report progress and check whether they have been cancelled.
+
+ Jobs that contain loops that may take time should check this routine each iteration of the loop,
+ both to (optionally) report progress, and to check for cancellation.
+
+ \param progress the amount of the job performed, out of total.
+ \param total the total amount of processing to be performed
+ \return if true, the job has been asked to cancel.
+
+ \sa IJobCallback::OnJobProgress()
+ */
+ bool ShouldCancel(unsigned int progress, unsigned int total) const;
+private:
+ friend class CJobManager;
+ CJobManager *m_callback;
+};
diff --git a/src/utils/JobManager.cpp b/src/utils/JobManager.cpp
new file mode 100644
index 0000000000..d2d2a13f7b
--- /dev/null
+++ b/src/utils/JobManager.cpp
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "JobManager.h"
+#include <algorithm>
+#include <stdexcept>
+#include "threads/SingleLock.h"
+#include "utils/log.h"
+
+#include "system.h"
+
+
+using namespace std;
+
+bool CJob::ShouldCancel(unsigned int progress, unsigned int total) const
+{
+ if (m_callback)
+ return m_callback->OnJobProgress(progress, total, this);
+ return false;
+}
+
+CJobWorker::CJobWorker(CJobManager *manager) : CThread("JobWorker")
+{
+ m_jobManager = manager;
+ Create(true); // start work immediately, and kill ourselves when we're done
+}
+
+CJobWorker::~CJobWorker()
+{
+ // while we should already be removed from the job manager, if an exception
+ // occurs during processing that we haven't caught, we may skip over that step.
+ // Thus, before we go out of scope, ensure the job manager knows we're gone.
+ m_jobManager->RemoveWorker(this);
+ if(!IsAutoDelete())
+ StopThread();
+}
+
+void CJobWorker::Process()
+{
+ SetPriority( GetMinPriority() );
+ while (true)
+ {
+ // request an item from our manager (this call is blocking)
+ CJob *job = m_jobManager->GetNextJob(this);
+ if (!job)
+ break;
+
+ bool success = false;
+ try
+ {
+ success = job->DoWork();
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "%s error processing job %s", __FUNCTION__, job->GetType());
+ }
+ m_jobManager->OnJobComplete(success, job);
+ }
+}
+
+void CJobQueue::CJobPointer::CancelJob()
+{
+ CJobManager::GetInstance().CancelJob(m_id);
+ m_id = 0;
+}
+
+CJobQueue::CJobQueue(bool lifo, unsigned int jobsAtOnce, CJob::PRIORITY priority)
+: m_jobsAtOnce(jobsAtOnce), m_priority(priority), m_lifo(lifo)
+{
+}
+
+CJobQueue::~CJobQueue()
+{
+ CancelJobs();
+}
+
+void CJobQueue::OnJobComplete(unsigned int jobID, bool success, CJob *job)
+{
+ CSingleLock lock(m_section);
+ // check if this job is in our processing list
+ Processing::iterator i = find(m_processing.begin(), m_processing.end(), job);
+ if (i != m_processing.end())
+ m_processing.erase(i);
+ // request a new job be queued
+ QueueNextJob();
+}
+
+void CJobQueue::CancelJob(const CJob *job)
+{
+ CSingleLock lock(m_section);
+ Processing::iterator i = find(m_processing.begin(), m_processing.end(), job);
+ if (i != m_processing.end())
+ {
+ i->CancelJob();
+ m_processing.erase(i);
+ return;
+ }
+ Queue::iterator j = find(m_jobQueue.begin(), m_jobQueue.end(), job);
+ if (j != m_jobQueue.end())
+ {
+ j->FreeJob();
+ m_jobQueue.erase(j);
+ }
+}
+
+void CJobQueue::AddJob(CJob *job)
+{
+ CSingleLock lock(m_section);
+ // check if we have this job already. If so, we're done.
+ if (find(m_jobQueue.begin(), m_jobQueue.end(), job) != m_jobQueue.end() ||
+ find(m_processing.begin(), m_processing.end(), job) != m_processing.end())
+ {
+ delete job;
+ return;
+ }
+
+ if (m_lifo)
+ m_jobQueue.push_back(CJobPointer(job));
+ else
+ m_jobQueue.push_front(CJobPointer(job));
+ QueueNextJob();
+}
+
+void CJobQueue::QueueNextJob()
+{
+ CSingleLock lock(m_section);
+ if (m_jobQueue.size() && m_processing.size() < m_jobsAtOnce)
+ {
+ CJobPointer &job = m_jobQueue.back();
+ job.m_id = CJobManager::GetInstance().AddJob(job.m_job, this, m_priority);
+ m_processing.push_back(job);
+ m_jobQueue.pop_back();
+ }
+}
+
+void CJobQueue::CancelJobs()
+{
+ CSingleLock lock(m_section);
+ for_each(m_processing.begin(), m_processing.end(), mem_fun_ref(&CJobPointer::CancelJob));
+ for_each(m_jobQueue.begin(), m_jobQueue.end(), mem_fun_ref(&CJobPointer::FreeJob));
+ m_jobQueue.clear();
+ m_processing.clear();
+}
+
+
+bool CJobQueue::QueueEmpty() const
+{
+ CSingleLock lock(m_section);
+ return m_jobQueue.empty();
+}
+
+CJobManager &CJobManager::GetInstance()
+{
+ static CJobManager sJobManager;
+ return sJobManager;
+}
+
+CJobManager::CJobManager()
+{
+ m_jobCounter = 0;
+ m_running = true;
+ m_pauseJobs = false;
+}
+
+void CJobManager::Restart()
+{
+ CSingleLock lock(m_section);
+
+ if (m_running)
+ throw std::logic_error("CJobManager already running");
+ m_running = true;
+}
+
+void CJobManager::CancelJobs()
+{
+ CSingleLock lock(m_section);
+ m_running = false;
+
+ // clear any pending jobs
+ for (unsigned int priority = CJob::PRIORITY_LOW_PAUSABLE; priority <= CJob::PRIORITY_HIGH; ++priority)
+ {
+ for_each(m_jobQueue[priority].begin(), m_jobQueue[priority].end(), mem_fun_ref(&CWorkItem::FreeJob));
+ m_jobQueue[priority].clear();
+ }
+
+ // cancel any callbacks on jobs still processing
+ for_each(m_processing.begin(), m_processing.end(), mem_fun_ref(&CWorkItem::Cancel));
+
+ // tell our workers to finish
+ while (m_workers.size())
+ {
+ lock.Leave();
+ m_jobEvent.Set();
+ Sleep(0); // yield after setting the event to give the workers some time to die
+ lock.Enter();
+ }
+}
+
+CJobManager::~CJobManager()
+{
+}
+
+unsigned int CJobManager::AddJob(CJob *job, IJobCallback *callback, CJob::PRIORITY priority)
+{
+ CSingleLock lock(m_section);
+
+ if (!m_running)
+ return 0;
+
+ // increment the job counter, ensuring 0 (invalid job) is never hit
+ m_jobCounter++;
+ if (m_jobCounter == 0)
+ m_jobCounter++;
+
+ // create a work item for this job
+ CWorkItem work(job, m_jobCounter, priority, callback);
+ m_jobQueue[priority].push_back(work);
+
+ StartWorkers(priority);
+ return work.m_id;
+}
+
+void CJobManager::CancelJob(unsigned int jobID)
+{
+ CSingleLock lock(m_section);
+
+ // check whether we have this job in the queue
+ for (unsigned int priority = CJob::PRIORITY_LOW_PAUSABLE; priority <= CJob::PRIORITY_HIGH; ++priority)
+ {
+ JobQueue::iterator i = find(m_jobQueue[priority].begin(), m_jobQueue[priority].end(), jobID);
+ if (i != m_jobQueue[priority].end())
+ {
+ delete i->m_job;
+ m_jobQueue[priority].erase(i);
+ return;
+ }
+ }
+ // or if we're processing it
+ Processing::iterator it = find(m_processing.begin(), m_processing.end(), jobID);
+ if (it != m_processing.end())
+ it->m_callback = NULL; // job is in progress, so only thing to do is to remove callback
+}
+
+void CJobManager::StartWorkers(CJob::PRIORITY priority)
+{
+ CSingleLock lock(m_section);
+
+ // check how many free threads we have
+ if (m_processing.size() >= GetMaxWorkers(priority))
+ return;
+
+ // do we have any sleeping threads?
+ if (m_processing.size() < m_workers.size())
+ {
+ m_jobEvent.Set();
+ return;
+ }
+
+ // everyone is busy - we need more workers
+ m_workers.push_back(new CJobWorker(this));
+}
+
+CJob *CJobManager::PopJob()
+{
+ CSingleLock lock(m_section);
+ for (int priority = CJob::PRIORITY_HIGH; priority >= CJob::PRIORITY_LOW_PAUSABLE; --priority)
+ {
+ // Check whether we're pausing pausable jobs
+ if (priority == CJob::PRIORITY_LOW_PAUSABLE && m_pauseJobs)
+ continue;
+
+ if (m_jobQueue[priority].size() && m_processing.size() < GetMaxWorkers(CJob::PRIORITY(priority)))
+ {
+ // pop the job off the queue
+ CWorkItem job = m_jobQueue[priority].front();
+ m_jobQueue[priority].pop_front();
+
+ // add to the processing vector
+ m_processing.push_back(job);
+ job.m_job->m_callback = this;
+ return job.m_job;
+ }
+ }
+ return NULL;
+}
+
+void CJobManager::PauseJobs()
+{
+ CSingleLock lock(m_section);
+ m_pauseJobs = true;
+}
+
+void CJobManager::UnPauseJobs()
+{
+ CSingleLock lock(m_section);
+ m_pauseJobs = false;
+}
+
+bool CJobManager::IsProcessing(const CJob::PRIORITY &priority) const
+{
+ CSingleLock lock(m_section);
+
+ if (m_pauseJobs)
+ return false;
+
+ for(Processing::const_iterator it = m_processing.begin(); it < m_processing.end(); ++it)
+ {
+ if (priority == it->m_priority)
+ return true;
+ }
+ return false;
+}
+
+int CJobManager::IsProcessing(const std::string &type) const
+{
+ int jobsMatched = 0;
+ CSingleLock lock(m_section);
+
+ if (m_pauseJobs)
+ return 0;
+
+ for(Processing::const_iterator it = m_processing.begin(); it < m_processing.end(); ++it)
+ {
+ if (type == std::string(it->m_job->GetType()))
+ jobsMatched++;
+ }
+ return jobsMatched;
+}
+
+CJob *CJobManager::GetNextJob(const CJobWorker *worker)
+{
+ CSingleLock lock(m_section);
+ while (m_running)
+ {
+ // grab a job off the queue if we have one
+ CJob *job = PopJob();
+ if (job)
+ return job;
+ // no jobs are left - sleep for 30 seconds to allow new jobs to come in
+ lock.Leave();
+ bool newJob = m_jobEvent.WaitMSec(30000);
+ lock.Enter();
+ if (!newJob)
+ break;
+ }
+ // ensure no jobs have come in during the period after
+ // timeout and before we held the lock
+ CJob *job = PopJob();
+ if (job)
+ return job;
+ // have no jobs
+ RemoveWorker(worker);
+ return NULL;
+}
+
+bool CJobManager::OnJobProgress(unsigned int progress, unsigned int total, const CJob *job) const
+{
+ CSingleLock lock(m_section);
+ // find the job in the processing queue, and check whether it's cancelled (no callback)
+ Processing::const_iterator i = find(m_processing.begin(), m_processing.end(), job);
+ if (i != m_processing.end())
+ {
+ CWorkItem item(*i);
+ lock.Leave(); // leave section prior to call
+ if (item.m_callback)
+ {
+ item.m_callback->OnJobProgress(item.m_id, progress, total, job);
+ return false;
+ }
+ }
+ return true; // couldn't find the job, or it's been cancelled
+}
+
+void CJobManager::OnJobComplete(bool success, CJob *job)
+{
+ CSingleLock lock(m_section);
+ // remove the job from the processing queue
+ Processing::iterator i = find(m_processing.begin(), m_processing.end(), job);
+ if (i != m_processing.end())
+ {
+ // tell any listeners we're done with the job, then delete it
+ CWorkItem item(*i);
+ lock.Leave();
+ try
+ {
+ if (item.m_callback)
+ item.m_callback->OnJobComplete(item.m_id, success, item.m_job);
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "%s error processing job %s", __FUNCTION__, item.m_job->GetType());
+ }
+ lock.Enter();
+ Processing::iterator j = find(m_processing.begin(), m_processing.end(), job);
+ if (j != m_processing.end())
+ m_processing.erase(j);
+ lock.Leave();
+ item.FreeJob();
+ }
+}
+
+void CJobManager::RemoveWorker(const CJobWorker *worker)
+{
+ CSingleLock lock(m_section);
+ // remove our worker
+ Workers::iterator i = find(m_workers.begin(), m_workers.end(), worker);
+ if (i != m_workers.end())
+ m_workers.erase(i); // workers auto-delete
+}
+
+unsigned int CJobManager::GetMaxWorkers(CJob::PRIORITY priority)
+{
+ static const unsigned int max_workers = 5;
+ return max_workers - (CJob::PRIORITY_HIGH - priority);
+}
diff --git a/src/utils/JobManager.h b/src/utils/JobManager.h
new file mode 100644
index 0000000000..1df2287207
--- /dev/null
+++ b/src/utils/JobManager.h
@@ -0,0 +1,333 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <queue>
+#include <vector>
+#include <string>
+#include "threads/CriticalSection.h"
+#include "threads/Thread.h"
+#include "Job.h"
+
+class CJobManager;
+
+class CJobWorker : public CThread
+{
+public:
+ CJobWorker(CJobManager *manager);
+ virtual ~CJobWorker();
+
+ void Process();
+private:
+ CJobManager *m_jobManager;
+};
+
+/*!
+ \ingroup jobs
+ \brief Job Queue class to handle a queue of unique jobs to be processed sequentially
+
+ Holds a queue of jobs to be processed sequentially, either first in,first out
+ or last in, first out. Jobs are unique, so queueing multiple copies of the same job
+ (based on the CJob::operator==) will not add additional jobs.
+
+ Classes should subclass this class and override OnJobCallback should they require
+ information from the job.
+
+ \sa CJob and IJobCallback
+ */
+class CJobQueue: public IJobCallback
+{
+ class CJobPointer
+ {
+ public:
+ CJobPointer(CJob *job)
+ {
+ m_job = job;
+ m_id = 0;
+ };
+ void CancelJob();
+ void FreeJob()
+ {
+ delete m_job;
+ m_job = NULL;
+ };
+ bool operator==(const CJob *job) const
+ {
+ if (m_job)
+ return *m_job == job;
+ return false;
+ };
+ CJob *m_job;
+ unsigned int m_id;
+ };
+public:
+ /*!
+ \brief CJobQueue constructor
+ \param lifo whether the queue should be processed last in first out or first in first out. Defaults to false (first in first out)
+ \param jobsAtOnce number of jobs at once to process. Defaults to 1.
+ \param priority priority of this queue.
+ \sa CJob
+ */
+ CJobQueue(bool lifo = false, unsigned int jobsAtOnce = 1, CJob::PRIORITY priority = CJob::PRIORITY_LOW);
+
+ /*!
+ \brief CJobQueue destructor
+ Cancels any in-process jobs, and destroys the job queue.
+ \sa CJob
+ */
+ virtual ~CJobQueue();
+
+ /*!
+ \brief Add a job to the queue
+ On completion of the job (or destruction of the job queue) the CJob object will be destroyed.
+ \param job a pointer to the job to add. The job should be subclassed from CJob.
+ \sa CJob
+ */
+ void AddJob(CJob *job);
+
+ /*!
+ \brief Cancel a job in the queue
+ Cancels a job in the queue. Any job currently being processed may complete after this
+ call has completed, but OnJobComplete will not be performed. If the job is only queued
+ then it will be removed from the queue and deleted.
+ \param job a pointer to the job to cancel. The job should be subclassed from CJob.
+ \sa CJob
+ */
+ void CancelJob(const CJob *job);
+
+ /*!
+ \brief Cancel all jobs in the queue
+ Removes all jobs from the queue. Any job currently being processed may complete after this
+ call has completed, but OnJobComplete will not be performed.
+ \sa CJob
+ */
+ void CancelJobs();
+
+ /*!
+ \brief The callback used when a job completes.
+
+ OnJobComplete is called at the completion of the CJob::DoWork function, and is used
+ to return information to the caller on the result of the job. On returning from this function
+ the CJobManager will destroy this job.
+
+ Subclasses should override this function if they wish to transfer information from the job prior
+ to it's deletion. They must then call this base class function, which will move on to the next
+ job.
+
+ \sa CJobManager, IJobCallback and CJob
+ */
+ virtual void OnJobComplete(unsigned int jobID, bool success, CJob *job);
+
+protected:
+ /*!
+ \brief Returns if we still have jobs waiting to be processed
+ NOTE: This function does not take into account the jobs that are currently processing
+ */
+ bool QueueEmpty() const;
+
+private:
+ void QueueNextJob();
+
+ typedef std::deque<CJobPointer> Queue;
+ typedef std::vector<CJobPointer> Processing;
+ Queue m_jobQueue;
+ Processing m_processing;
+
+ unsigned int m_jobsAtOnce;
+ CJob::PRIORITY m_priority;
+ CCriticalSection m_section;
+ bool m_lifo;
+};
+
+/*!
+ \ingroup jobs
+ \brief Job Manager class for scheduling asynchronous jobs.
+
+ Controls asynchronous job execution, by allowing clients to add and cancel jobs.
+ Should be accessed via CJobManager::GetInstance(). Jobs are allocated based on
+ priority levels. Lower priority jobs are executed only if there are sufficient
+ spare worker threads free to allow for higher priority jobs that may arise.
+
+ \sa CJob and IJobCallback
+ */
+class CJobManager
+{
+ class CWorkItem
+ {
+ public:
+ CWorkItem(CJob *job, unsigned int id, CJob::PRIORITY priority, IJobCallback *callback)
+ {
+ m_job = job;
+ m_id = id;
+ m_callback = callback;
+ m_priority = priority;
+ }
+ bool operator==(unsigned int jobID) const
+ {
+ return m_id == jobID;
+ };
+ bool operator==(const CJob *job) const
+ {
+ return m_job == job;
+ };
+ void FreeJob()
+ {
+ delete m_job;
+ m_job = NULL;
+ };
+ void Cancel()
+ {
+ m_callback = NULL;
+ };
+ CJob *m_job;
+ unsigned int m_id;
+ IJobCallback *m_callback;
+ CJob::PRIORITY m_priority;
+ };
+
+public:
+ /*!
+ \brief The only way through which the global instance of the CJobManager should be accessed.
+ \return the global instance.
+ */
+ static CJobManager &GetInstance();
+
+ /*!
+ \brief Add a job to the threaded job manager.
+ \param job a pointer to the job to add. The job should be subclassed from CJob
+ \param callback a pointer to an IJobCallback instance to receive job progress and completion notices.
+ \param priority the priority that this job should run at.
+ \return a unique identifier for this job, to be used with other interaction
+ \sa CJob, IJobCallback, CancelJob()
+ */
+ unsigned int AddJob(CJob *job, IJobCallback *callback, CJob::PRIORITY priority = CJob::PRIORITY_LOW);
+
+ /*!
+ \brief Cancel a job with the given id.
+ \param jobID the id of the job to cancel, retrieved previously from AddJob()
+ \sa AddJob()
+ */
+ void CancelJob(unsigned int jobID);
+
+ /*!
+ \brief Cancel all remaining jobs, preparing for shutdown
+ Should be called prior to destroying any objects that may be being used as callbacks
+ \sa CancelJob(), AddJob()
+ */
+ void CancelJobs();
+
+ /*!
+ \brief Re-start accepting jobs again
+ Called after calling CancelJobs() to allow this manager to accept more jobs
+ \throws std::logic_error if the manager was not previously cancelled
+ \sa CancelJobs()
+ */
+ void Restart();
+
+ /*!
+ \brief Checks to see if any jobs of a specific type are currently processing.
+ \param type Job type to search for
+ \return Number of matching jobs
+ */
+ int IsProcessing(const std::string &type) const;
+
+ /*!
+ \brief Suspends queueing of jobs with priority PRIORITY_LOW_PAUSABLE until unpaused
+ Useful to (for ex) stop queuing thumb jobs during video start/playback.
+ Does not affect currently processing jobs, use IsProcessing to see if any need to be waited on
+ \sa UnPauseJobs()
+ */
+ void PauseJobs();
+
+ /*!
+ \brief Resumes queueing of (previously paused) jobs with priority PRIORITY_LOW_PAUSABLE
+ \sa PauseJobs()
+ */
+ void UnPauseJobs();
+
+ /*!
+ \brief Checks to see if any jobs with specific priority are currently processing.
+ \param priority to search for
+ \return true if processing jobs, else returns false
+ */
+ bool IsProcessing(const CJob::PRIORITY &priority) const;
+
+protected:
+ friend class CJobWorker;
+ friend class CJob;
+
+ /*!
+ \brief Get a new job to process. Blocks until a new job is available, or a timeout has occurred.
+ \param worker a pointer to the current CJobWorker instance requesting a job.
+ \sa CJob
+ */
+ CJob *GetNextJob(const CJobWorker *worker);
+
+ /*!
+ \brief Callback from CJobWorker after a job has completed.
+ Calls IJobCallback::OnJobComplete(), and then destroys job.
+ \param job a pointer to the calling subclassed CJob instance.
+ \param success the result from the DoWork call
+ \sa IJobCallback, CJob
+ */
+ void OnJobComplete(bool success, CJob *job);
+
+ /*!
+ \brief Callback from CJob to report progress and check for cancellation.
+ Checks for cancellation, and calls IJobCallback::OnJobProgress().
+ \param progress amount of processing performed to date, out of total.
+ \param total total amount of processing.
+ \param job pointer to the calling subclassed CJob instance.
+ \return true if the job has been cancelled, else returns false.
+ \sa IJobCallback, CJob
+ */
+ bool OnJobProgress(unsigned int progress, unsigned int total, const CJob *job) const;
+
+private:
+ // private construction, and no assignements; use the provided singleton methods
+ CJobManager();
+ CJobManager(const CJobManager&);
+ CJobManager const& operator=(CJobManager const&);
+ virtual ~CJobManager();
+
+ /*! \brief Pop a job off the job queue and add to the processing queue ready to process
+ \return the job to process, NULL if no jobs are available
+ */
+ CJob *PopJob();
+
+ void StartWorkers(CJob::PRIORITY priority);
+ void RemoveWorker(const CJobWorker *worker);
+ static unsigned int GetMaxWorkers(CJob::PRIORITY priority);
+
+ unsigned int m_jobCounter;
+
+ typedef std::deque<CWorkItem> JobQueue;
+ typedef std::vector<CWorkItem> Processing;
+ typedef std::vector<CJobWorker*> Workers;
+
+ JobQueue m_jobQueue[CJob::PRIORITY_HIGH+1];
+ bool m_pauseJobs;
+ Processing m_processing;
+ Workers m_workers;
+
+ CCriticalSection m_section;
+ CEvent m_jobEvent;
+ bool m_running;
+};
diff --git a/src/utils/LabelFormatter.cpp b/src/utils/LabelFormatter.cpp
new file mode 100644
index 0000000000..c54856964f
--- /dev/null
+++ b/src/utils/LabelFormatter.cpp
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "LabelFormatter.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/Settings.h"
+#include "RegExp.h"
+#include "Util.h"
+#include "video/VideoInfoTag.h"
+#include "music/tags/MusicInfoTag.h"
+#include "pictures/PictureInfoTag.h"
+#include "FileItem.h"
+#include "StringUtils.h"
+#include "URIUtils.h"
+#include "guilib/LocalizeStrings.h"
+
+using namespace MUSIC_INFO;
+
+/* LabelFormatter
+ * ==============
+ *
+ * The purpose of this class is to parse a mask string of the form
+ *
+ * [%N. ][%T] - [%A][ (%Y)]
+ *
+ * and provide methods to format up a CFileItem's label(s).
+ *
+ * The %N/%A/%B masks are replaced with the corresponding metadata (if available).
+ *
+ * Square brackets are treated as a metadata block. Anything inside the block other
+ * than the metadata mask is treated as either a prefix or postfix to the metadata. This
+ * information is only included in the formatted string when the metadata is non-empty.
+ *
+ * Any metadata tags not enclosed with square brackets are treated as if it were immediately
+ * enclosed - i.e. with no prefix or postfix.
+ *
+ * The special characters %, [, and ] can be produced using %%, %[, and %] respectively.
+ *
+ * Any static text outside of the metadata blocks is only shown if the blocks on either side
+ * (or just one side in the case of an end) are both non-empty.
+ *
+ * Examples (using the above expression):
+ *
+ * Track Title Artist Year Resulting Label
+ * ----- ----- ------ ---- ---------------
+ * 10 "40" U2 1983 10. "40" - U2 (1983)
+ * "40" U2 1983 "40" - U2 (1983)
+ * 10 U2 1983 10. U2 (1983)
+ * 10 "40" 1983 "40" (1983)
+ * 10 "40" U2 10. "40" - U2
+ * 10 "40" 10. "40"
+ *
+ * Available metadata masks:
+ *
+ * %A - Artist
+ * %B - Album
+ * %C - Programs count
+ * %D - Duration
+ * %E - episode number
+ * %F - FileName
+ * %G - Genre
+ * %H - season*100+episode
+ * %I - Size
+ * %J - Date
+ * %K - Movie/Game title
+ * %L - existing Label
+ * %M - number of episodes
+ * %N - Track Number
+ * %O - mpaa rating
+ * %P - production code
+ * %Q - file time
+ * %R - Movie rating
+ * %S - Disc Number
+ * %T - Title
+ * %U - studio
+ * %V - Playcount
+ * %W - Listeners
+ * %X - Bitrate
+ * %Y - Year
+ * %Z - tvshow title
+ * %a - Date Added
+ * %p - Last Played
+ * *t - Date Taken (suitable for Pictures)
+ */
+
+#define MASK_CHARS "NSATBGYFLDIJRCKMEPHZOQUVXWapt"
+
+CLabelFormatter::CLabelFormatter(const std::string &mask, const std::string &mask2)
+{
+ // assemble our label masks
+ AssembleMask(0, mask);
+ AssembleMask(1, mask2);
+ // save a bool for faster lookups
+ m_hideFileExtensions = !CSettings::Get().GetBool("filelists.showextensions");
+}
+
+std::string CLabelFormatter::GetContent(unsigned int label, const CFileItem *item) const
+{
+ assert(label < 2);
+ assert(m_staticContent[label].size() == m_dynamicContent[label].size() + 1);
+
+ if (!item) return "";
+
+ std::string strLabel, dynamicLeft, dynamicRight;
+ for (unsigned int i = 0; i < m_dynamicContent[label].size(); i++)
+ {
+ dynamicRight = GetMaskContent(m_dynamicContent[label][i], item);
+ if ((i == 0 || !dynamicLeft.empty()) && !dynamicRight.empty())
+ strLabel += m_staticContent[label][i];
+ strLabel += dynamicRight;
+ dynamicLeft = dynamicRight;
+ }
+ if (!dynamicLeft.empty())
+ strLabel += m_staticContent[label][m_dynamicContent[label].size()];
+
+ return strLabel;
+}
+
+void CLabelFormatter::FormatLabel(CFileItem *item) const
+{
+ std::string maskedLabel = GetContent(0, item);
+ if (!maskedLabel.empty())
+ item->SetLabel(maskedLabel);
+ else if (!item->m_bIsFolder && m_hideFileExtensions)
+ item->RemoveExtension();
+}
+
+void CLabelFormatter::FormatLabel2(CFileItem *item) const
+{
+ item->SetLabel2(GetContent(1, item));
+}
+
+std::string CLabelFormatter::GetMaskContent(const CMaskString &mask, const CFileItem *item) const
+{
+ if (!item) return "";
+ const CMusicInfoTag *music = item->GetMusicInfoTag();
+ const CVideoInfoTag *movie = item->GetVideoInfoTag();
+ const CPictureInfoTag *pic = item->GetPictureInfoTag();
+ std::string value;
+ switch (mask.m_content)
+ {
+ case 'N':
+ if (music && music->GetTrackNumber() > 0)
+ value = StringUtils::Format("%02.2i", music->GetTrackNumber());
+ if (movie&& movie->m_iTrack > 0)
+ value = StringUtils::Format("%02.2i", movie->m_iTrack);
+ break;
+ case 'S':
+ if (music && music->GetDiscNumber() > 0)
+ value = StringUtils::Format("%02.2i", music->GetDiscNumber());
+ break;
+ case 'A':
+ if (music && music->GetArtist().size())
+ value = StringUtils::Join(music->GetArtist(), g_advancedSettings.m_musicItemSeparator);
+ if (movie && movie->m_artist.size())
+ value = StringUtils::Join(movie->m_artist, g_advancedSettings.m_videoItemSeparator);
+ break;
+ case 'T':
+ if (music && music->GetTitle().size())
+ value = music->GetTitle();
+ if (movie && movie->m_strTitle.size())
+ value = movie->m_strTitle;
+ break;
+ case 'Z':
+ if (movie && !movie->m_strShowTitle.empty())
+ value = movie->m_strShowTitle;
+ break;
+ case 'B':
+ if (music && music->GetAlbum().size())
+ value = music->GetAlbum();
+ else if (movie)
+ value = movie->m_strAlbum;
+ break;
+ case 'G':
+ if (music && music->GetGenre().size())
+ value = StringUtils::Join(music->GetGenre(), g_advancedSettings.m_musicItemSeparator);
+ if (movie && movie->m_genre.size())
+ value = StringUtils::Join(movie->m_genre, g_advancedSettings.m_videoItemSeparator);
+ break;
+ case 'Y':
+ if (music)
+ value = music->GetYearString();
+ if (movie)
+ {
+ if (movie->m_firstAired.IsValid())
+ value = movie->m_firstAired.GetAsLocalizedDate();
+ else if (movie->m_premiered.IsValid())
+ value = movie->m_premiered.GetAsLocalizedDate();
+ else if (movie->m_iYear > 0)
+ value = StringUtils::Format("%i", movie->m_iYear);
+ }
+ break;
+ case 'F': // filename
+ value = CUtil::GetTitleFromPath(item->GetPath(), item->m_bIsFolder && !item->IsFileFolder());
+ break;
+ case 'L':
+ value = item->GetLabel();
+ // is the label the actual file or folder name?
+ if (value == URIUtils::GetFileName(item->GetPath()))
+ { // label is the same as filename, clean it up as appropriate
+ value = CUtil::GetTitleFromPath(item->GetPath(), item->m_bIsFolder && !item->IsFileFolder());
+ }
+ break;
+ case 'D':
+ { // duration
+ int nDuration=0;
+ if (music)
+ nDuration = music->GetDuration();
+ if (movie)
+ nDuration = movie->GetDuration();
+ if (nDuration > 0)
+ value = StringUtils::SecondsToTimeString(nDuration, (nDuration >= 3600) ? TIME_FORMAT_H_MM_SS : TIME_FORMAT_MM_SS);
+ else if (item->m_dwSize > 0)
+ value = StringUtils::SizeToString(item->m_dwSize);
+ }
+ break;
+ case 'I': // size
+ if( !item->m_bIsFolder || item->m_dwSize != 0 )
+ value = StringUtils::SizeToString(item->m_dwSize);
+ break;
+ case 'J': // date
+ if (item->m_dateTime.IsValid())
+ value = item->m_dateTime.GetAsLocalizedDate();
+ break;
+ case 'Q': // time
+ if (item->m_dateTime.IsValid())
+ value = item->m_dateTime.GetAsLocalizedTime("", false);
+ break;
+ case 'R': // rating
+ if (music && music->GetRating() != '0')
+ value.assign(1, music->GetRating());
+ else if (movie && movie->m_fRating != 0.f)
+ value = StringUtils::Format("%.1f", movie->m_fRating);
+ break;
+ case 'C': // programs count
+ value = StringUtils::Format("%i", item->m_iprogramCount);
+ break;
+ case 'K':
+ value = item->m_strTitle;
+ break;
+ case 'M':
+ if (movie && movie->m_iEpisode > 0)
+ value = StringUtils::Format("%i %s",
+ movie->m_iEpisode,
+ g_localizeStrings.Get(movie->m_iEpisode == 1 ? 20452 : 20453).c_str());
+ break;
+ case 'E':
+ if (movie && movie->m_iEpisode > 0)
+ { // episode number
+ if (movie->m_iSeason == 0)
+ value = StringUtils::Format("S%02.2i", movie->m_iEpisode);
+ else
+ value = StringUtils::Format("%02.2i", movie->m_iEpisode);
+ }
+ break;
+ case 'P':
+ if (movie) // tvshow production code
+ value = movie->m_strProductionCode;
+ break;
+ case 'H':
+ if (movie && movie->m_iEpisode > 0)
+ { // season*100+episode number
+ if (movie->m_iSeason == 0)
+ value = StringUtils::Format("S%02.2i", movie->m_iEpisode);
+ else
+ value = StringUtils::Format("%ix%02.2i", movie->m_iSeason,movie->m_iEpisode);
+ }
+ break;
+ case 'O':
+ if (movie && movie->m_strMPAARating)
+ {// MPAA Rating
+ value = movie->m_strMPAARating;
+ }
+ break;
+ case 'U':
+ if (movie && movie->m_studio.size() > 0)
+ {// Studios
+ value = StringUtils::Join(movie ->m_studio, g_advancedSettings.m_videoItemSeparator);
+ }
+ break;
+ case 'V': // Playcount
+ if (music)
+ value = StringUtils::Format("%i", music->GetPlayCount());
+ if (movie)
+ value = StringUtils::Format("%i", movie->m_playCount);
+ break;
+ case 'X': // Bitrate
+ if( !item->m_bIsFolder && item->m_dwSize != 0 )
+ value = StringUtils::Format("%" PRId64" kbps", item->m_dwSize);
+ break;
+ case 'W': // Listeners
+ if( !item->m_bIsFolder && music && music->GetListeners() != 0 )
+ value = StringUtils::Format("%i %s",
+ music->GetListeners(),
+ g_localizeStrings.Get(music->GetListeners() == 1 ? 20454 : 20455).c_str());
+ break;
+ case 'a': // Date Added
+ if (movie && movie->m_dateAdded.IsValid())
+ value = movie->m_dateAdded.GetAsLocalizedDate();
+ break;
+ case 'p': // Last played
+ if (movie && movie->m_lastPlayed.IsValid())
+ value = movie->m_lastPlayed.GetAsLocalizedDate();
+ break;
+ case 't': // Date Taken
+ if (pic && pic->GetDateTimeTaken().IsValid())
+ value = pic->GetDateTimeTaken().GetAsLocalizedDate();
+ break;
+ }
+ if (!value.empty())
+ return mask.m_prefix + value + mask.m_postfix;
+ return "";
+}
+
+void CLabelFormatter::SplitMask(unsigned int label, const std::string &mask)
+{
+ assert(label < 2);
+ CRegExp reg;
+ reg.RegComp("%([" MASK_CHARS "])");
+ std::string work(mask);
+ int findStart = -1;
+ while ((findStart = reg.RegFind(work.c_str())) >= 0)
+ { // we've found a match
+ m_staticContent[label].push_back(work.substr(0, findStart));
+ m_dynamicContent[label].push_back(CMaskString("",
+ reg.GetMatch(1)[0], ""));
+ work = work.substr(findStart + reg.GetFindLen());
+ }
+ m_staticContent[label].push_back(work);
+}
+
+void CLabelFormatter::AssembleMask(unsigned int label, const std::string& mask)
+{
+ assert(label < 2);
+ m_staticContent[label].clear();
+ m_dynamicContent[label].clear();
+
+ // we want to match [<prefix>%A<postfix]
+ // but allow %%, %[, %] to be in the prefix and postfix. Anything before the first [
+ // could be a mask that's not surrounded with [], so pass to SplitMask.
+ CRegExp reg;
+ reg.RegComp("(^|[^%])\\[(([^%]|%%|%\\]|%\\[)*)%([" MASK_CHARS "])(([^%]|%%|%\\]|%\\[)*)\\]");
+ std::string work(mask);
+ int findStart = -1;
+ while ((findStart = reg.RegFind(work.c_str())) >= 0)
+ { // we've found a match for a pre/postfixed string
+ // send anything
+ SplitMask(label, work.substr(0, findStart) + reg.GetMatch(1));
+ m_dynamicContent[label].push_back(CMaskString(
+ reg.GetMatch(2),
+ reg.GetMatch(4)[0],
+ reg.GetMatch(5)));
+ work = work.substr(findStart + reg.GetFindLen());
+ }
+ SplitMask(label, work);
+ assert(m_staticContent[label].size() == m_dynamicContent[label].size() + 1);
+}
+
+bool CLabelFormatter::FillMusicTag(const std::string &fileName, CMusicInfoTag *tag) const
+{
+ // run through and find static content to split the string up
+ size_t pos1 = fileName.find(m_staticContent[0][0], 0);
+ if (pos1 == std::string::npos)
+ return false;
+ for (unsigned int i = 1; i < m_staticContent[0].size(); i++)
+ {
+ size_t pos2 = m_staticContent[0][i].size() ? fileName.find(m_staticContent[0][i], pos1) : fileName.size();
+ if (pos2 == std::string::npos)
+ return false;
+ // found static content - thus we have the dynamic content surrounded
+ FillMusicMaskContent(m_dynamicContent[0][i - 1].m_content, fileName.substr(pos1, pos2 - pos1), tag);
+ pos1 = pos2 + m_staticContent[0][i].size();
+ }
+ return true;
+}
+
+void CLabelFormatter::FillMusicMaskContent(const char mask, const std::string &value, CMusicInfoTag *tag) const
+{
+ if (!tag) return;
+ switch (mask)
+ {
+ case 'N':
+ tag->SetTrackNumber(atol(value.c_str()));
+ break;
+ case 'S':
+ tag->SetDiscNumber(atol(value.c_str()));
+ break;
+ case 'A':
+ tag->SetArtist(value);
+ break;
+ case 'T':
+ tag->SetTitle(value);
+ break;
+ case 'B':
+ tag->SetAlbum(value);
+ break;
+ case 'G':
+ tag->SetGenre(value);
+ break;
+ case 'Y':
+ tag->SetYear(atol(value.c_str()));
+ break;
+ case 'D':
+ tag->SetDuration(StringUtils::TimeStringToSeconds(value));
+ break;
+ case 'R': // rating
+ tag->SetRating(value[0]);
+ break;
+ }
+}
+
diff --git a/src/utils/LabelFormatter.h b/src/utils/LabelFormatter.h
new file mode 100644
index 0000000000..d63e8041ca
--- /dev/null
+++ b/src/utils/LabelFormatter.h
@@ -0,0 +1,88 @@
+#pragma once
+
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string>
+#include <vector>
+
+namespace MUSIC_INFO
+{
+ class CMusicInfoTag;
+}
+
+class CFileItem; // forward
+
+struct LABEL_MASKS
+{
+ LABEL_MASKS(const std::string& strLabelFile="", const std::string& strLabel2File="", const std::string& strLabelFolder="", const std::string& strLabel2Folder="") :
+ m_strLabelFile(strLabelFile),
+ m_strLabel2File(strLabel2File),
+ m_strLabelFolder(strLabelFolder),
+ m_strLabel2Folder(strLabel2Folder)
+ {}
+ std::string m_strLabelFile;
+ std::string m_strLabel2File;
+ std::string m_strLabelFolder;
+ std::string m_strLabel2Folder;
+};
+
+class CLabelFormatter
+{
+public:
+ CLabelFormatter(const std::string &mask, const std::string &mask2);
+
+ void FormatLabel(CFileItem *item) const;
+ void FormatLabel2(CFileItem *item) const;
+ void FormatLabels(CFileItem *item) const // convenient shorthand
+ {
+ FormatLabel(item);
+ FormatLabel2(item);
+ }
+
+ bool FillMusicTag(const std::string &fileName, MUSIC_INFO::CMusicInfoTag *tag) const;
+
+private:
+ class CMaskString
+ {
+ public:
+ CMaskString(const std::string &prefix, char content, const std::string &postfix) :
+ m_prefix(prefix),
+ m_postfix(postfix),
+ m_content(content)
+ {};
+ std::string m_prefix;
+ std::string m_postfix;
+ char m_content;
+ };
+
+ // functions for assembling the mask vectors
+ void AssembleMask(unsigned int label, const std::string &mask);
+ void SplitMask(unsigned int label, const std::string &mask);
+
+ // functions for retrieving content based on our mask vectors
+ std::string GetContent(unsigned int label, const CFileItem *item) const;
+ std::string GetMaskContent(const CMaskString &mask, const CFileItem *item) const;
+ void FillMusicMaskContent(const char mask, const std::string &value, MUSIC_INFO::CMusicInfoTag *tag) const;
+
+ std::vector<std::string> m_staticContent[2];
+ std::vector<CMaskString> m_dynamicContent[2];
+ bool m_hideFileExtensions;
+};
diff --git a/src/utils/LangCodeExpander.cpp b/src/utils/LangCodeExpander.cpp
new file mode 100644
index 0000000000..599609c429
--- /dev/null
+++ b/src/utils/LangCodeExpander.cpp
@@ -0,0 +1,1679 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "LangCodeExpander.h"
+#include "utils/XBMCTinyXML.h"
+#include "LangInfo.h"
+#include "utils/log.h"
+#include "utils/StringUtils.h"
+#include "Util.h"
+
+#define MAKECODE(a, b, c, d) ((((long)(a))<<24) | (((long)(b))<<16) | (((long)(c))<<8) | (long)(d))
+#define MAKETWOCHARCODE(a, b) ((((long)(a))<<8) | (long)(b))
+
+typedef struct LCENTRY
+{
+ long code;
+ const char *name;
+} LCENTRY;
+
+extern const struct LCENTRY g_iso639_1[186];
+extern const struct LCENTRY g_iso639_2[538];
+
+struct CharCodeConvertionWithHack
+{
+ const char* old;
+ const char* id;
+ const char* win_id;
+};
+
+struct CharCodeConvertion
+{
+ const char* old;
+ const char* id;
+};
+
+// declared as extern to allow forward declaration
+extern const CharCodeConvertionWithHack CharCode2To3[189];
+extern const CharCodeConvertion RegionCode2To3[246];
+
+CLangCodeExpander::CLangCodeExpander(void)
+{}
+
+CLangCodeExpander::~CLangCodeExpander(void)
+{
+}
+
+void CLangCodeExpander::Clear()
+{
+ m_mapUser.clear();
+}
+
+void CLangCodeExpander::LoadUserCodes(const TiXmlElement* pRootElement)
+{
+ if (pRootElement)
+ {
+ m_mapUser.clear();
+
+ std::string sShort, sLong;
+
+ const TiXmlNode* pLangCode = pRootElement->FirstChild("code");
+ while (pLangCode)
+ {
+ const TiXmlNode* pShort = pLangCode->FirstChildElement("short");
+ const TiXmlNode* pLong = pLangCode->FirstChildElement("long");
+ if (pShort && pLong)
+ {
+ sShort = pShort->FirstChild()->Value();
+ sLong = pLong->FirstChild()->Value();
+ StringUtils::ToLower(sShort);
+ m_mapUser[sShort] = sLong;
+ }
+ pLangCode = pLangCode->NextSibling();
+ }
+ }
+}
+
+bool CLangCodeExpander::Lookup(std::string& desc, const std::string& code)
+{
+ int iSplit = code.find("-");
+ if (iSplit > 0)
+ {
+ std::string strLeft, strRight;
+ const bool bLeft = Lookup(strLeft, code.substr(0, iSplit));
+ const bool bRight = Lookup(strRight, code.substr(iSplit + 1));
+ if (bLeft || bRight)
+ {
+ desc = "";
+ if (strLeft.length() > 0)
+ desc = strLeft;
+ else
+ desc = code.substr(0, iSplit);
+
+ if (strRight.length() > 0)
+ {
+ desc += " - ";
+ desc += strRight;
+ }
+ else
+ {
+ desc += " - ";
+ desc += code.substr(iSplit + 1);
+ }
+ return true;
+ }
+ return false;
+ }
+ else
+ {
+ if( LookupInMap(desc, code) )
+ return true;
+
+ if( LookupInDb(desc, code) )
+ return true;
+ }
+ return false;
+}
+
+bool CLangCodeExpander::Lookup(std::string& desc, const int code)
+{
+
+ char lang[3];
+ lang[2] = 0;
+ lang[1] = (code & 255);
+ lang[0] = (code >> 8) & 255;
+
+ return Lookup(desc, lang);
+}
+
+bool CLangCodeExpander::ConvertTwoToThreeCharCode(std::string& strThreeCharCode, const std::string& strTwoCharCode, bool checkWin32Locales /*= false*/)
+{
+ if ( strTwoCharCode.length() == 2 )
+ {
+ std::string strTwoCharCodeLower( strTwoCharCode );
+ StringUtils::ToLower(strTwoCharCodeLower);
+ StringUtils::Trim(strTwoCharCodeLower);
+
+ for (unsigned int index = 0; index < ARRAY_SIZE(CharCode2To3); ++index)
+ {
+ if (strTwoCharCodeLower == CharCode2To3[index].old)
+ {
+ if (checkWin32Locales && CharCode2To3[index].win_id)
+ {
+ strThreeCharCode = CharCode2To3[index].win_id;
+ return true;
+ }
+ strThreeCharCode = CharCode2To3[index].id;
+ return true;
+ }
+ }
+ }
+
+ // not a 2 char code
+ return false;
+}
+
+bool CLangCodeExpander::ConvertToThreeCharCode(std::string& strThreeCharCode, const std::string& strCharCode, bool checkXbmcLocales /*= true*/, bool checkWin32Locales /*= false*/)
+{
+ if (strCharCode.size() == 2)
+ return g_LangCodeExpander.ConvertTwoToThreeCharCode(strThreeCharCode, strCharCode, checkWin32Locales);
+ else if (strCharCode.size() == 3)
+ {
+ std::string charCode(strCharCode); StringUtils::ToLower(charCode);
+ for (unsigned int index = 0; index < ARRAY_SIZE(CharCode2To3); ++index)
+ {
+ if (charCode == CharCode2To3[index].id ||
+ (checkWin32Locales && CharCode2To3[index].win_id != NULL && charCode == CharCode2To3[index].win_id) )
+ {
+ strThreeCharCode = charCode;
+ return true;
+ }
+ }
+ for (unsigned int index = 0; index < ARRAY_SIZE(RegionCode2To3); ++index)
+ {
+ if (charCode == RegionCode2To3[index].id)
+ {
+ strThreeCharCode = charCode;
+ return true;
+ }
+ }
+ }
+ else if (strCharCode.size() > 3)
+ {
+ for(unsigned int i = 0; i < sizeof(g_iso639_2) / sizeof(LCENTRY); i++)
+ {
+ if (StringUtils::EqualsNoCase(strCharCode, g_iso639_2[i].name))
+ {
+ CodeToString(g_iso639_2[i].code, strThreeCharCode);
+ return true;
+ }
+ }
+
+ if (checkXbmcLocales)
+ {
+ CLangInfo langInfo;
+ if (!langInfo.CheckLoadLanguage(strCharCode))
+ return false;
+
+ strThreeCharCode = langInfo.GetLanguageCode();
+ return !strThreeCharCode.empty();
+ }
+ }
+
+ return false;
+}
+
+#ifdef TARGET_WINDOWS
+bool CLangCodeExpander::ConvertLinuxToWindowsRegionCodes(const std::string& strTwoCharCode, std::string& strThreeCharCode)
+{
+ if (strTwoCharCode.length() != 2)
+ return false;
+
+ std::string strLower( strTwoCharCode );
+ StringUtils::ToLower(strLower);
+ StringUtils::Trim(strLower);
+ for (unsigned int index = 0; index < ARRAY_SIZE(RegionCode2To3); ++index)
+ {
+ if (strLower == RegionCode2To3[index].old)
+ {
+ strThreeCharCode = RegionCode2To3[index].id;
+ return true;
+ }
+ }
+
+ return true;
+}
+
+bool CLangCodeExpander::ConvertWindowsToGeneralCharCode(const std::string& strWindowsCharCode, std::string& strThreeCharCode)
+{
+ if (strWindowsCharCode.length() != 3)
+ return false;
+
+ std::string strLower(strWindowsCharCode);
+ StringUtils::ToLower(strLower);
+ for (unsigned int index = 0; index < ARRAY_SIZE(CharCode2To3); ++index)
+ {
+ if ((CharCode2To3[index].win_id && strLower == CharCode2To3[index].win_id) ||
+ strLower == CharCode2To3[index].id)
+ {
+ strThreeCharCode = CharCode2To3[index].id;
+ return true;
+ }
+ }
+
+ return true;
+}
+#endif
+
+bool CLangCodeExpander::ConvertToTwoCharCode(std::string& code, const std::string& lang, bool checkXbmcLocales /*= true*/)
+{
+ if (lang.empty())
+ return false;
+
+ if (lang.length() == 2)
+ {
+ std::string tmp;
+ if (Lookup(tmp, lang))
+ {
+ code = lang;
+ return true;
+ }
+ }
+ else if (lang.length() == 3)
+ {
+ std::string lower(lang); StringUtils::ToLower(lower);
+ for (unsigned int index = 0; index < ARRAY_SIZE(CharCode2To3); ++index)
+ {
+ if (lower == CharCode2To3[index].id || (CharCode2To3[index].win_id && lower == CharCode2To3[index].win_id))
+ {
+ code = CharCode2To3[index].old;
+ return true;
+ }
+ }
+
+ for (unsigned int index = 0; index < ARRAY_SIZE(RegionCode2To3); ++index)
+ {
+ if (lower == RegionCode2To3[index].id)
+ {
+ code = RegionCode2To3[index].old;
+ return true;
+ }
+ }
+ }
+
+ // check if lang is full language name
+ std::string tmp;
+ if (ReverseLookup(lang, tmp))
+ {
+ if (tmp.length() == 2)
+ {
+ code = tmp;
+ return true;
+ }
+ else if (tmp.length() == 3)
+ return ConvertToTwoCharCode(code, tmp);
+ }
+
+ if (!checkXbmcLocales)
+ return false;
+
+ // try xbmc specific language names
+ CLangInfo langInfo;
+ if (!langInfo.CheckLoadLanguage(lang))
+ return false;
+
+ return ConvertToTwoCharCode(code, langInfo.GetLanguageCode(), false);
+}
+
+bool CLangCodeExpander::ReverseLookup(const std::string& desc, std::string& code)
+{
+ if (desc.empty())
+ return false;
+
+ std::string descTmp(desc);
+ StringUtils::Trim(descTmp);
+ StringUtils::ToLower(descTmp);
+ STRINGLOOKUPTABLE::iterator it;
+ for (it = m_mapUser.begin(); it != m_mapUser.end() ; ++it)
+ {
+ if (StringUtils::EqualsNoCase(descTmp, it->second))
+ {
+ code = it->first;
+ return true;
+ }
+ }
+ for(unsigned int i = 0; i < sizeof(g_iso639_1) / sizeof(LCENTRY); i++)
+ {
+ if (descTmp == g_iso639_1[i].name)
+ {
+ CodeToString(g_iso639_1[i].code, code);
+ return true;
+ }
+ }
+ for(unsigned int i = 0; i < sizeof(g_iso639_2) / sizeof(LCENTRY); i++)
+ {
+ if (descTmp == g_iso639_2[i].name)
+ {
+ CodeToString(g_iso639_2[i].code, code);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool CLangCodeExpander::LookupInMap(std::string& desc, const std::string& code)
+{
+ if (code.empty())
+ return false;
+
+ STRINGLOOKUPTABLE::iterator it;
+ //Make sure we convert to lowercase before trying to find it
+ std::string sCode(code);
+ StringUtils::ToLower(sCode);
+ StringUtils::Trim(sCode);
+
+ it = m_mapUser.find(sCode);
+ if (it != m_mapUser.end())
+ {
+ desc = it->second;
+ return true;
+ }
+ return false;
+}
+
+bool CLangCodeExpander::LookupInDb(std::string& desc, const std::string& code)
+{
+ if (code.empty())
+ return false;
+
+ long longcode;
+ std::string sCode(code);
+ StringUtils::ToLower(sCode);
+ StringUtils::Trim(sCode);
+
+ if(sCode.length() == 2)
+ {
+ longcode = MAKECODE('\0', '\0', sCode[0], sCode[1]);
+ for(unsigned int i = 0; i < sizeof(g_iso639_1) / sizeof(LCENTRY); i++)
+ {
+ if(g_iso639_1[i].code == longcode)
+ {
+ desc = g_iso639_1[i].name;
+ return true;
+ }
+ }
+ }
+ else if(code.length() == 3)
+ {
+ longcode = MAKECODE('\0', sCode[0], sCode[1], sCode[2]);
+ for(unsigned int i = 0; i < sizeof(g_iso639_2) / sizeof(LCENTRY); i++)
+ {
+ if(g_iso639_2[i].code == longcode)
+ {
+ desc = g_iso639_2[i].name;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void CLangCodeExpander::CodeToString(long code, std::string& ret)
+{
+ ret.clear();
+ for (unsigned int j = 0 ; j < 4 ; j++)
+ {
+ char c = (char) code & 0xFF;
+ if (c == '\0')
+ return;
+ ret.insert(0, 1, c);
+ code >>= 8;
+ }
+}
+
+bool CLangCodeExpander::CompareFullLangNames(const std::string& lang1, const std::string& lang2)
+{
+ if (StringUtils::EqualsNoCase(lang1, lang2))
+ return true;
+
+ std::string expandedLang1, expandedLang2, code1, code2;
+
+ if (!ReverseLookup(lang1, code1))
+ return false;
+ else
+ code1 = lang1;
+
+ if (!ReverseLookup(lang2, code2))
+ return false;
+ else
+ code2 = lang2;
+
+ Lookup(expandedLang1, code1);
+ Lookup(expandedLang2, code2);
+ return StringUtils::EqualsNoCase(expandedLang1, expandedLang2);
+}
+
+std::vector<std::string> CLangCodeExpander::GetLanguageNames(LANGFORMATS format /* = CLangCodeExpander::ISO_639_1 */)
+{
+ std::vector<std::string> languages;
+ const LCENTRY *lang = g_iso639_1;
+ size_t length = sizeof(g_iso639_1);
+ if (format == CLangCodeExpander::ISO_639_2)
+ {
+ lang = g_iso639_2;
+ length = sizeof(g_iso639_2);
+ }
+ length /= sizeof(LCENTRY);
+
+ for (size_t i = 0; i < length; i++)
+ {
+ languages.push_back(lang->name);
+ ++lang;
+ }
+
+ return languages;
+}
+
+bool CLangCodeExpander::CompareLangCodes(const std::string& code1, const std::string& code2)
+{
+ if (StringUtils::EqualsNoCase(code1, code2))
+ return true;
+
+ std::string expandedLang1, expandedLang2;
+
+ if (!Lookup(expandedLang1, code1))
+ return false;
+
+ if (!Lookup(expandedLang2, code2))
+ return false;
+
+ return StringUtils::EqualsNoCase(expandedLang1, expandedLang2);
+}
+
+std::string CLangCodeExpander::ConvertToISO6392T(const std::string& lang)
+{
+ if (lang.empty())
+ return lang;
+
+ std::string two, three;
+ if (ConvertToTwoCharCode(two, lang))
+ {
+ if (ConvertToThreeCharCode(three, two))
+ return three;
+ }
+ return lang;
+}
+
+extern const LCENTRY g_iso639_1[186] =
+{
+ { MAKECODE('\0','\0','c','c'), "Closed Caption" },
+ { MAKECODE('\0','\0','a','a'), "Afar" },
+ { MAKECODE('\0','\0','a','b'), "Abkhazian" },
+ { MAKECODE('\0','\0','a','e'), "Avestan" },
+ { MAKECODE('\0','\0','a','f'), "Afrikaans" },
+ { MAKECODE('\0','\0','a','k'), "Akan" },
+ { MAKECODE('\0','\0','a','m'), "Amharic" },
+ { MAKECODE('\0','\0','a','n'), "Aragonese" },
+ { MAKECODE('\0','\0','a','r'), "Arabic" },
+ { MAKECODE('\0','\0','a','s'), "Assamese" },
+ { MAKECODE('\0','\0','a','v'), "Avaric" },
+ { MAKECODE('\0','\0','a','y'), "Aymara" },
+ { MAKECODE('\0','\0','a','z'), "Azerbaijani" },
+ { MAKECODE('\0','\0','b','a'), "Bashkir" },
+ { MAKECODE('\0','\0','b','e'), "Belarusian" },
+ { MAKECODE('\0','\0','b','g'), "Bulgarian" },
+ { MAKECODE('\0','\0','b','h'), "Bihari" },
+ { MAKECODE('\0','\0','b','i'), "Bislama" },
+ { MAKECODE('\0','\0','b','m'), "Bambara" },
+ { MAKECODE('\0','\0','b','n'), "Bengali; Bangla" },
+ { MAKECODE('\0','\0','b','o'), "Tibetan" },
+ { MAKECODE('\0','\0','b','r'), "Breton" },
+ { MAKECODE('\0','\0','b','s'), "Bosnian" },
+ { MAKECODE('\0','\0','c','a'), "Catalan" },
+ { MAKECODE('\0','\0','c','e'), "Chechen" },
+ { MAKECODE('\0','\0','c','h'), "Chamorro" },
+ { MAKECODE('\0','\0','c','o'), "Corsican" },
+ { MAKECODE('\0','\0','c','r'), "Cree" },
+ { MAKECODE('\0','\0','c','s'), "Czech" },
+ { MAKECODE('\0','\0','c','u'), "Church Slavic" },
+ { MAKECODE('\0','\0','c','v'), "Chuvash" },
+ { MAKECODE('\0','\0','c','y'), "Welsh" },
+ { MAKECODE('\0','\0','d','a'), "Danish" },
+ { MAKECODE('\0','\0','d','e'), "German" },
+ { MAKECODE('\0','\0','d','v'), "Dhivehi" },
+ { MAKECODE('\0','\0','d','z'), "Dzongkha" },
+ { MAKECODE('\0','\0','e','e'), "Ewe" },
+ { MAKECODE('\0','\0','e','l'), "Greek" },
+ { MAKECODE('\0','\0','e','n'), "English" },
+ { MAKECODE('\0','\0','e','o'), "Esperanto" },
+ { MAKECODE('\0','\0','e','s'), "Spanish" },
+ { MAKECODE('\0','\0','e','t'), "Estonian" },
+ { MAKECODE('\0','\0','e','u'), "Basque" },
+ { MAKECODE('\0','\0','f','a'), "Persian" },
+ { MAKECODE('\0','\0','f','f'), "Fulah" },
+ { MAKECODE('\0','\0','f','i'), "Finnish" },
+ { MAKECODE('\0','\0','f','j'), "Fijian" },
+ { MAKECODE('\0','\0','f','o'), "Faroese" },
+ { MAKECODE('\0','\0','f','r'), "French" },
+ { MAKECODE('\0','\0','f','y'), "Western Frisian" },
+ { MAKECODE('\0','\0','g','a'), "Irish" },
+ { MAKECODE('\0','\0','g','d'), "Scottish Gaelic" },
+ { MAKECODE('\0','\0','g','l'), "Galician" },
+ { MAKECODE('\0','\0','g','n'), "Guarani" },
+ { MAKECODE('\0','\0','g','u'), "Gujarati" },
+ { MAKECODE('\0','\0','g','v'), "Manx" },
+ { MAKECODE('\0','\0','h','a'), "Hausa" },
+ { MAKECODE('\0','\0','h','e'), "Hebrew" },
+ { MAKECODE('\0','\0','h','i'), "Hindi" },
+ { MAKECODE('\0','\0','h','o'), "Hiri Motu" },
+ { MAKECODE('\0','\0','h','r'), "Croatian" },
+ { MAKECODE('\0','\0','h','t'), "Haitian" },
+ { MAKECODE('\0','\0','h','u'), "Hungarian" },
+ { MAKECODE('\0','\0','h','y'), "Armenian" },
+ { MAKECODE('\0','\0','h','z'), "Herero" },
+ { MAKECODE('\0','\0','i','a'), "Interlingua" },
+ { MAKECODE('\0','\0','i','d'), "Indonesian" },
+ { MAKECODE('\0','\0','i','e'), "Interlingue" },
+ { MAKECODE('\0','\0','i','g'), "Igbo" },
+ { MAKECODE('\0','\0','i','i'), "Sichuan Yi" },
+ { MAKECODE('\0','\0','i','k'), "Inupiat" },
+ { MAKECODE('\0','\0','i','o'), "Ido" },
+ { MAKECODE('\0','\0','i','s'), "Icelandic" },
+ { MAKECODE('\0','\0','i','t'), "Italian" },
+ { MAKECODE('\0','\0','i','u'), "Inuktitut" },
+ { MAKECODE('\0','\0','j','a'), "Japanese" },
+ { MAKECODE('\0','\0','j','v'), "Javanese" },
+ { MAKECODE('\0','\0','k','a'), "Georgian" },
+ { MAKECODE('\0','\0','k','g'), "Kongo" },
+ { MAKECODE('\0','\0','k','i'), "Kikuyu" },
+ { MAKECODE('\0','\0','k','j'), "Kuanyama" },
+ { MAKECODE('\0','\0','k','k'), "Kazakh" },
+ { MAKECODE('\0','\0','k','l'), "Kalaallisut" },
+ { MAKECODE('\0','\0','k','m'), "Khmer" },
+ { MAKECODE('\0','\0','k','n'), "Kannada" },
+ { MAKECODE('\0','\0','k','o'), "Korean" },
+ { MAKECODE('\0','\0','k','r'), "Kanuri" },
+ { MAKECODE('\0','\0','k','s'), "Kashmiri" },
+ { MAKECODE('\0','\0','k','u'), "Kurdish" },
+ { MAKECODE('\0','\0','k','v'), "Komi" },
+ { MAKECODE('\0','\0','k','w'), "Cornish" },
+ { MAKECODE('\0','\0','k','y'), "Kirghiz" },
+ { MAKECODE('\0','\0','l','a'), "Latin" },
+ { MAKECODE('\0','\0','l','b'), "Luxembourgish" },
+ { MAKECODE('\0','\0','l','g'), "Ganda" },
+ { MAKECODE('\0','\0','l','i'), "Limburgan" },
+ { MAKECODE('\0','\0','l','n'), "Lingala" },
+ { MAKECODE('\0','\0','l','o'), "Lao" },
+ { MAKECODE('\0','\0','l','t'), "Lithuanian" },
+ { MAKECODE('\0','\0','l','u'), "Luba-Katanga" },
+ { MAKECODE('\0','\0','l','v'), "Latvian, Lettish" },
+ { MAKECODE('\0','\0','m','g'), "Malagasy" },
+ { MAKECODE('\0','\0','m','h'), "Marshallese" },
+ { MAKECODE('\0','\0','m','i'), "Maori" },
+ { MAKECODE('\0','\0','m','k'), "Macedonian" },
+ { MAKECODE('\0','\0','m','l'), "Malayalam" },
+ { MAKECODE('\0','\0','m','n'), "Mongolian" },
+ { MAKECODE('\0','\0','m','r'), "Marathi" },
+ { MAKECODE('\0','\0','m','s'), "Malay" },
+ { MAKECODE('\0','\0','m','t'), "Maltese" },
+ { MAKECODE('\0','\0','m','y'), "Burmese" },
+ { MAKECODE('\0','\0','n','a'), "Nauru" },
+ { MAKECODE('\0','\0','n','b'), "Bokm\xC3\xA5l, Norwegian" },
+ { MAKECODE('\0','\0','n','d'), "Ndebele, North" },
+ { MAKECODE('\0','\0','n','e'), "Nepali" },
+ { MAKECODE('\0','\0','n','g'), "Ndonga" },
+ { MAKECODE('\0','\0','n','l'), "Dutch" },
+ { MAKECODE('\0','\0','n','n'), "Norwegian Nynorsk" },
+ { MAKECODE('\0','\0','n','o'), "Norwegian" },
+ { MAKECODE('\0','\0','n','r'), "Ndebele, South" },
+ { MAKECODE('\0','\0','n','v'), "Navajo" },
+ { MAKECODE('\0','\0','n','y'), "Chichewa" },
+ { MAKECODE('\0','\0','o','c'), "Occitan" },
+ { MAKECODE('\0','\0','o','j'), "Ojibwa" },
+ { MAKECODE('\0','\0','o','m'), "Oromo" },
+ { MAKECODE('\0','\0','o','r'), "Oriya" },
+ { MAKECODE('\0','\0','o','s'), "Ossetic" },
+ { MAKECODE('\0','\0','p','a'), "Punjabi" },
+ { MAKECODE('\0','\0','p','i'), "Pali" },
+ { MAKECODE('\0','\0','p','l'), "Polish" },
+ { MAKECODE('\0','\0','p','s'), "Pashto, Pushto" },
+ { MAKECODE('\0','\0','p','t'), "Portuguese" },
+ { MAKECODE('\0','\0','q','u'), "Quechua" },
+ { MAKECODE('\0','\0','r','m'), "Romansh" },
+ { MAKECODE('\0','\0','r','n'), "Kirundi" },
+ { MAKECODE('\0','\0','r','o'), "Romanian" },
+ { MAKECODE('\0','\0','r','u'), "Russian" },
+ { MAKECODE('\0','\0','r','w'), "Kinyarwanda" },
+ { MAKECODE('\0','\0','s','a'), "Sanskrit" },
+ { MAKECODE('\0','\0','s','c'), "Sardinian" },
+ { MAKECODE('\0','\0','s','d'), "Sindhi" },
+ { MAKECODE('\0','\0','s','e'), "Northern Sami" },
+ { MAKECODE('\0','\0','s','g'), "Sangho" },
+ { MAKECODE('\0','\0','s','h'), "Serbo-Croatian" },
+ { MAKECODE('\0','\0','s','i'), "Sinhalese" },
+ { MAKECODE('\0','\0','s','k'), "Slovak" },
+ { MAKECODE('\0','\0','s','l'), "Slovenian" },
+ { MAKECODE('\0','\0','s','m'), "Samoan" },
+ { MAKECODE('\0','\0','s','n'), "Shona" },
+ { MAKECODE('\0','\0','s','o'), "Somali" },
+ { MAKECODE('\0','\0','s','q'), "Albanian" },
+ { MAKECODE('\0','\0','s','r'), "Serbian" },
+ { MAKECODE('\0','\0','s','s'), "Swati" },
+ { MAKECODE('\0','\0','s','t'), "Sesotho" },
+ { MAKECODE('\0','\0','s','u'), "Sundanese" },
+ { MAKECODE('\0','\0','s','v'), "Swedish" },
+ { MAKECODE('\0','\0','s','w'), "Swahili" },
+ { MAKECODE('\0','\0','t','a'), "Tamil" },
+ { MAKECODE('\0','\0','t','e'), "Telugu" },
+ { MAKECODE('\0','\0','t','g'), "Tajik" },
+ { MAKECODE('\0','\0','t','h'), "Thai" },
+ { MAKECODE('\0','\0','t','i'), "Tigrinya" },
+ { MAKECODE('\0','\0','t','k'), "Turkmen" },
+ { MAKECODE('\0','\0','t','l'), "Tagalog" },
+ { MAKECODE('\0','\0','t','n'), "Tswana" },
+ { MAKECODE('\0','\0','t','o'), "Tonga" },
+ { MAKECODE('\0','\0','t','r'), "Turkish" },
+ { MAKECODE('\0','\0','t','s'), "Tsonga" },
+ { MAKECODE('\0','\0','t','t'), "Tatar" },
+ { MAKECODE('\0','\0','t','w'), "Twi" },
+ { MAKECODE('\0','\0','t','y'), "Tahitian" },
+ { MAKECODE('\0','\0','u','g'), "Uighur" },
+ { MAKECODE('\0','\0','u','k'), "Ukrainian" },
+ { MAKECODE('\0','\0','u','r'), "Urdu" },
+ { MAKECODE('\0','\0','u','z'), "Uzbek" },
+ { MAKECODE('\0','\0','v','e'), "Venda" },
+ { MAKECODE('\0','\0','v','i'), "Vietnamese" },
+ { MAKECODE('\0','\0','v','o'), "Volapuk" },
+ { MAKECODE('\0','\0','w','a'), "Walloon" },
+ { MAKECODE('\0','\0','w','o'), "Wolof" },
+ { MAKECODE('\0','\0','x','h'), "Xhosa" },
+ { MAKECODE('\0','\0','y','i'), "Yiddish" },
+ { MAKECODE('\0','\0','y','o'), "Yoruba" },
+ { MAKECODE('\0','\0','z','a'), "Zhuang" },
+ { MAKECODE('\0','\0','z','h'), "Chinese" },
+ { MAKECODE('\0','\0','z','u'), "Zulu" },
+};
+
+extern const LCENTRY g_iso639_2[538] =
+{
+ { MAKECODE('\0','a','b','k'), "Abkhaz" },
+ { MAKECODE('\0','a','b','k'), "Abkhazian" },
+ { MAKECODE('\0','a','c','e'), "Achinese" },
+ { MAKECODE('\0','a','c','h'), "Acoli" },
+ { MAKECODE('\0','a','d','a'), "Adangme" },
+ { MAKECODE('\0','a','d','y'), "Adygei" },
+ { MAKECODE('\0','a','d','y'), "Adyghe" },
+ { MAKECODE('\0','a','a','r'), "Afar" },
+ { MAKECODE('\0','a','f','h'), "Afrihili" },
+ { MAKECODE('\0','a','f','r'), "Afrikaans" },
+ { MAKECODE('\0','a','f','a'), "Afro-Asiatic (Other)" },
+ { MAKECODE('\0','a','k','a'), "Akan" },
+ { MAKECODE('\0','a','k','k'), "Akkadian" },
+ { MAKECODE('\0','a','l','b'), "Albanian" },
+ { MAKECODE('\0','s','q','i'), "Albanian" },
+ { MAKECODE('\0','a','l','e'), "Aleut" },
+ { MAKECODE('\0','a','l','g'), "Algonquian languages" },
+ { MAKECODE('\0','t','u','t'), "Altaic (Other)" },
+ { MAKECODE('\0','a','m','h'), "Amharic" },
+ { MAKECODE('\0','a','p','a'), "Apache languages" },
+ { MAKECODE('\0','a','r','a'), "Arabic" },
+ { MAKECODE('\0','a','r','g'), "Aragonese" },
+ { MAKECODE('\0','a','r','c'), "Aramaic" },
+ { MAKECODE('\0','a','r','p'), "Arapaho" },
+ { MAKECODE('\0','a','r','n'), "Araucanian" },
+ { MAKECODE('\0','a','r','w'), "Arawak" },
+ { MAKECODE('\0','a','r','m'), "Armenian" },
+ { MAKECODE('\0','h','y','e'), "Armenian" },
+ { MAKECODE('\0','a','r','t'), "Artificial (Other)" },
+ { MAKECODE('\0','a','s','m'), "Assamese" },
+ { MAKECODE('\0','a','s','t'), "Asturian" },
+ { MAKECODE('\0','a','t','h'), "Athapascan languages" },
+ { MAKECODE('\0','a','u','s'), "Australian languages" },
+ { MAKECODE('\0','m','a','p'), "Austronesian (Other)" },
+ { MAKECODE('\0','a','v','a'), "Avaric" },
+ { MAKECODE('\0','a','v','e'), "Avestan" },
+ { MAKECODE('\0','a','w','a'), "Awadhi" },
+ { MAKECODE('\0','a','y','m'), "Aymara" },
+ { MAKECODE('\0','a','z','e'), "Azerbaijani" },
+ { MAKECODE('\0','a','s','t'), "Bable" },
+ { MAKECODE('\0','b','a','n'), "Balinese" },
+ { MAKECODE('\0','b','a','t'), "Baltic (Other)" },
+ { MAKECODE('\0','b','a','l'), "Baluchi" },
+ { MAKECODE('\0','b','a','m'), "Bambara" },
+ { MAKECODE('\0','b','a','i'), "Bamileke languages" },
+ { MAKECODE('\0','b','a','d'), "Banda" },
+ { MAKECODE('\0','b','n','t'), "Bantu (Other)" },
+ { MAKECODE('\0','b','a','s'), "Basa" },
+ { MAKECODE('\0','b','a','k'), "Bashkir" },
+ { MAKECODE('\0','b','a','q'), "Basque" },
+ { MAKECODE('\0','e','u','s'), "Basque" },
+ { MAKECODE('\0','b','t','k'), "Batak (Indonesia)" },
+ { MAKECODE('\0','b','e','j'), "Beja" },
+ { MAKECODE('\0','b','e','l'), "Belarusian" },
+ { MAKECODE('\0','b','e','m'), "Bemba" },
+ { MAKECODE('\0','b','e','n'), "Bengali" },
+ { MAKECODE('\0','b','e','r'), "Berber (Other)" },
+ { MAKECODE('\0','b','h','o'), "Bhojpuri" },
+ { MAKECODE('\0','b','i','h'), "Bihari" },
+ { MAKECODE('\0','b','i','k'), "Bikol" },
+ { MAKECODE('\0','b','y','n'), "Bilin" },
+ { MAKECODE('\0','b','i','n'), "Bini" },
+ { MAKECODE('\0','b','i','s'), "Bislama" },
+ { MAKECODE('\0','b','y','n'), "Blin" },
+ { MAKECODE('\0','n','o','b'), "Bokm\xC3\xA5l, Norwegian" },
+ { MAKECODE('\0','b','o','s'), "Bosnian" },
+ { MAKECODE('\0','b','r','a'), "Braj" },
+ { MAKECODE('\0','b','r','e'), "Breton" },
+ { MAKECODE('\0','b','u','g'), "Buginese" },
+ { MAKECODE('\0','b','u','l'), "Bulgarian" },
+ { MAKECODE('\0','b','u','a'), "Buriat" },
+ { MAKECODE('\0','b','u','r'), "Burmese" },
+ { MAKECODE('\0','m','y','a'), "Burmese" },
+ { MAKECODE('\0','c','a','d'), "Caddo" },
+ { MAKECODE('\0','c','a','r'), "Carib" },
+ { MAKECODE('\0','s','p','a'), "Spanish" },
+ { MAKECODE('\0','c','a','t'), "Catalan" },
+ { MAKECODE('\0','c','a','u'), "Caucasian (Other)" },
+ { MAKECODE('\0','c','e','b'), "Cebuano" },
+ { MAKECODE('\0','c','e','l'), "Celtic (Other)" },
+ { MAKECODE('\0','c','h','g'), "Chagatai" },
+ { MAKECODE('\0','c','m','c'), "Chamic languages" },
+ { MAKECODE('\0','c','h','a'), "Chamorro" },
+ { MAKECODE('\0','c','h','e'), "Chechen" },
+ { MAKECODE('\0','c','h','r'), "Cherokee" },
+ { MAKECODE('\0','n','y','a'), "Chewa" },
+ { MAKECODE('\0','c','h','y'), "Cheyenne" },
+ { MAKECODE('\0','c','h','b'), "Chibcha" },
+ { MAKECODE('\0','n','y','a'), "Chichewa" },
+ { MAKECODE('\0','c','h','i'), "Chinese" },
+ { MAKECODE('\0','z','h','o'), "Chinese" },
+ { MAKECODE('\0','c','h','n'), "Chinook jargon" },
+ { MAKECODE('\0','c','h','p'), "Chipewyan" },
+ { MAKECODE('\0','c','h','o'), "Choctaw" },
+ { MAKECODE('\0','z','h','a'), "Chuang" },
+ { MAKECODE('\0','c','h','u'), "Church Slavonic" },
+ { MAKECODE('\0','c','h','k'), "Chuukese" },
+ { MAKECODE('\0','c','h','v'), "Chuvash" },
+ { MAKECODE('\0','n','w','c'), "Classical Nepal Bhasa" },
+ { MAKECODE('\0','n','w','c'), "Classical Newari" },
+ { MAKECODE('\0','c','o','p'), "Coptic" },
+ { MAKECODE('\0','c','o','r'), "Cornish" },
+ { MAKECODE('\0','c','o','s'), "Corsican" },
+ { MAKECODE('\0','c','r','e'), "Cree" },
+ { MAKECODE('\0','m','u','s'), "Creek" },
+ { MAKECODE('\0','c','r','p'), "Creoles and pidgins (Other)" },
+ { MAKECODE('\0','c','p','e'), "English-based (Other)" },
+ { MAKECODE('\0','c','p','f'), "French-based (Other)" },
+ { MAKECODE('\0','c','p','p'), "Portuguese-based (Other)" },
+ { MAKECODE('\0','c','r','h'), "Crimean Tatar" },
+ { MAKECODE('\0','c','r','h'), "Crimean Turkish" },
+ { MAKECODE('\0','h','r','v'), "Croatian" },
+ { MAKECODE('\0','s','c','r'), "Croatian" },
+ { MAKECODE('\0','c','u','s'), "Cushitic (Other)" },
+ { MAKECODE('\0','c','z','e'), "Czech" },
+ { MAKECODE('\0','c','e','s'), "Czech" },
+ { MAKECODE('\0','d','a','k'), "Dakota" },
+ { MAKECODE('\0','d','a','n'), "Danish" },
+ { MAKECODE('\0','d','a','r'), "Dargwa" },
+ { MAKECODE('\0','d','a','y'), "Dayak" },
+ { MAKECODE('\0','d','e','l'), "Delaware" },
+ { MAKECODE('\0','d','i','n'), "Dinka" },
+ { MAKECODE('\0','d','i','v'), "Divehi" },
+ { MAKECODE('\0','d','o','i'), "Dogri" },
+ { MAKECODE('\0','d','g','r'), "Dogrib" },
+ { MAKECODE('\0','d','r','a'), "Dravidian (Other)" },
+ { MAKECODE('\0','d','u','a'), "Duala" },
+ { MAKECODE('\0','d','u','t'), "Dutch" },
+ { MAKECODE('\0','n','l','d'), "Dutch" },
+ { MAKECODE('\0','d','u','m'), "Dutch, Middle (ca. 1050-1350)" },
+ { MAKECODE('\0','d','y','u'), "Dyula" },
+ { MAKECODE('\0','d','z','o'), "Dzongkha" },
+ { MAKECODE('\0','e','f','i'), "Efik" },
+ { MAKECODE('\0','e','g','y'), "Egyptian (Ancient)" },
+ { MAKECODE('\0','e','k','a'), "Ekajuk" },
+ { MAKECODE('\0','e','l','x'), "Elamite" },
+ { MAKECODE('\0','e','n','g'), "English" },
+ { MAKECODE('\0','e','n','m'), "English, Middle (1100-1500)" },
+ { MAKECODE('\0','a','n','g'), "English, Old (ca.450-1100)" },
+ { MAKECODE('\0','m','y','v'), "Erzya" },
+ { MAKECODE('\0','e','p','o'), "Esperanto" },
+ { MAKECODE('\0','e','s','t'), "Estonian" },
+ { MAKECODE('\0','e','w','e'), "Ewe" },
+ { MAKECODE('\0','e','w','o'), "Ewondo" },
+ { MAKECODE('\0','f','a','n'), "Fang" },
+ { MAKECODE('\0','f','a','t'), "Fanti" },
+ { MAKECODE('\0','f','a','o'), "Faroese" },
+ { MAKECODE('\0','f','i','j'), "Fijian" },
+ { MAKECODE('\0','f','i','l'), "Filipino" },
+ { MAKECODE('\0','f','i','n'), "Finnish" },
+ { MAKECODE('\0','f','i','u'), "Finno-Ugrian (Other)" },
+ { MAKECODE('\0','d','u','t'), "Flemish" },
+ { MAKECODE('\0','n','l','d'), "Flemish" },
+ { MAKECODE('\0','f','o','n'), "Fon" },
+ { MAKECODE('\0','f','r','e'), "French" },
+ { MAKECODE('\0','f','r','a'), "French" },
+ { MAKECODE('\0','f','r','m'), "French, Middle (ca.1400-1600)" },
+ { MAKECODE('\0','f','r','o'), "French, Old (842-ca.1400)" },
+ { MAKECODE('\0','f','r','y'), "Frisian" },
+ { MAKECODE('\0','f','u','r'), "Friulian" },
+ { MAKECODE('\0','f','u','l'), "Fulah" },
+ { MAKECODE('\0','g','a','a'), "Ga" },
+ { MAKECODE('\0','g','l','a'), "Gaelic" },
+ { MAKECODE('\0','g','l','g'), "Gallegan" },
+ { MAKECODE('\0','l','u','g'), "Ganda" },
+ { MAKECODE('\0','g','a','y'), "Gayo" },
+ { MAKECODE('\0','g','b','a'), "Gbaya" },
+ { MAKECODE('\0','g','e','z'), "Geez" },
+ { MAKECODE('\0','g','e','o'), "Georgian" },
+ { MAKECODE('\0','k','a','t'), "Georgian" },
+ { MAKECODE('\0','g','e','r'), "German" },
+ { MAKECODE('\0','d','e','u'), "German" },
+ { MAKECODE('\0','n','d','s'), "German, Low" },
+ { MAKECODE('\0','g','m','h'), "German, Middle High (ca.1050-1500)" },
+ { MAKECODE('\0','g','o','h'), "German, Old High (ca.750-1050)" },
+ { MAKECODE('\0','g','e','m'), "Germanic (Other)" },
+ { MAKECODE('\0','k','i','k'), "Gikuyu" },
+ { MAKECODE('\0','g','i','l'), "Gilbertese" },
+ { MAKECODE('\0','g','o','n'), "Gondi" },
+ { MAKECODE('\0','g','o','r'), "Gorontalo" },
+ { MAKECODE('\0','g','o','t'), "Gothic" },
+ { MAKECODE('\0','g','r','b'), "Grebo" },
+ { MAKECODE('\0','g','r','c'), "Greek, Ancient (to 1453)" },
+ { MAKECODE('\0','g','r','e'), "Greek, Modern (1453-)" },
+ { MAKECODE('\0','e','l','l'), "Greek, Modern (1453-)" },
+ { MAKECODE('\0','k','a','l'), "Greenlandic" },
+ { MAKECODE('\0','g','r','n'), "Guarani" },
+ { MAKECODE('\0','g','u','j'), "Gujarati" },
+ { MAKECODE('\0','g','w','i'), "Gwich\xC2\xB4in" },
+ { MAKECODE('\0','h','a','i'), "Haida" },
+ { MAKECODE('\0','h','a','t'), "Haitian" },
+ { MAKECODE('\0','h','a','t'), "Haitian Creole" },
+ { MAKECODE('\0','h','a','u'), "Hausa" },
+ { MAKECODE('\0','h','a','w'), "Hawaiian" },
+ { MAKECODE('\0','h','e','b'), "Hebrew" },
+ { MAKECODE('\0','h','e','r'), "Herero" },
+ { MAKECODE('\0','h','i','l'), "Hiligaynon" },
+ { MAKECODE('\0','h','i','m'), "Himachali" },
+ { MAKECODE('\0','h','i','n'), "Hindi" },
+ { MAKECODE('\0','h','m','o'), "Hiri Motu" },
+ { MAKECODE('\0','h','i','t'), "Hittite" },
+ { MAKECODE('\0','h','m','n'), "Hmong" },
+ { MAKECODE('\0','h','u','n'), "Hungarian" },
+ { MAKECODE('\0','h','u','p'), "Hupa" },
+ { MAKECODE('\0','i','b','a'), "Iban" },
+ { MAKECODE('\0','i','c','e'), "Icelandic" },
+ { MAKECODE('\0','i','s','l'), "Icelandic" },
+ { MAKECODE('\0','i','d','o'), "Ido" },
+ { MAKECODE('\0','i','b','o'), "Igbo" },
+ { MAKECODE('\0','i','j','o'), "Ijo" },
+ { MAKECODE('\0','i','l','o'), "Iloko" },
+ { MAKECODE('\0','s','m','n'), "Inari Sami" },
+ { MAKECODE('\0','i','n','c'), "Indic (Other)" },
+ { MAKECODE('\0','i','n','e'), "Indo-European (Other)" },
+ { MAKECODE('\0','i','n','d'), "Indonesian" },
+ { MAKECODE('\0','i','n','h'), "Ingush" },
+ { MAKECODE('\0','i','n','a'), "Auxiliary Language Association)" },
+ { MAKECODE('\0','i','l','e'), "Interlingue" },
+ { MAKECODE('\0','i','k','u'), "Inuktitut" },
+ { MAKECODE('\0','i','p','k'), "Inupiaq" },
+ { MAKECODE('\0','i','r','a'), "Iranian (Other)" },
+ { MAKECODE('\0','g','l','e'), "Irish" },
+ { MAKECODE('\0','m','g','a'), "Irish, Middle (900-1200)" },
+ { MAKECODE('\0','s','g','a'), "Irish, Old (to 900)" },
+ { MAKECODE('\0','i','r','o'), "Iroquoian languages" },
+ { MAKECODE('\0','i','t','a'), "Italian" },
+ { MAKECODE('\0','j','p','n'), "Japanese" },
+ { MAKECODE('\0','j','a','v'), "Javanese" },
+ { MAKECODE('\0','j','r','b'), "Judeo-Arabic" },
+ { MAKECODE('\0','j','p','r'), "Judeo-Persian" },
+ { MAKECODE('\0','k','b','d'), "Kabardian" },
+ { MAKECODE('\0','k','a','b'), "Kabyle" },
+ { MAKECODE('\0','k','a','c'), "Kachin" },
+ { MAKECODE('\0','k','a','l'), "Kalaallisut" },
+ { MAKECODE('\0','x','a','l'), "Kalmyk" },
+ { MAKECODE('\0','k','a','m'), "Kamba" },
+ { MAKECODE('\0','k','a','n'), "Kannada" },
+ { MAKECODE('\0','k','a','u'), "Kanuri" },
+ { MAKECODE('\0','k','r','c'), "Karachay-Balkar" },
+ { MAKECODE('\0','k','a','a'), "Kara-Kalpak" },
+ { MAKECODE('\0','k','a','r'), "Karen" },
+ { MAKECODE('\0','k','a','s'), "Kashmiri" },
+ { MAKECODE('\0','c','s','b'), "Kashubian" },
+ { MAKECODE('\0','k','a','w'), "Kawi" },
+ { MAKECODE('\0','k','a','z'), "Kazakh" },
+ { MAKECODE('\0','k','h','a'), "Khasi" },
+ { MAKECODE('\0','k','h','m'), "Khmer" },
+ { MAKECODE('\0','k','h','i'), "Khoisan (Other)" },
+ { MAKECODE('\0','k','h','o'), "Khotanese" },
+ { MAKECODE('\0','k','i','k'), "Kikuyu" },
+ { MAKECODE('\0','k','m','b'), "Kimbundu" },
+ { MAKECODE('\0','k','i','n'), "Kinyarwanda" },
+ { MAKECODE('\0','k','i','r'), "Kirghiz" },
+ { MAKECODE('\0','t','l','h'), "Klingon" },
+ { MAKECODE('\0','k','o','m'), "Komi" },
+ { MAKECODE('\0','k','o','n'), "Kongo" },
+ { MAKECODE('\0','k','o','k'), "Konkani" },
+ { MAKECODE('\0','k','o','r'), "Korean" },
+ { MAKECODE('\0','k','o','s'), "Kosraean" },
+ { MAKECODE('\0','k','p','e'), "Kpelle" },
+ { MAKECODE('\0','k','r','o'), "Kru" },
+ { MAKECODE('\0','k','u','a'), "Kuanyama" },
+ { MAKECODE('\0','k','u','m'), "Kumyk" },
+ { MAKECODE('\0','k','u','r'), "Kurdish" },
+ { MAKECODE('\0','k','r','u'), "Kurukh" },
+ { MAKECODE('\0','k','u','t'), "Kutenai" },
+ { MAKECODE('\0','k','u','a'), "Kwanyama, Kuanyama" },
+ { MAKECODE('\0','l','a','d'), "Ladino" },
+ { MAKECODE('\0','l','a','h'), "Lahnda" },
+ { MAKECODE('\0','l','a','m'), "Lamba" },
+ { MAKECODE('\0','l','a','o'), "Lao" },
+ { MAKECODE('\0','l','a','t'), "Latin" },
+ { MAKECODE('\0','l','a','v'), "Latvian" },
+ { MAKECODE('\0','l','t','z'), "Letzeburgesch" },
+ { MAKECODE('\0','l','e','z'), "Lezghian" },
+ { MAKECODE('\0','l','i','m'), "Limburgan" },
+ { MAKECODE('\0','l','i','m'), "Limburger" },
+ { MAKECODE('\0','l','i','m'), "Limburgish" },
+ { MAKECODE('\0','l','i','n'), "Lingala" },
+ { MAKECODE('\0','l','i','t'), "Lithuanian" },
+ { MAKECODE('\0','j','b','o'), "Lojban" },
+ { MAKECODE('\0','n','d','s'), "Low German" },
+ { MAKECODE('\0','n','d','s'), "Low Saxon" },
+ { MAKECODE('\0','d','s','b'), "Lower Sorbian" },
+ { MAKECODE('\0','l','o','z'), "Lozi" },
+ { MAKECODE('\0','l','u','b'), "Luba-Katanga" },
+ { MAKECODE('\0','l','u','a'), "Luba-Lulua" },
+ { MAKECODE('\0','l','u','i'), "Luiseno" },
+ { MAKECODE('\0','s','m','j'), "Lule Sami" },
+ { MAKECODE('\0','l','u','n'), "Lunda" },
+ { MAKECODE('\0','l','u','o'), "Luo (Kenya and Tanzania)" },
+ { MAKECODE('\0','l','u','s'), "Lushai" },
+ { MAKECODE('\0','l','t','z'), "Luxembourgish" },
+ { MAKECODE('\0','m','a','c'), "Macedonian" },
+ { MAKECODE('\0','m','k','d'), "Macedonian" },
+ { MAKECODE('\0','m','a','d'), "Madurese" },
+ { MAKECODE('\0','m','a','g'), "Magahi" },
+ { MAKECODE('\0','m','a','i'), "Maithili" },
+ { MAKECODE('\0','m','a','k'), "Makasar" },
+ { MAKECODE('\0','m','l','g'), "Malagasy" },
+ { MAKECODE('\0','m','a','y'), "Malay" },
+ { MAKECODE('\0','m','s','a'), "Malay" },
+ { MAKECODE('\0','m','a','l'), "Malayalam" },
+ { MAKECODE('\0','m','l','t'), "Maltese" },
+ { MAKECODE('\0','m','n','c'), "Manchu" },
+ { MAKECODE('\0','m','d','r'), "Mandar" },
+ { MAKECODE('\0','m','a','n'), "Mandingo" },
+ { MAKECODE('\0','m','n','i'), "Manipuri" },
+ { MAKECODE('\0','m','n','o'), "Manobo languages" },
+ { MAKECODE('\0','g','l','v'), "Manx" },
+ { MAKECODE('\0','m','a','o'), "Maori" },
+ { MAKECODE('\0','m','r','i'), "Maori" },
+ { MAKECODE('\0','m','a','r'), "Marathi" },
+ { MAKECODE('\0','c','h','m'), "Mari" },
+ { MAKECODE('\0','m','a','h'), "Marshallese" },
+ { MAKECODE('\0','m','w','r'), "Marwari" },
+ { MAKECODE('\0','m','a','s'), "Masai" },
+ { MAKECODE('\0','m','y','n'), "Mayan languages" },
+ { MAKECODE('\0','m','e','n'), "Mende" },
+ { MAKECODE('\0','m','i','c'), "Micmac" },
+ { MAKECODE('\0','m','i','c'), "Mi'kmaq" },
+ { MAKECODE('\0','m','i','n'), "Minangkabau" },
+ { MAKECODE('\0','m','w','l'), "Mirandese" },
+ { MAKECODE('\0','m','i','s'), "Miscellaneous languages" },
+ { MAKECODE('\0','m','o','h'), "Mohawk" },
+ { MAKECODE('\0','m','d','f'), "Moksha" },
+ { MAKECODE('\0','m','o','l'), "Moldavian" },
+ { MAKECODE('\0','m','k','h'), "Mon-Khmer (Other)" },
+ { MAKECODE('\0','l','o','l'), "Mongo" },
+ { MAKECODE('\0','m','o','n'), "Mongolian" },
+ { MAKECODE('\0','m','o','s'), "Mossi" },
+ { MAKECODE('\0','m','u','l'), "Multiple languages" },
+ { MAKECODE('\0','m','u','n'), "Munda languages" },
+ { MAKECODE('\0','n','a','h'), "Nahuatl" },
+ { MAKECODE('\0','n','a','u'), "Nauru" },
+ { MAKECODE('\0','n','a','v'), "Navaho, Navajo" },
+ { MAKECODE('\0','n','a','v'), "Navajo" },
+ { MAKECODE('\0','n','d','e'), "Ndebele, North" },
+ { MAKECODE('\0','n','b','l'), "Ndebele, South" },
+ { MAKECODE('\0','n','d','o'), "Ndonga" },
+ { MAKECODE('\0','n','a','p'), "Neapolitan" },
+ { MAKECODE('\0','n','e','w'), "Nepal Bhasa" },
+ { MAKECODE('\0','n','e','p'), "Nepali" },
+ { MAKECODE('\0','n','e','w'), "Newari" },
+ { MAKECODE('\0','n','i','a'), "Nias" },
+ { MAKECODE('\0','n','i','c'), "Niger-Kordofanian (Other)" },
+ { MAKECODE('\0','s','s','a'), "Nilo-Saharan (Other)" },
+ { MAKECODE('\0','n','i','u'), "Niuean" },
+ { MAKECODE('\0','z','x','x'), "No linguistic content" },
+ { MAKECODE('\0','n','o','g'), "Nogai" },
+ { MAKECODE('\0','n','o','n'), "Norse, Old" },
+ { MAKECODE('\0','n','a','i'), "North American Indian (Other)" },
+ { MAKECODE('\0','s','m','e'), "Northern Sami" },
+ { MAKECODE('\0','n','s','o'), "Northern Sotho" },
+ { MAKECODE('\0','n','d','e'), "North Ndebele" },
+ { MAKECODE('\0','n','o','r'), "Norwegian" },
+ { MAKECODE('\0','n','o','b'), "Norwegian Bokm\xC3\xA5l" },
+ { MAKECODE('\0','n','n','o'), "Norwegian Nynorsk" },
+ { MAKECODE('\0','n','u','b'), "Nubian languages" },
+ { MAKECODE('\0','n','y','m'), "Nyamwezi" },
+ { MAKECODE('\0','n','y','a'), "Nyanja" },
+ { MAKECODE('\0','n','y','n'), "Nyankole" },
+ { MAKECODE('\0','n','n','o'), "Nynorsk, Norwegian" },
+ { MAKECODE('\0','n','y','o'), "Nyoro" },
+ { MAKECODE('\0','n','z','i'), "Nzima" },
+ { MAKECODE('\0','o','c','i'), "Occitan (post 1500)" },
+ { MAKECODE('\0','o','j','i'), "Ojibwa" },
+ { MAKECODE('\0','c','h','u'), "Old Bulgarian" },
+ { MAKECODE('\0','c','h','u'), "Old Church Slavonic" },
+ { MAKECODE('\0','n','w','c'), "Old Newari" },
+ { MAKECODE('\0','c','h','u'), "Old Slavonic" },
+ { MAKECODE('\0','o','r','i'), "Oriya" },
+ { MAKECODE('\0','o','r','m'), "Oromo" },
+ { MAKECODE('\0','o','s','a'), "Osage" },
+ { MAKECODE('\0','o','s','s'), "Ossetian" },
+ { MAKECODE('\0','o','s','s'), "Ossetic" },
+ { MAKECODE('\0','o','t','o'), "Otomian languages" },
+ { MAKECODE('\0','p','a','l'), "Pahlavi" },
+ { MAKECODE('\0','p','a','u'), "Palauan" },
+ { MAKECODE('\0','p','l','i'), "Pali" },
+ { MAKECODE('\0','p','a','m'), "Pampanga" },
+ { MAKECODE('\0','p','a','g'), "Pangasinan" },
+ { MAKECODE('\0','p','a','n'), "Panjabi" },
+ { MAKECODE('\0','p','a','p'), "Papiamento" },
+ { MAKECODE('\0','p','a','a'), "Papuan (Other)" },
+ { MAKECODE('\0','n','s','o'), "Pedi" },
+ { MAKECODE('\0','p','e','r'), "Persian" },
+ { MAKECODE('\0','f','a','s'), "Persian" },
+ { MAKECODE('\0','p','e','o'), "Persian, Old (ca.600-400 B.C.)" },
+ { MAKECODE('\0','p','h','i'), "Philippine (Other)" },
+ { MAKECODE('\0','p','h','n'), "Phoenician" },
+ { MAKECODE('\0','f','i','l'), "Pilipino" },
+ { MAKECODE('\0','p','o','n'), "Pohnpeian" },
+ { MAKECODE('\0','p','o','l'), "Polish" },
+ { MAKECODE('\0','p','o','r'), "Portuguese" },
+ { MAKECODE('\0','p','r','a'), "Prakrit languages" },
+ { MAKECODE('\0','o','c','i'), "Proven\xC3\xA7""al" },
+ { MAKECODE('\0','p','r','o'), "Proven\xC3\xA7""al, Old (to 1500)" },
+ { MAKECODE('\0','p','a','n'), "Punjabi" },
+ { MAKECODE('\0','p','u','s'), "Pushto" },
+ { MAKECODE('\0','q','u','e'), "Quechua" },
+ { MAKECODE('\0','r','o','h'), "Raeto-Romance" },
+ { MAKECODE('\0','r','a','j'), "Rajasthani" },
+ { MAKECODE('\0','r','a','p'), "Rapanui" },
+ { MAKECODE('\0','r','a','r'), "Rarotongan" },
+// { "qaa-qtz", "Reserved for local use" },
+ { MAKECODE('\0','r','o','a'), "Romance (Other)" },
+ { MAKECODE('\0','r','u','m'), "Romanian" },
+ { MAKECODE('\0','r','o','n'), "Romanian" },
+ { MAKECODE('\0','r','o','m'), "Romany" },
+ { MAKECODE('\0','r','u','n'), "Rundi" },
+ { MAKECODE('\0','r','u','s'), "Russian" },
+ { MAKECODE('\0','s','a','l'), "Salishan languages" },
+ { MAKECODE('\0','s','a','m'), "Samaritan Aramaic" },
+ { MAKECODE('\0','s','m','i'), "Sami languages (Other)" },
+ { MAKECODE('\0','s','m','o'), "Samoan" },
+ { MAKECODE('\0','s','a','d'), "Sandawe" },
+ { MAKECODE('\0','s','a','g'), "Sango" },
+ { MAKECODE('\0','s','a','n'), "Sanskrit" },
+ { MAKECODE('\0','s','a','t'), "Santali" },
+ { MAKECODE('\0','s','r','d'), "Sardinian" },
+ { MAKECODE('\0','s','a','s'), "Sasak" },
+ { MAKECODE('\0','n','d','s'), "Saxon, Low" },
+ { MAKECODE('\0','s','c','o'), "Scots" },
+ { MAKECODE('\0','g','l','a'), "Scottish Gaelic" },
+ { MAKECODE('\0','s','e','l'), "Selkup" },
+ { MAKECODE('\0','s','e','m'), "Semitic (Other)" },
+ { MAKECODE('\0','n','s','o'), "Sepedi" },
+ { MAKECODE('\0','s','c','c'), "Serbian" },
+ { MAKECODE('\0','s','r','p'), "Serbian" },
+ { MAKECODE('\0','s','r','r'), "Serer" },
+ { MAKECODE('\0','s','h','n'), "Shan" },
+ { MAKECODE('\0','s','n','a'), "Shona" },
+ { MAKECODE('\0','i','i','i'), "Sichuan Yi" },
+ { MAKECODE('\0','s','c','n'), "Sicilian" },
+ { MAKECODE('\0','s','i','d'), "Sidamo" },
+ { MAKECODE('\0','s','g','n'), "Sign languages" },
+ { MAKECODE('\0','b','l','a'), "Siksika" },
+ { MAKECODE('\0','s','n','d'), "Sindhi" },
+ { MAKECODE('\0','s','i','n'), "Sinhala" },
+ { MAKECODE('\0','s','i','n'), "Sinhalese" },
+ { MAKECODE('\0','s','i','t'), "Sino-Tibetan (Other)" },
+ { MAKECODE('\0','s','i','o'), "Siouan languages" },
+ { MAKECODE('\0','s','m','s'), "Skolt Sami" },
+ { MAKECODE('\0','d','e','n'), "Slave (Athapascan)" },
+ { MAKECODE('\0','s','l','a'), "Slavic (Other)" },
+ { MAKECODE('\0','s','l','o'), "Slovak" },
+ { MAKECODE('\0','s','l','k'), "Slovak" },
+ { MAKECODE('\0','s','l','v'), "Slovenian" },
+ { MAKECODE('\0','s','o','g'), "Sogdian" },
+ { MAKECODE('\0','s','o','m'), "Somali" },
+ { MAKECODE('\0','s','o','n'), "Songhai" },
+ { MAKECODE('\0','s','n','k'), "Soninke" },
+ { MAKECODE('\0','w','e','n'), "Sorbian languages" },
+ { MAKECODE('\0','n','s','o'), "Sotho, Northern" },
+ { MAKECODE('\0','s','o','t'), "Sotho, Southern" },
+ { MAKECODE('\0','s','a','i'), "South American Indian (Other)" },
+ { MAKECODE('\0','s','m','a'), "Southern Sami" },
+ { MAKECODE('\0','n','b','l'), "South Ndebele" },
+ { MAKECODE('\0','s','p','a'), "Castilian" },
+ { MAKECODE('\0','s','u','k'), "Sukuma" },
+ { MAKECODE('\0','s','u','x'), "Sumerian" },
+ { MAKECODE('\0','s','u','n'), "Sundanese" },
+ { MAKECODE('\0','s','u','s'), "Susu" },
+ { MAKECODE('\0','s','w','a'), "Swahili" },
+ { MAKECODE('\0','s','s','w'), "Swati" },
+ { MAKECODE('\0','s','w','e'), "Swedish" },
+ { MAKECODE('\0','s','y','r'), "Syriac" },
+ { MAKECODE('\0','t','g','l'), "Tagalog" },
+ { MAKECODE('\0','t','a','h'), "Tahitian" },
+ { MAKECODE('\0','t','a','i'), "Tai (Other)" },
+ { MAKECODE('\0','t','g','k'), "Tajik" },
+ { MAKECODE('\0','t','m','h'), "Tamashek" },
+ { MAKECODE('\0','t','a','m'), "Tamil" },
+ { MAKECODE('\0','t','a','t'), "Tatar" },
+ { MAKECODE('\0','t','e','l'), "Telugu" },
+ { MAKECODE('\0','t','e','r'), "Tereno" },
+ { MAKECODE('\0','t','e','t'), "Tetum" },
+ { MAKECODE('\0','t','h','a'), "Thai" },
+ { MAKECODE('\0','t','i','b'), "Tibetan" },
+ { MAKECODE('\0','b','o','d'), "Tibetan" },
+ { MAKECODE('\0','t','i','g'), "Tigre" },
+ { MAKECODE('\0','t','i','r'), "Tigrinya" },
+ { MAKECODE('\0','t','e','m'), "Timne" },
+ { MAKECODE('\0','t','i','v'), "Tiv" },
+ { MAKECODE('\0','t','l','h'), "tlhIngan-Hol" },
+ { MAKECODE('\0','t','l','i'), "Tlingit" },
+ { MAKECODE('\0','t','p','i'), "Tok Pisin" },
+ { MAKECODE('\0','t','k','l'), "Tokelau" },
+ { MAKECODE('\0','t','o','g'), "Tonga (Nyasa)" },
+ { MAKECODE('\0','t','o','n'), "Tonga (Tonga Islands)" },
+ { MAKECODE('\0','t','s','i'), "Tsimshian" },
+ { MAKECODE('\0','t','s','o'), "Tsonga" },
+ { MAKECODE('\0','t','s','n'), "Tswana" },
+ { MAKECODE('\0','t','u','m'), "Tumbuka" },
+ { MAKECODE('\0','t','u','p'), "Tupi languages" },
+ { MAKECODE('\0','t','u','r'), "Turkish" },
+ { MAKECODE('\0','o','t','a'), "Turkish, Ottoman (1500-1928)" },
+ { MAKECODE('\0','t','u','k'), "Turkmen" },
+ { MAKECODE('\0','t','v','l'), "Tuvalu" },
+ { MAKECODE('\0','t','y','v'), "Tuvinian" },
+ { MAKECODE('\0','t','w','i'), "Twi" },
+ { MAKECODE('\0','u','d','m'), "Udmurt" },
+ { MAKECODE('\0','u','g','a'), "Ugaritic" },
+ { MAKECODE('\0','u','i','g'), "Uighur" },
+ { MAKECODE('\0','u','k','r'), "Ukrainian" },
+ { MAKECODE('\0','u','m','b'), "Umbundu" },
+ { MAKECODE('\0','u','n','d'), "Undetermined" },
+ { MAKECODE('\0','h','s','b'), "Upper Sorbian" },
+ { MAKECODE('\0','u','r','d'), "Urdu" },
+ { MAKECODE('\0','u','i','g'), "Uyghur" },
+ { MAKECODE('\0','u','z','b'), "Uzbek" },
+ { MAKECODE('\0','v','a','i'), "Vai" },
+ { MAKECODE('\0','c','a','t'), "Valencian" },
+ { MAKECODE('\0','v','e','n'), "Venda" },
+ { MAKECODE('\0','v','i','e'), "Vietnamese" },
+ { MAKECODE('\0','v','o','l'), "Volap\xC3\xBCk" },
+ { MAKECODE('\0','v','o','t'), "Votic" },
+ { MAKECODE('\0','w','a','k'), "Wakashan languages" },
+ { MAKECODE('\0','w','a','l'), "Walamo" },
+ { MAKECODE('\0','w','l','n'), "Walloon" },
+ { MAKECODE('\0','w','a','r'), "Waray" },
+ { MAKECODE('\0','w','a','s'), "Washo" },
+ { MAKECODE('\0','w','e','l'), "Welsh" },
+ { MAKECODE('\0','c','y','m'), "Welsh" },
+ { MAKECODE('\0','w','o','l'), "Wolof" },
+ { MAKECODE('\0','x','h','o'), "Xhosa" },
+ { MAKECODE('\0','s','a','h'), "Yakut" },
+ { MAKECODE('\0','y','a','o'), "Yao" },
+ { MAKECODE('\0','y','a','p'), "Yapese" },
+ { MAKECODE('\0','y','i','d'), "Yiddish" },
+ { MAKECODE('\0','y','o','r'), "Yoruba" },
+ { MAKECODE('\0','y','p','k'), "Yupik languages" },
+ { MAKECODE('\0','z','n','d'), "Zande" },
+ { MAKECODE('\0','z','a','p'), "Zapotec" },
+ { MAKECODE('\0','z','e','n'), "Zenaga" },
+ { MAKECODE('\0','z','h','a'), "Zhuang" },
+ { MAKECODE('\0','z','u','l'), "Zulu" },
+ { MAKECODE('\0','z','u','n'), "Zuni" },
+};
+
+const CharCodeConvertionWithHack CharCode2To3[189] =
+{
+ { "aa", "aar", NULL },
+ { "ab", "abk", NULL },
+ { "af", "afr", NULL },
+ { "ak", "aka", NULL },
+ { "am", "amh", NULL },
+ { "ar", "ara", NULL },
+ { "an", "arg", NULL },
+ { "as", "asm", NULL },
+ { "av", "ava", NULL },
+ { "ae", "ave", NULL },
+ { "ay", "aym", NULL },
+ { "az", "aze", NULL },
+ { "ba", "bak", NULL },
+ { "bm", "bam", NULL },
+ { "be", "bel", NULL },
+ { "bn", "ben", NULL },
+ { "bh", "bih", NULL },
+ { "bi", "bis", NULL },
+ { "bo", "tib", NULL },
+ { "bs", "bos", NULL },
+ { "br", "bre", NULL },
+ { "bg", "bul", NULL },
+ { "ca", "cat", NULL },
+ { "cs", "cze", "ces" },
+ { "ch", "cha", NULL },
+ { "ce", "che", NULL },
+ { "cu", "chu", NULL },
+ { "cv", "chv", NULL },
+ { "kw", "cor", NULL },
+ { "co", "cos", NULL },
+ { "cr", "cre", NULL },
+ { "cy", "wel", NULL },
+ { "da", "dan", NULL },
+ { "de", "ger", "deu" },
+ { "dv", "div", NULL },
+ { "dz", "dzo", NULL },
+ { "el", "gre", "ell" },
+ { "en", "eng", NULL },
+ { "eo", "epo", NULL },
+ { "et", "est", NULL },
+ { "eu", "baq", NULL },
+ { "ee", "ewe", NULL },
+ { "fo", "fao", NULL },
+ { "fa", "per", NULL },
+ { "fj", "fij", NULL },
+ { "fi", "fin", NULL },
+ { "fr", "fre", "fra" },
+ { "fy", "fry", NULL },
+ { "ff", "ful", NULL },
+ { "gd", "gla", NULL },
+ { "ga", "gle", NULL },
+ { "gl", "glg", NULL },
+ { "gv", "glv", NULL },
+ { "gn", "grn", NULL },
+ { "gu", "guj", NULL },
+ { "ht", "hat", NULL },
+ { "ha", "hau", NULL },
+ { "he", "heb", NULL },
+ { "hz", "her", NULL },
+ { "hi", "hin", NULL },
+ { "ho", "hmo", NULL },
+ { "hr", "hrv", NULL },
+ { "hu", "hun", NULL },
+ { "hy", "arm", NULL },
+ { "ig", "ibo", NULL },
+ { "io", "ido", NULL },
+ { "ii", "iii", NULL },
+ { "iu", "iku", NULL },
+ { "ie", "ile", NULL },
+ { "ia", "ina", NULL },
+ { "id", "ind", NULL },
+ { "ik", "ipk", NULL },
+ { "is", "ice", "isl" },
+ { "it", "ita", NULL },
+ { "jv", "jav", NULL },
+ { "ja", "jpn", NULL },
+ { "kl", "kal", NULL },
+ { "kn", "kan", NULL },
+ { "ks", "kas", NULL },
+ { "ka", "geo", NULL },
+ { "kr", "kau", NULL },
+ { "kk", "kaz", NULL },
+ { "km", "khm", NULL },
+ { "ki", "kik", NULL },
+ { "rw", "kin", NULL },
+ { "ky", "kir", NULL },
+ { "kv", "kom", NULL },
+ { "kg", "kon", NULL },
+ { "ko", "kor", NULL },
+ { "kj", "kua", NULL },
+ { "ku", "kur", NULL },
+ { "lo", "lao", NULL },
+ { "la", "lat", NULL },
+ { "lv", "lav", NULL },
+ { "li", "lim", NULL },
+ { "ln", "lin", NULL },
+ { "lt", "lit", NULL },
+ { "lb", "ltz", NULL },
+ { "lu", "lub", NULL },
+ { "lg", "lug", NULL },
+ { "mk", "mac", NULL },
+ { "mh", "mah", NULL },
+ { "ml", "mal", NULL },
+ { "mi", "mao", NULL },
+ { "mr", "mar", NULL },
+ { "ms", "may", NULL },
+ { "mg", "mlg", NULL },
+ { "mt", "mlt", NULL },
+ { "mn", "mon", NULL },
+ { "my", "bur", NULL },
+ { "na", "nau", NULL },
+ { "nv", "nav", NULL },
+ { "nr", "nbl", NULL },
+ { "nd", "nde", NULL },
+ { "ng", "ndo", NULL },
+ { "ne", "nep", NULL },
+ { "nl", "dut", "nld" },
+ { "nn", "nno", NULL },
+ { "nb", "nob", NULL },
+ { "no", "nor", NULL },
+ { "ny", "nya", NULL },
+ { "oc", "oci", NULL },
+ { "oj", "oji", NULL },
+ { "or", "ori", NULL },
+ { "om", "orm", NULL },
+ { "os", "oss", NULL },
+ { "pa", "pan", NULL },
+ { "pi", "pli", NULL },
+ { "pl", "pol", "plk" },
+ { "pt", "por", "ptg" },
+ { "ps", "pus", NULL },
+ { "qu", "que", NULL },
+ { "rm", "roh", NULL },
+ { "ro", "rum", "ron" },
+ { "rn", "run", NULL },
+ { "ru", "rus", NULL },
+ { "sh", "scr", NULL },
+ { "sg", "sag", NULL },
+ { "sa", "san", NULL },
+ { "si", "sin", NULL },
+ { "sk", "slo", "sky" },
+ { "sl", "slv", NULL },
+ { "se", "sme", NULL },
+ { "sm", "smo", NULL },
+ { "sn", "sna", NULL },
+ { "sd", "snd", NULL },
+ { "so", "som", NULL },
+ { "st", "sot", NULL },
+ { "es", "spa", "esp" },
+ { "sq", "alb", NULL },
+ { "sc", "srd", NULL },
+ { "sr", "srp", NULL },
+ { "ss", "ssw", NULL },
+ { "su", "sun", NULL },
+ { "sw", "swa", NULL },
+ { "sv", "swe", "sve" },
+ { "ty", "tah", NULL },
+ { "ta", "tam", NULL },
+ { "tt", "tat", NULL },
+ { "te", "tel", NULL },
+ { "tg", "tgk", NULL },
+ { "tl", "tgl", NULL },
+ { "th", "tha", NULL },
+ { "ti", "tir", NULL },
+ { "to", "ton", NULL },
+ { "tn", "tsn", NULL },
+ { "ts", "tso", NULL },
+ { "tk", "tuk", NULL },
+ { "tr", "tur", "trk" },
+ { "tw", "twi", NULL },
+ { "ug", "uig", NULL },
+ { "uk", "ukr", NULL },
+ { "ur", "urd", NULL },
+ { "uz", "uzb", NULL },
+ { "ve", "ven", NULL },
+ { "vi", "vie", NULL },
+ { "vo", "vol", NULL },
+ { "wa", "wln", NULL },
+ { "wo", "wol", NULL },
+ { "xh", "xho", NULL },
+ { "yi", "yid", NULL },
+ { "yo", "yor", NULL },
+ { "za", "zha", NULL },
+ { "zh", "chi", "zho" },
+ { "zu", "zul", NULL },
+ { "zv", "und", NULL }, // Kodi intern mapping for missing "Undetermined" iso639-1 code
+ { "zx", "zxx", NULL }, // Kodi intern mapping for missing "No linguistic content" iso639-1 code
+ { "zy", "mis", NULL }, // Kodi intern mapping for missing "Miscellaneous languages" iso639-1 code
+ { "zz", "mul", NULL } // Kodi intern mapping for missing "Multiple languages" iso639-1 code
+};
+
+// Based on ISO 3166
+const CharCodeConvertion RegionCode2To3[246] =
+{
+ { "af", "afg" },
+ { "ax", "ala" },
+ { "al", "alb" },
+ { "dz", "dza" },
+ { "as", "asm" },
+ { "ad", "and" },
+ { "ao", "ago" },
+ { "ai", "aia" },
+ { "aq", "ata" },
+ { "ag", "atg" },
+ { "ar", "arg" },
+ { "am", "arm" },
+ { "aw", "abw" },
+ { "au", "aus" },
+ { "at", "aut" },
+ { "az", "aze" },
+ { "bs", "bhs" },
+ { "bh", "bhr" },
+ { "bd", "bgd" },
+ { "bb", "brb" },
+ { "by", "blr" },
+ { "be", "bel" },
+ { "bz", "blz" },
+ { "bj", "ben" },
+ { "bm", "bmu" },
+ { "bt", "btn" },
+ { "bo", "bol" },
+ { "ba", "bih" },
+ { "bw", "bwa" },
+ { "bv", "bvt" },
+ { "br", "bra" },
+ { "io", "iot" },
+ { "bn", "brn" },
+ { "bg", "bgr" },
+ { "bf", "bfa" },
+ { "bi", "bdi" },
+ { "kh", "khm" },
+ { "cm", "cmr" },
+ { "ca", "can" },
+ { "cv", "cpv" },
+ { "ky", "cym" },
+ { "cf", "caf" },
+ { "td", "tcd" },
+ { "cl", "chl" },
+ { "cn", "chn" },
+ { "cx", "cxr" },
+ { "cc", "cck" },
+ { "co", "col" },
+ { "km", "com" },
+ { "cg", "cog" },
+ { "cd", "cod" },
+ { "ck", "cok" },
+ { "cr", "cri" },
+ { "ci", "civ" },
+ { "hr", "hrv" },
+ { "cu", "cub" },
+ { "cy", "cyp" },
+ { "cz", "cze" },
+ { "dk", "dnk" },
+ { "dj", "dji" },
+ { "dm", "dma" },
+ { "do", "dom" },
+ { "ec", "ecu" },
+ { "eg", "egy" },
+ { "sv", "slv" },
+ { "gq", "gnq" },
+ { "er", "eri" },
+ { "ee", "est" },
+ { "et", "eth" },
+ { "fk", "flk" },
+ { "fo", "fro" },
+ { "fj", "fji" },
+ { "fi", "fin" },
+ { "fr", "fra" },
+ { "gf", "guf" },
+ { "pf", "pyf" },
+ { "tf", "atf" },
+ { "ga", "gab" },
+ { "gm", "gmb" },
+ { "ge", "geo" },
+ { "de", "deu" },
+ { "gh", "gha" },
+ { "gi", "gib" },
+ { "gr", "grc" },
+ { "gl", "grl" },
+ { "gd", "grd" },
+ { "gp", "glp" },
+ { "gu", "gum" },
+ { "gt", "gtm" },
+ { "gg", "ggy" },
+ { "gn", "gin" },
+ { "gw", "gnb" },
+ { "gy", "guy" },
+ { "ht", "hti" },
+ { "hm", "hmd" },
+ { "va", "vat" },
+ { "hn", "hnd" },
+ { "hk", "hkg" },
+ { "hu", "hun" },
+ { "is", "isl" },
+ { "in", "ind" },
+ { "id", "idn" },
+ { "ir", "irn" },
+ { "iq", "irq" },
+ { "ie", "irl" },
+ { "im", "imn" },
+ { "il", "isr" },
+ { "it", "ita" },
+ { "jm", "jam" },
+ { "jp", "jpn" },
+ { "je", "jey" },
+ { "jo", "jor" },
+ { "kz", "kaz" },
+ { "ke", "ken" },
+ { "ki", "kir" },
+ { "kp", "prk" },
+ { "kr", "kor" },
+ { "kw", "kwt" },
+ { "kg", "kgz" },
+ { "la", "lao" },
+ { "lv", "lva" },
+ { "lb", "lbn" },
+ { "ls", "lso" },
+ { "lr", "lbr" },
+ { "ly", "lby" },
+ { "li", "lie" },
+ { "lt", "ltu" },
+ { "lu", "lux" },
+ { "mo", "mac" },
+ { "mk", "mkd" },
+ { "mg", "mdg" },
+ { "mw", "mwi" },
+ { "my", "mys" },
+ { "mv", "mdv" },
+ { "ml", "mli" },
+ { "mt", "mlt" },
+ { "mh", "mhl" },
+ { "mq", "mtq" },
+ { "mr", "mrt" },
+ { "mu", "mus" },
+ { "yt", "myt" },
+ { "mx", "mex" },
+ { "fm", "fsm" },
+ { "md", "mda" },
+ { "mc", "mco" },
+ { "mn", "mng" },
+ { "me", "mne" },
+ { "ms", "msr" },
+ { "ma", "mar" },
+ { "mz", "moz" },
+ { "mm", "mmr" },
+ { "na", "nam" },
+ { "nr", "nru" },
+ { "np", "npl" },
+ { "nl", "nld" },
+ { "an", "ant" },
+ { "nc", "ncl" },
+ { "nz", "nzl" },
+ { "ni", "nic" },
+ { "ne", "ner" },
+ { "ng", "nga" },
+ { "nu", "niu" },
+ { "nf", "nfk" },
+ { "mp", "mnp" },
+ { "no", "nor" },
+ { "om", "omn" },
+ { "pk", "pak" },
+ { "pw", "plw" },
+ { "ps", "pse" },
+ { "pa", "pan" },
+ { "pg", "png" },
+ { "py", "pry" },
+ { "pe", "per" },
+ { "ph", "phl" },
+ { "pn", "pcn" },
+ { "pl", "pol" },
+ { "pt", "prt" },
+ { "pr", "pri" },
+ { "qa", "qat" },
+ { "re", "reu" },
+ { "ro", "rou" },
+ { "ru", "rus" },
+ { "rw", "rwa" },
+ { "bl", "blm" },
+ { "sh", "shn" },
+ { "kn", "kna" },
+ { "lc", "lca" },
+ { "mf", "maf" },
+ { "pm", "spm" },
+ { "vc", "vct" },
+ { "ws", "wsm" },
+ { "sm", "smr" },
+ { "st", "stp" },
+ { "sa", "sau" },
+ { "sn", "sen" },
+ { "rs", "srb" },
+ { "sc", "syc" },
+ { "sl", "sle" },
+ { "sg", "sgp" },
+ { "sk", "svk" },
+ { "si", "svn" },
+ { "sb", "slb" },
+ { "so", "som" },
+ { "za", "zaf" },
+ { "gs", "sgs" },
+ { "es", "esp" },
+ { "lk", "lka" },
+ { "sd", "sdn" },
+ { "sr", "sur" },
+ { "sj", "sjm" },
+ { "sz", "swz" },
+ { "se", "swe" },
+ { "ch", "che" },
+ { "sy", "syr" },
+ { "tw", "twn" },
+ { "tj", "tjk" },
+ { "tz", "tza" },
+ { "th", "tha" },
+ { "tl", "tls" },
+ { "tg", "tgo" },
+ { "tk", "tkl" },
+ { "to", "ton" },
+ { "tt", "tto" },
+ { "tn", "tun" },
+ { "tr", "tur" },
+ { "tm", "tkm" },
+ { "tc", "tca" },
+ { "tv", "tuv" },
+ { "ug", "uga" },
+ { "ua", "ukr" },
+ { "ae", "are" },
+ { "gb", "gbr" },
+ { "us", "usa" },
+ { "um", "umi" },
+ { "uy", "ury" },
+ { "uz", "uzb" },
+ { "vu", "vut" },
+ { "ve", "ven" },
+ { "vn", "vnm" },
+ { "vg", "vgb" },
+ { "vi", "vir" },
+ { "wf", "wlf" },
+ { "eh", "esh" },
+ { "ye", "yem" },
+ { "zm", "zmb" },
+ { "zw", "zwe" }
+};
diff --git a/src/utils/LangCodeExpander.h b/src/utils/LangCodeExpander.h
new file mode 100644
index 0000000000..400493aec8
--- /dev/null
+++ b/src/utils/LangCodeExpander.h
@@ -0,0 +1,114 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <map>
+#include <string>
+#include <vector>
+
+class TiXmlElement;
+
+class CLangCodeExpander
+{
+public:
+
+ enum LANGFORMATS
+ {
+ ISO_639_1,
+ ISO_639_2,
+ ENGLISH_NAME
+ };
+
+ CLangCodeExpander(void);
+ ~CLangCodeExpander(void);
+
+ bool Lookup(std::string& desc, const std::string& code);
+ bool Lookup(std::string& desc, const int code);
+
+ /** \brief Determines if two english language names represent the same language.
+ * \param[in] lang1 The first language string to compare given as english language name.
+ * \param[in] lang2 The second language string to compare given as english language name.
+ * \return true if the two language strings represent the same language, false otherwise.
+ * For example "Abkhaz" and "Abkhazian" represent the same language.
+ */
+ bool CompareFullLangNames(const std::string& lang1, const std::string& lang2);
+
+ /** \brief Determines if two languages given as ISO 639-1, ISO 639-2/T, or ISO 639-2/B codes represent the same language.
+ * \param[in] code1 The first language to compare given as ISO 639-1, ISO 639-2/T, or ISO 639-2/B code.
+ * \param[in] code2 The second language to compare given as ISO 639-1, ISO 639-2/T, or ISO 639-2/B code.
+ * \return true if the two language codes represent the same language, false otherwise.
+ * For example "ger", "deu" and "de" represent the same language.
+ */
+ bool CompareLangCodes(const std::string& code1, const std::string& code2);
+
+ /** \brief Converts a language given as 2-Char (ISO 639-1),
+ * 3-Char (ISO 639-2/T or ISO 639-2/B),
+ * or full english name string to a 2-Char (ISO 639-1) code.
+ * \param[out] code The 2-Char language code of the given language lang.
+ * \param[in] lang The language that should be converted.
+ * \param[in] checkXbmcLocales Try to find in XBMC specific locales
+ * \return true if the conversion succeeded, false otherwise.
+ */
+ bool ConvertToTwoCharCode(std::string& code, const std::string& lang, bool checkXbmcLocales = true);
+
+ /** \brief Converts a language given as 2-Char (ISO 639-1),
+ * 3-Char (ISO 639-2/T or ISO 639-2/B),
+ * or full english name string to a 3-Char ISO 639-2/T code.
+ * \param[in] lang The language that should be converted.
+ * \return The 3-Char ISO 639-2/T code of lang if that code exists, lang otherwise.
+ */
+ std::string ConvertToISO6392T(const std::string& lang);
+ static bool ConvertTwoToThreeCharCode(std::string& strThreeCharCode, const std::string& strTwoCharCode, bool checkWin32Locales = false);
+ static bool ConvertToThreeCharCode(std::string& strThreeCharCode, const std::string& strCharCode, bool checkXbmcLocales = true, bool checkWin32Locales = false);
+
+#ifdef TARGET_WINDOWS
+ static bool ConvertLinuxToWindowsRegionCodes(const std::string& strTwoCharCode, std::string& strThreeCharCode);
+ static bool ConvertWindowsToGeneralCharCode(const std::string& strWindowsCharCode, std::string& strThreeCharCode);
+#endif
+
+ void LoadUserCodes(const TiXmlElement* pRootElement);
+ void Clear();
+
+ static std::vector<std::string> GetLanguageNames(LANGFORMATS format = ISO_639_1);
+protected:
+
+ /** \brief Converts a language code given as a long, see #MAKECODE(a, b, c, d)
+ * to its string representation.
+ * \param[in] code The language code given as a long, see #MAKECODE(a, b, c, d).
+ * \param[out] ret The string representation of the given language code code.
+ */
+ static void CodeToString(long code, std::string& ret);
+
+ typedef std::map<std::string, std::string> STRINGLOOKUPTABLE;
+ STRINGLOOKUPTABLE m_mapUser;
+
+ static bool LookupInDb(std::string& desc, const std::string& code);
+ bool LookupInMap(std::string& desc, const std::string& code);
+
+ /** \brief Looks up the ISO 639-1, ISO 639-2/T, or ISO 639-2/B, whichever it finds first,
+ * code of the given english language name.
+ * \param[in] desc The english language name for which a code is looked for.
+ * \param[out] code The ISO 639-1, ISO 639-2/T, or ISO 639-2/B code of the given language desc.
+ * \return true if the a code was found, false otherwise.
+ */
+ bool ReverseLookup(const std::string& desc, std::string& code);
+};
+
+extern CLangCodeExpander g_LangCodeExpander;
diff --git a/src/utils/LegacyPathTranslation.cpp b/src/utils/LegacyPathTranslation.cpp
new file mode 100644
index 0000000000..f13a578eb8
--- /dev/null
+++ b/src/utils/LegacyPathTranslation.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "LegacyPathTranslation.h"
+#include "utils/StringUtils.h"
+#include "URL.h"
+
+typedef struct Translator {
+ const char *legacyPath;
+ const char *newPath;
+} Translator;
+
+// ATTENTION: Make sure the longer match strings go first
+// because the string match is performed with StringUtils::StartsWith()
+static Translator s_videoDbTranslator[] = {
+ { "videodb://1/1", "videodb://movies/genres" },
+ { "videodb://1/2", "videodb://movies/titles" },
+ { "videodb://1/3", "videodb://movies/years" },
+ { "videodb://1/4", "videodb://movies/actors" },
+ { "videodb://1/5", "videodb://movies/directors" },
+ { "videodb://1/6", "videodb://movies/studios" },
+ { "videodb://1/7", "videodb://movies/sets" },
+ { "videodb://1/8", "videodb://movies/countries" },
+ { "videodb://1/9", "videodb://movies/tags" },
+ { "videodb://1", "videodb://movies" },
+ { "videodb://2/1", "videodb://tvshows/genres" },
+ { "videodb://2/2", "videodb://tvshows/titles" },
+ { "videodb://2/3", "videodb://tvshows/years" },
+ { "videodb://2/4", "videodb://tvshows/actors" },
+ { "videodb://2/5", "videodb://tvshows/studios" },
+ { "videodb://2/9", "videodb://tvshows/tags" },
+ { "videodb://2", "videodb://tvshows" },
+ { "videodb://3/1", "videodb://musicvideos/genres" },
+ { "videodb://3/2", "videodb://musicvideos/titles" },
+ { "videodb://3/3", "videodb://musicvideos/years" },
+ { "videodb://3/4", "videodb://musicvideos/artists" },
+ { "videodb://3/5", "videodb://musicvideos/albums" },
+ { "videodb://3/9", "videodb://musicvideos/tags" },
+ { "videodb://3", "videodb://musicvideos" },
+ { "videodb://4", "videodb://recentlyaddedmovies" },
+ { "videodb://5", "videodb://recentlyaddedepisodes" },
+ { "videodb://6", "videodb://recentlyaddedmusicvideos" }
+};
+
+#define VideoDbTranslatorSize sizeof(s_videoDbTranslator) / sizeof(Translator)
+
+// ATTENTION: Make sure the longer match strings go first
+// because the string match is performed with StringUtils::StartsWith()
+static Translator s_musicDbTranslator[] = {
+ { "musicdb://10", "musicdb://singles" },
+ { "musicdb://1", "musicdb://genres" },
+ { "musicdb://2", "musicdb://artists" },
+ { "musicdb://3", "musicdb://albums" },
+ { "musicdb://4", "musicdb://songs" },
+ { "musicdb://5/1", "musicdb://top100/albums" },
+ { "musicdb://5/2", "musicdb://top100/songs" },
+ { "musicdb://5", "musicdb://top100" },
+ { "musicdb://6", "musicdb://recentlyaddedalbums" },
+ { "musicdb://7", "musicdb://recentlyplayedalbums" },
+ { "musicdb://8", "musicdb://compilations" },
+ { "musicdb://9", "musicdb://years" }
+};
+
+#define MusicDbTranslatorSize sizeof(s_musicDbTranslator) / sizeof(Translator)
+
+std::string CLegacyPathTranslation::TranslateVideoDbPath(const CURL &legacyPath)
+{
+ return TranslatePath(legacyPath.Get(), s_videoDbTranslator, VideoDbTranslatorSize);
+}
+
+std::string CLegacyPathTranslation::TranslateMusicDbPath(const CURL &legacyPath)
+{
+ return TranslatePath(legacyPath.Get(), s_musicDbTranslator, MusicDbTranslatorSize);
+}
+
+std::string CLegacyPathTranslation::TranslateVideoDbPath(const std::string &legacyPath)
+{
+ return TranslatePath(legacyPath, s_videoDbTranslator, VideoDbTranslatorSize);
+}
+
+std::string CLegacyPathTranslation::TranslateMusicDbPath(const std::string &legacyPath)
+{
+ return TranslatePath(legacyPath, s_musicDbTranslator, MusicDbTranslatorSize);
+}
+
+std::string CLegacyPathTranslation::TranslatePath(const std::string &legacyPath, Translator *translationMap, size_t translationMapSize)
+{
+ std::string newPath = legacyPath;
+ for (size_t index = 0; index < translationMapSize; index++)
+ {
+ if (StringUtils::StartsWithNoCase(newPath, translationMap[index].legacyPath))
+ {
+ StringUtils::Replace(newPath, translationMap[index].legacyPath, translationMap[index].newPath);
+ break;
+ }
+ }
+
+ return newPath;
+}
diff --git a/src/utils/LegacyPathTranslation.h b/src/utils/LegacyPathTranslation.h
new file mode 100644
index 0000000000..26aa6d59d6
--- /dev/null
+++ b/src/utils/LegacyPathTranslation.h
@@ -0,0 +1,58 @@
+#pragma once
+/*
+ * Copyright (C) 2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string>
+
+typedef struct Translator Translator;
+
+class CURL;
+
+/*!
+ \brief Translates old internal paths into new ones
+
+ Translates old videodb:// and musicdb:// paths which used numbers
+ to indicate a specific category to new paths using more descriptive
+ strings to indicate categories.
+ */
+class CLegacyPathTranslation
+{
+public:
+ /*!
+ \brief Translates old videodb:// paths to new ones
+
+ \param legacyPath Path in the old videodb:// format using numbers
+ \return Path in the new videodb:// format using descriptive strings
+ */
+ static std::string TranslateVideoDbPath(const CURL &legacyPath);
+ static std::string TranslateVideoDbPath(const std::string &legacyPath);
+
+ /*!
+ \brief Translates old musicdb:// paths to new ones
+
+ \param legacyPath Path in the old musicdb:// format using numbers
+ \return Path in the new musicdb:// format using descriptive strings
+ */
+ static std::string TranslateMusicDbPath(const CURL &legacyPath);
+ static std::string TranslateMusicDbPath(const std::string &legacyPath);
+
+private:
+ static std::string TranslatePath(const std::string &legacyPath, Translator *translationMap, size_t translationMapSize);
+};
diff --git a/src/utils/Makefile.in b/src/utils/Makefile.in
new file mode 100644
index 0000000000..e2034470c6
--- /dev/null
+++ b/src/utils/Makefile.in
@@ -0,0 +1,90 @@
+SRCS += AlarmClock.cpp
+SRCS += AliasShortcutUtils.cpp
+SRCS += Archive.cpp
+SRCS += AsyncFileCopy.cpp
+SRCS += AutoPtrHandle.cpp
+SRCS += auto_buffer.cpp
+SRCS += Base64.cpp
+SRCS += BitstreamConverter.cpp
+SRCS += BitstreamStats.cpp
+SRCS += BooleanLogic.cpp
+SRCS += CharsetConverter.cpp
+SRCS += CharsetDetection.cpp
+SRCS += CPUInfo.cpp
+SRCS += Crc32.cpp
+SRCS += CryptThreading.cpp
+SRCS += DatabaseUtils.cpp
+SRCS += EndianSwap.cpp
+SRCS += EdenVideoArtUpdater.cpp
+SRCS += Environment.cpp
+SRCS += Fanart.cpp
+SRCS += fastmemcpy.c
+SRCS += fastmemcpy-arm.S
+SRCS += FileOperationJob.cpp
+SRCS += FileUtils.cpp
+SRCS += fstrcmp.c
+SRCS += fft.cpp
+SRCS += GLUtils.cpp
+SRCS += GroupUtils.cpp
+SRCS += HTMLTable.cpp
+SRCS += HTMLUtil.cpp
+SRCS += HttpHeader.cpp
+SRCS += HttpParser.cpp
+SRCS += HttpResponse.cpp
+SRCS += InfoLoader.cpp
+SRCS += JobManager.cpp
+SRCS += JSONVariantParser.cpp
+SRCS += JSONVariantWriter.cpp
+SRCS += LabelFormatter.cpp
+SRCS += LangCodeExpander.cpp
+SRCS += LegacyPathTranslation.cpp
+SRCS += log.cpp
+SRCS += md5.cpp
+SRCS += MarkWatchedJob.cpp
+SRCS += Mime.cpp
+SRCS += Observer.cpp
+SRCS += PerformanceSample.cpp
+SRCS += PerformanceStats.cpp
+SRCS += posix/PosixInterfaceForCLog.cpp
+SRCS += POUtils.cpp
+SRCS += RecentlyAddedJob.cpp
+SRCS += RegExp.cpp
+SRCS += RingBuffer.cpp
+SRCS += RssManager.cpp
+SRCS += RssReader.cpp
+SRCS += SaveFileStateJob.cpp
+SRCS += ScraperParser.cpp
+SRCS += ScraperUrl.cpp
+SRCS += Screenshot.cpp
+SRCS += SeekHandler.cpp
+SRCS += SortUtils.cpp
+SRCS += Splash.cpp
+SRCS += Stopwatch.cpp
+SRCS += StreamDetails.cpp
+SRCS += StreamUtils.cpp
+SRCS += StringUtils.cpp
+SRCS += StringValidation.cpp
+SRCS += SystemInfo.cpp
+SRCS += TextSearch.cpp
+SRCS += TimeSmoother.cpp
+SRCS += TimeUtils.cpp
+SRCS += TuxBoxUtil.cpp
+SRCS += URIUtils.cpp
+SRCS += UrlOptions.cpp
+SRCS += Variant.cpp
+SRCS += Vector.cpp
+SRCS += Weather.cpp
+SRCS += XBMCTinyXML.cpp
+SRCS += XMLUtils.cpp
+SRCS += Utf8Utils.cpp
+SRCS += XSLTUtils.cpp
+SRCS += ActorProtocol.cpp
+
+ifeq (@USE_OPENGLES@,1)
+SRCS += AMLUtils.cpp
+endif
+
+LIB = utils.a
+
+include @abs_top_srcdir@/Makefile.include
+-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(patsubst %.S,,$(SRCS))))
diff --git a/src/utils/MarkWatchedJob.cpp b/src/utils/MarkWatchedJob.cpp
new file mode 100644
index 0000000000..054da5c124
--- /dev/null
+++ b/src/utils/MarkWatchedJob.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2014 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "FileItem.h"
+#include "filesystem/Directory.h"
+#include "GUIUserMessages.h"
+#include "guilib/GUIWindowManager.h"
+#include "MarkWatchedJob.h"
+#include "profiles/ProfilesManager.h"
+#include "Util.h"
+#include "utils/URIUtils.h"
+#include "video/VideoDatabase.h"
+
+#ifdef HAS_UPNP
+#include "network/upnp/UPnP.h"
+#endif
+
+#include <vector>
+
+CMarkWatchedJob::CMarkWatchedJob(const CFileItemPtr &item, bool bMark)
+{
+ m_item = item;
+ m_bMark = bMark;
+}
+
+CMarkWatchedJob::~CMarkWatchedJob()
+{
+}
+
+bool CMarkWatchedJob::operator==(const CJob* job) const
+{
+ if (strcmp(job->GetType(), GetType()) == 0)
+ {
+ const CMarkWatchedJob* markJob = dynamic_cast<const CMarkWatchedJob*>(job);
+ if (markJob)
+ return (m_item->IsSamePath(markJob->m_item.get()) && markJob->m_bMark == m_bMark);
+ }
+ return false;
+}
+
+bool CMarkWatchedJob::DoWork()
+{
+ if (!CProfilesManager::Get().GetCurrentProfile().canWriteDatabases())
+ return false;
+
+ CFileItemList items;
+ items.Add(CFileItemPtr(new CFileItem(*m_item)));
+
+ if (m_item->m_bIsFolder)
+ CUtil::GetRecursiveListing(m_item->GetPath(), items, "", XFILE::DIR_FLAG_NO_FILE_INFO);
+
+ std::vector<CFileItemPtr> markItems;
+ for (int i = 0; i < items.Size(); i++)
+ {
+ if (items[i]->HasVideoInfoTag() &&
+ (( m_bMark && items[i]->GetVideoInfoTag()->m_playCount) ||
+ (!m_bMark && !(items[i]->GetVideoInfoTag()->m_playCount))))
+ continue;
+
+#ifdef HAS_UPNP
+ if (!URIUtils::IsUPnP(items[i]->GetPath()) || !UPNP::CUPnP::MarkWatched(*items[i], m_bMark))
+#endif
+ {
+ markItems.push_back(items[i]);
+ }
+ }
+
+ if (!markItems.empty())
+ {
+ CVideoDatabase database;
+ if (!database.Open())
+ return false;
+
+ database.BeginTransaction();
+
+ for (std::vector<CFileItemPtr>::const_iterator iter = markItems.begin(); iter != markItems.end(); ++iter)
+ {
+ CFileItemPtr pItem = *iter;
+ if (m_bMark)
+ {
+ std::string path(pItem->GetPath());
+ if (pItem->HasVideoInfoTag())
+ path = pItem->GetVideoInfoTag()->GetPath();
+
+ database.ClearBookMarksOfFile(path, CBookmark::RESUME);
+ database.IncrementPlayCount(*pItem);
+ }
+ else
+ database.SetPlayCount(*pItem, 0);
+ }
+
+ database.CommitTransaction();
+ database.Close();
+ }
+
+ return true;
+}
+
+CMarkWatchedQueue &CMarkWatchedQueue::Get()
+{
+ static CMarkWatchedQueue markWatchedQueue;
+ return markWatchedQueue;
+}
+
+void CMarkWatchedQueue::OnJobComplete(unsigned int jobID, bool success, CJob *job)
+{
+ if (success)
+ {
+ if (QueueEmpty())
+ {
+ CUtil::DeleteVideoDatabaseDirectoryCache();
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE);
+ g_windowManager.SendThreadMessage(msg);
+ }
+ return CJobQueue::OnJobComplete(jobID, success, job);
+ }
+ CancelJobs();
+}
diff --git a/src/utils/MarkWatchedJob.h b/src/utils/MarkWatchedJob.h
new file mode 100644
index 0000000000..cd59fd746e
--- /dev/null
+++ b/src/utils/MarkWatchedJob.h
@@ -0,0 +1,44 @@
+#pragma once
+/*
+ * Copyright (C) 2014 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "Job.h"
+#include "utils/JobManager.h"
+
+class CMarkWatchedJob : public CJob
+{
+public:
+ CMarkWatchedJob(const CFileItemPtr &item, bool bMark);
+private:
+ virtual ~CMarkWatchedJob();
+ virtual const char *GetType() const { return "markwatched"; }
+ virtual bool operator==(const CJob* job) const;
+ virtual bool DoWork();
+ CFileItemPtr m_item;
+ bool m_bMark;
+};
+
+class CMarkWatchedQueue: public CJobQueue
+{
+public:
+ static CMarkWatchedQueue &Get();
+private:
+ virtual void OnJobComplete(unsigned int jobID, bool success, CJob *job);
+};
diff --git a/src/utils/MathUtils.h b/src/utils/MathUtils.h
new file mode 100644
index 0000000000..ea32f37c71
--- /dev/null
+++ b/src/utils/MathUtils.h
@@ -0,0 +1,204 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdint.h>
+#include <cassert>
+#include <climits>
+#include <cmath>
+
+#ifdef __SSE2__
+#include <emmintrin.h>
+#endif
+
+// use real compiler defines in here as we want to
+// avoid including system.h or other magic includes.
+// use 'gcc -dM -E - < /dev/null' or similar to find them.
+
+#if defined(__ppc__) || \
+ defined(__powerpc__) || \
+ defined(__arm__)
+ #define DISABLE_MATHUTILS_ASM_ROUND_INT
+#endif
+
+/*! \brief Math utility class.
+ Note that the test() routine should return true for all implementations
+
+ See http://ldesoras.free.fr/doc/articles/rounding_en.pdf for an explanation
+ of the technique used on x86.
+ */
+namespace MathUtils
+{
+ // GCC does something stupid with optimization on release builds if we try
+ // to assert in these functions
+
+ /*! \brief Round to nearest integer.
+ This routine does fast rounding to the nearest integer.
+ In the case (k + 0.5 for any integer k) we round up to k+1, and in all other
+ instances we should return the nearest integer.
+ Thus, { -1.5, -0.5, 0.5, 1.5 } is rounded to { -1, 0, 1, 2 }.
+ It preserves the property that round(k) - round(k-1) = 1 for all doubles k.
+
+ Make sure MathUtils::test() returns true for each implementation.
+ \sa truncate_int, test
+ */
+ inline int round_int(double x)
+ {
+ assert(x > static_cast<double>(INT_MIN / 2) - 1.0);
+ assert(x < static_cast<double>(INT_MAX / 2) + 1.0);
+
+#if defined(DISABLE_MATHUTILS_ASM_ROUND_INT)
+ /* This implementation warrants some further explanation.
+ *
+ * First, a couple of notes on rounding:
+ * 1) C casts from float/double to integer round towards zero.
+ * 2) Float/double additions are rounded according to the normal rules,
+ * in other words: on some architectures, it's fixed at compile-time,
+ * and on others it can be set using fesetround()). The following
+ * analysis assumes round-to-nearest with ties rounding to even. This
+ * is a fairly sensible choice, and is the default with ARM VFP.
+ *
+ * What this function wants is round-to-nearest with ties rounding to
+ * +infinity. This isn't an IEEE rounding mode, even if we could guarantee
+ * that all architectures supported fesetround(), which they don't. Instead,
+ * this adds an offset of 2147483648.5 (= 0x80000000.8p0), then casts to
+ * an unsigned int (crucially, all possible inputs are now in a range where
+ * round to zero acts the same as round to -infinity) and then subtracts
+ * 0x80000000 in the integer domain. The 0.5 component of the offset
+ * converts what is effectively a round down into a round to nearest, with
+ * ties rounding up, as desired.
+ *
+ * There is a catch, that because there is a double rounding, there is a
+ * small region where the input falls just *below* a tie, where the addition
+ * of the offset causes a round *up* to an exact integer, due to the finite
+ * level of precision available in floating point. You need to be aware of
+ * this when calling this function, although at present it is not believed
+ * that XBMC ever attempts to round numbers in this window.
+ *
+ * It is worth proving the size of the affected window. Recall that double
+ * precision employs a mantissa of 52 bits.
+ * 1) For all inputs -0.5 <= x <= INT_MAX
+ * Once the offset is applied, the most significant binary digit in the
+ * floating-point representation is +2^31.
+ * At this magnitude, the smallest step representable in double precision
+ * is 2^31 / 2^52 = 0.000000476837158203125
+ * So the size of the range which is rounded up due to the addition is
+ * half the size of this step, or 0.0000002384185791015625
+ *
+ * 2) For all inputs INT_MIN/2 < x < -0.5
+ * Once the offset is applied, the most significant binary digit in the
+ * floating-point representation is +2^30.
+ * At this magnitude, the smallest step representable in double precision
+ * is 2^30 / 2^52 = 0.0000002384185791015625
+ * So the size of the range which is rounded up due to the addition is
+ * half the size of this step, or 0.00000011920928955078125
+ *
+ * 3) For all inputs INT_MIN <= x <= INT_MIN/2
+ * The representation once the offset is applied has equal or greater
+ * precision than the input, so the addition does not cause rounding.
+ */
+ return ((unsigned int) (x + 0x80000000.8p0)) - 0x80000000;
+
+#else
+ const float round_to_nearest = 0.5f;
+ int i;
+#if defined(__SSE2__)
+ const float round_dn_to_nearest = 0.4999999f;
+ i = (x > 0) ? _mm_cvttsd_si32(_mm_set_sd(x + round_to_nearest)) : _mm_cvttsd_si32(_mm_set_sd(x - round_dn_to_nearest));
+
+#elif defined(TARGET_WINDOWS)
+ __asm
+ {
+ fld x
+ fadd st, st (0)
+ fadd round_to_nearest
+ fistp i
+ sar i, 1
+ }
+
+#else
+ __asm__ __volatile__ (
+ "fadd %%st\n\t"
+ "fadd %%st(1)\n\t"
+ "fistpl %0\n\t"
+ "sarl $1, %0\n"
+ : "=m"(i) : "u"(round_to_nearest), "t"(x) : "st"
+ );
+
+#endif
+ return i;
+#endif
+ }
+
+ /*! \brief Truncate to nearest integer.
+ This routine does fast truncation to an integer.
+ It should simply drop the fractional portion of the floating point number.
+
+ Make sure MathUtils::test() returns true for each implementation.
+ \sa round_int, test
+ */
+ inline int truncate_int(double x)
+ {
+ assert(x > static_cast<double>(INT_MIN / 2) - 1.0);
+ assert(x < static_cast<double>(INT_MAX / 2) + 1.0);
+ return x;
+ }
+
+ inline int64_t abs(int64_t a)
+ {
+ return (a < 0) ? -a : a;
+ }
+
+ inline unsigned bitcount(unsigned v)
+ {
+ unsigned c = 0;
+ for (c = 0; v; c++)
+ v &= v - 1; // clear the least significant bit set
+ return c;
+ }
+
+ inline void hack()
+ {
+ // stupid hack to keep compiler from dropping these
+ // functions as unused
+ MathUtils::round_int(0.0);
+ MathUtils::truncate_int(0.0);
+ MathUtils::abs(0);
+ }
+
+#if 0
+ /*! \brief test routine for round_int and truncate_int
+ Must return true on all platforms.
+ */
+ inline bool test()
+ {
+ for (int i = -8; i < 8; ++i)
+ {
+ double d = 0.25*i;
+ int r = (i < 0) ? (i - 1) / 4 : (i + 2) / 4;
+ int t = i / 4;
+ if (round_int(d) != r || truncate_int(d) != t)
+ return false;
+ }
+ return true;
+ }
+#endif
+} // namespace MathUtils
+
diff --git a/src/utils/Mime.cpp b/src/utils/Mime.cpp
new file mode 100644
index 0000000000..874085f5b1
--- /dev/null
+++ b/src/utils/Mime.cpp
@@ -0,0 +1,697 @@
+/*
+ * Copyright (C) 2012-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <algorithm>
+
+#include "Mime.h"
+#include "FileItem.h"
+#include "StdString.h"
+#include "URIUtils.h"
+#include "music/tags/MusicInfoTag.h"
+#include "video/VideoInfoTag.h"
+#include "URL.h"
+#include "utils/StringUtils.h"
+#include "filesystem/CurlFile.h"
+
+using namespace std;
+
+map<string, string> fillMimeTypes()
+{
+ map<string, string> mimeTypes;
+
+ mimeTypes.insert(pair<string, string>("3dm", "x-world/x-3dmf"));
+ mimeTypes.insert(pair<string, string>("3dmf", "x-world/x-3dmf"));
+ mimeTypes.insert(pair<string, string>("a", "application/octet-stream"));
+ mimeTypes.insert(pair<string, string>("aab", "application/x-authorware-bin"));
+ mimeTypes.insert(pair<string, string>("aam", "application/x-authorware-map"));
+ mimeTypes.insert(pair<string, string>("aas", "application/x-authorware-seg"));
+ mimeTypes.insert(pair<string, string>("abc", "text/vnd.abc"));
+ mimeTypes.insert(pair<string, string>("acgi", "text/html"));
+ mimeTypes.insert(pair<string, string>("afl", "video/animaflex"));
+ mimeTypes.insert(pair<string, string>("ai", "application/postscript"));
+ mimeTypes.insert(pair<string, string>("aif", "audio/aiff"));
+ mimeTypes.insert(pair<string, string>("aifc", "audio/x-aiff"));
+ mimeTypes.insert(pair<string, string>("aiff", "audio/aiff"));
+ mimeTypes.insert(pair<string, string>("aim", "application/x-aim"));
+ mimeTypes.insert(pair<string, string>("aip", "text/x-audiosoft-intra"));
+ mimeTypes.insert(pair<string, string>("ani", "application/x-navi-animation"));
+ mimeTypes.insert(pair<string, string>("aos", "application/x-nokia-9000-communicator-add-on-software"));
+ mimeTypes.insert(pair<string, string>("aps", "application/mime"));
+ mimeTypes.insert(pair<string, string>("arc", "application/octet-stream"));
+ mimeTypes.insert(pair<string, string>("arj", "application/arj"));
+ mimeTypes.insert(pair<string, string>("art", "image/x-jg"));
+ mimeTypes.insert(pair<string, string>("asf", "video/x-ms-asf"));
+ mimeTypes.insert(pair<string, string>("asm", "text/x-asm"));
+ mimeTypes.insert(pair<string, string>("asp", "text/asp"));
+ mimeTypes.insert(pair<string, string>("asx", "video/x-ms-asf"));
+ mimeTypes.insert(pair<string, string>("au", "audio/basic"));
+ mimeTypes.insert(pair<string, string>("avi", "video/avi"));
+ mimeTypes.insert(pair<string, string>("avs", "video/avs-video"));
+ mimeTypes.insert(pair<string, string>("bcpio", "application/x-bcpio"));
+ mimeTypes.insert(pair<string, string>("bin", "application/octet-stream"));
+ mimeTypes.insert(pair<string, string>("bm", "image/bmp"));
+ mimeTypes.insert(pair<string, string>("bmp", "image/bmp"));
+ mimeTypes.insert(pair<string, string>("boo", "application/book"));
+ mimeTypes.insert(pair<string, string>("book", "application/book"));
+ mimeTypes.insert(pair<string, string>("boz", "application/x-bzip2"));
+ mimeTypes.insert(pair<string, string>("bsh", "application/x-bsh"));
+ mimeTypes.insert(pair<string, string>("bz", "application/x-bzip"));
+ mimeTypes.insert(pair<string, string>("bz2", "application/x-bzip2"));
+ mimeTypes.insert(pair<string, string>("c", "text/plain"));
+ mimeTypes.insert(pair<string, string>("c++", "text/plain"));
+ mimeTypes.insert(pair<string, string>("cat", "application/vnd.ms-pki.seccat"));
+ mimeTypes.insert(pair<string, string>("cc", "text/plain"));
+ mimeTypes.insert(pair<string, string>("ccad", "application/clariscad"));
+ mimeTypes.insert(pair<string, string>("cco", "application/x-cocoa"));
+ mimeTypes.insert(pair<string, string>("cdf", "application/cdf"));
+ mimeTypes.insert(pair<string, string>("cer", "application/pkix-cert"));
+ mimeTypes.insert(pair<string, string>("cer", "application/x-x509-ca-cert"));
+ mimeTypes.insert(pair<string, string>("cha", "application/x-chat"));
+ mimeTypes.insert(pair<string, string>("chat", "application/x-chat"));
+ mimeTypes.insert(pair<string, string>("class", "application/java"));
+ mimeTypes.insert(pair<string, string>("com", "application/octet-stream"));
+ mimeTypes.insert(pair<string, string>("conf", "text/plain"));
+ mimeTypes.insert(pair<string, string>("cpio", "application/x-cpio"));
+ mimeTypes.insert(pair<string, string>("cpp", "text/x-c"));
+ mimeTypes.insert(pair<string, string>("cpt", "application/x-cpt"));
+ mimeTypes.insert(pair<string, string>("crl", "application/pkcs-crl"));
+ mimeTypes.insert(pair<string, string>("crt", "application/pkix-cert"));
+ mimeTypes.insert(pair<string, string>("csh", "application/x-csh"));
+ mimeTypes.insert(pair<string, string>("css", "text/css"));
+ mimeTypes.insert(pair<string, string>("cxx", "text/plain"));
+ mimeTypes.insert(pair<string, string>("dcr", "application/x-director"));
+ mimeTypes.insert(pair<string, string>("deepv", "application/x-deepv"));
+ mimeTypes.insert(pair<string, string>("def", "text/plain"));
+ mimeTypes.insert(pair<string, string>("der", "application/x-x509-ca-cert"));
+ mimeTypes.insert(pair<string, string>("dif", "video/x-dv"));
+ mimeTypes.insert(pair<string, string>("dir", "application/x-director"));
+ mimeTypes.insert(pair<string, string>("dl", "video/dl"));
+ mimeTypes.insert(pair<string, string>("divx", "video/x-msvideo"));
+ mimeTypes.insert(pair<string, string>("doc", "application/msword"));
+ mimeTypes.insert(pair<string, string>("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"));
+ mimeTypes.insert(pair<string, string>("dot", "application/msword"));
+ mimeTypes.insert(pair<string, string>("dp", "application/commonground"));
+ mimeTypes.insert(pair<string, string>("drw", "application/drafting"));
+ mimeTypes.insert(pair<string, string>("dump", "application/octet-stream"));
+ mimeTypes.insert(pair<string, string>("dv", "video/x-dv"));
+ mimeTypes.insert(pair<string, string>("dvi", "application/x-dvi"));
+ mimeTypes.insert(pair<string, string>("dwf", "model/vnd.dwf"));
+ mimeTypes.insert(pair<string, string>("dwg", "image/vnd.dwg"));
+ mimeTypes.insert(pair<string, string>("dxf", "image/vnd.dwg"));
+ mimeTypes.insert(pair<string, string>("dxr", "application/x-director"));
+ mimeTypes.insert(pair<string, string>("el", "text/x-script.elisp"));
+ mimeTypes.insert(pair<string, string>("elc", "application/x-elc"));
+ mimeTypes.insert(pair<string, string>("env", "application/x-envoy"));
+ mimeTypes.insert(pair<string, string>("eps", "application/postscript"));
+ mimeTypes.insert(pair<string, string>("es", "application/x-esrehber"));
+ mimeTypes.insert(pair<string, string>("etx", "text/x-setext"));
+ mimeTypes.insert(pair<string, string>("evy", "application/envoy"));
+ mimeTypes.insert(pair<string, string>("exe", "application/octet-stream"));
+ mimeTypes.insert(pair<string, string>("f", "text/x-fortran"));
+ mimeTypes.insert(pair<string, string>("f77", "text/x-fortran"));
+ mimeTypes.insert(pair<string, string>("f90", "text/x-fortran"));
+ mimeTypes.insert(pair<string, string>("fdf", "application/vnd.fdf"));
+ mimeTypes.insert(pair<string, string>("fif", "image/fif"));
+ mimeTypes.insert(pair<string, string>("flac", "audio/flac"));
+ mimeTypes.insert(pair<string, string>("fli", "video/fli"));
+ mimeTypes.insert(pair<string, string>("flo", "image/florian"));
+ mimeTypes.insert(pair<string, string>("flv", "video/x-flv"));
+ mimeTypes.insert(pair<string, string>("flx", "text/vnd.fmi.flexstor"));
+ mimeTypes.insert(pair<string, string>("fmf", "video/x-atomic3d-feature"));
+ mimeTypes.insert(pair<string, string>("for", "text/plain"));
+ mimeTypes.insert(pair<string, string>("for", "text/x-fortran"));
+ mimeTypes.insert(pair<string, string>("fpx", "image/vnd.fpx"));
+ mimeTypes.insert(pair<string, string>("frl", "application/freeloader"));
+ mimeTypes.insert(pair<string, string>("funk", "audio/make"));
+ mimeTypes.insert(pair<string, string>("g", "text/plain"));
+ mimeTypes.insert(pair<string, string>("g3", "image/g3fax"));
+ mimeTypes.insert(pair<string, string>("gif", "image/gif"));
+ mimeTypes.insert(pair<string, string>("gl", "video/x-gl"));
+ mimeTypes.insert(pair<string, string>("gsd", "audio/x-gsm"));
+ mimeTypes.insert(pair<string, string>("gsm", "audio/x-gsm"));
+ mimeTypes.insert(pair<string, string>("gsp", "application/x-gsp"));
+ mimeTypes.insert(pair<string, string>("gss", "application/x-gss"));
+ mimeTypes.insert(pair<string, string>("gtar", "application/x-gtar"));
+ mimeTypes.insert(pair<string, string>("gz", "application/x-compressed"));
+ mimeTypes.insert(pair<string, string>("gzip", "application/x-gzip"));
+ mimeTypes.insert(pair<string, string>("h", "text/plain"));
+ mimeTypes.insert(pair<string, string>("hdf", "application/x-hdf"));
+ mimeTypes.insert(pair<string, string>("help", "application/x-helpfile"));
+ mimeTypes.insert(pair<string, string>("hgl", "application/vnd.hp-hpgl"));
+ mimeTypes.insert(pair<string, string>("hh", "text/plain"));
+ mimeTypes.insert(pair<string, string>("hlb", "text/x-script"));
+ mimeTypes.insert(pair<string, string>("hlp", "application/hlp"));
+ mimeTypes.insert(pair<string, string>("hpg", "application/vnd.hp-hpgl"));
+ mimeTypes.insert(pair<string, string>("hpgl", "application/vnd.hp-hpgl"));
+ mimeTypes.insert(pair<string, string>("hqx", "application/binhex"));
+ mimeTypes.insert(pair<string, string>("hta", "application/hta"));
+ mimeTypes.insert(pair<string, string>("htc", "text/x-component"));
+ mimeTypes.insert(pair<string, string>("htm", "text/html"));
+ mimeTypes.insert(pair<string, string>("html", "text/html"));
+ mimeTypes.insert(pair<string, string>("htmls", "text/html"));
+ mimeTypes.insert(pair<string, string>("htt", "text/webviewhtml"));
+ mimeTypes.insert(pair<string, string>("htx", "text/html"));
+ mimeTypes.insert(pair<string, string>("ice", "x-conference/x-cooltalk"));
+ mimeTypes.insert(pair<string, string>("ico", "image/x-icon"));
+ mimeTypes.insert(pair<string, string>("idc", "text/plain"));
+ mimeTypes.insert(pair<string, string>("ief", "image/ief"));
+ mimeTypes.insert(pair<string, string>("iefs", "image/ief"));
+ mimeTypes.insert(pair<string, string>("iges", "application/iges"));
+ mimeTypes.insert(pair<string, string>("igs", "application/iges"));
+ mimeTypes.insert(pair<string, string>("igs", "model/iges"));
+ mimeTypes.insert(pair<string, string>("ima", "application/x-ima"));
+ mimeTypes.insert(pair<string, string>("imap", "application/x-httpd-imap"));
+ mimeTypes.insert(pair<string, string>("inf", "application/inf"));
+ mimeTypes.insert(pair<string, string>("ins", "application/x-internett-signup"));
+ mimeTypes.insert(pair<string, string>("ip", "application/x-ip2"));
+ mimeTypes.insert(pair<string, string>("isu", "video/x-isvideo"));
+ mimeTypes.insert(pair<string, string>("it", "audio/it"));
+ mimeTypes.insert(pair<string, string>("iv", "application/x-inventor"));
+ mimeTypes.insert(pair<string, string>("ivr", "i-world/i-vrml"));
+ mimeTypes.insert(pair<string, string>("ivy", "application/x-livescreen"));
+ mimeTypes.insert(pair<string, string>("jam", "audio/x-jam"));
+ mimeTypes.insert(pair<string, string>("jav", "text/x-java-source"));
+ mimeTypes.insert(pair<string, string>("java", "text/x-java-source"));
+ mimeTypes.insert(pair<string, string>("jcm", "application/x-java-commerce"));
+ mimeTypes.insert(pair<string, string>("jfif", "image/jpeg"));
+ mimeTypes.insert(pair<string, string>("jfif-tbnl", "image/jpeg"));
+ mimeTypes.insert(pair<string, string>("jpe", "image/jpeg"));
+ mimeTypes.insert(pair<string, string>("jpeg", "image/jpeg"));
+ mimeTypes.insert(pair<string, string>("jpg", "image/jpeg"));
+ mimeTypes.insert(pair<string, string>("jps", "image/x-jps"));
+ mimeTypes.insert(pair<string, string>("js", "application/javascript"));
+ mimeTypes.insert(pair<string, string>("json", "application/json"));
+ mimeTypes.insert(pair<string, string>("jut", "image/jutvision"));
+ mimeTypes.insert(pair<string, string>("kar", "music/x-karaoke"));
+ mimeTypes.insert(pair<string, string>("ksh", "application/x-ksh"));
+ mimeTypes.insert(pair<string, string>("ksh", "text/x-script.ksh"));
+ mimeTypes.insert(pair<string, string>("la", "audio/nspaudio"));
+ mimeTypes.insert(pair<string, string>("lam", "audio/x-liveaudio"));
+ mimeTypes.insert(pair<string, string>("latex", "application/x-latex"));
+ mimeTypes.insert(pair<string, string>("lha", "application/lha"));
+ mimeTypes.insert(pair<string, string>("lhx", "application/octet-stream"));
+ mimeTypes.insert(pair<string, string>("list", "text/plain"));
+ mimeTypes.insert(pair<string, string>("lma", "audio/nspaudio"));
+ mimeTypes.insert(pair<string, string>("log", "text/plain"));
+ mimeTypes.insert(pair<string, string>("lsp", "application/x-lisp"));
+ mimeTypes.insert(pair<string, string>("lst", "text/plain"));
+ mimeTypes.insert(pair<string, string>("lsx", "text/x-la-asf"));
+ mimeTypes.insert(pair<string, string>("ltx", "application/x-latex"));
+ mimeTypes.insert(pair<string, string>("lzh", "application/x-lzh"));
+ mimeTypes.insert(pair<string, string>("lzx", "application/lzx"));
+ mimeTypes.insert(pair<string, string>("m", "text/x-m"));
+ mimeTypes.insert(pair<string, string>("m1v", "video/mpeg"));
+ mimeTypes.insert(pair<string, string>("m2a", "audio/mpeg"));
+ mimeTypes.insert(pair<string, string>("m2v", "video/mpeg"));
+ mimeTypes.insert(pair<string, string>("m3u", "audio/x-mpequrl"));
+ mimeTypes.insert(pair<string, string>("man", "application/x-troff-man"));
+ mimeTypes.insert(pair<string, string>("map", "application/x-navimap"));
+ mimeTypes.insert(pair<string, string>("mar", "text/plain"));
+ mimeTypes.insert(pair<string, string>("mbd", "application/mbedlet"));
+ mimeTypes.insert(pair<string, string>("mc$", "application/x-magic-cap-package-1.0"));
+ mimeTypes.insert(pair<string, string>("mcd", "application/x-mathcad"));
+ mimeTypes.insert(pair<string, string>("mcf", "text/mcf"));
+ mimeTypes.insert(pair<string, string>("mcp", "application/netmc"));
+ mimeTypes.insert(pair<string, string>("me", "application/x-troff-me"));
+ mimeTypes.insert(pair<string, string>("mht", "message/rfc822"));
+ mimeTypes.insert(pair<string, string>("mhtml", "message/rfc822"));
+ mimeTypes.insert(pair<string, string>("mid", "audio/midi"));
+ mimeTypes.insert(pair<string, string>("midi", "audio/midi"));
+ mimeTypes.insert(pair<string, string>("mif", "application/x-mif"));
+ mimeTypes.insert(pair<string, string>("mime", "message/rfc822"));
+ mimeTypes.insert(pair<string, string>("mjf", "audio/x-vnd.audioexplosion.mjuicemediafile"));
+ mimeTypes.insert(pair<string, string>("mjpg", "video/x-motion-jpeg"));
+ mimeTypes.insert(pair<string, string>("mka", "audio/x-matroska"));
+ mimeTypes.insert(pair<string, string>("mkv", "video/x-matroska"));
+ mimeTypes.insert(pair<string, string>("mk3d", "video/x-matroska-3d"));
+ mimeTypes.insert(pair<string, string>("mm", "application/x-meme"));
+ mimeTypes.insert(pair<string, string>("mme", "application/base64"));
+ mimeTypes.insert(pair<string, string>("mod", "audio/mod"));
+ mimeTypes.insert(pair<string, string>("moov", "video/quicktime"));
+ mimeTypes.insert(pair<string, string>("mov", "video/quicktime"));
+ mimeTypes.insert(pair<string, string>("movie", "video/x-sgi-movie"));
+ mimeTypes.insert(pair<string, string>("mp2", "audio/mpeg"));
+ mimeTypes.insert(pair<string, string>("mp3", "audio/mpeg3"));
+ mimeTypes.insert(pair<string, string>("mp4", "video/mp4"));
+ mimeTypes.insert(pair<string, string>("mpa", "audio/mpeg"));
+ mimeTypes.insert(pair<string, string>("mpc", "application/x-project"));
+ mimeTypes.insert(pair<string, string>("mpe", "video/mpeg"));
+ mimeTypes.insert(pair<string, string>("mpeg", "video/mpeg"));
+ mimeTypes.insert(pair<string, string>("mpg", "video/mpeg"));
+ mimeTypes.insert(pair<string, string>("mpga", "audio/mpeg"));
+ mimeTypes.insert(pair<string, string>("mpp", "application/vnd.ms-project"));
+ mimeTypes.insert(pair<string, string>("mpt", "application/x-project"));
+ mimeTypes.insert(pair<string, string>("mpv", "application/x-project"));
+ mimeTypes.insert(pair<string, string>("mpx", "application/x-project"));
+ mimeTypes.insert(pair<string, string>("mrc", "application/marc"));
+ mimeTypes.insert(pair<string, string>("ms", "application/x-troff-ms"));
+ mimeTypes.insert(pair<string, string>("mv", "video/x-sgi-movie"));
+ mimeTypes.insert(pair<string, string>("my", "audio/make"));
+ mimeTypes.insert(pair<string, string>("mzz", "application/x-vnd.audioexplosion.mzz"));
+ mimeTypes.insert(pair<string, string>("nap", "image/naplps"));
+ mimeTypes.insert(pair<string, string>("naplps", "image/naplps"));
+ mimeTypes.insert(pair<string, string>("nc", "application/x-netcdf"));
+ mimeTypes.insert(pair<string, string>("ncm", "application/vnd.nokia.configuration-message"));
+ mimeTypes.insert(pair<string, string>("nfo", "text/xml"));
+ mimeTypes.insert(pair<string, string>("nif", "image/x-niff"));
+ mimeTypes.insert(pair<string, string>("niff", "image/x-niff"));
+ mimeTypes.insert(pair<string, string>("nix", "application/x-mix-transfer"));
+ mimeTypes.insert(pair<string, string>("nsc", "application/x-conference"));
+ mimeTypes.insert(pair<string, string>("nvd", "application/x-navidoc"));
+ mimeTypes.insert(pair<string, string>("o", "application/octet-stream"));
+ mimeTypes.insert(pair<string, string>("oda", "application/oda"));
+ mimeTypes.insert(pair<string, string>("ogg", "audio/ogg"));
+ mimeTypes.insert(pair<string, string>("omc", "application/x-omc"));
+ mimeTypes.insert(pair<string, string>("omcd", "application/x-omcdatamaker"));
+ mimeTypes.insert(pair<string, string>("omcr", "application/x-omcregerator"));
+ mimeTypes.insert(pair<string, string>("p", "text/x-pascal"));
+ mimeTypes.insert(pair<string, string>("p10", "application/pkcs10"));
+ mimeTypes.insert(pair<string, string>("p12", "application/pkcs-12"));
+ mimeTypes.insert(pair<string, string>("p7a", "application/x-pkcs7-signature"));
+ mimeTypes.insert(pair<string, string>("p7c", "application/pkcs7-mime"));
+ mimeTypes.insert(pair<string, string>("p7m", "application/pkcs7-mime"));
+ mimeTypes.insert(pair<string, string>("p7r", "application/x-pkcs7-certreqresp"));
+ mimeTypes.insert(pair<string, string>("p7s", "application/pkcs7-signature"));
+ mimeTypes.insert(pair<string, string>("part", "application/pro_eng"));
+ mimeTypes.insert(pair<string, string>("pas", "text/pascal"));
+ mimeTypes.insert(pair<string, string>("pbm", "image/x-portable-bitmap"));
+ mimeTypes.insert(pair<string, string>("pcl", "application/vnd.hp-pcl"));
+ mimeTypes.insert(pair<string, string>("pct", "image/x-pict"));
+ mimeTypes.insert(pair<string, string>("pcx", "image/x-pcx"));
+ mimeTypes.insert(pair<string, string>("pdb", "chemical/x-pdb"));
+ mimeTypes.insert(pair<string, string>("pdf", "application/pdf"));
+ mimeTypes.insert(pair<string, string>("pfunk", "audio/make.my.funk"));
+ mimeTypes.insert(pair<string, string>("pgm", "image/x-portable-greymap"));
+ mimeTypes.insert(pair<string, string>("pic", "image/pict"));
+ mimeTypes.insert(pair<string, string>("pict", "image/pict"));
+ mimeTypes.insert(pair<string, string>("pkg", "application/x-newton-compatible-pkg"));
+ mimeTypes.insert(pair<string, string>("pko", "application/vnd.ms-pki.pko"));
+ mimeTypes.insert(pair<string, string>("pl", "text/x-script.perl"));
+ mimeTypes.insert(pair<string, string>("plx", "application/x-pixclscript"));
+ mimeTypes.insert(pair<string, string>("pm", "text/x-script.perl-module"));
+ mimeTypes.insert(pair<string, string>("pm4", "application/x-pagemaker"));
+ mimeTypes.insert(pair<string, string>("pm5", "application/x-pagemaker"));
+ mimeTypes.insert(pair<string, string>("png", "image/png"));
+ mimeTypes.insert(pair<string, string>("pnm", "application/x-portable-anymap"));
+ mimeTypes.insert(pair<string, string>("pot", "application/vnd.ms-powerpoint"));
+ mimeTypes.insert(pair<string, string>("pov", "model/x-pov"));
+ mimeTypes.insert(pair<string, string>("ppa", "application/vnd.ms-powerpoint"));
+ mimeTypes.insert(pair<string, string>("ppm", "image/x-portable-pixmap"));
+ mimeTypes.insert(pair<string, string>("pps", "application/mspowerpoint"));
+ mimeTypes.insert(pair<string, string>("ppt", "application/mspowerpoint"));
+ mimeTypes.insert(pair<string, string>("ppz", "application/mspowerpoint"));
+ mimeTypes.insert(pair<string, string>("pre", "application/x-freelance"));
+ mimeTypes.insert(pair<string, string>("prt", "application/pro_eng"));
+ mimeTypes.insert(pair<string, string>("ps", "application/postscript"));
+ mimeTypes.insert(pair<string, string>("psd", "application/octet-stream"));
+ mimeTypes.insert(pair<string, string>("pvu", "paleovu/x-pv"));
+ mimeTypes.insert(pair<string, string>("pwz", "application/vnd.ms-powerpoint"));
+ mimeTypes.insert(pair<string, string>("py", "text/x-script.phyton"));
+ mimeTypes.insert(pair<string, string>("pyc", "applicaiton/x-bytecode.python"));
+ mimeTypes.insert(pair<string, string>("qcp", "audio/vnd.qcelp"));
+ mimeTypes.insert(pair<string, string>("qd3", "x-world/x-3dmf"));
+ mimeTypes.insert(pair<string, string>("qd3d", "x-world/x-3dmf"));
+ mimeTypes.insert(pair<string, string>("qif", "image/x-quicktime"));
+ mimeTypes.insert(pair<string, string>("qt", "video/quicktime"));
+ mimeTypes.insert(pair<string, string>("qtc", "video/x-qtc"));
+ mimeTypes.insert(pair<string, string>("qti", "image/x-quicktime"));
+ mimeTypes.insert(pair<string, string>("qtif", "image/x-quicktime"));
+ mimeTypes.insert(pair<string, string>("ra", "audio/x-realaudio"));
+ mimeTypes.insert(pair<string, string>("ram", "audio/x-pn-realaudio"));
+ mimeTypes.insert(pair<string, string>("ras", "image/cmu-raster"));
+ mimeTypes.insert(pair<string, string>("rast", "image/cmu-raster"));
+ mimeTypes.insert(pair<string, string>("rexx", "text/x-script.rexx"));
+ mimeTypes.insert(pair<string, string>("rf", "image/vnd.rn-realflash"));
+ mimeTypes.insert(pair<string, string>("rgb", "image/x-rgb"));
+ mimeTypes.insert(pair<string, string>("rm", "audio/x-pn-realaudio"));
+ mimeTypes.insert(pair<string, string>("rmi", "audio/mid"));
+ mimeTypes.insert(pair<string, string>("rmm", "audio/x-pn-realaudio"));
+ mimeTypes.insert(pair<string, string>("rmp", "audio/x-pn-realaudio"));
+ mimeTypes.insert(pair<string, string>("rng", "application/ringing-tones"));
+ mimeTypes.insert(pair<string, string>("rnx", "application/vnd.rn-realplayer"));
+ mimeTypes.insert(pair<string, string>("roff", "application/x-troff"));
+ mimeTypes.insert(pair<string, string>("rp", "image/vnd.rn-realpix"));
+ mimeTypes.insert(pair<string, string>("rpm", "audio/x-pn-realaudio-plugin"));
+ mimeTypes.insert(pair<string, string>("rt", "text/richtext"));
+ mimeTypes.insert(pair<string, string>("rtf", "text/richtext"));
+ mimeTypes.insert(pair<string, string>("rtx", "text/richtext"));
+ mimeTypes.insert(pair<string, string>("rv", "video/vnd.rn-realvideo"));
+ mimeTypes.insert(pair<string, string>("s", "text/x-asm"));
+ mimeTypes.insert(pair<string, string>("s3m", "audio/s3m"));
+ mimeTypes.insert(pair<string, string>("saveme", "application/octet-stream"));
+ mimeTypes.insert(pair<string, string>("sbk", "application/x-tbook"));
+ mimeTypes.insert(pair<string, string>("scm", "video/x-scm"));
+ mimeTypes.insert(pair<string, string>("sdml", "text/plain"));
+ mimeTypes.insert(pair<string, string>("sdp", "application/sdp"));
+ mimeTypes.insert(pair<string, string>("sdr", "application/sounder"));
+ mimeTypes.insert(pair<string, string>("sea", "application/sea"));
+ mimeTypes.insert(pair<string, string>("set", "application/set"));
+ mimeTypes.insert(pair<string, string>("sgm", "text/sgml"));
+ mimeTypes.insert(pair<string, string>("sgml", "text/sgml"));
+ mimeTypes.insert(pair<string, string>("sh", "text/x-script.sh"));
+ mimeTypes.insert(pair<string, string>("shar", "application/x-bsh"));
+ mimeTypes.insert(pair<string, string>("shtml", "text/html"));
+ mimeTypes.insert(pair<string, string>("shtml", "text/x-server-parsed-html"));
+ mimeTypes.insert(pair<string, string>("sid", "audio/x-psid"));
+ mimeTypes.insert(pair<string, string>("sit", "application/x-sit"));
+ mimeTypes.insert(pair<string, string>("sit", "application/x-stuffit"));
+ mimeTypes.insert(pair<string, string>("skd", "application/x-koan"));
+ mimeTypes.insert(pair<string, string>("skm", "application/x-koan"));
+ mimeTypes.insert(pair<string, string>("skp", "application/x-koan"));
+ mimeTypes.insert(pair<string, string>("skt", "application/x-koan"));
+ mimeTypes.insert(pair<string, string>("sl", "application/x-seelogo"));
+ mimeTypes.insert(pair<string, string>("smi", "application/smil"));
+ mimeTypes.insert(pair<string, string>("smil", "application/smil"));
+ mimeTypes.insert(pair<string, string>("snd", "audio/basic"));
+ mimeTypes.insert(pair<string, string>("sol", "application/solids"));
+ mimeTypes.insert(pair<string, string>("spc", "text/x-speech"));
+ mimeTypes.insert(pair<string, string>("spl", "application/futuresplash"));
+ mimeTypes.insert(pair<string, string>("spr", "application/x-sprite"));
+ mimeTypes.insert(pair<string, string>("sprite", "application/x-sprite"));
+ mimeTypes.insert(pair<string, string>("src", "application/x-wais-source"));
+ mimeTypes.insert(pair<string, string>("ssi", "text/x-server-parsed-html"));
+ mimeTypes.insert(pair<string, string>("ssm", "application/streamingmedia"));
+ mimeTypes.insert(pair<string, string>("sst", "application/vnd.ms-pki.certstore"));
+ mimeTypes.insert(pair<string, string>("step", "application/step"));
+ mimeTypes.insert(pair<string, string>("stl", "application/sla"));
+ mimeTypes.insert(pair<string, string>("stp", "application/step"));
+ mimeTypes.insert(pair<string, string>("sv4cpio", "application/x-sv4cpio"));
+ mimeTypes.insert(pair<string, string>("sv4crc", "application/x-sv4crc"));
+ mimeTypes.insert(pair<string, string>("svf", "image/vnd.dwg"));
+ mimeTypes.insert(pair<string, string>("svr", "application/x-world"));
+ mimeTypes.insert(pair<string, string>("swf", "application/x-shockwave-flash"));
+ mimeTypes.insert(pair<string, string>("t", "application/x-troff"));
+ mimeTypes.insert(pair<string, string>("talk", "text/x-speech"));
+ mimeTypes.insert(pair<string, string>("tar", "application/x-tar"));
+ mimeTypes.insert(pair<string, string>("tbk", "application/toolbook"));
+ mimeTypes.insert(pair<string, string>("tcl", "text/x-script.tcl"));
+ mimeTypes.insert(pair<string, string>("tcsh", "text/x-script.tcsh"));
+ mimeTypes.insert(pair<string, string>("tex", "application/x-tex"));
+ mimeTypes.insert(pair<string, string>("texi", "application/x-texinfo"));
+ mimeTypes.insert(pair<string, string>("texinfo", "application/x-texinfo"));
+ mimeTypes.insert(pair<string, string>("text", "text/plain"));
+ mimeTypes.insert(pair<string, string>("tgz", "application/x-compressed"));
+ mimeTypes.insert(pair<string, string>("tif", "image/tiff"));
+ mimeTypes.insert(pair<string, string>("tiff", "image/tiff"));
+ mimeTypes.insert(pair<string, string>("tr", "application/x-troff"));
+ mimeTypes.insert(pair<string, string>("ts", "video/mp2t"));
+ mimeTypes.insert(pair<string, string>("tsi", "audio/tsp-audio"));
+ mimeTypes.insert(pair<string, string>("tsp", "audio/tsplayer"));
+ mimeTypes.insert(pair<string, string>("tsv", "text/tab-separated-values"));
+ mimeTypes.insert(pair<string, string>("turbot", "image/florian"));
+ mimeTypes.insert(pair<string, string>("txt", "text/plain"));
+ mimeTypes.insert(pair<string, string>("uil", "text/x-uil"));
+ mimeTypes.insert(pair<string, string>("uni", "text/uri-list"));
+ mimeTypes.insert(pair<string, string>("unis", "text/uri-list"));
+ mimeTypes.insert(pair<string, string>("unv", "application/i-deas"));
+ mimeTypes.insert(pair<string, string>("uri", "text/uri-list"));
+ mimeTypes.insert(pair<string, string>("uris", "text/uri-list"));
+ mimeTypes.insert(pair<string, string>("ustar", "application/x-ustar"));
+ mimeTypes.insert(pair<string, string>("uu", "text/x-uuencode"));
+ mimeTypes.insert(pair<string, string>("uue", "text/x-uuencode"));
+ mimeTypes.insert(pair<string, string>("vcd", "application/x-cdlink"));
+ mimeTypes.insert(pair<string, string>("vcs", "text/x-vcalendar"));
+ mimeTypes.insert(pair<string, string>("vda", "application/vda"));
+ mimeTypes.insert(pair<string, string>("vdo", "video/vdo"));
+ mimeTypes.insert(pair<string, string>("vew", "application/groupwise"));
+ mimeTypes.insert(pair<string, string>("viv", "video/vivo"));
+ mimeTypes.insert(pair<string, string>("vivo", "video/vivo"));
+ mimeTypes.insert(pair<string, string>("vmd", "application/vocaltec-media-desc"));
+ mimeTypes.insert(pair<string, string>("vmf", "application/vocaltec-media-file"));
+ mimeTypes.insert(pair<string, string>("voc", "audio/voc"));
+ mimeTypes.insert(pair<string, string>("vos", "video/vosaic"));
+ mimeTypes.insert(pair<string, string>("vox", "audio/voxware"));
+ mimeTypes.insert(pair<string, string>("vqe", "audio/x-twinvq-plugin"));
+ mimeTypes.insert(pair<string, string>("vqf", "audio/x-twinvq"));
+ mimeTypes.insert(pair<string, string>("vql", "audio/x-twinvq-plugin"));
+ mimeTypes.insert(pair<string, string>("vrml", "application/x-vrml"));
+ mimeTypes.insert(pair<string, string>("vrt", "x-world/x-vrt"));
+ mimeTypes.insert(pair<string, string>("vsd", "application/x-visio"));
+ mimeTypes.insert(pair<string, string>("vst", "application/x-visio"));
+ mimeTypes.insert(pair<string, string>("vsw", "application/x-visio"));
+ mimeTypes.insert(pair<string, string>("w60", "application/wordperfect6.0"));
+ mimeTypes.insert(pair<string, string>("w61", "application/wordperfect6.1"));
+ mimeTypes.insert(pair<string, string>("w6w", "application/msword"));
+ mimeTypes.insert(pair<string, string>("wav", "audio/wav"));
+ mimeTypes.insert(pair<string, string>("wb1", "application/x-qpro"));
+ mimeTypes.insert(pair<string, string>("wbmp", "image/vnd.wap.wbmp"));
+ mimeTypes.insert(pair<string, string>("web", "application/vnd.xara"));
+ mimeTypes.insert(pair<string, string>("wiz", "application/msword"));
+ mimeTypes.insert(pair<string, string>("wk1", "application/x-123"));
+ mimeTypes.insert(pair<string, string>("wma", "audio/x-ms-wma"));
+ mimeTypes.insert(pair<string, string>("wmf", "windows/metafile"));
+ mimeTypes.insert(pair<string, string>("wml", "text/vnd.wap.wml"));
+ mimeTypes.insert(pair<string, string>("wmlc", "application/vnd.wap.wmlc"));
+ mimeTypes.insert(pair<string, string>("wmls", "text/vnd.wap.wmlscript"));
+ mimeTypes.insert(pair<string, string>("wmlsc", "application/vnd.wap.wmlscriptc"));
+ mimeTypes.insert(pair<string, string>("wmv", "video/x-ms-wmv"));
+ mimeTypes.insert(pair<string, string>("word", "application/msword"));
+ mimeTypes.insert(pair<string, string>("wp", "application/wordperfect"));
+ mimeTypes.insert(pair<string, string>("wp5", "application/wordperfect"));
+ mimeTypes.insert(pair<string, string>("wp6", "application/wordperfect"));
+ mimeTypes.insert(pair<string, string>("wpd", "application/wordperfect"));
+ mimeTypes.insert(pair<string, string>("wq1", "application/x-lotus"));
+ mimeTypes.insert(pair<string, string>("wri", "application/mswrite"));
+ mimeTypes.insert(pair<string, string>("wrl", "model/vrml"));
+ mimeTypes.insert(pair<string, string>("wrz", "model/vrml"));
+ mimeTypes.insert(pair<string, string>("wsc", "text/scriplet"));
+ mimeTypes.insert(pair<string, string>("wsrc", "application/x-wais-source"));
+ mimeTypes.insert(pair<string, string>("wtk", "application/x-wintalk"));
+ mimeTypes.insert(pair<string, string>("xbm", "image/xbm"));
+ mimeTypes.insert(pair<string, string>("xdr", "video/x-amt-demorun"));
+ mimeTypes.insert(pair<string, string>("xgz", "xgl/drawing"));
+ mimeTypes.insert(pair<string, string>("xif", "image/vnd.xiff"));
+ mimeTypes.insert(pair<string, string>("xl", "application/excel"));
+ mimeTypes.insert(pair<string, string>("xla", "application/excel"));
+ mimeTypes.insert(pair<string, string>("xlb", "application/excel"));
+ mimeTypes.insert(pair<string, string>("xlc", "application/excel"));
+ mimeTypes.insert(pair<string, string>("xld", "application/excel"));
+ mimeTypes.insert(pair<string, string>("xlk", "application/excel"));
+ mimeTypes.insert(pair<string, string>("xll", "application/excel"));
+ mimeTypes.insert(pair<string, string>("xlm", "application/excel"));
+ mimeTypes.insert(pair<string, string>("xls", "application/excel"));
+ mimeTypes.insert(pair<string, string>("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"));
+ mimeTypes.insert(pair<string, string>("xlt", "application/excel"));
+ mimeTypes.insert(pair<string, string>("xlv", "application/excel"));
+ mimeTypes.insert(pair<string, string>("xlw", "application/excel"));
+ mimeTypes.insert(pair<string, string>("xm", "audio/xm"));
+ mimeTypes.insert(pair<string, string>("xml", "text/xml"));
+ mimeTypes.insert(pair<string, string>("xmz", "xgl/movie"));
+ mimeTypes.insert(pair<string, string>("xpix", "application/x-vnd.ls-xpix"));
+ mimeTypes.insert(pair<string, string>("xpm", "image/xpm"));
+ mimeTypes.insert(pair<string, string>("x-png", "image/png"));
+ mimeTypes.insert(pair<string, string>("xsr", "video/x-amt-showrun"));
+ mimeTypes.insert(pair<string, string>("xvid", "video/x-msvideo"));
+ mimeTypes.insert(pair<string, string>("xwd", "image/x-xwd"));
+ mimeTypes.insert(pair<string, string>("xyz", "chemical/x-pdb"));
+ mimeTypes.insert(pair<string, string>("z", "application/x-compressed"));
+ mimeTypes.insert(pair<string, string>("zip", "application/zip"));
+ mimeTypes.insert(pair<string, string>("zoo", "application/octet-stream"));
+ mimeTypes.insert(pair<string, string>("zsh", "text/x-script.zsh"));
+
+ return mimeTypes;
+}
+
+map<string, string> CMime::m_mimetypes = fillMimeTypes();
+
+string CMime::GetMimeType(const string &extension)
+{
+ if (extension.empty())
+ return "";
+
+ string ext = extension;
+ size_t posNotPoint = ext.find_first_not_of('.');
+ if (posNotPoint != string::npos && posNotPoint > 0)
+ ext = extension.substr(posNotPoint);
+ transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
+
+ map<string, string>::const_iterator it = m_mimetypes.find(ext);
+ if (it != m_mimetypes.end())
+ return it->second;
+
+ return "";
+}
+
+string CMime::GetMimeType(const CFileItem &item)
+{
+ CStdString path = item.GetPath();
+ if (item.HasVideoInfoTag() && !item.GetVideoInfoTag()->GetPath().empty())
+ path = item.GetVideoInfoTag()->GetPath();
+ else if (item.HasMusicInfoTag() && !item.GetMusicInfoTag()->GetURL().empty())
+ path = item.GetMusicInfoTag()->GetURL();
+
+ return GetMimeType(URIUtils::GetExtension(path));
+}
+
+string CMime::GetMimeType(const CURL &url, bool lookup)
+{
+
+ std::string strMimeType;
+
+ if( url.IsProtocol("shout") || url.IsProtocol("http") || url.IsProtocol("https"))
+ {
+ // If lookup is false, bail out early to leave mime type empty
+ if (!lookup)
+ return strMimeType;
+
+ CStdString strmime;
+ XFILE::CCurlFile::GetMimeType(url, strmime);
+
+ // try to get mime-type again but with an NSPlayer User-Agent
+ // in order for server to provide correct mime-type. Allows us
+ // to properly detect an MMS stream
+ if (StringUtils::StartsWithNoCase(strmime, "video/x-ms-"))
+ XFILE::CCurlFile::GetMimeType(url, strmime, "NSPlayer/11.00.6001.7000");
+
+ // make sure there are no options set in mime-type
+ // mime-type can look like "video/x-ms-asf ; charset=utf8"
+ size_t i = strmime.find(';');
+ if(i != std::string::npos)
+ strmime.erase(i, strmime.length() - i);
+ StringUtils::Trim(strmime);
+ strMimeType = strmime;
+ }
+ else
+ strMimeType = GetMimeType(url.GetFileType());
+
+ // if it's still empty set to an unknown type
+ if (strMimeType.empty())
+ strMimeType = "application/octet-stream";
+
+ return strMimeType;
+}
+
+CMime::EFileType CMime::GetFileTypeFromMime(const std::string& mimeType)
+{
+ // based on http://mimesniff.spec.whatwg.org/
+
+ std::string type, subtype;
+ if (!parseMimeType(mimeType, type, subtype))
+ return FileTypeUnknown;
+
+ if (type == "application")
+ {
+ if (subtype == "zip")
+ return FileTypeZip;
+ if (subtype == "x-gzip")
+ return FileTypeGZip;
+ if (subtype == "x-rar-compressed")
+ return FileTypeRar;
+
+ if (subtype == "xml")
+ return FileTypeXml;
+ }
+ else if (type == "text")
+ {
+ if (subtype == "xml")
+ return FileTypeXml;
+ if (subtype == "html")
+ return FileTypeHtml;
+ if (subtype == "plain")
+ return FileTypePlainText;
+ }
+ else if (type == "image")
+ {
+ if (subtype == "bmp")
+ return FileTypeBmp;
+ if (subtype == "gif")
+ return FileTypeGif;
+ if (subtype == "png")
+ return FileTypePng;
+ if (subtype == "jpeg" || subtype == "pjpeg")
+ return FileTypeJpeg;
+ }
+
+ if (StringUtils::EndsWith(subtype, "+zip"))
+ return FileTypeZip;
+ if (StringUtils::EndsWith(subtype, "+xml"))
+ return FileTypeXml;
+
+ return FileTypeUnknown;
+}
+
+CMime::EFileType CMime::GetFileTypeFromContent(const std::string& fileContent)
+{
+ // based on http://mimesniff.spec.whatwg.org/#matching-a-mime-type-pattern
+
+ const size_t len = fileContent.length();
+ if (len < 2)
+ return FileTypeUnknown;
+
+ const unsigned char* const b = (const unsigned char*)fileContent.c_str();
+
+ // TODO: add detection for text types
+
+ // check image types
+ if (b[0] == 'B' && b[1] == 'M')
+ return FileTypeBmp;
+ if (len >= 6 && b[0] == 'G' && b[1] == 'I' && b[2] == 'F' && b[3] == '8' && (b[4] == '7' || b[4] == '9') && b[5] == 'a')
+ return FileTypeGif;
+ if (len >= 8 && b[0] == 0x89 && b[1] == 'P' && b[2] == 'N' && b[3] == 'G' && b[4] == 0x0D && b[5] == 0x0A && b[6] == 0x1A && b[7] == 0x0A)
+ return FileTypePng;
+ if (len >= 3 && b[0] == 0xFF && b[1] == 0xD8 && b[2] == 0xFF)
+ return FileTypeJpeg;
+
+ // check archive types
+ if (len >= 3 && b[0] == 0x1F && b[1] == 0x8B && b[2] == 0x08)
+ return FileTypeGZip;
+ if (len >= 4 && b[0] == 'P' && b[1] == 'K' && b[2] == 0x03 && b[3] == 0x04)
+ return FileTypeZip;
+ if (len >= 7 && b[0] == 'R' && b[1] == 'a' && b[2] == 'r' && b[3] == ' ' && b[4] == 0x1A && b[5] == 0x07 && b[6] == 0x00)
+ return FileTypeRar;
+
+ // TODO: add detection for other types if required
+
+ return FileTypeUnknown;
+}
+
+bool CMime::parseMimeType(const std::string& mimeType, std::string& type, std::string& subtype)
+{
+ static const char* const whitespaceChars = "\x09\x0A\x0C\x0D\x20"; // tab, LF, FF, CR and space
+
+ type.clear();
+ subtype.clear();
+
+ const size_t slashPos = mimeType.find('/');
+ if (slashPos == std::string::npos)
+ return false;
+
+ type.assign(mimeType, 0, slashPos);
+ subtype.assign(mimeType, slashPos + 1, std::string::npos);
+
+ const size_t semicolonPos = subtype.find(';');
+ if (semicolonPos != std::string::npos)
+ subtype.erase(semicolonPos);
+
+ StringUtils::Trim(type, whitespaceChars);
+ StringUtils::Trim(subtype, whitespaceChars);
+
+ if (type.empty() || subtype.empty())
+ {
+ type.clear();
+ subtype.clear();
+ return false;
+ }
+
+ StringUtils::ToLower(type);
+ StringUtils::ToLower(subtype);
+
+ return true;
+}
diff --git a/src/utils/Mime.h b/src/utils/Mime.h
new file mode 100644
index 0000000000..330b01f55e
--- /dev/null
+++ b/src/utils/Mime.h
@@ -0,0 +1,56 @@
+#pragma once
+/*
+ * Copyright (C) 2012-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string>
+#include <map>
+
+class CURL;
+
+class CFileItem;
+
+class CMime
+{
+public:
+ static std::string GetMimeType(const std::string &extension);
+ static std::string GetMimeType(const CFileItem &item);
+ static std::string GetMimeType(const CURL &url, bool lookup = true);
+
+ enum EFileType
+ {
+ FileTypeUnknown = 0,
+ FileTypeHtml,
+ FileTypeXml,
+ FileTypePlainText,
+ FileTypeZip,
+ FileTypeGZip,
+ FileTypeRar,
+ FileTypeBmp,
+ FileTypeGif,
+ FileTypePng,
+ FileTypeJpeg,
+ };
+ static EFileType GetFileTypeFromMime(const std::string& mimeType);
+ static EFileType GetFileTypeFromContent(const std::string& fileContent);
+ static bool parseMimeType(const std::string& mimeType, std::string& type, std::string& subtype);
+
+private:
+ static std::map<std::string, std::string> m_mimetypes;
+};
diff --git a/src/utils/Observer.cpp b/src/utils/Observer.cpp
new file mode 100644
index 0000000000..9d3ef14a99
--- /dev/null
+++ b/src/utils/Observer.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "Application.h"
+#include "Observer.h"
+#include "threads/SingleLock.h"
+#include "utils/JobManager.h"
+
+using namespace std;
+
+Observer::~Observer(void)
+{
+ StopObserving();
+}
+
+void Observer::StopObserving(void)
+{
+ CSingleLock lock(m_obsCritSection);
+ std::vector<Observable *> observables = m_observables;
+ for (unsigned int iObsPtr = 0; iObsPtr < observables.size(); iObsPtr++)
+ observables.at(iObsPtr)->UnregisterObserver(this);
+}
+
+bool Observer::IsObserving(const Observable &obs) const
+{
+ CSingleLock lock(m_obsCritSection);
+ return find(m_observables.begin(), m_observables.end(), &obs) != m_observables.end();
+}
+
+void Observer::RegisterObservable(Observable *obs)
+{
+ CSingleLock lock(m_obsCritSection);
+ if (!IsObserving(*obs))
+ m_observables.push_back(obs);
+}
+
+void Observer::UnregisterObservable(Observable *obs)
+{
+ CSingleLock lock(m_obsCritSection);
+ vector<Observable *>::iterator it = find(m_observables.begin(), m_observables.end(), obs);
+ if (it != m_observables.end())
+ m_observables.erase(it);
+}
+
+Observable::Observable() :
+ m_bObservableChanged(false)
+{
+}
+
+Observable::~Observable()
+{
+ StopObserver();
+}
+
+Observable &Observable::operator=(const Observable &observable)
+{
+ CSingleLock lock(m_obsCritSection);
+
+ m_bObservableChanged = observable.m_bObservableChanged;
+ m_observers.clear();
+ for (unsigned int iObsPtr = 0; iObsPtr < observable.m_observers.size(); iObsPtr++)
+ m_observers.push_back(observable.m_observers.at(iObsPtr));
+
+ return *this;
+}
+
+void Observable::StopObserver(void)
+{
+ CSingleLock lock(m_obsCritSection);
+ std::vector<Observer *> observers = m_observers;
+ for (unsigned int iObsPtr = 0; iObsPtr < observers.size(); iObsPtr++)
+ observers.at(iObsPtr)->UnregisterObservable(this);
+}
+
+bool Observable::IsObserving(const Observer &obs) const
+{
+ CSingleLock lock(m_obsCritSection);
+ return find(m_observers.begin(), m_observers.end(), &obs) != m_observers.end();
+}
+
+void Observable::RegisterObserver(Observer *obs)
+{
+ CSingleLock lock(m_obsCritSection);
+ if (!IsObserving(*obs))
+ {
+ m_observers.push_back(obs);
+ obs->RegisterObservable(this);
+ }
+}
+
+void Observable::UnregisterObserver(Observer *obs)
+{
+ CSingleLock lock(m_obsCritSection);
+ vector<Observer *>::iterator it = find(m_observers.begin(), m_observers.end(), obs);
+ if (it != m_observers.end())
+ {
+ obs->UnregisterObservable(this);
+ m_observers.erase(it);
+ }
+}
+
+void Observable::NotifyObservers(const ObservableMessage message /* = ObservableMessageNone */)
+{
+ bool bNotify(false);
+ {
+ CSingleLock lock(m_obsCritSection);
+ if (m_bObservableChanged && !g_application.m_bStop)
+ bNotify = true;
+ m_bObservableChanged = false;
+ }
+
+ if (bNotify)
+ SendMessage(*this, message);
+}
+
+void Observable::SetChanged(bool SetTo)
+{
+ CSingleLock lock(m_obsCritSection);
+ m_bObservableChanged = SetTo;
+}
+
+void Observable::SendMessage(const Observable& obs, const ObservableMessage message)
+{
+ CSingleLock lock(obs.m_obsCritSection);
+ for(int ptr = obs.m_observers.size() - 1; ptr >= 0; ptr--)
+ {
+ if (ptr < (int)obs.m_observers.size())
+ {
+ Observer *observer = obs.m_observers.at(ptr);
+ if (observer)
+ {
+ lock.Leave();
+ observer->Notify(obs, message);
+ lock.Enter();
+ }
+ }
+ }
+}
diff --git a/src/utils/Observer.h b/src/utils/Observer.h
new file mode 100644
index 0000000000..3ac032a056
--- /dev/null
+++ b/src/utils/Observer.h
@@ -0,0 +1,146 @@
+#pragma once
+
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "threads/CriticalSection.h"
+#include <vector>
+
+class Observable;
+class ObservableMessageJob;
+
+typedef enum
+{
+ ObservableMessageNone,
+ ObservableMessageCurrentItem,
+ ObservableMessageAddons,
+ ObservableMessageEpg,
+ ObservableMessageEpgContainer,
+ ObservableMessageEpgActiveItem,
+ ObservableMessageChannelGroup,
+ ObservableMessageChannelGroupReset,
+ ObservableMessageTimers,
+ ObservableMessageTimersReset,
+ ObservableMessageRecordings,
+ ObservableMessagePeripheralsChanged,
+ ObservableMessageManagerStateChanged
+} ObservableMessage;
+
+class Observer
+{
+ friend class Observable;
+
+public:
+ Observer(void) {};
+ virtual ~Observer(void);
+
+ /*!
+ * @brief Remove this observer from all observables.
+ */
+ virtual void StopObserving(void);
+
+ /*!
+ * @brief Check whether this observer is observing an observable.
+ * @param obs The observable to check.
+ * @return True if this observer is observing the given observable, false otherwise.
+ */
+ virtual bool IsObserving(const Observable &obs) const;
+
+ /*!
+ * @brief Process a message from an observable.
+ * @param obs The observable that sends the message.
+ * @param msg The message.
+ */
+ virtual void Notify(const Observable &obs, const ObservableMessage msg) = 0;
+
+protected:
+ /*!
+ * @brief Callback to register an observable.
+ * @param obs The observable to register.
+ */
+ virtual void RegisterObservable(Observable *obs);
+
+ /*!
+ * @brief Callback to unregister an observable.
+ * @param obs The observable to unregister.
+ */
+ virtual void UnregisterObservable(Observable *obs);
+
+ std::vector<Observable *> m_observables; /*!< all observables that are watched */
+ CCriticalSection m_obsCritSection; /*!< mutex */
+};
+
+class Observable
+{
+ friend class ObservableMessageJob;
+
+public:
+ Observable();
+ virtual ~Observable();
+ virtual Observable &operator=(const Observable &observable);
+
+ /*!
+ * @brief Remove this observable from all observers.
+ */
+ virtual void StopObserver(void);
+
+ /*!
+ * @brief Register an observer.
+ * @param obs The observer to register.
+ */
+ virtual void RegisterObserver(Observer *obs);
+
+ /*!
+ * @brief Unregister an observer.
+ * @param obs The observer to unregister.
+ */
+ virtual void UnregisterObserver(Observer *obs);
+
+ /*!
+ * @brief Send a message to all observers when m_bObservableChanged is true.
+ * @param message The message to send.
+ */
+ virtual void NotifyObservers(const ObservableMessage message = ObservableMessageNone);
+
+ /*!
+ * @brief Mark an observable changed.
+ * @param bSetTo True to mark the observable changed, false to mark it as unchanged.
+ */
+ virtual void SetChanged(bool bSetTo = true);
+
+ /*!
+ * @brief Check whether this observable is being observed by an observer.
+ * @param obs The observer to check.
+ * @return True if this observable is being observed by the given observer, false otherwise.
+ */
+ virtual bool IsObserving(const Observer &obs) const;
+
+protected:
+ /*!
+ * @brief Send a message to all observer when m_bObservableChanged is true.
+ * @param obs The observer that sends the message.
+ * @param message The message to send.
+ */
+ static void SendMessage(const Observable& obs, const ObservableMessage message);
+
+ bool m_bObservableChanged; /*!< true when the observable is marked as changed, false otherwise */
+ std::vector<Observer *> m_observers; /*!< all observers */
+ CCriticalSection m_obsCritSection; /*!< mutex */
+};
diff --git a/src/utils/POUtils.cpp b/src/utils/POUtils.cpp
new file mode 100644
index 0000000000..ce9ea3b516
--- /dev/null
+++ b/src/utils/POUtils.cpp
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2012-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/POUtils.h"
+#include "URL.h"
+#include "filesystem/File.h"
+#include "utils/log.h"
+#include <stdlib.h>
+
+CPODocument::CPODocument()
+{
+ m_CursorPos = 0;
+ m_nextEntryPos = 0;
+ m_POfilelength = 0;
+ m_Entry.msgStrPlural.clear();
+ m_Entry.msgStrPlural.resize(1);
+}
+
+CPODocument::~CPODocument() {}
+
+bool CPODocument::LoadFile(const std::string &pofilename)
+{
+ CURL poFileUrl(pofilename);
+ if (!XFILE::CFile::Exists(poFileUrl))
+ return false;
+
+ XFILE::CFile file;
+ XFILE::auto_buffer buf;
+ if (file.LoadFile(poFileUrl, buf) < 18) // at least a size of a minimalistic header
+ {
+ CLog::Log(LOGERROR, "%s: can't load file \"%s\" or file is too small", __FUNCTION__, pofilename.c_str());
+ return false;
+ }
+
+ m_strBuffer = '\n';
+ m_strBuffer.append(buf.get(), buf.size());
+ buf.clear();
+
+ ConvertLineEnds(pofilename);
+
+ // we make sure, to have an LF at the end of buffer
+ if (*m_strBuffer.rbegin() != '\n')
+ {
+ m_strBuffer += "\n";
+ }
+
+ m_POfilelength = m_strBuffer.size();
+
+ if (GetNextEntry() && m_Entry.Type == MSGID_FOUND)
+ return true;
+
+ CLog::Log(LOGERROR, "POParser: unable to read PO file header from file: %s", pofilename.c_str());
+ return false;
+}
+
+bool CPODocument::GetNextEntry()
+{
+ do
+ {
+ // if we don't find LFLF, we reached the end of the buffer and the last entry to check
+ // we indicate this with setting m_nextEntryPos to the end of the buffer
+ if ((m_nextEntryPos = m_strBuffer.find("\n\n", m_CursorPos)) == std::string::npos)
+ m_nextEntryPos = m_POfilelength-1;
+
+ // now we read the actual entry into a temp string for further processing
+ m_Entry.Content.assign(m_strBuffer, m_CursorPos, m_nextEntryPos - m_CursorPos +1);
+ m_CursorPos = m_nextEntryPos+1; // jump cursor to the second LF character
+
+ if (FindLineStart ("\nmsgid ", m_Entry.msgID.Pos))
+ {
+ if (FindLineStart ("\nmsgctxt \"#", m_Entry.xIDPos) && ParseNumID())
+ {
+ m_Entry.Type = ID_FOUND; // we found an entry with a valid numeric id
+ return true;
+ }
+
+ size_t plurPos;
+ if (FindLineStart ("\nmsgid_plural ", plurPos))
+ {
+ m_Entry.Type = MSGID_PLURAL_FOUND; // we found a pluralized entry
+ return true;
+ }
+
+ m_Entry.Type = MSGID_FOUND; // we found a normal entry, with no numeric id
+ return true;
+ }
+ }
+ while (m_nextEntryPos != m_POfilelength-1);
+ // we reached the end of buffer AND we have not found a valid entry
+
+ return false;
+}
+
+void CPODocument::ParseEntry(bool bisSourceLang)
+{
+ if (bisSourceLang)
+ {
+ if (m_Entry.Type == ID_FOUND)
+ GetString(m_Entry.msgID);
+ else
+ m_Entry.msgID.Str.clear();
+ return;
+ }
+
+ if (m_Entry.Type != ID_FOUND)
+ {
+ GetString(m_Entry.msgID);
+ if (FindLineStart ("\nmsgctxt ", m_Entry.msgCtxt.Pos))
+ GetString(m_Entry.msgCtxt);
+ else
+ m_Entry.msgCtxt.Str.clear();
+ }
+
+ if (m_Entry.Type != MSGID_PLURAL_FOUND)
+ {
+ if (FindLineStart ("\nmsgstr ", m_Entry.msgStr.Pos))
+ {
+ GetString(m_Entry.msgStr);
+ GetString(m_Entry.msgID);
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "POParser: missing msgstr line in entry. Failed entry: %s",
+ m_Entry.Content.c_str());
+ m_Entry.msgStr.Str.clear();
+ }
+ return;
+ }
+
+ // We found a plural form entry. We read it into a vector of CStrEntry types
+ m_Entry.msgStrPlural.clear();
+ std::string strPattern = "\nmsgstr[0] ";
+ CStrEntry strEntry;
+
+ for (int n=0; n<7 ; n++)
+ {
+ strPattern[8] = static_cast<char>(n+'0');
+ if (FindLineStart (strPattern, strEntry.Pos))
+ {
+ GetString(strEntry);
+ if (strEntry.Str.empty())
+ break;
+ m_Entry.msgStrPlural.push_back(strEntry);
+ }
+ else
+ break;
+ }
+
+ if (m_Entry.msgStrPlural.size() == 0)
+ {
+ CLog::Log(LOGERROR, "POParser: msgstr[] plural lines have zero valid strings. "
+ "Failed entry: %s", m_Entry.Content.c_str());
+ m_Entry.msgStrPlural.resize(1); // Put 1 element with an empty string into the vector
+ }
+
+ return;
+}
+
+const std::string& CPODocument::GetPlurMsgstr(size_t plural) const
+{
+ if (m_Entry.msgStrPlural.size() < plural+1)
+ {
+ CLog::Log(LOGERROR, "POParser: msgstr[%i] plural field requested, but not found in PO file. "
+ "Failed entry: %s", static_cast<int>(plural), m_Entry.Content.c_str());
+ plural = m_Entry.msgStrPlural.size()-1;
+ }
+ return m_Entry.msgStrPlural[plural].Str;
+}
+
+std::string CPODocument::UnescapeString(const std::string &strInput)
+{
+ std::string strOutput;
+ if (strInput.empty())
+ return strOutput;
+
+ char oescchar;
+ strOutput.reserve(strInput.size());
+ std::string::const_iterator it = strInput.begin();
+ while (it < strInput.end())
+ {
+ oescchar = *it++;
+ if (oescchar == '\\')
+ {
+ if (it == strInput.end())
+ {
+ CLog::Log(LOGERROR,
+ "POParser: warning, unhandled escape character "
+ "at line-end. Problematic entry: %s",
+ m_Entry.Content.c_str());
+ break;
+ }
+ switch (*it++)
+ {
+ case 'a': oescchar = '\a'; break;
+ case 'b': oescchar = '\b'; break;
+ case 'v': oescchar = '\v'; break;
+ case 'n': oescchar = '\n'; break;
+ case 't': oescchar = '\t'; break;
+ case 'r': oescchar = '\r'; break;
+ case '"': oescchar = '"' ; break;
+ case '0': oescchar = '\0'; break;
+ case 'f': oescchar = '\f'; break;
+ case '?': oescchar = '\?'; break;
+ case '\'': oescchar = '\''; break;
+ case '\\': oescchar = '\\'; break;
+
+ default:
+ {
+ CLog::Log(LOGERROR,
+ "POParser: warning, unhandled escape character. Problematic entry: %s",
+ m_Entry.Content.c_str());
+ continue;
+ }
+ }
+ }
+ strOutput.push_back(oescchar);
+ }
+ return strOutput;
+}
+
+bool CPODocument::FindLineStart(const std::string &strToFind, size_t &FoundPos)
+{
+
+ FoundPos = m_Entry.Content.find(strToFind);
+
+ if (FoundPos == std::string::npos || FoundPos + strToFind.size() + 2 > m_Entry.Content.size())
+ return false; // if we don't find the string or if we don't have at least one char after it
+
+ FoundPos += strToFind.size(); // to set the pos marker to the exact start of the real data
+ return true;
+}
+
+bool CPODocument::ParseNumID()
+{
+ if (isdigit(m_Entry.Content.at(m_Entry.xIDPos))) // verify if the first char is digit
+ {
+ // we check for the numeric id for the fist 10 chars (uint32)
+ m_Entry.xID = strtol(&m_Entry.Content[m_Entry.xIDPos], NULL, 10);
+ return true;
+ }
+
+ CLog::Log(LOGERROR, "POParser: found numeric id descriptor, but no valid id can be read, "
+ "entry was handled as normal msgid entry");
+ CLog::Log(LOGERROR, "POParser: The problematic entry: %s",
+ m_Entry.Content.c_str());
+ return false;
+}
+
+void CPODocument::GetString(CStrEntry &strEntry)
+{
+ size_t nextLFPos;
+ size_t startPos = strEntry.Pos;
+ strEntry.Str.clear();
+
+ while (startPos < m_Entry.Content.size())
+ {
+ nextLFPos = m_Entry.Content.find("\n", startPos);
+ if (nextLFPos == std::string::npos)
+ nextLFPos = m_Entry.Content.size();
+
+ // check syntax, if it really is a valid quoted string line
+ if (nextLFPos-startPos < 2 || m_Entry.Content[startPos] != '\"' ||
+ m_Entry.Content[nextLFPos-1] != '\"')
+ break;
+
+ strEntry.Str.append(m_Entry.Content, startPos+1, nextLFPos-2-startPos);
+ startPos = nextLFPos+1;
+ }
+
+ strEntry.Str = UnescapeString(strEntry.Str);
+}
+
+void CPODocument::ConvertLineEnds(const std::string &filename)
+{
+ size_t foundPos = m_strBuffer.find_first_of("\r");
+ if (foundPos == std::string::npos)
+ return; // We have only Linux style line endings in the file, nothing to do
+
+ if (foundPos+1 >= m_strBuffer.size() || m_strBuffer[foundPos+1] != '\n')
+ CLog::Log(LOGDEBUG, "POParser: PO file has Mac Style Line Endings. "
+ "Converted in memory to Linux LF for file: %s", filename.c_str());
+ else
+ CLog::Log(LOGDEBUG, "POParser: PO file has Win Style Line Endings. "
+ "Converted in memory to Linux LF for file: %s", filename.c_str());
+
+ std::string strTemp;
+ strTemp.reserve(m_strBuffer.size());
+ for (std::string::const_iterator it = m_strBuffer.begin(); it < m_strBuffer.end(); ++it)
+ {
+ if (*it == '\r')
+ {
+ if (it+1 == m_strBuffer.end() || *(it+1) != '\n')
+ strTemp.push_back('\n'); // convert Mac style line ending and continue
+ continue; // we have Win style line ending so we exclude this CR now
+ }
+ strTemp.push_back(*it);
+ }
+ m_strBuffer.swap(strTemp);
+ m_POfilelength = m_strBuffer.size();
+}
diff --git a/src/utils/POUtils.h b/src/utils/POUtils.h
new file mode 100644
index 0000000000..7eb2cc30a8
--- /dev/null
+++ b/src/utils/POUtils.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2012-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string>
+#include <vector>
+#include <stdint.h>
+
+enum
+{
+ ID_FOUND = 0, // We have an entry with a numeric (previously XML) identification number.
+ MSGID_FOUND = 1, // We have a classic gettext entry with textual msgid. No numeric ID.
+ MSGID_PLURAL_FOUND = 2 // We have a classic gettext entry with textual msgid in plural form.
+};
+
+enum
+{
+ ISSOURCELANG=true
+};
+
+// Struct to hold current position and text of the string field in the main PO entry.
+struct CStrEntry
+{
+ size_t Pos;
+ std::string Str;
+};
+
+// Struct to collect all important data of the current processed entry.
+struct CPOEntry
+{
+ int Type;
+ uint32_t xID;
+ size_t xIDPos;
+ std::string Content;
+ CStrEntry msgCtxt;
+ CStrEntry msgID;
+ CStrEntry msgStr;
+ std::vector<CStrEntry> msgStrPlural;
+};
+
+class CPODocument
+{
+public:
+ CPODocument();
+ ~CPODocument();
+
+ /*! \brief Tries to load a PO file into a temporary memory buffer.
+ * It also tries to parse the header of the PO file.
+ \param pofilename filename of the PO file to load.
+ \return true if the load was successful, unless return false
+ */
+ bool LoadFile(const std::string &pofilename);
+
+ /*! \brief Fast jumps to the next entry in PO buffer.
+ * Finds next entry started with "#: id:" or msgctx or msgid.
+ * to be as fast as possible this does not even get the id number
+ * just the type of the entry found. GetEntryID() has to be called
+ * for getting the id. After that ParseEntry() needs a call for
+ * actually getting the msg strings. The reason for this is to
+ * have calls and checks as fast as possible generally and specially
+ * for parsing weather tokens and to parse only the needed strings from
+ * the fallback language (missing from the gui language translation)
+ \return true if there was an entry found, false if reached the end of buffer
+ */
+ bool GetNextEntry();
+
+ /*! \brief Gets the type of entry found with GetNextEntry.
+ \return the type of entry: ID_FOUND || MSGID_FOUND || MSGID_PLURAL_FOUND
+ */
+ int GetEntryType() const {return m_Entry.Type;}
+
+ /*! \brief Parses the numeric ID from current entry.
+ * This function can only be called right after GetNextEntry()
+ * to make sure that we have a valid entry detected.
+ \return parsed ID number
+ */
+ uint32_t GetEntryID() const {return m_Entry.xID;}
+
+ /*! \brief Parses current entry.
+ * Reads msgid, msgstr, msgstr[x], msgctxt strings.
+ * Note that this function also back-converts the c++ style escape sequences.
+ * The function only parses the needed strings, considering if it is a source language file.
+ \param bisSourceLang if we parse a source English file.
+ */
+ void ParseEntry(bool bisSourceLang);
+
+ /*! \brief Gets the msgctxt string previously parsed by ParseEntry().
+ \return string* containing the msgctxt string, unescaped and linked together.
+ */
+ const std::string& GetMsgctxt() const {return m_Entry.msgCtxt.Str;}
+
+ /*! \brief Gets the msgid string previously parsed by ParseEntry().
+ \return string* containing the msgid string, unescaped and linked together.
+ */
+ const std::string& GetMsgid() const {return m_Entry.msgID.Str;}
+
+ /*! \brief Gets the msgstr string previously parsed by ParseEntry().
+ \return string* containing the msgstr string, unescaped and linked together.
+ */
+ const std::string& GetMsgstr() const {return m_Entry.msgStr.Str;}
+
+ /*! \brief Gets the msgstr[x] string previously parsed by ParseEntry().
+ \param plural the number of plural-form expected to get (0-6).
+ \return string* containing the msgstr string, unescaped and linked together.
+ */
+ const std::string& GetPlurMsgstr (size_t plural) const;
+
+protected:
+
+ /*! \brief Converts c++ style char escape sequences back to char.
+ * Supports: \a \v \n \t \r \" \0 \f \? \' \\
+ \param strInput string contains the string to be unescaped.
+ \return unescaped string.
+ */
+ std::string UnescapeString(const std::string &strInput);
+
+ /*! \brief Finds the position of line, starting with a given string in current entry.
+ * This function can only be called after GetNextEntry()
+ \param strToFind a string what we look for, at beginning of the lines.
+ \param FoundPos will get the position where we found the line starting with the string.
+ \return false if no line like that can be found in the entry (m_Entry)
+ */
+ bool FindLineStart(const std::string &strToFind, size_t &FoundPos);
+
+ /*! \brief Reads, and links together the quoted strings found with ParseEntry().
+ * This function can only be called after GetNextEntry() called.
+ \param strEntry.Str a string where we get the appended string lines.
+ \param strEntry.Pos the position in m_Entry.Content to start reading the string.
+ */
+ void GetString(CStrEntry &strEntry);
+
+ /*! \brief Parses the numeric id and checks if it is valid.
+ * This function can only be called after GetNextEntry()
+ * It checks m_Entry.Content at position m_Entry.xIDPos for the numeric id.
+ * The converted ID number goes into m_Entry.xID for public read out.
+ \return false, if parse and convert of the id number was unsuccessful.
+ */
+ bool ParseNumID();
+
+ /*! \brief If we have Windows or Mac line-end chars in PO file, convert them to Unix LFs
+ */
+ void ConvertLineEnds(const std::string &filename);
+
+ // Temporary string buffer to read file in.
+ std::string m_strBuffer;
+ // Size of the string buffer.
+ size_t m_POfilelength;
+
+ // Current cursor position in m_strBuffer.
+ size_t m_CursorPos;
+ // The next PO entry position in m_strBuffer.
+ size_t m_nextEntryPos;
+
+ // Variable to hold all data of currently processed entry.
+ CPOEntry m_Entry;
+};
diff --git a/src/utils/PerformanceSample.cpp b/src/utils/PerformanceSample.cpp
new file mode 100644
index 0000000000..0da95ab29b
--- /dev/null
+++ b/src/utils/PerformanceSample.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "system.h"
+#include "PerformanceSample.h"
+
+#ifdef TARGET_POSIX
+#include "linux/PlatformInclude.h"
+#endif
+
+#include "Application.h"
+#include "log.h"
+#include "TimeUtils.h"
+
+using namespace std;
+
+int64_t CPerformanceSample::m_tmFreq;
+
+CPerformanceSample::CPerformanceSample(const string &statName, bool bCheckWhenDone) : m_statName(statName)
+{
+ m_bCheckWhenDone = bCheckWhenDone;
+ if (m_tmFreq == 0LL)
+ m_tmFreq = CurrentHostFrequency();
+
+ Reset();
+}
+
+CPerformanceSample::~CPerformanceSample()
+{
+ if (m_bCheckWhenDone)
+ CheckPoint();
+}
+
+void CPerformanceSample::Reset()
+{
+ m_tmStart = CurrentHostCounter();
+#ifdef TARGET_POSIX
+ if (getrusage(RUSAGE_SELF, &m_usage) == -1)
+ CLog::Log(LOGERROR,"error %d in getrusage", errno);
+#endif
+}
+
+void CPerformanceSample::CheckPoint()
+{
+#ifdef HAS_PERFORMANCE_SAMPLE
+ int64_t tmNow;
+ tmNow = CurrentHostCounter();
+ double elapsed = (double)(tmNow - m_tmStart) / (double)m_tmFreq.QuadPart;
+
+ double dUser=0.0, dSys=0.0;
+#ifdef TARGET_POSIX
+ struct rusage usage;
+ if (getrusage(RUSAGE_SELF, &usage) == -1)
+ CLog::Log(LOGERROR,"error %d in getrusage", errno);
+ else
+ {
+ dUser = ( ((double)usage.ru_utime.tv_sec + (double)usage.ru_utime.tv_usec / 1000000.0) -
+ ((double)m_usage.ru_utime.tv_sec + (double)m_usage.ru_utime.tv_usec / 1000000.0) );
+ dSys = ( ((double)usage.ru_stime.tv_sec + (double)usage.ru_stime.tv_usec / 1000000.0) -
+ ((double)m_usage.ru_stime.tv_sec + (double)m_usage.ru_stime.tv_usec / 1000000.0) );
+ }
+#endif
+
+ g_application.GetPerformanceStats().AddSample(m_statName, PerformanceCounter(elapsed,dUser,dSys));
+#endif
+
+ Reset();
+}
+
+double CPerformanceSample::GetEstimatedError()
+{
+ if (m_tmFreq == 0LL)
+ m_tmFreq = CurrentHostFrequency();
+
+ int64_t tmStart, tmEnd;
+ tmStart = CurrentHostCounter();
+
+ for (int i=0; i<100000;i++)
+ {
+ DECLARE_UNUSED(int64_t,tmDummy);
+ tmDummy = CurrentHostCounter();
+ }
+
+ tmEnd = CurrentHostCounter();
+ double elapsed = (double)(tmEnd - tmStart) / (double)m_tmFreq;
+
+ return (elapsed / 100000.0) * 2.0; // one measure at start time and another when done.
+}
+
+
+
+
diff --git a/src/utils/PerformanceSample.h b/src/utils/PerformanceSample.h
new file mode 100644
index 0000000000..d148cc4c7d
--- /dev/null
+++ b/src/utils/PerformanceSample.h
@@ -0,0 +1,68 @@
+#ifndef __PERFORMANCE_SAMPLE__
+#define __PERFORMANCE_SAMPLE__
+
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef TARGET_POSIX
+#include "linux/PlatformDefs.h"
+#include <sys/time.h>
+#include <sys/times.h>
+#include <sys/resource.h>
+#elif TARGET_WINDOWS
+#include "win32/PlatformDefs.h"
+#endif
+
+#include <string>
+
+#ifndef NO_PERFORMANCE_MEASURE
+#define MEASURE_FUNCTION CPerformanceSample aSample(__FUNCTION__,true);
+#define BEGIN_MEASURE_BLOCK(n) { CPerformanceSample aSample(n,true);
+#define END_MEASURE_BLOCK }
+#else
+#define MEASURE_FUNCTION
+#define BEGIN_MEASURE_BLOCK(n)
+#define END_MEASURE_BLOCK
+#endif
+
+class CPerformanceSample
+{
+public:
+ CPerformanceSample(const std::string &statName, bool bCheckWhenDone=true);
+ virtual ~CPerformanceSample();
+
+ void Reset();
+ void CheckPoint(); // will add a sample to stats and restart counting.
+
+ static double GetEstimatedError();
+
+protected:
+ std::string m_statName;
+ bool m_bCheckWhenDone;
+
+#ifdef TARGET_POSIX
+ struct rusage m_usage;
+#endif
+
+ int64_t m_tmStart;
+ static int64_t m_tmFreq;
+};
+
+#endif
diff --git a/src/utils/PerformanceStats.cpp b/src/utils/PerformanceStats.cpp
new file mode 100644
index 0000000000..5ef8cadadf
--- /dev/null
+++ b/src/utils/PerformanceStats.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "PerformanceStats.h"
+#include "PerformanceSample.h"
+#include "log.h"
+
+using namespace std;
+
+CPerformanceStats::CPerformanceStats()
+{
+}
+
+
+CPerformanceStats::~CPerformanceStats()
+{
+ map<string, PerformanceCounter*>::iterator iter = m_mapStats.begin();
+ while (iter != m_mapStats.end())
+ {
+ delete iter->second;
+ ++iter;
+ }
+ m_mapStats.clear();
+}
+
+void CPerformanceStats::AddSample(const string &strStatName, const PerformanceCounter &perf)
+{
+ map<string, PerformanceCounter*>::iterator iter = m_mapStats.find(strStatName);
+ if (iter == m_mapStats.end())
+ m_mapStats[strStatName] = new PerformanceCounter(perf);
+ else
+ {
+ iter->second->m_time += perf.m_time;
+ iter->second->m_samples += perf.m_samples;
+ }
+}
+
+void CPerformanceStats::AddSample(const string &strStatName, double dTime)
+{
+ AddSample(strStatName, PerformanceCounter(dTime));
+}
+
+void CPerformanceStats::DumpStats()
+{
+ double dError = CPerformanceSample::GetEstimatedError();
+ CLog::Log(LOGINFO, "%s - estimated error: %f", __FUNCTION__, dError);
+ CLog::Log(LOGINFO, "%s - ignore user/sys values when sample count is low", __FUNCTION__);
+
+ map<string, PerformanceCounter*>::iterator iter = m_mapStats.begin();
+ while (iter != m_mapStats.end())
+ {
+ double dAvg = iter->second->m_time / (double)iter->second->m_samples;
+ double dAvgUser = iter->second->m_user / (double)iter->second->m_samples;
+ double dAvgSys = iter->second->m_sys / (double)iter->second->m_samples;
+ CLog::Log(LOGINFO, "%s - counter <%s>. avg duration: <%f sec>, avg user: <%f>, avg sys: <%f> (%" PRIu64" samples)",
+ __FUNCTION__, iter->first.c_str(), dAvg, dAvgUser, dAvgSys, iter->second->m_samples);
+ ++iter;
+ }
+}
+
+
diff --git a/src/utils/PerformanceStats.h b/src/utils/PerformanceStats.h
new file mode 100644
index 0000000000..4f72d6dca2
--- /dev/null
+++ b/src/utils/PerformanceStats.h
@@ -0,0 +1,58 @@
+#ifndef PERFORMANCESTATS_H
+#define PERFORMANCESTATS_H
+
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <map>
+#include <string>
+#include "PlatformDefs.h"
+#include "threads/CriticalSection.h"
+
+class PerformanceCounter
+{
+public:
+ double m_time;
+ double m_user;
+ double m_sys;
+ int64_t m_samples;
+
+ PerformanceCounter(double dTime=0.0, double dUser=0.0, double dSys=0.0, int64_t nSamples=1LL) :
+ m_time(dTime), m_user(dUser), m_sys(dSys), m_samples(nSamples) { }
+ virtual ~PerformanceCounter() { }
+};
+
+/**
+*/
+class CPerformanceStats{
+public:
+ CPerformanceStats();
+ virtual ~CPerformanceStats();
+
+ void AddSample(const std::string &strStatName, const PerformanceCounter &perf);
+ void AddSample(const std::string &strStatName, double dTime);
+ void DumpStats();
+
+protected:
+ CCriticalSection m_lock;
+ std::map<std::string, PerformanceCounter*> m_mapStats;
+};
+
+#endif
diff --git a/src/utils/RecentlyAddedJob.cpp b/src/utils/RecentlyAddedJob.cpp
new file mode 100644
index 0000000000..f4770c714f
--- /dev/null
+++ b/src/utils/RecentlyAddedJob.cpp
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/log.h"
+#include "video/VideoDatabase.h"
+#include "video/VideoInfoTag.h"
+#include "FileItem.h"
+#include "RecentlyAddedJob.h"
+#include "guilib/GUIWindow.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/WindowIDs.h"
+#include "music/MusicDatabase.h"
+#include "music/tags/MusicInfoTag.h"
+#include "utils/Variant.h"
+#include "utils/StringUtils.h"
+#include "settings/AdvancedSettings.h"
+#include "music/MusicThumbLoader.h"
+#include "video/VideoThumbLoader.h"
+
+#define NUM_ITEMS 10
+
+CRecentlyAddedJob::CRecentlyAddedJob(int flag)
+{
+ m_flag = flag;
+}
+
+bool CRecentlyAddedJob::UpdateVideo()
+{
+ CGUIWindow* home = g_windowManager.GetWindow(WINDOW_HOME);
+
+ if ( home == NULL )
+ return false;
+
+ CLog::Log(LOGDEBUG, "CRecentlyAddedJob::UpdateVideos() - Running RecentlyAdded home screen update");
+
+ int i = 0;
+ CFileItemList items;
+ CVideoDatabase videodatabase;
+ CVideoThumbLoader loader;
+ loader.OnLoaderStart();
+
+ videodatabase.Open();
+
+ if (videodatabase.GetRecentlyAddedMoviesNav("videodb://recentlyaddedmovies/", items, NUM_ITEMS))
+ {
+ for (; i < items.Size(); ++i)
+ {
+ CFileItemPtr item = items.Get(i);
+ CStdString value = StringUtils::Format("%i", i + 1);
+ CStdString strRating = StringUtils::Format("%.1f", item->GetVideoInfoTag()->m_fRating);;
+
+ home->SetProperty("LatestMovie." + value + ".Title" , item->GetLabel());
+ home->SetProperty("LatestMovie." + value + ".Rating" , strRating);
+ home->SetProperty("LatestMovie." + value + ".Year" , item->GetVideoInfoTag()->m_iYear);
+ home->SetProperty("LatestMovie." + value + ".Plot" , item->GetVideoInfoTag()->m_strPlot);
+ home->SetProperty("LatestMovie." + value + ".RunningTime" , item->GetVideoInfoTag()->GetDuration() / 60);
+ home->SetProperty("LatestMovie." + value + ".Path" , item->GetVideoInfoTag()->m_strFileNameAndPath);
+ home->SetProperty("LatestMovie." + value + ".Trailer" , item->GetVideoInfoTag()->m_strTrailer);
+
+ if (!item->HasArt("thumb"))
+ loader.LoadItem(item.get());
+
+ home->SetProperty("LatestMovie." + value + ".Thumb" , item->GetArt("thumb"));
+ home->SetProperty("LatestMovie." + value + ".Fanart" , item->GetArt("fanart"));
+ }
+ }
+ for (; i < NUM_ITEMS; ++i)
+ {
+ CStdString value = StringUtils::Format("%i", i + 1);
+ home->SetProperty("LatestMovie." + value + ".Title" , "");
+ home->SetProperty("LatestMovie." + value + ".Thumb" , "");
+ home->SetProperty("LatestMovie." + value + ".Rating" , "");
+ home->SetProperty("LatestMovie." + value + ".Year" , "");
+ home->SetProperty("LatestMovie." + value + ".Plot" , "");
+ home->SetProperty("LatestMovie." + value + ".RunningTime" , "");
+ home->SetProperty("LatestMovie." + value + ".Path" , "");
+ home->SetProperty("LatestMovie." + value + ".Trailer" , "");
+ home->SetProperty("LatestMovie." + value + ".Fanart" , "");
+ }
+
+ i = 0;
+ CFileItemList TVShowItems;
+
+ if (videodatabase.GetRecentlyAddedEpisodesNav("videodb://recentlyaddedepisodes/", TVShowItems, NUM_ITEMS))
+ {
+ for (; i < TVShowItems.Size(); ++i)
+ {
+ CFileItemPtr item = TVShowItems.Get(i);
+ int EpisodeSeason = item->GetVideoInfoTag()->m_iSeason;
+ int EpisodeNumber = item->GetVideoInfoTag()->m_iEpisode;
+ CStdString EpisodeNo = StringUtils::Format("s%02de%02d", EpisodeSeason, EpisodeNumber);
+ CStdString value = StringUtils::Format("%i", i + 1);
+ CStdString strRating = StringUtils::Format("%.1f", item->GetVideoInfoTag()->m_fRating);
+
+ home->SetProperty("LatestEpisode." + value + ".ShowTitle" , item->GetVideoInfoTag()->m_strShowTitle);
+ home->SetProperty("LatestEpisode." + value + ".EpisodeTitle" , item->GetVideoInfoTag()->m_strTitle);
+ home->SetProperty("LatestEpisode." + value + ".Rating" , strRating);
+ home->SetProperty("LatestEpisode." + value + ".Plot" , item->GetVideoInfoTag()->m_strPlot);
+ home->SetProperty("LatestEpisode." + value + ".EpisodeNo" , EpisodeNo);
+ home->SetProperty("LatestEpisode." + value + ".EpisodeSeason" , EpisodeSeason);
+ home->SetProperty("LatestEpisode." + value + ".EpisodeNumber" , EpisodeNumber);
+ home->SetProperty("LatestEpisode." + value + ".Path" , item->GetVideoInfoTag()->m_strFileNameAndPath);
+
+ if (!item->HasArt("thumb"))
+ loader.LoadItem(item.get());
+
+ std::string seasonThumb;
+ if (item->GetVideoInfoTag()->m_iIdSeason > 0)
+ seasonThumb = videodatabase.GetArtForItem(item->GetVideoInfoTag()->m_iIdSeason, MediaTypeSeason, "thumb");
+
+ home->SetProperty("LatestEpisode." + value + ".Thumb" , item->GetArt("thumb"));
+ home->SetProperty("LatestEpisode." + value + ".ShowThumb" , item->GetArt("tvshow.thumb"));
+ home->SetProperty("LatestEpisode." + value + ".SeasonThumb" , seasonThumb);
+ home->SetProperty("LatestEpisode." + value + ".Fanart" , item->GetArt("fanart"));
+ }
+ }
+ for (; i < NUM_ITEMS; ++i)
+ {
+ CStdString value = StringUtils::Format("%i", i + 1);
+ home->SetProperty("LatestEpisode." + value + ".ShowTitle" , "");
+ home->SetProperty("LatestEpisode." + value + ".EpisodeTitle" , "");
+ home->SetProperty("LatestEpisode." + value + ".Rating" , "");
+ home->SetProperty("LatestEpisode." + value + ".Plot" , "");
+ home->SetProperty("LatestEpisode." + value + ".EpisodeNo" , "");
+ home->SetProperty("LatestEpisode." + value + ".EpisodeSeason" , "");
+ home->SetProperty("LatestEpisode." + value + ".EpisodeNumber" , "");
+ home->SetProperty("LatestEpisode." + value + ".Path" , "");
+ home->SetProperty("LatestEpisode." + value + ".Thumb" , "");
+ home->SetProperty("LatestEpisode." + value + ".ShowThumb" , "");
+ home->SetProperty("LatestEpisode." + value + ".SeasonThumb" , "");
+ home->SetProperty("LatestEpisode." + value + ".Fanart" , "");
+ }
+
+ i = 0;
+ CFileItemList MusicVideoItems;
+
+ if (videodatabase.GetRecentlyAddedMusicVideosNav("videodb://recentlyaddedmusicvideos/", MusicVideoItems, NUM_ITEMS))
+ {
+ for (; i < MusicVideoItems.Size(); ++i)
+ {
+ CFileItemPtr item = MusicVideoItems.Get(i);
+ CStdString value = StringUtils::Format("%i", i + 1);
+
+ home->SetProperty("LatestMusicVideo." + value + ".Title" , item->GetLabel());
+ home->SetProperty("LatestMusicVideo." + value + ".Year" , item->GetVideoInfoTag()->m_iYear);
+ home->SetProperty("LatestMusicVideo." + value + ".Plot" , item->GetVideoInfoTag()->m_strPlot);
+ home->SetProperty("LatestMusicVideo." + value + ".RunningTime" , item->GetVideoInfoTag()->GetDuration() / 60);
+ home->SetProperty("LatestMusicVideo." + value + ".Path" , item->GetVideoInfoTag()->m_strFileNameAndPath);
+ home->SetProperty("LatestMusicVideo." + value + ".Artist" , StringUtils::Join(item->GetVideoInfoTag()->m_artist, g_advancedSettings.m_videoItemSeparator));
+
+ if (!item->HasArt("thumb"))
+ loader.LoadItem(item.get());
+
+ home->SetProperty("LatestMusicVideo." + value + ".Thumb" , item->GetArt("thumb"));
+ home->SetProperty("LatestMusicVideo." + value + ".Fanart" , item->GetArt("fanart"));
+ }
+ }
+ for (; i < NUM_ITEMS; ++i)
+ {
+ CStdString value = StringUtils::Format("%i", i + 1);
+ home->SetProperty("LatestMusicVideo." + value + ".Title" , "");
+ home->SetProperty("LatestMusicVideo." + value + ".Thumb" , "");
+ home->SetProperty("LatestMusicVideo." + value + ".Year" , "");
+ home->SetProperty("LatestMusicVideo." + value + ".Plot" , "");
+ home->SetProperty("LatestMusicVideo." + value + ".RunningTime" , "");
+ home->SetProperty("LatestMusicVideo." + value + ".Path" , "");
+ home->SetProperty("LatestMusicVideo." + value + ".Artist" , "");
+ home->SetProperty("LatestMusicVideo." + value + ".Fanart" , "");
+ }
+
+ videodatabase.Close();
+ return true;
+}
+
+bool CRecentlyAddedJob::UpdateMusic()
+{
+ CGUIWindow* home = g_windowManager.GetWindow(WINDOW_HOME);
+
+ if ( home == NULL )
+ return false;
+
+ CLog::Log(LOGDEBUG, "CRecentlyAddedJob::UpdateMusic() - Running RecentlyAdded home screen update");
+
+ int i = 0;
+ CFileItemList musicItems;
+ CMusicDatabase musicdatabase;
+ CMusicThumbLoader loader;
+ loader.OnLoaderStart();
+
+ musicdatabase.Open();
+
+ if (musicdatabase.GetRecentlyAddedAlbumSongs("musicdb://songs/", musicItems, NUM_ITEMS))
+ {
+ long idAlbum = -1;
+ CStdString strAlbumThumb;
+ CStdString strAlbumFanart;
+ for (; i < musicItems.Size(); ++i)
+ {
+ CFileItemPtr item = musicItems.Get(i);
+ CStdString value = StringUtils::Format("%i", i + 1);
+
+ CStdString strRating;
+ CStdString strAlbum = item->GetMusicInfoTag()->GetAlbum();
+ CStdString strArtist = StringUtils::Join(item->GetMusicInfoTag()->GetArtist(), g_advancedSettings.m_musicItemSeparator);
+
+ if (idAlbum != item->GetMusicInfoTag()->GetAlbumId())
+ {
+ strAlbumThumb.clear();
+ strAlbumFanart.clear();
+ idAlbum = item->GetMusicInfoTag()->GetAlbumId();
+
+ if (loader.LoadItem(item.get()))
+ {
+ strAlbumThumb = item->GetArt("thumb");
+ strAlbumFanart = item->GetArt("fanart");
+ }
+ }
+
+ strRating = StringUtils::Format("%c", item->GetMusicInfoTag()->GetRating());
+
+ home->SetProperty("LatestSong." + value + ".Title" , item->GetMusicInfoTag()->GetTitle());
+ home->SetProperty("LatestSong." + value + ".Year" , item->GetMusicInfoTag()->GetYear());
+ home->SetProperty("LatestSong." + value + ".Artist" , strArtist);
+ home->SetProperty("LatestSong." + value + ".Album" , strAlbum);
+ home->SetProperty("LatestSong." + value + ".Rating" , strRating);
+ home->SetProperty("LatestSong." + value + ".Path" , item->GetMusicInfoTag()->GetURL());
+ home->SetProperty("LatestSong." + value + ".Thumb" , strAlbumThumb);
+ home->SetProperty("LatestSong." + value + ".Fanart" , strAlbumFanart);
+ }
+ }
+ for (; i < NUM_ITEMS; ++i)
+ {
+ CStdString value = StringUtils::Format("%i", i + 1);
+ home->SetProperty("LatestSong." + value + ".Title" , "");
+ home->SetProperty("LatestSong." + value + ".Year" , "");
+ home->SetProperty("LatestSong." + value + ".Artist" , "");
+ home->SetProperty("LatestSong." + value + ".Album" , "");
+ home->SetProperty("LatestSong." + value + ".Rating" , "");
+ home->SetProperty("LatestSong." + value + ".Path" , "");
+ home->SetProperty("LatestSong." + value + ".Thumb" , "");
+ home->SetProperty("LatestSong." + value + ".Fanart" , "");
+ }
+
+ i = 0;
+ VECALBUMS albums;
+
+ if (musicdatabase.GetRecentlyAddedAlbums(albums, NUM_ITEMS))
+ {
+ for (; i < (int)albums.size(); ++i)
+ {
+ CAlbum& album=albums[i];
+ CStdString value = StringUtils::Format("%i", i + 1);
+ CStdString strThumb = musicdatabase.GetArtForItem(album.idAlbum, MediaTypeAlbum, "thumb");
+ CStdString strFanart = musicdatabase.GetArtistArtForItem(album.idAlbum, MediaTypeAlbum, "fanart");
+ CStdString strDBpath = StringUtils::Format("musicdb://albums/%li/", album.idAlbum);
+ CStdString strSQLAlbum = StringUtils::Format("idAlbum=%li", album.idAlbum);
+ CStdString strArtist = musicdatabase.GetSingleValue("albumview", "strArtists", strSQLAlbum);
+
+ home->SetProperty("LatestAlbum." + value + ".Title" , album.strAlbum);
+ home->SetProperty("LatestAlbum." + value + ".Year" , album.iYear);
+ home->SetProperty("LatestAlbum." + value + ".Artist" , strArtist);
+ home->SetProperty("LatestAlbum." + value + ".Rating" , album.iRating);
+ home->SetProperty("LatestAlbum." + value + ".Path" , strDBpath);
+ home->SetProperty("LatestAlbum." + value + ".Thumb" , strThumb);
+ home->SetProperty("LatestAlbum." + value + ".Fanart" , strFanart);
+ }
+ }
+ for (; i < NUM_ITEMS; ++i)
+ {
+ CStdString value = StringUtils::Format("%i", i + 1);
+ home->SetProperty("LatestAlbum." + value + ".Title" , "");
+ home->SetProperty("LatestAlbum." + value + ".Year" , "");
+ home->SetProperty("LatestAlbum." + value + ".Artist" , "");
+ home->SetProperty("LatestAlbum." + value + ".Rating" , "");
+ home->SetProperty("LatestAlbum." + value + ".Path" , "");
+ home->SetProperty("LatestAlbum." + value + ".Thumb" , "");
+ home->SetProperty("LatestAlbum." + value + ".Fanart" , "");
+ }
+
+ musicdatabase.Close();
+ return true;
+}
+
+bool CRecentlyAddedJob::UpdateTotal()
+{
+ CGUIWindow* home = g_windowManager.GetWindow(WINDOW_HOME);
+
+ if ( home == NULL )
+ return false;
+
+ CLog::Log(LOGDEBUG, "CRecentlyAddedJob::UpdateTotal() - Running RecentlyAdded home screen update");
+
+ CVideoDatabase videodatabase;
+ CMusicDatabase musicdatabase;
+
+ musicdatabase.Open();
+ int MusSongTotals = atoi(musicdatabase.GetSingleValue("songview" , "count(1)").c_str());
+ int MusAlbumTotals = atoi(musicdatabase.GetSingleValue("songview" , "count(distinct strAlbum)").c_str());
+ int MusArtistTotals = atoi(musicdatabase.GetSingleValue("songview" , "count(distinct strArtists)").c_str());
+ musicdatabase.Close();
+
+ videodatabase.Open();
+ int tvShowCount = atoi(videodatabase.GetSingleValue("tvshowview" , "count(1)").c_str());
+ int movieTotals = atoi(videodatabase.GetSingleValue("movieview" , "count(1)").c_str());
+ int movieWatched = atoi(videodatabase.GetSingleValue("movieview" , "count(playCount)").c_str());
+ int MusVidTotals = atoi(videodatabase.GetSingleValue("musicvideoview" , "count(1)").c_str());
+ int MusVidWatched = atoi(videodatabase.GetSingleValue("musicvideoview" , "count(playCount)").c_str());
+ int EpWatched = atoi(videodatabase.GetSingleValue("tvshowview" , "sum(watchedcount)").c_str());
+ int EpCount = atoi(videodatabase.GetSingleValue("tvshowview" , "sum(totalcount)").c_str());
+ int TvShowsWatched = atoi(videodatabase.GetSingleValue("tvshowview" , "sum(watchedcount = totalcount)").c_str());
+ videodatabase.Close();
+
+ home->SetProperty("TVShows.Count" , tvShowCount);
+ home->SetProperty("TVShows.Watched" , TvShowsWatched);
+ home->SetProperty("TVShows.UnWatched" , tvShowCount - TvShowsWatched);
+ home->SetProperty("Episodes.Count" , EpCount);
+ home->SetProperty("Episodes.Watched" , EpWatched);
+ home->SetProperty("Episodes.UnWatched" , EpCount-EpWatched);
+ home->SetProperty("Movies.Count" , movieTotals);
+ home->SetProperty("Movies.Watched" , movieWatched);
+ home->SetProperty("Movies.UnWatched" , movieTotals - movieWatched);
+ home->SetProperty("MusicVideos.Count" , MusVidTotals);
+ home->SetProperty("MusicVideos.Watched" , MusVidWatched);
+ home->SetProperty("MusicVideos.UnWatched" , MusVidTotals - MusVidWatched);
+ home->SetProperty("Music.SongsCount" , MusSongTotals);
+ home->SetProperty("Music.AlbumsCount" , MusAlbumTotals);
+ home->SetProperty("Music.ArtistsCount" , MusArtistTotals);
+
+ return true;
+}
+
+
+bool CRecentlyAddedJob::DoWork()
+{
+ bool ret = true;
+ if (m_flag & Audio)
+ ret &= UpdateMusic();
+
+ if (m_flag & Video)
+ ret &= UpdateVideo();
+
+ if (m_flag & Totals)
+ ret &= UpdateTotal();
+
+ return ret;
+}
diff --git a/src/utils/RecentlyAddedJob.h b/src/utils/RecentlyAddedJob.h
new file mode 100644
index 0000000000..380b4c7002
--- /dev/null
+++ b/src/utils/RecentlyAddedJob.h
@@ -0,0 +1,41 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "Job.h"
+
+enum ERecentlyAddedFlag
+{
+ Audio = 0x1,
+ Video = 0x2,
+ Totals = 0x4
+};
+
+class CRecentlyAddedJob : public CJob
+{
+public:
+ CRecentlyAddedJob(int flag);
+ static bool UpdateVideo();
+ static bool UpdateMusic();
+ static bool UpdateTotal();
+ virtual bool DoWork();
+private:
+ int m_flag;
+};
diff --git a/src/utils/RegExp.cpp b/src/utils/RegExp.cpp
new file mode 100644
index 0000000000..97a8bcbec9
--- /dev/null
+++ b/src/utils/RegExp.cpp
@@ -0,0 +1,650 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <algorithm>
+#include "RegExp.h"
+#include "StdString.h"
+#include "log.h"
+#include "utils/StringUtils.h"
+#include "utils/Utf8Utils.h"
+
+using namespace PCRE;
+
+#ifndef PCRE_UCP
+#define PCRE_UCP 0
+#endif // PCRE_UCP
+
+#ifdef PCRE_CONFIG_JIT
+#define PCRE_HAS_JIT_CODE 1
+#endif
+
+#ifndef PCRE_STUDY_JIT_COMPILE
+#define PCRE_STUDY_JIT_COMPILE 0
+#endif
+#ifndef PCRE_INFO_JIT
+// some unused number
+#define PCRE_INFO_JIT 2048
+#endif
+#ifndef PCRE_HAS_JIT_CODE
+#define pcre_free_study(x) pcre_free((x))
+#endif
+
+int CRegExp::m_Utf8Supported = -1;
+int CRegExp::m_UcpSupported = -1;
+int CRegExp::m_JitSupported = -1;
+
+
+CRegExp::CRegExp(bool caseless /*= false*/, CRegExp::utf8Mode utf8 /*= asciiOnly*/)
+{
+ InitValues(caseless, utf8);
+}
+
+void CRegExp::InitValues(bool caseless /*= false*/, CRegExp::utf8Mode utf8 /*= asciiOnly*/)
+{
+ m_utf8Mode = utf8;
+ m_re = NULL;
+ m_sd = NULL;
+ m_iOptions = PCRE_DOTALL | PCRE_NEWLINE_ANY;
+ if(caseless)
+ m_iOptions |= PCRE_CASELESS;
+ if (m_utf8Mode == forceUtf8)
+ {
+ if (IsUtf8Supported())
+ m_iOptions |= PCRE_UTF8;
+ if (AreUnicodePropertiesSupported())
+ m_iOptions |= PCRE_UCP;
+ }
+
+ m_offset = 0;
+ m_jitCompiled = false;
+ m_bMatched = false;
+ m_iMatchCount = 0;
+ m_jitStack = NULL;
+
+ memset(m_iOvector, 0, sizeof(m_iOvector));
+}
+
+CRegExp::CRegExp(bool caseless, CRegExp::utf8Mode utf8, const char *re, studyMode study /*= NoStudy*/)
+{
+ if (utf8 == autoUtf8)
+ utf8 = requireUtf8(re) ? forceUtf8 : asciiOnly;
+
+ InitValues(caseless, utf8);
+ RegComp(re, study);
+}
+
+bool CRegExp::requireUtf8(const std::string& regexp)
+{
+ // enable UTF-8 mode if regexp string has UTF-8 multibyte sequences
+ if (CUtf8Utils::checkStrForUtf8(regexp) == CUtf8Utils::utf8string)
+ return true;
+
+ // check for explicit Unicode Properties (\p, \P, \X) and for Unicode character codes (greater than 0xFF) in form \x{hhh..}
+ // note: PCRE change meaning of \w, \s, \d (and \W, \S, \D) when Unicode Properties are enabled,
+ // but in auto mode we enable UNP for US-ASCII regexp only if regexp contains explicit \p, \P, \X or Unicode character code
+ const char* const regexpC = regexp.c_str();
+ const size_t len = regexp.length();
+ size_t pos = 0;
+
+ while (pos < len)
+ {
+ const char chr = regexpC[pos];
+ if (chr == '\\')
+ {
+ const char nextChr = regexpC[pos + 1];
+
+ if (nextChr == 'p' || nextChr == 'P' || nextChr == 'X')
+ return true; // found Unicode Properties
+ else if (nextChr == 'Q')
+ pos = regexp.find("\\E", pos + 2); // skip all literals in "\Q...\E"
+ else if (nextChr == 'x' && regexpC[pos + 2] == '{')
+ { // Unicode character with hex code
+ if (readCharXCode(regexp, pos) >= 0x100)
+ return true; // found Unicode character code
+ }
+ else if (nextChr == '\\' || nextChr == '(' || nextChr == ')'
+ || nextChr == '[' || nextChr == ']')
+ pos++; // exclude next character from analyze
+
+ } // chr != '\\'
+ else if (chr == '(' && regexpC[pos + 1] == '?' && regexpC[pos + 2] == '#') // comment in regexp
+ pos = regexp.find(')', pos); // skip comment
+ else if (chr == '[')
+ {
+ if (isCharClassWithUnicode(regexp, pos))
+ return true;
+ }
+
+ if (pos == std::string::npos) // check results of regexp.find() and isCharClassWithUnicode
+ return false;
+
+ pos++;
+ }
+
+ // no Unicode Properties was found
+ return false;
+}
+
+inline int CRegExp::readCharXCode(const std::string& regexp, size_t& pos)
+{
+ // read hex character code in form "\x{hh..}"
+ // 'pos' must point to '\'
+ if (pos >= regexp.length())
+ return -1;
+ const char* const regexpC = regexp.c_str();
+ if (regexpC[pos] != '\\' || regexpC[pos + 1] != 'x' || regexpC[pos + 2] != '{')
+ return -1;
+
+ pos++;
+ const size_t startPos = pos; // 'startPos' points to 'x'
+ const size_t closingBracketPos = regexp.find('}', startPos + 2);
+ if (closingBracketPos == std::string::npos)
+ return 0; // return character zero code, leave 'pos' at 'x'
+
+ pos++; // 'pos' points to '{'
+ int chCode = 0;
+ while (++pos < closingBracketPos)
+ {
+ const int xdigitVal = StringUtils::asciixdigitvalue(regexpC[pos]);
+ if (xdigitVal >= 0)
+ chCode = chCode * 16 + xdigitVal;
+ else
+ { // found non-hexdigit
+ pos = startPos; // reset 'pos' to 'startPos', process "{hh..}" as non-code
+ return 0; // return character zero code
+ }
+ }
+
+ return chCode;
+}
+
+bool CRegExp::isCharClassWithUnicode(const std::string& regexp, size_t& pos)
+{
+ const char* const regexpC = regexp.c_str();
+ const size_t len = regexp.length();
+ if (pos > len || regexpC[pos] != '[')
+ return false;
+
+ // look for Unicode character code "\x{hhh..}" and Unicode properties "\P", "\p" and "\X"
+ // find end (terminating ']') of character class (like "[a-h45]")
+ // detect nested POSIX classes like "[[:lower:]]" and escaped brackets like "[\]]"
+ bool needUnicode = false;
+ while (++pos < len)
+ {
+ if (regexpC[pos] == '[' && regexpC[pos + 1] == ':')
+ { // possible POSIX character class, like "[:alpha:]"
+ const size_t nextClosingBracketPos = regexp.find(']', pos + 2); // don't care about "\]", as it produce error if used inside POSIX char class
+
+ if (nextClosingBracketPos == std::string::npos)
+ { // error in regexp: no closing ']' for character class
+ pos = std::string::npos;
+ return needUnicode;
+ }
+ else if (regexpC[nextClosingBracketPos - 1] == ':')
+ pos = nextClosingBracketPos; // skip POSIX character class
+ // if ":]" is not found, process "[:..." as part of normal character class
+ }
+ else if (regexpC[pos] == ']')
+ return needUnicode; // end of character class
+ else if (regexpC[pos] == '\\')
+ {
+ const char nextChar = regexpC[pos + 1];
+ if (nextChar == ']' || nextChar == '[')
+ pos++; // skip next character
+ else if (nextChar == 'Q')
+ {
+ pos = regexp.find("\\E", pos + 2);
+ if (pos == std::string::npos)
+ return needUnicode; // error in regexp: no closing "\E" after "\Q" in character class
+ else
+ pos++; // skip "\E"
+ }
+ else if (nextChar == 'p' || nextChar == 'P' || nextChar == 'X')
+ needUnicode = true; // don't care about property name as it can contain only ASCII chars
+ else if (nextChar == 'x')
+ {
+ if (readCharXCode(regexp, pos) >= 0x100)
+ needUnicode = true;
+ }
+ }
+ }
+ pos = std::string::npos; // closing square bracket was not found
+
+ return needUnicode;
+}
+
+
+CRegExp::CRegExp(const CRegExp& re)
+{
+ m_re = NULL;
+ m_sd = NULL;
+ m_jitStack = NULL;
+ m_utf8Mode = re.m_utf8Mode;
+ m_iOptions = re.m_iOptions;
+ *this = re;
+}
+
+CRegExp& CRegExp::operator=(const CRegExp& re)
+{
+ size_t size;
+ Cleanup();
+ m_jitCompiled = false;
+ m_pattern = re.m_pattern;
+ if (re.m_re)
+ {
+ if (pcre_fullinfo(re.m_re, NULL, PCRE_INFO_SIZE, &size) >= 0)
+ {
+ if ((m_re = (pcre*)malloc(size)))
+ {
+ memcpy(m_re, re.m_re, size);
+ memcpy(m_iOvector, re.m_iOvector, OVECCOUNT*sizeof(int));
+ m_offset = re.m_offset;
+ m_iMatchCount = re.m_iMatchCount;
+ m_bMatched = re.m_bMatched;
+ m_subject = re.m_subject;
+ m_iOptions = re.m_iOptions;
+ }
+ else
+ CLog::Log(LOGSEVERE, "%s: Failed to allocate memory", __FUNCTION__);
+ }
+ }
+ return *this;
+}
+
+CRegExp::~CRegExp()
+{
+ Cleanup();
+}
+
+bool CRegExp::RegComp(const char *re, studyMode study /*= NoStudy*/)
+{
+ if (!re)
+ return false;
+
+ m_offset = 0;
+ m_jitCompiled = false;
+ m_bMatched = false;
+ m_iMatchCount = 0;
+ const char *errMsg = NULL;
+ int errOffset = 0;
+ int options = m_iOptions;
+ if (m_utf8Mode == autoUtf8 && requireUtf8(re))
+ options |= (IsUtf8Supported() ? PCRE_UTF8 : 0) | (AreUnicodePropertiesSupported() ? PCRE_UCP : 0);
+
+ Cleanup();
+
+ m_re = pcre_compile(re, options, &errMsg, &errOffset, NULL);
+ if (!m_re)
+ {
+ m_pattern.clear();
+ CLog::Log(LOGERROR, "PCRE: %s. Compilation failed at offset %d in expression '%s'",
+ errMsg, errOffset, re);
+ return false;
+ }
+
+ m_pattern = re;
+
+ if (study)
+ {
+ const bool jitCompile = (study == StudyWithJitComp) && IsJitSupported();
+ const int studyOptions = jitCompile ? PCRE_STUDY_JIT_COMPILE : 0;
+
+ m_sd = pcre_study(m_re, studyOptions, &errMsg);
+ if (errMsg != NULL)
+ {
+ CLog::Log(LOGWARNING, "%s: PCRE error \"%s\" while studying expression", __FUNCTION__, errMsg);
+ if (m_sd != NULL)
+ {
+ pcre_free_study(m_sd);
+ m_sd = NULL;
+ }
+ }
+ else if (jitCompile)
+ {
+ int jitPresent = 0;
+ m_jitCompiled = (pcre_fullinfo(m_re, m_sd, PCRE_INFO_JIT, &jitPresent) == 0 && jitPresent == 1);
+ }
+ }
+
+ return true;
+}
+
+int CRegExp::RegFind(const char *str, unsigned int startoffset /*= 0*/, int maxNumberOfCharsToTest /*= -1*/)
+{
+ return PrivateRegFind(strlen(str), str, startoffset, maxNumberOfCharsToTest);
+}
+
+int CRegExp::PrivateRegFind(size_t bufferLen, const char *str, unsigned int startoffset /* = 0*/, int maxNumberOfCharsToTest /*= -1*/)
+{
+ m_offset = 0;
+ m_bMatched = false;
+ m_iMatchCount = 0;
+
+ if (!m_re)
+ {
+ CLog::Log(LOGERROR, "PCRE: Called before compilation");
+ return -1;
+ }
+
+ if (!str)
+ {
+ CLog::Log(LOGERROR, "PCRE: Called without a string to match");
+ return -1;
+ }
+
+ if (startoffset > bufferLen)
+ {
+ CLog::Log(LOGERROR, "%s: startoffset is beyond end of string to match", __FUNCTION__);
+ return -1;
+ }
+
+#ifdef PCRE_HAS_JIT_CODE
+ if (m_jitCompiled && !m_jitStack)
+ {
+ m_jitStack = pcre_jit_stack_alloc(32*1024, 512*1024);
+ if (m_jitStack == NULL)
+ CLog::Log(LOGWARNING, "%s: can't allocate address space for JIT stack", __FUNCTION__);
+
+ pcre_assign_jit_stack(m_sd, NULL, m_jitStack);
+ }
+#endif
+
+ if (maxNumberOfCharsToTest >= 0)
+ bufferLen = std::min<size_t>(bufferLen, startoffset + maxNumberOfCharsToTest);
+
+ m_subject.assign(str + startoffset, bufferLen - startoffset);
+ int rc = pcre_exec(m_re, NULL, m_subject.c_str(), m_subject.length(), 0, 0, m_iOvector, OVECCOUNT);
+
+ if (rc<1)
+ {
+ static const int fragmentLen = 80; // length of excerpt before erroneous char for log
+ switch(rc)
+ {
+ case PCRE_ERROR_NOMATCH:
+ return -1;
+
+ case PCRE_ERROR_MATCHLIMIT:
+ CLog::Log(LOGERROR, "PCRE: Match limit reached");
+ return -1;
+
+#ifdef PCRE_ERROR_SHORTUTF8
+ case PCRE_ERROR_SHORTUTF8:
+ {
+ const size_t startPos = (m_subject.length() > fragmentLen) ? CUtf8Utils::RFindValidUtf8Char(m_subject, m_subject.length() - fragmentLen) : 0;
+ if (startPos != std::string::npos)
+ CLog::Log(LOGERROR, "PCRE: Bad UTF-8 character at the end of string. Text before bad character: \"%s\"", m_subject.substr(startPos).c_str());
+ else
+ CLog::Log(LOGERROR, "PCRE: Bad UTF-8 character at the end of string");
+ return -1;
+ }
+#endif
+ case PCRE_ERROR_BADUTF8:
+ {
+ const size_t startPos = (m_iOvector[0] > fragmentLen) ? CUtf8Utils::RFindValidUtf8Char(m_subject, m_iOvector[0] - fragmentLen) : 0;
+ if (m_iOvector[0] >= 0 && startPos != std::string::npos)
+ CLog::Log(LOGERROR, "PCRE: Bad UTF-8 character, error code: %d, position: %d. Text before bad char: \"%s\"", m_iOvector[1], m_iOvector[0], m_subject.substr(startPos, m_iOvector[0] - startPos + 1).c_str());
+ else
+ CLog::Log(LOGERROR, "PCRE: Bad UTF-8 character, error code: %d, position: %d", m_iOvector[1], m_iOvector[0]);
+ return -1;
+ }
+ case PCRE_ERROR_BADUTF8_OFFSET:
+ CLog::Log(LOGERROR, "PCRE: Offset is pointing to the middle of UTF-8 character");
+ return -1;
+
+ default:
+ CLog::Log(LOGERROR, "PCRE: Unknown error: %d", rc);
+ return -1;
+ }
+ }
+ m_offset = startoffset;
+ m_bMatched = true;
+ m_iMatchCount = rc;
+ return m_iOvector[0] + m_offset;
+}
+
+int CRegExp::GetCaptureTotal() const
+{
+ int c = -1;
+ if (m_re)
+ pcre_fullinfo(m_re, NULL, PCRE_INFO_CAPTURECOUNT, &c);
+ return c;
+}
+
+std::string CRegExp::GetReplaceString(const std::string& sReplaceExp) const
+{
+ if (!m_bMatched || sReplaceExp.empty())
+ return "";
+
+ const char* const expr = sReplaceExp.c_str();
+
+ size_t pos = sReplaceExp.find_first_of("\\&");
+ std::string result(sReplaceExp, 0, pos);
+ result.reserve(sReplaceExp.size()); // very rough estimate
+
+ while(pos != std::string::npos)
+ {
+ if (expr[pos] == '\\')
+ {
+ // string is null-terminated and current char isn't null, so it's safe to advance to next char
+ pos++; // advance to next char
+ const char nextChar = expr[pos];
+ if (nextChar == '&' || nextChar == '\\')
+ { // this is "\&" or "\\" combination
+ result.push_back(nextChar); // add '&' or '\' to result
+ pos++;
+ }
+ else if (isdigit(nextChar))
+ { // this is "\0" - "\9" combination
+ int subNum = nextChar - '0';
+ pos++; // advance to second next char
+ const char secondNextChar = expr[pos];
+ if (isdigit(secondNextChar))
+ { // this is "\00" - "\99" combination
+ subNum = subNum * 10 + (secondNextChar - '0');
+ pos++;
+ }
+ result.append(GetMatch(subNum));
+ }
+ }
+ else
+ { // '&' char
+ result.append(GetMatch(0));
+ pos++;
+ }
+
+ const size_t nextPos = sReplaceExp.find_first_of("\\&", pos);
+ result.append(sReplaceExp, pos, nextPos - pos);
+ pos = nextPos;
+ }
+
+ return result;
+}
+
+int CRegExp::GetSubStart(int iSub) const
+{
+ if (!IsValidSubNumber(iSub))
+ return -1;
+
+ return m_iOvector[iSub*2] + m_offset;
+}
+
+int CRegExp::GetSubStart(const std::string& subName) const
+{
+ return GetSubStart(GetNamedSubPatternNumber(subName.c_str()));
+}
+
+int CRegExp::GetSubLength(int iSub) const
+{
+ if (!IsValidSubNumber(iSub))
+ return -1;
+
+ return m_iOvector[(iSub*2)+1] - m_iOvector[(iSub*2)];
+}
+
+int CRegExp::GetSubLength(const std::string& subName) const
+{
+ return GetSubLength(GetNamedSubPatternNumber(subName.c_str()));
+}
+
+std::string CRegExp::GetMatch(int iSub /* = 0 */) const
+{
+ if (!IsValidSubNumber(iSub))
+ return "";
+
+ int pos = m_iOvector[(iSub*2)];
+ int len = m_iOvector[(iSub*2)+1] - pos;
+ if (pos < 0 || len <= 0)
+ return "";
+
+ return m_subject.substr(pos, len);
+}
+
+std::string CRegExp::GetMatch(const std::string& subName) const
+{
+ return GetMatch(GetNamedSubPatternNumber(subName.c_str()));
+}
+
+bool CRegExp::GetNamedSubPattern(const char* strName, std::string& strMatch) const
+{
+ strMatch.clear();
+ int iSub = pcre_get_stringnumber(m_re, strName);
+ if (!IsValidSubNumber(iSub))
+ return false;
+ strMatch = GetMatch(iSub);
+ return true;
+}
+
+int CRegExp::GetNamedSubPatternNumber(const char* strName) const
+{
+ return pcre_get_stringnumber(m_re, strName);
+}
+
+void CRegExp::DumpOvector(int iLog /* = LOGDEBUG */)
+{
+ if (iLog < LOGDEBUG || iLog > LOGNONE)
+ return;
+
+ CStdString str = "{";
+ int size = GetSubCount(); // past the subpatterns is junk
+ for (int i = 0; i <= size; i++)
+ {
+ CStdString t = StringUtils::Format("[%i,%i]", m_iOvector[(i*2)], m_iOvector[(i*2)+1]);
+ if (i != size)
+ t += ",";
+ str += t;
+ }
+ str += "}";
+ CLog::Log(iLog, "regexp ovector=%s", str.c_str());
+}
+
+void CRegExp::Cleanup()
+{
+ if (m_re)
+ {
+ pcre_free(m_re);
+ m_re = NULL;
+ }
+
+ if (m_sd)
+ {
+ pcre_free_study(m_sd);
+ m_sd = NULL;
+ }
+
+#ifdef PCRE_HAS_JIT_CODE
+ if (m_jitStack)
+ {
+ pcre_jit_stack_free(m_jitStack);
+ m_jitStack = NULL;
+ }
+#endif
+}
+
+inline bool CRegExp::IsValidSubNumber(int iSub) const
+{
+ return iSub >= 0 && iSub <= m_iMatchCount && iSub <= m_MaxNumOfBackrefrences;
+}
+
+
+bool CRegExp::IsUtf8Supported(void)
+{
+ if (m_Utf8Supported == -1)
+ {
+ if (pcre_config(PCRE_CONFIG_UTF8, &m_Utf8Supported) != 0)
+ m_Utf8Supported = 0;
+ }
+
+ return m_Utf8Supported == 1;
+}
+
+bool CRegExp::AreUnicodePropertiesSupported(void)
+{
+#if defined(PCRE_CONFIG_UNICODE_PROPERTIES) && PCRE_UCP != 0
+ if (m_UcpSupported == -1)
+ {
+ if (pcre_config(PCRE_CONFIG_UNICODE_PROPERTIES, &m_UcpSupported) != 0)
+ m_UcpSupported = 0;
+ }
+#endif
+
+ return m_UcpSupported == 1;
+}
+
+bool CRegExp::LogCheckUtf8Support(void)
+{
+ bool utf8FullSupport = true;
+
+ if (!CRegExp::IsUtf8Supported())
+ {
+ utf8FullSupport = false;
+ CLog::Log(LOGWARNING, "UTF-8 is not supported in PCRE lib, support for national symbols is limited!");
+ }
+
+ if (!CRegExp::AreUnicodePropertiesSupported())
+ {
+ utf8FullSupport = false;
+ CLog::Log(LOGWARNING, "Unicode properties are not enabled in PCRE lib, support for national symbols may be limited!");
+ }
+
+ if (!utf8FullSupport)
+ {
+ CLog::Log(LOGNOTICE, "Consider installing PCRE lib version 8.10 or later with enabled Unicode properties and UTF-8 support. Your PCRE lib version: %s", PCRE::pcre_version());
+#if PCRE_UCP == 0
+ CLog::Log(LOGNOTICE, "You will need to rebuild XBMC after PCRE lib update.");
+#endif
+ }
+
+ return utf8FullSupport;
+}
+
+bool CRegExp::IsJitSupported(void)
+{
+ if (m_JitSupported == -1)
+ {
+#ifdef PCRE_HAS_JIT_CODE
+ if (pcre_config(PCRE_CONFIG_JIT, &m_JitSupported) != 0)
+#endif
+ m_JitSupported = 0;
+ }
+
+ return m_JitSupported == 1;
+}
diff --git a/src/utils/RegExp.h b/src/utils/RegExp.h
new file mode 100644
index 0000000000..69483494ec
--- /dev/null
+++ b/src/utils/RegExp.h
@@ -0,0 +1,182 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef REGEXP_H
+#define REGEXP_H
+
+#include <string>
+#include <vector>
+
+namespace PCRE {
+struct real_pcre_jit_stack; // forward declaration for PCRE without JIT
+typedef struct real_pcre_jit_stack pcre_jit_stack;
+#ifdef TARGET_WINDOWS
+#define PCRE_STATIC 1
+#ifdef _DEBUG
+#pragma comment(lib, "pcred.lib")
+#else // ! _DEBUG
+#pragma comment(lib, "pcre.lib")
+#endif // ! _DEBUG
+#endif // TARGET_WINDOWS
+#include <pcre.h>
+}
+
+class CRegExp
+{
+public:
+ enum studyMode
+ {
+ NoStudy = 0, // do not study expression
+ StudyRegExp = 1, // study expression (slower compilation, faster find)
+ StudyWithJitComp // study expression and JIT-compile it, if possible (heavyweight optimization)
+ };
+ enum utf8Mode
+ {
+ autoUtf8 = -1, // analyze regexp for UTF-8 multi-byte chars, for Unicode codes > 0xFF
+ // or explicit Unicode properties (\p, \P and \X), enable UTF-8 mode if any of them are found
+ asciiOnly = 0, // process regexp and strings as single-byte encoded strings
+ forceUtf8 = 1 // enable UTF-8 mode (with Unicode properties)
+ };
+
+ static const int m_MaxNumOfBackrefrences = 20;
+ /**
+ * @param caseless (optional) Matching will be case insensitive if set to true
+ * or case sensitive if set to false
+ * @param utf8 (optional) Control UTF-8 processing
+ */
+ CRegExp(bool caseless = false, utf8Mode utf8 = asciiOnly);
+ /**
+ * Create new CRegExp object and compile regexp expression in one step
+ * @warning Use only with hardcoded regexp when you're sure that regexp is compiled without errors
+ * @param caseless Matching will be case insensitive if set to true
+ * or case sensitive if set to false
+ * @param utf8 Control UTF-8 processing
+ * @param re The regular expression
+ * @param study (optional) Controls study of expression, useful if expression will be used
+ * several times
+ */
+ CRegExp(bool caseless, utf8Mode utf8, const char *re, studyMode study = NoStudy);
+
+ CRegExp(const CRegExp& re);
+ ~CRegExp();
+
+ /**
+ * Compile (prepare) regular expression
+ * @param re The regular expression
+ * @param study (optional) Controls study of expression, useful if expression will be used
+ * several times
+ * @return true on success, false on any error
+ */
+ bool RegComp(const char *re, studyMode study = NoStudy);
+
+ /**
+ * Compile (prepare) regular expression
+ * @param re The regular expression
+ * @param study (optional) Controls study of expression, useful if expression will be used
+ * several times
+ * @return true on success, false on any error
+ */
+ bool RegComp(const std::string& re, studyMode study = NoStudy)
+ { return RegComp(re.c_str(), study); }
+
+ /**
+ * Find first match of regular expression in given string
+ * @param str The string to match against regular expression
+ * @param startoffset (optional) The string offset to start matching
+ * @param maxNumberOfCharsToTest (optional) The maximum number of characters to test (match) in
+ * string. If set to -1 string checked up to the end.
+ * @return staring position of match in string, negative value in case of error or no match
+ */
+ int RegFind(const char* str, unsigned int startoffset = 0, int maxNumberOfCharsToTest = -1);
+ /**
+ * Find first match of regular expression in given string
+ * @param str The string to match against regular expression
+ * @param startoffset (optional) The string offset to start matching
+ * @param maxNumberOfCharsToTest (optional) The maximum number of characters to test (match) in
+ * string. If set to -1 string checked up to the end.
+ * @return staring position of match in string, negative value in case of error or no match
+ */
+ int RegFind(const std::string& str, unsigned int startoffset = 0, int maxNumberOfCharsToTest = -1)
+ { return PrivateRegFind(str.length(), str.c_str(), startoffset, maxNumberOfCharsToTest); }
+ std::string GetReplaceString(const std::string& sReplaceExp) const;
+ int GetFindLen() const
+ {
+ if (!m_re || !m_bMatched)
+ return 0;
+
+ return (m_iOvector[1] - m_iOvector[0]);
+ };
+ int GetSubCount() const { return m_iMatchCount - 1; } // PCRE returns the number of sub-patterns + 1
+ int GetSubStart(int iSub) const;
+ int GetSubStart(const std::string& subName) const;
+ int GetSubLength(int iSub) const;
+ int GetSubLength(const std::string& subName) const;
+ int GetCaptureTotal() const;
+ std::string GetMatch(int iSub = 0) const;
+ std::string GetMatch(const std::string& subName) const;
+ const std::string& GetPattern() const { return m_pattern; }
+ bool GetNamedSubPattern(const char* strName, std::string& strMatch) const;
+ int GetNamedSubPatternNumber(const char* strName) const;
+ void DumpOvector(int iLog);
+ /**
+ * Check is RegExp object is ready for matching
+ * @return true if RegExp object is ready for matching, false otherwise
+ */
+ inline bool IsCompiled(void) const
+ { return !m_pattern.empty(); }
+ CRegExp& operator= (const CRegExp& re);
+ static bool IsUtf8Supported(void);
+ static bool AreUnicodePropertiesSupported(void);
+ static bool LogCheckUtf8Support(void);
+ static bool IsJitSupported(void);
+
+private:
+ int PrivateRegFind(size_t bufferLen, const char *str, unsigned int startoffset = 0, int maxNumberOfCharsToTest = -1);
+ void InitValues(bool caseless = false, CRegExp::utf8Mode utf8 = asciiOnly);
+ static bool requireUtf8(const std::string& regexp);
+ static int readCharXCode(const std::string& regexp, size_t& pos);
+ static bool isCharClassWithUnicode(const std::string& regexp, size_t& pos);
+
+ void Cleanup();
+ inline bool IsValidSubNumber(int iSub) const;
+
+ PCRE::pcre* m_re;
+ PCRE::pcre_extra* m_sd;
+ static const int OVECCOUNT=(m_MaxNumOfBackrefrences + 1) * 3;
+ unsigned int m_offset;
+ int m_iOvector[OVECCOUNT];
+ utf8Mode m_utf8Mode;
+ int m_iMatchCount;
+ int m_iOptions;
+ bool m_jitCompiled;
+ bool m_bMatched;
+ PCRE::pcre_jit_stack* m_jitStack;
+ std::string m_subject;
+ std::string m_pattern;
+ static int m_Utf8Supported;
+ static int m_UcpSupported;
+ static int m_JitSupported;
+};
+
+typedef std::vector<CRegExp> VECCREGEXP;
+
+#endif
+
diff --git a/src/utils/RingBuffer.cpp b/src/utils/RingBuffer.cpp
new file mode 100644
index 0000000000..1afc8940a2
--- /dev/null
+++ b/src/utils/RingBuffer.cpp
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2010-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "RingBuffer.h"
+#include "threads/SingleLock.h"
+
+#include <cstring>
+#include <cstdlib>
+#include <algorithm>
+
+/* Constructor */
+CRingBuffer::CRingBuffer()
+{
+ m_buffer = NULL;
+ m_size = 0;
+ m_readPtr = 0;
+ m_writePtr = 0;
+ m_fillCount = 0;
+}
+
+/* Destructor */
+CRingBuffer::~CRingBuffer()
+{
+ Destroy();
+}
+
+/* Create a ring buffer with the specified 'size' */
+bool CRingBuffer::Create(unsigned int size)
+{
+ CSingleLock lock(m_critSection);
+ m_buffer = (char*)malloc(size);
+ if (m_buffer != NULL)
+ {
+ m_size = size;
+ return true;
+ }
+ return false;
+}
+
+/* Free the ring buffer and set all values to NULL or 0 */
+void CRingBuffer::Destroy()
+{
+ CSingleLock lock(m_critSection);
+ if (m_buffer != NULL)
+ {
+ free(m_buffer);
+ m_buffer = NULL;
+ }
+ m_size = 0;
+ m_readPtr = 0;
+ m_writePtr = 0;
+ m_fillCount = 0;
+}
+
+/* Clear the ring buffer */
+void CRingBuffer::Clear()
+{
+ CSingleLock lock(m_critSection);
+ m_readPtr = 0;
+ m_writePtr = 0;
+ m_fillCount = 0;
+}
+
+/* Read in data from the ring buffer to the supplied buffer 'buf'. The amount
+ * read in is specified by 'size'.
+ */
+bool CRingBuffer::ReadData(char *buf, unsigned int size)
+{
+ CSingleLock lock(m_critSection);
+ if (size > m_fillCount)
+ {
+ return false;
+ }
+ if (size + m_readPtr > m_size)
+ {
+ unsigned int chunk = m_size - m_readPtr;
+ memcpy(buf, m_buffer + m_readPtr, chunk);
+ memcpy(buf + chunk, m_buffer, size - chunk);
+ m_readPtr = size - chunk;
+ }
+ else
+ {
+ memcpy(buf, m_buffer + m_readPtr, size);
+ m_readPtr += size;
+ }
+ if (m_readPtr == m_size)
+ m_readPtr = 0;
+ m_fillCount -= size;
+ return true;
+}
+
+/* Read in data from the ring buffer to another ring buffer object specified by
+ * 'rBuf'.
+ */
+bool CRingBuffer::ReadData(CRingBuffer &rBuf, unsigned int size)
+{
+ CSingleLock lock(m_critSection);
+ if (rBuf.getBuffer() == NULL)
+ rBuf.Create(size);
+
+ bool bOk = size <= rBuf.getMaxWriteSize() && size <= getMaxReadSize();
+ if (bOk)
+ {
+ unsigned int chunksize = std::min(size, m_size - m_readPtr);
+ bOk = rBuf.WriteData(&getBuffer()[m_readPtr], chunksize);
+ if (bOk && chunksize < size)
+ bOk = rBuf.WriteData(&getBuffer()[0], size - chunksize);
+ if (bOk)
+ SkipBytes(size);
+ }
+
+ return bOk;
+}
+
+/* Write data to ring buffer from buffer specified in 'buf'. Amount read in is
+ * specified by 'size'.
+ */
+bool CRingBuffer::WriteData(const char *buf, unsigned int size)
+{
+ CSingleLock lock(m_critSection);
+ if (size > m_size - m_fillCount)
+ {
+ return false;
+ }
+ if (size + m_writePtr > m_size)
+ {
+ unsigned int chunk = m_size - m_writePtr;
+ memcpy(m_buffer + m_writePtr, buf, chunk);
+ memcpy(m_buffer, buf + chunk, size - chunk);
+ m_writePtr = size - chunk;
+ }
+ else
+ {
+ memcpy(m_buffer + m_writePtr, buf, size);
+ m_writePtr += size;
+ }
+ if (m_writePtr == m_size)
+ m_writePtr = 0;
+ m_fillCount += size;
+ return true;
+}
+
+/* Write data to ring buffer from another ring buffer object specified by
+ * 'rBuf'.
+ */
+bool CRingBuffer::WriteData(CRingBuffer &rBuf, unsigned int size)
+{
+ CSingleLock lock(m_critSection);
+ if (m_buffer == NULL)
+ Create(size);
+
+ bool bOk = size <= rBuf.getMaxReadSize() && size <= getMaxWriteSize();
+ if (bOk)
+ {
+ unsigned int readpos = rBuf.getReadPtr();
+ unsigned int chunksize = std::min(size, rBuf.getSize() - readpos);
+ bOk = WriteData(&rBuf.getBuffer()[readpos], chunksize);
+ if (bOk && chunksize < size)
+ bOk = WriteData(&rBuf.getBuffer()[0], size - chunksize);
+ }
+
+ return bOk;
+}
+
+/* Skip bytes in buffer to be read */
+bool CRingBuffer::SkipBytes(int skipSize)
+{
+ CSingleLock lock(m_critSection);
+ if (skipSize < 0)
+ {
+ return false; // skipping backwards is not supported
+ }
+
+ unsigned int size = skipSize;
+ if (size > m_fillCount)
+ {
+ return false;
+ }
+ if (size + m_readPtr > m_size)
+ {
+ unsigned int chunk = m_size - m_readPtr;
+ m_readPtr = size - chunk;
+ }
+ else
+ {
+ m_readPtr += size;
+ }
+ if (m_readPtr == m_size)
+ m_readPtr = 0;
+ m_fillCount -= size;
+ return true;
+}
+
+/* Append all content from ring buffer 'rBuf' to this ring buffer */
+bool CRingBuffer::Append(CRingBuffer &rBuf)
+{
+ return WriteData(rBuf, rBuf.getMaxReadSize());
+}
+
+/* Copy all content from ring buffer 'rBuf' to this ring buffer overwriting any existing data */
+bool CRingBuffer::Copy(CRingBuffer &rBuf)
+{
+ Clear();
+ return Append(rBuf);
+}
+
+/* Our various 'get' methods */
+char *CRingBuffer::getBuffer()
+{
+ return m_buffer;
+}
+
+unsigned int CRingBuffer::getSize()
+{
+ CSingleLock lock(m_critSection);
+ return m_size;
+}
+
+unsigned int CRingBuffer::getReadPtr() const
+{
+ return m_readPtr;
+}
+
+unsigned int CRingBuffer::getWritePtr()
+{
+ CSingleLock lock(m_critSection);
+ return m_writePtr;
+}
+
+unsigned int CRingBuffer::getMaxReadSize()
+{
+ CSingleLock lock(m_critSection);
+ return m_fillCount;
+}
+
+unsigned int CRingBuffer::getMaxWriteSize()
+{
+ CSingleLock lock(m_critSection);
+ return m_size - m_fillCount;
+}
diff --git a/src/utils/RingBuffer.h b/src/utils/RingBuffer.h
new file mode 100644
index 0000000000..2264e8e06a
--- /dev/null
+++ b/src/utils/RingBuffer.h
@@ -0,0 +1,51 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "threads/CriticalSection.h"
+
+class CRingBuffer
+{
+ CCriticalSection m_critSection;
+ char *m_buffer;
+ unsigned int m_size;
+ unsigned int m_readPtr;
+ unsigned int m_writePtr;
+ unsigned int m_fillCount;
+public:
+ CRingBuffer();
+ ~CRingBuffer();
+ bool Create(unsigned int size);
+ void Destroy();
+ void Clear();
+ bool ReadData(char *buf, unsigned int size);
+ bool ReadData(CRingBuffer &rBuf, unsigned int size);
+ bool WriteData(const char *buf, unsigned int size);
+ bool WriteData(CRingBuffer &rBuf, unsigned int size);
+ bool SkipBytes(int skipSize);
+ bool Append(CRingBuffer &rBuf);
+ bool Copy(CRingBuffer &rBuf);
+ char *getBuffer();
+ unsigned int getSize();
+ unsigned int getReadPtr() const;
+ unsigned int getWritePtr();
+ unsigned int getMaxReadSize();
+ unsigned int getMaxWriteSize();
+};
diff --git a/src/utils/RssManager.cpp b/src/utils/RssManager.cpp
new file mode 100644
index 0000000000..b4fd24e353
--- /dev/null
+++ b/src/utils/RssManager.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "RssManager.h"
+#include "addons/AddonInstaller.h"
+#include "addons/AddonManager.h"
+#include "dialogs/GUIDialogYesNo.h"
+#include "filesystem/File.h"
+#include "interfaces/Builtins.h"
+#include "profiles/ProfilesManager.h"
+#include "settings/lib/Setting.h"
+#include "threads/SingleLock.h"
+#include "utils/log.h"
+#include "utils/RssReader.h"
+#include "utils/StringUtils.h"
+
+using namespace std;
+using namespace XFILE;
+
+CRssManager::CRssManager()
+{
+ m_bActive = false;
+}
+
+CRssManager::~CRssManager()
+{
+ Stop();
+}
+
+CRssManager& CRssManager::Get()
+{
+ static CRssManager sRssManager;
+ return sRssManager;
+}
+
+void CRssManager::OnSettingsLoaded()
+{
+ Load();
+}
+
+void CRssManager::OnSettingsUnloaded()
+{
+ Clear();
+}
+
+void CRssManager::OnSettingAction(const CSetting *setting)
+{
+ if (setting == NULL)
+ return;
+
+ const std::string &settingId = setting->GetId();
+ if (settingId == "lookandfeel.rssedit")
+ {
+ ADDON::AddonPtr addon;
+ ADDON::CAddonMgr::Get().GetAddon("script.rss.editor",addon);
+ if (!addon)
+ {
+ if (!CGUIDialogYesNo::ShowAndGetInput(g_localizeStrings.Get(24076), g_localizeStrings.Get(24100), "RSS Editor", g_localizeStrings.Get(24101)))
+ return;
+ CAddonInstaller::Get().Install("script.rss.editor", true, "", false);
+ }
+ CBuiltins::Execute("RunScript(script.rss.editor)");
+ }
+}
+
+void CRssManager::Start()
+ {
+ m_bActive = true;
+}
+
+void CRssManager::Stop()
+{
+ CSingleLock lock(m_critical);
+ m_bActive = false;
+ for (unsigned int i = 0; i < m_readers.size(); i++)
+ {
+ if (m_readers[i].reader)
+ delete m_readers[i].reader;
+ }
+ m_readers.clear();
+}
+
+bool CRssManager::Load()
+{
+ CSingleLock lock(m_critical);
+ string rssXML = CProfilesManager::Get().GetUserDataItem("RssFeeds.xml");
+ if (!CFile::Exists(rssXML))
+ return false;
+
+ CXBMCTinyXML rssDoc;
+ if (!rssDoc.LoadFile(rssXML))
+ {
+ CLog::Log(LOGERROR, "CRssManager: error loading %s, Line %d\n%s", rssXML.c_str(), rssDoc.ErrorRow(), rssDoc.ErrorDesc());
+ return false;
+ }
+
+ const TiXmlElement *pRootElement = rssDoc.RootElement();
+ if (pRootElement == NULL || !StringUtils::EqualsNoCase(pRootElement->ValueStr(), "rssfeeds"))
+ {
+ CLog::Log(LOGERROR, "CRssManager: error loading %s, no <rssfeeds> node", rssXML.c_str());
+ return false;
+ }
+
+ m_mapRssUrls.clear();
+ const TiXmlElement* pSet = pRootElement->FirstChildElement("set");
+ while (pSet != NULL)
+ {
+ int iId;
+ if (pSet->QueryIntAttribute("id", &iId) == TIXML_SUCCESS)
+ {
+ RssSet set;
+ set.rtl = pSet->Attribute("rtl") != NULL && strcasecmp(pSet->Attribute("rtl"), "true") == 0;
+ const TiXmlElement* pFeed = pSet->FirstChildElement("feed");
+ while (pFeed != NULL)
+ {
+ int iInterval;
+ if (pFeed->QueryIntAttribute("updateinterval", &iInterval) != TIXML_SUCCESS)
+ {
+ iInterval = 30; // default to 30 min
+ CLog::Log(LOGDEBUG, "CRssManager: no interval set, default to 30!");
+ }
+
+ if (pFeed->FirstChild() != NULL)
+ {
+ // TODO: UTF-8: Do these URLs need to be converted to UTF-8?
+ // What about the xml encoding?
+ string strUrl = pFeed->FirstChild()->ValueStr();
+ set.url.push_back(strUrl);
+ set.interval.push_back(iInterval);
+ }
+ pFeed = pFeed->NextSiblingElement("feed");
+ }
+
+ m_mapRssUrls.insert(make_pair(iId,set));
+ }
+ else
+ CLog::Log(LOGERROR, "CRssManager: found rss url set with no id in RssFeeds.xml, ignored");
+
+ pSet = pSet->NextSiblingElement("set");
+ }
+
+ return true;
+}
+
+bool CRssManager::Reload()
+{
+ Stop();
+ if (!Load())
+ return false;
+ Start();
+
+ return true;
+}
+
+void CRssManager::Clear()
+{
+ CSingleLock lock(m_critical);
+ m_mapRssUrls.clear();
+}
+
+// returns true if the reader doesn't need creating, false otherwise
+bool CRssManager::GetReader(int controlID, int windowID, IRssObserver* observer, CRssReader *&reader)
+{
+ CSingleLock lock(m_critical);
+ // check to see if we've already created this reader
+ for (unsigned int i = 0; i < m_readers.size(); i++)
+ {
+ if (m_readers[i].controlID == controlID && m_readers[i].windowID == windowID)
+ {
+ reader = m_readers[i].reader;
+ reader->SetObserver(observer);
+ reader->UpdateObserver();
+ return true;
+ }
+ }
+ // need to create a new one
+ READERCONTROL readerControl;
+ readerControl.controlID = controlID;
+ readerControl.windowID = windowID;
+ reader = readerControl.reader = new CRssReader;
+ m_readers.push_back(readerControl);
+ return false;
+}
diff --git a/src/utils/RssManager.h b/src/utils/RssManager.h
new file mode 100644
index 0000000000..36b8e85bc6
--- /dev/null
+++ b/src/utils/RssManager.h
@@ -0,0 +1,80 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <map>
+#include <vector>
+#include <string>
+
+#include "threads/CriticalSection.h"
+
+#include "settings/lib/ISettingCallback.h"
+#include "settings/lib/ISettingsHandler.h"
+
+class CRssReader;
+class IRssObserver;
+
+typedef struct
+{
+ bool rtl;
+ std::vector<int> interval;
+ std::vector<std::string> url;
+} RssSet;
+typedef std::map<int, RssSet> RssUrls;
+
+class CRssManager : public ISettingCallback, public ISettingsHandler
+{
+public:
+ static CRssManager& Get();
+
+ virtual void OnSettingsLoaded();
+ virtual void OnSettingsUnloaded();
+
+ virtual void OnSettingAction(const CSetting *setting);
+
+ void Start();
+ void Stop();
+ bool Load();
+ bool Reload();
+ void Clear();
+ bool IsActive() const { return m_bActive; }
+
+ bool GetReader(int controlID, int windowID, IRssObserver* observer, CRssReader *&reader);
+ const RssUrls& GetUrls() const { return m_mapRssUrls; }
+
+protected:
+ CRssManager();
+ ~CRssManager();
+
+private:
+ CRssManager(const CRssManager&);
+ CRssManager& operator=(const CRssManager&);
+ struct READERCONTROL
+ {
+ int controlID;
+ int windowID;
+ CRssReader *reader;
+ };
+
+ std::vector<READERCONTROL> m_readers;
+ RssUrls m_mapRssUrls;
+ bool m_bActive;
+ CCriticalSection m_critical;
+};
diff --git a/src/utils/RssReader.cpp b/src/utils/RssReader.cpp
new file mode 100644
index 0000000000..53831562db
--- /dev/null
+++ b/src/utils/RssReader.cpp
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "network/Network.h"
+#include "threads/SystemClock.h"
+#include "RssReader.h"
+#include "utils/HTMLUtil.h"
+#include "Application.h"
+#include "CharsetConverter.h"
+#include "StringUtils.h"
+#include "URL.h"
+#include "filesystem/File.h"
+#include "filesystem/CurlFile.h"
+#if defined(TARGET_DARWIN)
+#include "osx/CocoaInterface.h"
+#endif
+#include "settings/AdvancedSettings.h"
+#include "guilib/LocalizeStrings.h"
+#include "guilib/GUIRSSControl.h"
+#include "utils/TimeUtils.h"
+#include "threads/SingleLock.h"
+#include "log.h"
+#include "utils/FileUtils.h"
+
+#define RSS_COLOR_BODY 0
+#define RSS_COLOR_HEADLINE 1
+#define RSS_COLOR_CHANNEL 2
+
+using namespace std;
+using namespace XFILE;
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+CRssReader::CRssReader() : CThread("RSSReader")
+{
+ m_pObserver = NULL;
+ m_spacesBetweenFeeds = 0;
+ m_bIsRunning = false;
+ m_SavedScrollPos = 0;
+ m_rtlText = false;
+ m_requestRefresh = false;
+}
+
+CRssReader::~CRssReader()
+{
+ if (m_pObserver)
+ m_pObserver->OnFeedRelease();
+ StopThread();
+ for (unsigned int i = 0; i < m_vecTimeStamps.size(); i++)
+ delete m_vecTimeStamps[i];
+}
+
+void CRssReader::Create(IRssObserver* aObserver, const vector<string>& aUrls, const vector<int> &times, int spacesBetweenFeeds, bool rtl)
+{
+ CSingleLock lock(m_critical);
+
+ m_pObserver = aObserver;
+ m_spacesBetweenFeeds = spacesBetweenFeeds;
+ m_vecUrls = aUrls;
+ m_strFeed.resize(aUrls.size());
+ m_strColors.resize(aUrls.size());
+ // set update times
+ m_vecUpdateTimes = times;
+ m_rtlText = rtl;
+ m_requestRefresh = false;
+
+ // update each feed on creation
+ for (unsigned int i = 0; i < m_vecUpdateTimes.size(); ++i)
+ {
+ AddToQueue(i);
+ SYSTEMTIME* time = new SYSTEMTIME;
+ GetLocalTime(time);
+ m_vecTimeStamps.push_back(time);
+ }
+}
+
+void CRssReader::requestRefresh()
+{
+ m_requestRefresh = true;
+}
+
+void CRssReader::AddToQueue(int iAdd)
+{
+ CSingleLock lock(m_critical);
+ if (iAdd < (int)m_vecUrls.size())
+ m_vecQueue.push_back(iAdd);
+ if (!m_bIsRunning)
+ {
+ StopThread();
+ m_bIsRunning = true;
+ CThread::Create(false, THREAD_MINSTACKSIZE);
+ }
+}
+
+void CRssReader::OnExit()
+{
+ m_bIsRunning = false;
+}
+
+int CRssReader::GetQueueSize()
+{
+ CSingleLock lock(m_critical);
+ return m_vecQueue.size();
+}
+
+void CRssReader::Process()
+{
+ while (GetQueueSize())
+ {
+ CSingleLock lock(m_critical);
+
+ int iFeed = m_vecQueue.front();
+ m_vecQueue.erase(m_vecQueue.begin());
+
+ m_strFeed[iFeed].clear();
+ m_strColors[iFeed].clear();
+
+ CCurlFile http;
+ http.SetUserAgent(g_advancedSettings.m_userAgent);
+ http.SetTimeout(2);
+ std::string strXML;
+ std::string strUrl = m_vecUrls[iFeed];
+ lock.Leave();
+
+ int nRetries = 3;
+ CURL url(strUrl);
+ std::string fileCharset;
+
+ // we wait for the network to come up
+ if ((url.IsProtocol("http") || url.IsProtocol("https")) &&
+ !g_application.getNetwork().IsAvailable(true))
+ {
+ CLog::Log(LOGWARNING, "RSS: No network connection");
+ strXML = "<rss><item><title>"+g_localizeStrings.Get(15301)+"</title></item></rss>";
+ }
+ else
+ {
+ XbmcThreads::EndTime timeout(15000);
+ while (!m_bStop && nRetries > 0)
+ {
+ if (timeout.IsTimePast())
+ {
+ CLog::Log(LOGERROR, "Timeout whilst retrieving %s", strUrl.c_str());
+ http.Cancel();
+ break;
+ }
+ nRetries--;
+
+ if (!url.IsProtocol("http") && !url.IsProtocol("https"))
+ {
+ CFile file;
+ auto_buffer buffer;
+ if (file.LoadFile(strUrl, buffer) > 0)
+ {
+ strXML.assign(buffer.get(), buffer.length());
+ break;
+ }
+ }
+ else
+ if (http.Get(strUrl, strXML))
+ {
+ fileCharset = http.GetServerReportedCharset();
+ CLog::Log(LOGDEBUG, "Got rss feed: %s", strUrl.c_str());
+ break;
+ }
+ }
+ http.Cancel();
+ }
+ if (!strXML.empty() && m_pObserver)
+ {
+ // erase any <content:encoded> tags (also unsupported by tinyxml)
+ size_t iStart = strXML.find("<content:encoded>");
+ size_t iEnd = 0;
+ while (iStart != std::string::npos)
+ {
+ // get <content:encoded> end position
+ iEnd = strXML.find("</content:encoded>", iStart) + 18;
+
+ // erase the section
+ strXML = strXML.erase(iStart, iEnd - iStart);
+
+ iStart = strXML.find("<content:encoded>");
+ }
+
+ if (Parse(strXML, iFeed, fileCharset))
+ CLog::Log(LOGDEBUG, "Parsed rss feed: %s", strUrl.c_str());
+ }
+ }
+ UpdateObserver();
+}
+
+void CRssReader::getFeed(vecText &text)
+{
+ text.clear();
+ // double the spaces at the start of the set
+ for (int j = 0; j < m_spacesBetweenFeeds; j++)
+ text.push_back(L' ');
+ for (unsigned int i = 0; i < m_strFeed.size(); i++)
+ {
+ for (int j = 0; j < m_spacesBetweenFeeds; j++)
+ text.push_back(L' ');
+
+ for (unsigned int j = 0; j < m_strFeed[i].size(); j++)
+ {
+ character_t letter = m_strFeed[i][j] | ((m_strColors[i][j] - 48) << 16);
+ text.push_back(letter);
+ }
+ }
+}
+
+void CRssReader::AddTag(const std::string &aString)
+{
+ m_tagSet.push_back(aString);
+}
+
+void CRssReader::AddString(std::wstring aString, int aColour, int iFeed)
+{
+ if (m_rtlText)
+ m_strFeed[iFeed] = aString + m_strFeed[iFeed];
+ else
+ m_strFeed[iFeed] += aString;
+
+ size_t nStringLength = aString.size();
+
+ for (size_t i = 0;i < nStringLength;i++)
+ aString[i] = (CHAR) (48 + aColour);
+
+ if (m_rtlText)
+ m_strColors[iFeed] = aString + m_strColors[iFeed];
+ else
+ m_strColors[iFeed] += aString;
+}
+
+void CRssReader::GetNewsItems(TiXmlElement* channelXmlNode, int iFeed)
+{
+ HTML::CHTMLUtil html;
+
+ TiXmlElement * itemNode = channelXmlNode->FirstChildElement("item");
+ map <std::string, std::wstring> mTagElements;
+ typedef pair <std::string, std::wstring> StrPair;
+ list <std::string>::iterator i;
+
+ // Add the title tag in if we didn't pass any tags in at all
+ // Represents default behaviour before configurability
+
+ if (m_tagSet.empty())
+ AddTag("title");
+
+ while (itemNode > 0)
+ {
+ TiXmlNode* childNode = itemNode->FirstChild();
+ mTagElements.clear();
+ while (childNode > 0)
+ {
+ std::string strName = childNode->ValueStr();
+
+ for (i = m_tagSet.begin(); i != m_tagSet.end(); ++i)
+ {
+ if (!childNode->NoChildren() && *i == strName)
+ {
+ std::string htmlText = childNode->FirstChild()->ValueStr();
+
+ // This usually happens in right-to-left languages where they want to
+ // specify in the RSS body that the text should be RTL.
+ // <title>
+ // <div dir="RTL">��� ����: ���� �� �����</div>
+ // </title>
+ if (htmlText == "div" || htmlText == "span")
+ htmlText = childNode->FirstChild()->FirstChild()->ValueStr();
+
+ std::wstring unicodeText, unicodeText2;
+
+ g_charsetConverter.utf8ToW(htmlText, unicodeText2, m_rtlText);
+ html.ConvertHTMLToW(unicodeText2, unicodeText);
+
+ mTagElements.insert(StrPair(*i, unicodeText));
+ }
+ }
+ childNode = childNode->NextSibling();
+ }
+
+ int rsscolour = RSS_COLOR_HEADLINE;
+ for (i = m_tagSet.begin(); i != m_tagSet.end(); ++i)
+ {
+ map <std::string, std::wstring>::iterator j = mTagElements.find(*i);
+
+ if (j == mTagElements.end())
+ continue;
+
+ std::wstring& text = j->second;
+ AddString(text, rsscolour, iFeed);
+ rsscolour = RSS_COLOR_BODY;
+ text = L" - ";
+ AddString(text, rsscolour, iFeed);
+ }
+ itemNode = itemNode->NextSiblingElement("item");
+ }
+}
+
+bool CRssReader::Parse(const std::string& data, int iFeed, const std::string& charset)
+{
+ m_xml.Clear();
+ m_xml.Parse(data, charset);
+
+ CLog::Log(LOGDEBUG, "RSS feed encoding: %s", m_xml.GetUsedCharset().c_str());
+
+ return Parse(iFeed);
+}
+
+bool CRssReader::Parse(int iFeed)
+{
+ TiXmlElement* rootXmlNode = m_xml.RootElement();
+
+ if (!rootXmlNode)
+ return false;
+
+ TiXmlElement* rssXmlNode = NULL;
+
+ std::string strValue = rootXmlNode->ValueStr();
+ if (strValue.find("rss") != std::string::npos ||
+ strValue.find("rdf") != std::string::npos)
+ rssXmlNode = rootXmlNode;
+ else
+ {
+ // Unable to find root <rss> or <rdf> node
+ return false;
+ }
+
+ TiXmlElement* channelXmlNode = rssXmlNode->FirstChildElement("channel");
+ if (channelXmlNode)
+ {
+ TiXmlElement* titleNode = channelXmlNode->FirstChildElement("title");
+ if (titleNode && !titleNode->NoChildren())
+ {
+ std::string strChannel = titleNode->FirstChild()->Value();
+ std::wstring strChannelUnicode;
+ g_charsetConverter.utf8ToW(strChannel, strChannelUnicode, m_rtlText);
+ AddString(strChannelUnicode, RSS_COLOR_CHANNEL, iFeed);
+
+ AddString(L":", RSS_COLOR_CHANNEL, iFeed);
+ AddString(L" ", RSS_COLOR_CHANNEL, iFeed);
+ }
+
+ GetNewsItems(channelXmlNode,iFeed);
+ }
+
+ GetNewsItems(rssXmlNode,iFeed);
+
+ // avoid trailing ' - '
+ if (m_strFeed[iFeed].size() > 3 && m_strFeed[iFeed].substr(m_strFeed[iFeed].size() - 3) == L" - ")
+ {
+ if (m_rtlText)
+ {
+ m_strFeed[iFeed].erase(0, 3);
+ m_strColors[iFeed].erase(0, 3);
+ }
+ else
+ {
+ m_strFeed[iFeed].erase(m_strFeed[iFeed].length() - 3);
+ m_strColors[iFeed].erase(m_strColors[iFeed].length() - 3);
+ }
+ }
+ return true;
+}
+
+void CRssReader::SetObserver(IRssObserver *observer)
+{
+ m_pObserver = observer;
+}
+
+void CRssReader::UpdateObserver()
+{
+ if (!m_pObserver)
+ return;
+
+ vecText feed;
+ getFeed(feed);
+ if (feed.size() > 0)
+ {
+ CSingleLock lock(g_graphicsContext);
+ if (m_pObserver) // need to check again when locked to make sure observer wasnt removed
+ m_pObserver->OnFeedUpdate(feed);
+ }
+}
+
+void CRssReader::CheckForUpdates()
+{
+ SYSTEMTIME time;
+ GetLocalTime(&time);
+
+ for (unsigned int i = 0;i < m_vecUpdateTimes.size(); ++i )
+ {
+ if (m_requestRefresh ||
+ ((time.wDay * 24 * 60) + (time.wHour * 60) + time.wMinute) - ((m_vecTimeStamps[i]->wDay * 24 * 60) + (m_vecTimeStamps[i]->wHour * 60) + m_vecTimeStamps[i]->wMinute) > m_vecUpdateTimes[i])
+ {
+ CLog::Log(LOGDEBUG, "Updating RSS");
+ GetLocalTime(m_vecTimeStamps[i]);
+ AddToQueue(i);
+ }
+ }
+
+ m_requestRefresh = false;
+}
diff --git a/src/utils/RssReader.h b/src/utils/RssReader.h
new file mode 100644
index 0000000000..2cda7267f1
--- /dev/null
+++ b/src/utils/RssReader.h
@@ -0,0 +1,73 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <list>
+#include <string>
+#include <vector>
+
+#include "threads/CriticalSection.h"
+#include "threads/Thread.h"
+#include "utils/IRssObserver.h"
+#include "utils/XBMCTinyXML.h"
+
+class CRssReader : public CThread
+{
+public:
+ CRssReader();
+ virtual ~CRssReader();
+
+ void Create(IRssObserver* aObserver, const std::vector<std::string>& aUrl, const std::vector<int>& times, int spacesBetweenFeeds, bool rtl);
+ bool Parse(const std::string& data, int iFeed, const std::string& charset);
+ void getFeed(vecText &text);
+ void AddTag(const std::string &addTag);
+ void AddToQueue(int iAdd);
+ void UpdateObserver();
+ void SetObserver(IRssObserver* observer);
+ void CheckForUpdates();
+ void requestRefresh();
+ unsigned int m_SavedScrollPos;
+
+private:
+ void Process();
+ bool Parse(int iFeed);
+ void GetNewsItems(TiXmlElement* channelXmlNode, int iFeed);
+ void AddString(std::wstring aString, int aColour, int iFeed);
+ void UpdateFeed();
+ virtual void OnExit();
+ int GetQueueSize();
+
+ IRssObserver* m_pObserver;
+
+ std::vector<std::wstring> m_strFeed;
+ std::vector<std::wstring> m_strColors;
+ std::vector<SYSTEMTIME *> m_vecTimeStamps;
+ std::vector<int> m_vecUpdateTimes;
+ int m_spacesBetweenFeeds;
+ CXBMCTinyXML m_xml;
+ std::list<std::string> m_tagSet;
+ std::vector<std::string> m_vecUrls;
+ std::vector<int> m_vecQueue;
+ bool m_bIsRunning;
+ bool m_rtlText;
+ bool m_requestRefresh;
+
+ CCriticalSection m_critical;
+};
diff --git a/src/utils/SaveFileStateJob.cpp b/src/utils/SaveFileStateJob.cpp
new file mode 100644
index 0000000000..3be229bb38
--- /dev/null
+++ b/src/utils/SaveFileStateJob.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2010-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "SaveFileStateJob.h"
+#include "pvr/PVRManager.h"
+#include "pvr/recordings/PVRRecordings.h"
+#include "settings/MediaSettings.h"
+#include "network/upnp/UPnP.h"
+#include "StringUtils.h"
+#include "Variant.h"
+#include "URIUtils.h"
+#include "URL.h"
+#include "log.h"
+#include "video/VideoDatabase.h"
+#include "interfaces/AnnouncementManager.h"
+#include "Util.h"
+#include "guilib/GUIMessage.h"
+#include "guilib/GUIWindowManager.h"
+#include "GUIUserMessages.h"
+#include "music/MusicDatabase.h"
+
+bool CSaveFileStateJob::DoWork()
+{
+ std::string progressTrackingFile = m_item.GetPath();
+
+ if (m_item.HasVideoInfoTag() && StringUtils::StartsWith(m_item.GetVideoInfoTag()->m_strFileNameAndPath, "removable://"))
+ progressTrackingFile = m_item.GetVideoInfoTag()->m_strFileNameAndPath; // this variable contains removable:// suffixed by disc label+uniqueid or is empty if label not uniquely identified
+ else if (m_item.HasProperty("original_listitem_url"))
+ {
+ // only use original_listitem_url for Python, UPnP and Bluray sources
+ std::string original = m_item.GetProperty("original_listitem_url").asString();
+ if (URIUtils::IsPlugin(original) || URIUtils::IsUPnP(original) || URIUtils::IsBluray(m_item.GetPath()))
+ progressTrackingFile = original;
+ }
+
+ if (progressTrackingFile != "")
+ {
+#ifdef HAS_UPNP
+ // checks if UPnP server of this file is available and supports updating
+ if (URIUtils::IsUPnP(progressTrackingFile)
+ && UPNP::CUPnP::SaveFileState(m_item, m_bookmark, m_updatePlayCount)) {
+ return true;
+ }
+#endif
+ if (m_item.IsVideo())
+ {
+ std::string redactPath = CURL::GetRedacted(progressTrackingFile);
+ CLog::Log(LOGDEBUG, "%s - Saving file state for video item %s", __FUNCTION__, redactPath.c_str());
+
+ CVideoDatabase videodatabase;
+ if (!videodatabase.Open())
+ {
+ CLog::Log(LOGWARNING, "%s - Unable to open video database. Can not save file state!", __FUNCTION__);
+ }
+ else
+ {
+ bool updateListing = false;
+ // No resume & watched status for livetv
+ if (!m_item.IsLiveTV())
+ {
+ if (m_updatePlayCount)
+ {
+ CLog::Log(LOGDEBUG, "%s - Marking video item %s as watched", __FUNCTION__, redactPath.c_str());
+
+ // consider this item as played
+ videodatabase.IncrementPlayCount(m_item);
+ m_item.GetVideoInfoTag()->m_playCount++;
+
+ // PVR: Set recording's play count on the backend (if supported)
+ if (m_item.HasPVRRecordingInfoTag())
+ m_item.GetPVRRecordingInfoTag()->IncrementPlayCount();
+
+ m_item.SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, true);
+ updateListing = true;
+ }
+ else
+ videodatabase.UpdateLastPlayed(m_item);
+
+ if (!m_item.HasVideoInfoTag() || m_item.GetVideoInfoTag()->m_resumePoint.timeInSeconds != m_bookmark.timeInSeconds)
+ {
+ if (m_bookmark.timeInSeconds <= 0.0f)
+ videodatabase.ClearBookMarksOfFile(progressTrackingFile, CBookmark::RESUME);
+ else
+ videodatabase.AddBookMarkToFile(progressTrackingFile, m_bookmark, CBookmark::RESUME);
+ if (m_item.HasVideoInfoTag())
+ m_item.GetVideoInfoTag()->m_resumePoint = m_bookmark;
+
+ // PVR: Set/clear recording's resume bookmark on the backend (if supported)
+ if (m_item.HasPVRRecordingInfoTag())
+ {
+ PVR::CPVRRecording *recording = m_item.GetPVRRecordingInfoTag();
+ recording->SetLastPlayedPosition(m_bookmark.timeInSeconds <= 0.0f ? 0 : (int)m_bookmark.timeInSeconds);
+ recording->m_resumePoint = m_bookmark;
+ }
+
+ // UPnP announce resume point changes to clients
+ // however not if playcount is modified as that already announces
+ if (m_item.IsVideoDb() && !m_updatePlayCount)
+ {
+ CVariant data;
+ data["id"] = m_item.GetVideoInfoTag()->m_iDbId;
+ data["type"] = m_item.GetVideoInfoTag()->m_type;
+ ANNOUNCEMENT::CAnnouncementManager::Get().Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnUpdate", data);
+ }
+
+ updateListing = true;
+ }
+ }
+
+ if (m_videoSettings != CMediaSettings::Get().GetDefaultVideoSettings())
+ {
+ videodatabase.SetVideoSettings(progressTrackingFile, m_videoSettings);
+ }
+
+ if (m_item.HasVideoInfoTag() && m_item.GetVideoInfoTag()->HasStreamDetails())
+ {
+ CFileItem dbItem(m_item);
+
+ // Check whether the item's db streamdetails need updating
+ if (!videodatabase.GetStreamDetails(dbItem) || dbItem.GetVideoInfoTag()->m_streamDetails != m_item.GetVideoInfoTag()->m_streamDetails)
+ {
+ videodatabase.SetStreamDetailsForFile(m_item.GetVideoInfoTag()->m_streamDetails, progressTrackingFile);
+ updateListing = true;
+ }
+ }
+
+ // in order to properly update the the list, we need to update the stack item which is held in g_application.m_stackFileItemToUpdate
+ if (m_item.HasProperty("stackFileItemToUpdate"))
+ {
+ m_item = m_item_discstack; // as of now, the item is replaced by the discstack item
+ videodatabase.GetResumePoint(*m_item.GetVideoInfoTag());
+ }
+ videodatabase.Close();
+
+ if (updateListing)
+ {
+ CUtil::DeleteVideoDatabaseDirectoryCache();
+ CFileItemPtr msgItem(new CFileItem(m_item));
+ if (m_item.HasProperty("original_listitem_url"))
+ msgItem->SetPath(m_item.GetProperty("original_listitem_url").asString());
+ CGUIMessage message(GUI_MSG_NOTIFY_ALL, g_windowManager.GetActiveWindow(), 0, GUI_MSG_UPDATE_ITEM, 1, msgItem); // 1 to update the listing as well
+ g_windowManager.SendThreadMessage(message);
+ }
+ }
+ }
+
+ if (m_item.IsAudio())
+ {
+ std::string redactPath = CURL::GetRedacted(progressTrackingFile);
+ CLog::Log(LOGDEBUG, "%s - Saving file state for audio item %s", __FUNCTION__, redactPath.c_str());
+
+ if (m_updatePlayCount)
+ {
+#if 0
+ // Can't write to the musicdatabase while scanning for music info
+ CGUIDialogMusicScan *dialog = (CGUIDialogMusicScan *)g_windowManager.GetWindow(WINDOW_DIALOG_MUSIC_SCAN);
+ if (dialog && !dialog->IsDialogRunning())
+#endif
+ {
+ CMusicDatabase musicdatabase;
+ if (!musicdatabase.Open())
+ {
+ CLog::Log(LOGWARNING, "%s - Unable to open music database. Can not save file state!", __FUNCTION__);
+ }
+ else
+ {
+ // consider this item as played
+ CLog::Log(LOGDEBUG, "%s - Marking audio item %s as listened", __FUNCTION__, redactPath.c_str());
+
+ musicdatabase.IncrementPlayCount(m_item);
+ musicdatabase.Close();
+ }
+ }
+ }
+ }
+ }
+ return true;
+}
diff --git a/src/utils/SaveFileStateJob.h b/src/utils/SaveFileStateJob.h
new file mode 100644
index 0000000000..90b5e5702e
--- /dev/null
+++ b/src/utils/SaveFileStateJob.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef SAVE_FILE_STATE_H__
+#define SAVE_FILE_STATE_H__
+
+#include "Job.h"
+#include "FileItem.h"
+#include "video/Bookmark.h"
+#include "settings/VideoSettings.h"
+
+class CSaveFileStateJob : public CJob
+{
+ CFileItem m_item;
+ CFileItem m_item_discstack;
+ CBookmark m_bookmark;
+ bool m_updatePlayCount;
+ CVideoSettings m_videoSettings;
+public:
+ CSaveFileStateJob(const CFileItem& item,
+ const CFileItem& item_discstack,
+ const CBookmark& bookmark,
+ bool updatePlayCount,
+ const CVideoSettings &videoSettings)
+ : m_item(item),
+ m_item_discstack(item_discstack),
+ m_bookmark(bookmark),
+ m_updatePlayCount(updatePlayCount),
+ m_videoSettings(videoSettings) {}
+ virtual ~CSaveFileStateJob() {}
+ virtual bool DoWork();
+};
+
+#endif // SAVE_FILE_STATE_H__
diff --git a/src/utils/ScraperParser.cpp b/src/utils/ScraperParser.cpp
new file mode 100644
index 0000000000..4dc3b485b7
--- /dev/null
+++ b/src/utils/ScraperParser.cpp
@@ -0,0 +1,616 @@
+/*
+ * Copyright (C) 2012-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "ScraperParser.h"
+
+#include "addons/AddonManager.h"
+#include "RegExp.h"
+#include "HTMLUtil.h"
+#include "addons/Scraper.h"
+#include "URL.h"
+#include "Util.h"
+#include "utils/StringUtils.h"
+#include "log.h"
+#include "CharsetConverter.h"
+#include "utils/StringUtils.h"
+#include "utils/XSLTUtils.h"
+#include "utils/XMLUtils.h"
+#include <sstream>
+#include <cstring>
+
+using namespace std;
+using namespace ADDON;
+using namespace XFILE;
+
+CScraperParser::CScraperParser()
+{
+ m_pRootElement = NULL;
+ m_document = NULL;
+ m_SearchStringEncoding = "UTF-8";
+ m_scraper = NULL;
+ m_isNoop = true;
+}
+
+CScraperParser::CScraperParser(const CScraperParser& parser)
+{
+ m_pRootElement = NULL;
+ m_document = NULL;
+ m_SearchStringEncoding = "UTF-8";
+ m_scraper = NULL;
+ m_isNoop = true;
+ *this = parser;
+}
+
+CScraperParser &CScraperParser::operator=(const CScraperParser &parser)
+{
+ if (this != &parser)
+ {
+ Clear();
+ if (parser.m_document)
+ {
+ m_scraper = parser.m_scraper;
+ m_document = new CXBMCTinyXML(*parser.m_document);
+ LoadFromXML();
+ }
+ else
+ m_scraper = NULL;
+ }
+ return *this;
+}
+
+CScraperParser::~CScraperParser()
+{
+ Clear();
+}
+
+void CScraperParser::Clear()
+{
+ m_pRootElement = NULL;
+ delete m_document;
+
+ m_document = NULL;
+ m_strFile.clear();
+}
+
+bool CScraperParser::Load(const std::string& strXMLFile)
+{
+ Clear();
+
+ m_document = new CXBMCTinyXML();
+
+ if (!m_document)
+ return false;
+
+ m_strFile = strXMLFile;
+
+ if (m_document->LoadFile(strXMLFile))
+ return LoadFromXML();
+
+ delete m_document;
+ m_document = NULL;
+ return false;
+}
+
+bool CScraperParser::LoadFromXML()
+{
+ if (!m_document)
+ return false;
+
+ m_pRootElement = m_document->RootElement();
+ std::string strValue = m_pRootElement->ValueStr();
+ if (strValue == "scraper")
+ {
+ TiXmlElement* pChildElement = m_pRootElement->FirstChildElement("CreateSearchUrl");
+ if (pChildElement)
+ {
+ m_isNoop = false;
+ if (!(m_SearchStringEncoding = pChildElement->Attribute("SearchStringEncoding")))
+ m_SearchStringEncoding = "UTF-8";
+ }
+
+ pChildElement = m_pRootElement->FirstChildElement("CreateArtistSearchUrl");
+ if (pChildElement)
+ {
+ m_isNoop = false;
+ if (!(m_SearchStringEncoding = pChildElement->Attribute("SearchStringEncoding")))
+ m_SearchStringEncoding = "UTF-8";
+ }
+ pChildElement = m_pRootElement->FirstChildElement("CreateAlbumSearchUrl");
+ if (pChildElement)
+ {
+ m_isNoop = false;
+ if (!(m_SearchStringEncoding = pChildElement->Attribute("SearchStringEncoding")))
+ m_SearchStringEncoding = "UTF-8";
+ }
+
+ return true;
+ }
+
+ delete m_document;
+ m_document = NULL;
+ m_pRootElement = NULL;
+ return false;
+}
+
+void CScraperParser::ReplaceBuffers(std::string& strDest)
+{
+ // insert buffers
+ size_t iIndex;
+ for (int i=MAX_SCRAPER_BUFFERS-1; i>=0; i--)
+ {
+ iIndex = 0;
+ std::string temp = StringUtils::Format("$$%i",i+1);
+ while ((iIndex = strDest.find(temp,iIndex)) != std::string::npos)
+ {
+ strDest.replace(strDest.begin()+iIndex,strDest.begin()+iIndex+temp.size(),m_param[i]);
+ iIndex += m_param[i].length();
+ }
+ }
+ // insert settings
+ iIndex = 0;
+ while ((iIndex = strDest.find("$INFO[", iIndex)) != std::string::npos)
+ {
+ size_t iEnd = strDest.find("]", iIndex);
+ std::string strInfo = strDest.substr(iIndex+6, iEnd - iIndex - 6);
+ std::string strReplace;
+ if (m_scraper)
+ strReplace = m_scraper->GetSetting(strInfo);
+ strDest.replace(strDest.begin()+iIndex,strDest.begin()+iEnd+1,strReplace);
+ iIndex += strReplace.length();
+ }
+ // insert localize strings
+ iIndex = 0;
+ while ((iIndex = strDest.find("$LOCALIZE[", iIndex)) != std::string::npos)
+ {
+ size_t iEnd = strDest.find("]", iIndex);
+ std::string strInfo = strDest.substr(iIndex+10, iEnd - iIndex - 10);
+ std::string strReplace;
+ if (m_scraper)
+ strReplace = m_scraper->GetString(strtol(strInfo.c_str(),NULL,10));
+ strDest.replace(strDest.begin()+iIndex,strDest.begin()+iEnd+1,strReplace);
+ iIndex += strReplace.length();
+ }
+ iIndex = 0;
+ while ((iIndex = strDest.find("\\n",iIndex)) != std::string::npos)
+ strDest.replace(strDest.begin()+iIndex,strDest.begin()+iIndex+2,"\n");
+}
+
+void CScraperParser::ParseExpression(const std::string& input, std::string& dest, TiXmlElement* element, bool bAppend)
+{
+ std::string strOutput = XMLUtils::GetAttribute(element, "output");
+
+ TiXmlElement* pExpression = element->FirstChildElement("expression");
+ if (pExpression)
+ {
+ bool bInsensitive=true;
+ const char* sensitive = pExpression->Attribute("cs");
+ if (sensitive)
+ if (stricmp(sensitive,"yes") == 0)
+ bInsensitive=false; // match case sensitive
+
+ CRegExp::utf8Mode eUtf8 = CRegExp::autoUtf8;
+ const char* const strUtf8 = pExpression->Attribute("utf8");
+ if (strUtf8)
+ {
+ if (stricmp(strUtf8, "yes") == 0)
+ eUtf8 = CRegExp::forceUtf8;
+ else if (stricmp(strUtf8, "no") == 0)
+ eUtf8 = CRegExp::asciiOnly;
+ else if (stricmp(strUtf8, "auto") == 0)
+ eUtf8 = CRegExp::autoUtf8;
+ }
+
+ CRegExp reg(bInsensitive, eUtf8);
+ std::string strExpression;
+ if (pExpression->FirstChild())
+ strExpression = pExpression->FirstChild()->Value();
+ else
+ strExpression = "(.*)";
+ ReplaceBuffers(strExpression);
+ ReplaceBuffers(strOutput);
+
+ if (!reg.RegComp(strExpression.c_str()))
+ {
+ return;
+ }
+
+ bool bRepeat = false;
+ const char* szRepeat = pExpression->Attribute("repeat");
+ if (szRepeat)
+ if (stricmp(szRepeat,"yes") == 0)
+ bRepeat = true;
+
+ const char* szClear = pExpression->Attribute("clear");
+ if (szClear)
+ if (stricmp(szClear,"yes") == 0)
+ dest=""; // clear no matter if regexp fails
+
+ bool bClean[MAX_SCRAPER_BUFFERS];
+ GetBufferParams(bClean,pExpression->Attribute("noclean"),true);
+
+ bool bTrim[MAX_SCRAPER_BUFFERS];
+ GetBufferParams(bTrim,pExpression->Attribute("trim"),false);
+
+ bool bFixChars[MAX_SCRAPER_BUFFERS];
+ GetBufferParams(bFixChars,pExpression->Attribute("fixchars"),false);
+
+ bool bEncode[MAX_SCRAPER_BUFFERS];
+ GetBufferParams(bEncode,pExpression->Attribute("encode"),false);
+
+ int iOptional = -1;
+ pExpression->QueryIntAttribute("optional",&iOptional);
+
+ int iCompare = -1;
+ pExpression->QueryIntAttribute("compare",&iCompare);
+ if (iCompare > -1)
+ StringUtils::ToLower(m_param[iCompare-1]);
+ std::string curInput = input;
+ for (int iBuf=0;iBuf<MAX_SCRAPER_BUFFERS;++iBuf)
+ {
+ if (bClean[iBuf])
+ InsertToken(strOutput,iBuf+1,"!!!CLEAN!!!");
+ if (bTrim[iBuf])
+ InsertToken(strOutput,iBuf+1,"!!!TRIM!!!");
+ if (bFixChars[iBuf])
+ InsertToken(strOutput,iBuf+1,"!!!FIXCHARS!!!");
+ if (bEncode[iBuf])
+ InsertToken(strOutput,iBuf+1,"!!!ENCODE!!!");
+ }
+ int i = reg.RegFind(curInput.c_str());
+ while (i > -1 && (i < (int)curInput.size() || curInput.size() == 0))
+ {
+ if (!bAppend)
+ {
+ dest = "";
+ bAppend = true;
+ }
+ std::string strCurOutput=strOutput;
+
+ if (iOptional > -1) // check that required param is there
+ {
+ char temp[4];
+ sprintf(temp,"\\%i",iOptional);
+ std::string szParam = reg.GetReplaceString(temp);
+ CRegExp reg2;
+ reg2.RegComp("(.*)(\\\\\\(.*\\\\2.*)\\\\\\)(.*)");
+ int i2=reg2.RegFind(strCurOutput.c_str());
+ while (i2 > -1)
+ {
+ std::string szRemove(reg2.GetMatch(2));
+ int iRemove = szRemove.size();
+ int i3 = strCurOutput.find(szRemove);
+ if (!szParam.empty())
+ {
+ strCurOutput.erase(i3+iRemove,2);
+ strCurOutput.erase(i3,2);
+ }
+ else
+ strCurOutput.replace(strCurOutput.begin()+i3,strCurOutput.begin()+i3+iRemove+2,"");
+
+ i2 = reg2.RegFind(strCurOutput.c_str());
+ }
+ }
+
+ int iLen = reg.GetFindLen();
+ // nasty hack #1 - & means \0 in a replace string
+ StringUtils::Replace(strCurOutput, "&","!!!AMPAMP!!!");
+ std::string result = reg.GetReplaceString(strCurOutput.c_str());
+ if (!result.empty())
+ {
+ std::string strResult(result);
+ StringUtils::Replace(strResult, "!!!AMPAMP!!!","&");
+ Clean(strResult);
+ ReplaceBuffers(strResult);
+ if (iCompare > -1)
+ {
+ std::string strResultNoCase = strResult;
+ StringUtils::ToLower(strResultNoCase);
+ if (strResultNoCase.find(m_param[iCompare-1]) != std::string::npos)
+ dest += strResult;
+ }
+ else
+ dest += strResult;
+ }
+ if (bRepeat && iLen > 0)
+ {
+ curInput.erase(0,i+iLen>(int)curInput.size()?curInput.size():i+iLen);
+ i = reg.RegFind(curInput.c_str());
+ }
+ else
+ i = -1;
+ }
+ }
+}
+
+void CScraperParser::ParseXSLT(const std::string& input, std::string& dest, TiXmlElement* element, bool bAppend)
+{
+ TiXmlElement* pSheet = element->FirstChildElement();
+ if (pSheet)
+ {
+ XSLTUtils xsltUtils;
+ std::string strXslt;
+ strXslt << *pSheet;
+ ReplaceBuffers(strXslt);
+
+ if (!xsltUtils.SetInput(input))
+ CLog::Log(LOGDEBUG, "could not parse input XML");
+
+ if (!xsltUtils.SetStylesheet(strXslt))
+ CLog::Log(LOGDEBUG, "could not parse stylesheet XML");
+
+ xsltUtils.XSLTTransform(dest);
+ }
+}
+
+TiXmlElement *FirstChildScraperElement(TiXmlElement *element)
+{
+ for (TiXmlElement *child = element->FirstChildElement(); child; child = child->NextSiblingElement())
+ {
+ if (child->ValueStr() == "RegExp" || child->ValueStr() == "XSLT")
+ return child;
+ }
+ return NULL;
+}
+
+TiXmlElement *NextSiblingScraperElement(TiXmlElement *element)
+{
+ for (TiXmlElement *next = element->NextSiblingElement(); next; next = next->NextSiblingElement())
+ {
+ if (next->ValueStr() == "RegExp" || next->ValueStr() == "XSLT")
+ return next;
+ }
+ return NULL;
+}
+
+void CScraperParser::ParseNext(TiXmlElement* element)
+{
+ TiXmlElement* pReg = element;
+ while (pReg)
+ {
+ TiXmlElement* pChildReg = FirstChildScraperElement(pReg);
+ if (pChildReg)
+ ParseNext(pChildReg);
+ else
+ {
+ TiXmlElement* pChildReg = pReg->FirstChildElement("clear");
+ if (pChildReg)
+ ParseNext(pChildReg);
+ }
+
+ int iDest = 1;
+ bool bAppend = false;
+ const char* szDest = pReg->Attribute("dest");
+ if (szDest && strlen(szDest))
+ {
+ if (szDest[strlen(szDest)-1] == '+')
+ bAppend = true;
+
+ iDest = atoi(szDest);
+ }
+
+ const char *szInput = pReg->Attribute("input");
+ std::string strInput;
+ if (szInput)
+ {
+ strInput = szInput;
+ ReplaceBuffers(strInput);
+ }
+ else
+ strInput = m_param[0];
+
+ const char* szConditional = pReg->Attribute("conditional");
+ bool bExecute = true;
+ if (szConditional)
+ {
+ bool bInverse=false;
+ if (szConditional[0] == '!')
+ {
+ bInverse = true;
+ szConditional++;
+ }
+ std::string strSetting;
+ if (m_scraper && m_scraper->HasSettings())
+ strSetting = m_scraper->GetSetting(szConditional);
+ bExecute = bInverse != (strSetting == "true");
+ }
+
+ if (bExecute)
+ {
+ if (iDest-1 < MAX_SCRAPER_BUFFERS && iDest-1 > -1)
+ {
+ if (pReg->ValueStr() == "XSLT")
+ ParseXSLT(strInput, m_param[iDest - 1], pReg, bAppend);
+ else
+ ParseExpression(strInput, m_param[iDest - 1],pReg,bAppend);
+ }
+ else
+ CLog::Log(LOGERROR,"CScraperParser::ParseNext: destination buffer "
+ "out of bounds, skipping expression");
+ }
+ pReg = NextSiblingScraperElement(pReg);
+ }
+}
+
+const std::string CScraperParser::Parse(const std::string& strTag,
+ CScraper* scraper)
+{
+ TiXmlElement* pChildElement = m_pRootElement->FirstChildElement(strTag.c_str());
+ if(pChildElement == NULL)
+ {
+ CLog::Log(LOGERROR,"%s: Could not find scraper function %s",__FUNCTION__,strTag.c_str());
+ return "";
+ }
+ int iResult = 1; // default to param 1
+ pChildElement->QueryIntAttribute("dest",&iResult);
+ TiXmlElement* pChildStart = FirstChildScraperElement(pChildElement);
+ m_scraper = scraper;
+ ParseNext(pChildStart);
+ std::string tmp = m_param[iResult-1];
+
+ const char* szClearBuffers = pChildElement->Attribute("clearbuffers");
+ if (!szClearBuffers || stricmp(szClearBuffers,"no") != 0)
+ ClearBuffers();
+
+ return tmp;
+}
+
+void CScraperParser::Clean(std::string& strDirty)
+{
+ size_t i = 0;
+ std::string strBuffer;
+ while ((i = strDirty.find("!!!CLEAN!!!",i)) != std::string::npos)
+ {
+ size_t i2;
+ if ((i2 = strDirty.find("!!!CLEAN!!!",i+11)) != std::string::npos)
+ {
+ strBuffer = strDirty.substr(i+11,i2-i-11);
+ std::string strConverted(strBuffer);
+ HTML::CHTMLUtil::RemoveTags(strConverted);
+ StringUtils::Trim(strConverted);
+ strDirty.replace(i, i2-i+11, strConverted);
+ i += strConverted.size();
+ }
+ else
+ break;
+ }
+ i=0;
+ while ((i = strDirty.find("!!!TRIM!!!",i)) != std::string::npos)
+ {
+ size_t i2;
+ if ((i2 = strDirty.find("!!!TRIM!!!",i+10)) != std::string::npos)
+ {
+ strBuffer = strDirty.substr(i+10,i2-i-10);
+ StringUtils::Trim(strBuffer);
+ strDirty.replace(i, i2-i+10, strBuffer);
+ i += strBuffer.size();
+ }
+ else
+ break;
+ }
+ i=0;
+ while ((i = strDirty.find("!!!FIXCHARS!!!",i)) != std::string::npos)
+ {
+ size_t i2;
+ if ((i2 = strDirty.find("!!!FIXCHARS!!!",i+14)) != std::string::npos)
+ {
+ strBuffer = strDirty.substr(i+14,i2-i-14);
+ std::wstring wbuffer;
+ g_charsetConverter.utf8ToW(strBuffer, wbuffer, false, false, false);
+ std::wstring wConverted;
+ HTML::CHTMLUtil::ConvertHTMLToW(wbuffer,wConverted);
+ g_charsetConverter.wToUTF8(wConverted, strBuffer, false);
+ StringUtils::Trim(strBuffer);
+ ConvertJSON(strBuffer);
+ strDirty.replace(i, i2-i+14, strBuffer);
+ i += strBuffer.size();
+ }
+ else
+ break;
+ }
+ i=0;
+ while ((i=strDirty.find("!!!ENCODE!!!",i)) != std::string::npos)
+ {
+ size_t i2;
+ if ((i2 = strDirty.find("!!!ENCODE!!!",i+12)) != std::string::npos)
+ {
+ strBuffer = CURL::Encode(strDirty.substr(i + 12, i2 - i - 12));
+ strDirty.replace(i, i2-i+12, strBuffer);
+ i += strBuffer.size();
+ }
+ else
+ break;
+ }
+}
+
+void CScraperParser::ConvertJSON(std::string &string)
+{
+ CRegExp reg;
+ reg.RegComp("\\\\u([0-f]{4})");
+ while (reg.RegFind(string.c_str()) > -1)
+ {
+ int pos = reg.GetSubStart(1);
+ std::string szReplace(reg.GetMatch(1));
+
+ std::string replace = StringUtils::Format("&#x%s;", szReplace.c_str());
+ string.replace(string.begin()+pos-2, string.begin()+pos+4, replace);
+ }
+
+ CRegExp reg2;
+ reg2.RegComp("\\\\x([0-9]{2})([^\\\\]+;)");
+ while (reg2.RegFind(string.c_str()) > -1)
+ {
+ int pos1 = reg2.GetSubStart(1);
+ int pos2 = reg2.GetSubStart(2);
+ std::string szHexValue(reg2.GetMatch(1));
+
+ std::string replace = StringUtils::Format("%li", strtol(szHexValue.c_str(), NULL, 16));
+ string.replace(string.begin()+pos1-2, string.begin()+pos2+reg2.GetSubLength(2), replace);
+ }
+
+ StringUtils::Replace(string, "\\\"","\"");
+}
+
+void CScraperParser::ClearBuffers()
+{
+ //clear all m_param strings
+ for (int i=0;i<MAX_SCRAPER_BUFFERS;++i)
+ m_param[i].clear();
+}
+
+void CScraperParser::GetBufferParams(bool* result, const char* attribute, bool defvalue)
+{
+ for (int iBuf=0;iBuf<MAX_SCRAPER_BUFFERS;++iBuf)
+ result[iBuf] = defvalue;;
+ if (attribute)
+ {
+ vector<std::string> vecBufs;
+ StringUtils::Tokenize(attribute,vecBufs,",");
+ for (size_t nToken=0; nToken < vecBufs.size(); nToken++)
+ {
+ int index = atoi(vecBufs[nToken].c_str())-1;
+ if (index < MAX_SCRAPER_BUFFERS)
+ result[index] = !defvalue;
+ }
+ }
+}
+
+void CScraperParser::InsertToken(std::string& strOutput, int buf, const char* token)
+{
+ char temp[4];
+ sprintf(temp,"\\%i",buf);
+ size_t i2=0;
+ while ((i2 = strOutput.find(temp,i2)) != std::string::npos)
+ {
+ strOutput.insert(i2,token);
+ i2 += strlen(token) + strlen(temp);
+ strOutput.insert(i2,token);
+ }
+}
+
+void CScraperParser::AddDocument(const CXBMCTinyXML* doc)
+{
+ const TiXmlNode* node = doc->RootElement()->FirstChild();
+ while (node)
+ {
+ m_pRootElement->InsertEndChild(*node);
+ node = node->NextSibling();
+ }
+}
+
diff --git a/src/utils/ScraperParser.h b/src/utils/ScraperParser.h
new file mode 100644
index 0000000000..8129ef7e75
--- /dev/null
+++ b/src/utils/ScraperParser.h
@@ -0,0 +1,94 @@
+#ifndef SCRAPER_PARSER_H
+#define SCRAPER_PARSER_H
+
+/*
+ * Copyright (C) 2012-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string>
+#include <vector>
+
+#define MAX_SCRAPER_BUFFERS 20
+
+namespace ADDON
+{
+ class CScraper;
+}
+
+class TiXmlElement;
+class CXBMCTinyXML;
+
+class CScraperSettings;
+
+class CScraperParser
+{
+public:
+ CScraperParser();
+ CScraperParser(const CScraperParser& parser);
+ ~CScraperParser();
+ CScraperParser& operator= (const CScraperParser& parser);
+ bool Load(const std::string& strXMLFile);
+ bool IsNoop() const { return m_isNoop; };
+
+ void Clear();
+ const std::string& GetFilename() const { return m_strFile; }
+ std::string GetSearchStringEncoding() const
+ { return m_SearchStringEncoding; }
+ const std::string Parse(const std::string& strTag,
+ ADDON::CScraper* scraper);
+
+ void AddDocument(const CXBMCTinyXML* doc);
+
+ std::string m_param[MAX_SCRAPER_BUFFERS];
+
+private:
+ bool LoadFromXML();
+ void ReplaceBuffers(std::string& strDest);
+ void ParseExpression(const std::string& input, std::string& dest, TiXmlElement* element, bool bAppend);
+
+ /*! \brief Parse an 'XSLT' declaration from the scraper
+ This allow us to transform an inbound XML document using XSLT
+ to a different type of XML document, ready to be output direct
+ to the album loaders or similar
+ \param input the input document
+ \param dest the output destation for the conversion
+ \param element the current XML element
+ \param bAppend append or clear the buffer
+ */
+ void ParseXSLT(const std::string& input, std::string& dest, TiXmlElement* element, bool bAppend);
+ void ParseNext(TiXmlElement* element);
+ void Clean(std::string& strDirty);
+ void ConvertJSON(std::string &string);
+ void ClearBuffers();
+ void GetBufferParams(bool* result, const char* attribute, bool defvalue);
+ void InsertToken(std::string& strOutput, int buf, const char* token);
+
+ CXBMCTinyXML* m_document;
+ TiXmlElement* m_pRootElement;
+
+ const char* m_SearchStringEncoding;
+ bool m_isNoop;
+
+ std::string m_strFile;
+ ADDON::CScraper* m_scraper;
+};
+
+#endif
+
+
diff --git a/src/utils/ScraperUrl.cpp b/src/utils/ScraperUrl.cpp
new file mode 100644
index 0000000000..a9eafddc9c
--- /dev/null
+++ b/src/utils/ScraperUrl.cpp
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "XMLUtils.h"
+#include "ScraperUrl.h"
+#include "settings/AdvancedSettings.h"
+#include "HTMLUtil.h"
+#include "CharsetConverter.h"
+#include "utils/CharsetDetection.h"
+#include "utils/StringUtils.h"
+#include "URL.h"
+#include "filesystem/CurlFile.h"
+#include "filesystem/ZipFile.h"
+#include "URIUtils.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/XMLUtils.h"
+#include "utils/Mime.h"
+
+#include <cstring>
+#include <sstream>
+
+using namespace std;
+
+CScraperUrl::CScraperUrl(const std::string& strUrl)
+{
+ relevance = 0;
+ ParseString(strUrl);
+}
+
+CScraperUrl::CScraperUrl(const TiXmlElement* element)
+{
+ relevance = 0;
+ ParseElement(element);
+}
+
+CScraperUrl::CScraperUrl()
+{
+ relevance = 0;
+}
+
+CScraperUrl::~CScraperUrl()
+{
+}
+
+void CScraperUrl::Clear()
+{
+ m_url.clear();
+ m_spoof.clear();
+ m_xml.clear();
+ relevance = 0;
+}
+
+bool CScraperUrl::Parse()
+{
+ std::string strToParse = m_xml;
+ m_xml.clear();
+ return ParseString(strToParse);
+}
+
+bool CScraperUrl::ParseElement(const TiXmlElement* element)
+{
+ if (!element || !element->FirstChild() ||
+ !element->FirstChild()->Value()) return false;
+
+ stringstream stream;
+ stream << *element;
+ m_xml += stream.str();
+
+ SUrlEntry url;
+ url.m_url = element->FirstChild()->Value();
+ url.m_spoof = XMLUtils::GetAttribute(element, "spoof");
+ const char* szPost=element->Attribute("post");
+ if (szPost && stricmp(szPost,"yes") == 0)
+ url.m_post = true;
+ else
+ url.m_post = false;
+ const char* szIsGz=element->Attribute("gzip");
+ if (szIsGz && stricmp(szIsGz,"yes") == 0)
+ url.m_isgz = true;
+ else
+ url.m_isgz = false;
+ url.m_cache = XMLUtils::GetAttribute(element, "cache");
+
+ const char* szType = element->Attribute("type");
+ url.m_type = URL_TYPE_GENERAL;
+ url.m_season = -1;
+ if (szType && stricmp(szType,"season") == 0)
+ {
+ url.m_type = URL_TYPE_SEASON;
+ const char* szSeason = element->Attribute("season");
+ if (szSeason)
+ url.m_season = atoi(szSeason);
+ }
+ url.m_aspect = XMLUtils::GetAttribute(element, "aspect");
+
+ m_url.push_back(url);
+
+ return true;
+}
+
+bool CScraperUrl::ParseString(std::string strUrl)
+{
+ if (strUrl.empty())
+ return false;
+
+ CXBMCTinyXML doc;
+ /* strUrl is coming from internal sources (usually generated by scraper or from database)
+ * so strUrl is always in UTF-8 */
+ doc.Parse(strUrl, TIXML_ENCODING_UTF8);
+
+ TiXmlElement* pElement = doc.RootElement();
+ if (!pElement)
+ {
+ SUrlEntry url;
+ url.m_url = strUrl;
+ url.m_type = URL_TYPE_GENERAL;
+ url.m_season = -1;
+ url.m_post = false;
+ url.m_isgz = false;
+ m_url.push_back(url);
+ m_xml = strUrl;
+ }
+ else
+ {
+ while (pElement)
+ {
+ ParseElement(pElement);
+ pElement = pElement->NextSiblingElement(pElement->Value());
+ }
+ }
+
+ return true;
+}
+
+const CScraperUrl::SUrlEntry CScraperUrl::GetFirstThumb(const std::string &type) const
+{
+ for (vector<SUrlEntry>::const_iterator iter=m_url.begin();iter != m_url.end();++iter)
+ {
+ if (iter->m_type == URL_TYPE_GENERAL && (type.empty() || type == "thumb" || iter->m_aspect == type))
+ return *iter;
+ }
+
+ SUrlEntry result;
+ result.m_type = URL_TYPE_GENERAL;
+ result.m_post = false;
+ result.m_isgz = false;
+ result.m_season = -1;
+ return result;
+}
+
+const CScraperUrl::SUrlEntry CScraperUrl::GetSeasonThumb(int season, const std::string &type) const
+{
+ for (vector<SUrlEntry>::const_iterator iter=m_url.begin();iter != m_url.end();++iter)
+ {
+ if (iter->m_type == URL_TYPE_SEASON && iter->m_season == season &&
+ (type.empty() || type == "thumb" || iter->m_aspect == type))
+ return *iter;
+ }
+
+ SUrlEntry result;
+ result.m_type = URL_TYPE_GENERAL;
+ result.m_post = false;
+ result.m_isgz = false;
+ result.m_season = -1;
+ return result;
+}
+
+unsigned int CScraperUrl::GetMaxSeasonThumb() const
+{
+ unsigned int maxSeason = 0;
+ for (vector<SUrlEntry>::const_iterator iter=m_url.begin();iter != m_url.end();++iter)
+ {
+ if (iter->m_type == URL_TYPE_SEASON && iter->m_season > 0 && (unsigned int)iter->m_season > maxSeason)
+ maxSeason = iter->m_season;
+ }
+ return maxSeason;
+}
+
+bool CScraperUrl::Get(const SUrlEntry& scrURL, std::string& strHTML, XFILE::CCurlFile& http, const std::string& cacheContext)
+{
+ CURL url(scrURL.m_url);
+ http.SetReferer(scrURL.m_spoof);
+ std::string strCachePath;
+
+ if (scrURL.m_isgz)
+ http.SetContentEncoding("gzip");
+
+ if (!scrURL.m_cache.empty())
+ {
+ strCachePath = URIUtils::AddFileToFolder(g_advancedSettings.m_cachePath,
+ "scrapers/" + cacheContext + "/" + scrURL.m_cache);
+ if (XFILE::CFile::Exists(strCachePath))
+ {
+ XFILE::CFile file;
+ XFILE::auto_buffer buffer;
+ if (file.LoadFile(strCachePath, buffer) > 0)
+ {
+ strHTML.assign(buffer.get(), buffer.length());
+ return true;
+ }
+ }
+ }
+
+ std::string strHTML1(strHTML);
+
+ if (scrURL.m_post)
+ {
+ std::string strOptions = url.GetOptions();
+ strOptions = strOptions.substr(1);
+ url.SetOptions("");
+
+ if (!http.Post(url.Get(), strOptions, strHTML1))
+ return false;
+ }
+ else
+ if (!http.Get(url.Get(), strHTML1))
+ return false;
+
+ strHTML = strHTML1;
+
+ std::string mimeType(http.GetMimeType());
+ CMime::EFileType ftype = CMime::GetFileTypeFromMime(mimeType);
+ if (ftype == CMime::FileTypeUnknown)
+ ftype = CMime::GetFileTypeFromContent(strHTML);
+
+ if (ftype == CMime::FileTypeZip || ftype == CMime::FileTypeGZip)
+ {
+ XFILE::CZipFile file;
+ std::string strBuffer;
+ int iSize = file.UnpackFromMemory(strBuffer,strHTML,scrURL.m_isgz); // FIXME: use FileTypeGZip instead of scrURL.m_isgz?
+ if (iSize > 0)
+ {
+ strHTML = strBuffer;
+ CLog::Log(LOGDEBUG, "%s: Archive \"%s\" was unpacked in memory", __FUNCTION__, scrURL.m_url.c_str());
+ }
+ else
+ CLog::Log(LOGWARNING, "%s: \"%s\" looks like archive, but cannot be unpacked", __FUNCTION__, scrURL.m_url.c_str());
+ }
+
+ std::string reportedCharset(http.GetServerReportedCharset());
+ if (ftype == CMime::FileTypeHtml)
+ {
+ std::string realHtmlCharset, converted;
+ if (!CCharsetDetection::ConvertHtmlToUtf8(strHTML, converted, reportedCharset, realHtmlCharset))
+ CLog::Log(LOGWARNING, "%s: Can't find precise charset for HTML \"%s\", using \"%s\" as fallback", __FUNCTION__, scrURL.m_url.c_str(), realHtmlCharset.c_str());
+ else
+ CLog::Log(LOGDEBUG, "%s: Using \"%s\" charset for HTML \"%s\"", __FUNCTION__, realHtmlCharset.c_str(), scrURL.m_url.c_str());
+
+ strHTML = converted;
+ }
+ else if (ftype == CMime::FileTypeXml)
+ {
+ CXBMCTinyXML xmlDoc;
+ xmlDoc.Parse(strHTML, reportedCharset);
+
+ std::string realXmlCharset(xmlDoc.GetUsedCharset());
+ if (!realXmlCharset.empty())
+ {
+ CLog::Log(LOGDEBUG, "%s: Using \"%s\" charset for XML \"%s\"", __FUNCTION__, realXmlCharset.c_str(), scrURL.m_url.c_str());
+ std::string converted;
+ g_charsetConverter.ToUtf8(realXmlCharset, strHTML, converted);
+ strHTML = converted;
+ }
+ }
+ else if (ftype == CMime::FileTypePlainText || StringUtils::CompareNoCase(mimeType.substr(0, 5), "text/") == 0)
+ {
+ std::string realTextCharset, converted;
+ CCharsetDetection::ConvertPlainTextToUtf8(strHTML, converted, reportedCharset, realTextCharset);
+ strHTML = converted;
+ if (reportedCharset != realTextCharset)
+ CLog::Log(LOGWARNING, "%s: Using \"%s\" charset for plain text \"%s\" instead of server reported \"%s\" charset", __FUNCTION__, realTextCharset.c_str(), scrURL.m_url.c_str(), reportedCharset.c_str());
+ else
+ CLog::Log(LOGDEBUG, "%s: Using \"%s\" charset for plain text \"%s\"", __FUNCTION__, realTextCharset.c_str(), scrURL.m_url.c_str());
+ }
+ else if (!reportedCharset.empty())
+ {
+ CLog::Log(LOGDEBUG, "%s: Using \"%s\" charset for \"%s\"", __FUNCTION__, reportedCharset.c_str(), scrURL.m_url.c_str());
+ if (reportedCharset != "UTF-8")
+ {
+ std::string converted;
+ g_charsetConverter.ToUtf8(reportedCharset, strHTML, converted);
+ strHTML = converted;
+ }
+ }
+ else
+ CLog::Log(LOGDEBUG, "%s: Using content of \"%s\" as binary or text with \"UTF-8\" charset", __FUNCTION__, scrURL.m_url.c_str());
+
+ if (!scrURL.m_cache.empty())
+ {
+ std::string strCachePath = URIUtils::AddFileToFolder(g_advancedSettings.m_cachePath,
+ "scrapers/" + cacheContext + "/" + scrURL.m_cache);
+ XFILE::CFile file;
+ if (!file.OpenForWrite(strCachePath, true) || file.Write(strHTML.data(), strHTML.size()) != strHTML.size())
+ return false;
+ }
+ return true;
+}
+
+// XML format is of strUrls is:
+// <TAG><url>...</url>...</TAG> (parsed by ParseElement) or <url>...</url> (ditto)
+bool CScraperUrl::ParseEpisodeGuide(std::string strUrls)
+{
+ if (strUrls.empty())
+ return false;
+
+ // ok, now parse the xml file
+ CXBMCTinyXML doc;
+ /* strUrls is coming from internal sources so strUrls is always in UTF-8 */
+ doc.Parse(strUrls, TIXML_ENCODING_UTF8);
+ if (doc.RootElement())
+ {
+ TiXmlHandle docHandle( &doc );
+ TiXmlElement *link = docHandle.FirstChild("episodeguide").Element();
+ if (link->FirstChildElement("url"))
+ {
+ for (link = link->FirstChildElement("url"); link; link = link->NextSiblingElement("url"))
+ ParseElement(link);
+ }
+ else if (link->FirstChild() && link->FirstChild()->Value())
+ ParseElement(link);
+ }
+ else
+ return false;
+
+ return true;
+}
+
+std::string CScraperUrl::GetThumbURL(const CScraperUrl::SUrlEntry &entry)
+{
+ if (entry.m_spoof.empty())
+ return entry.m_url;
+
+ return entry.m_url + "|Referer=" + CURL::Encode(entry.m_spoof);
+}
+
+void CScraperUrl::GetThumbURLs(std::vector<std::string> &thumbs, const std::string &type, int season) const
+{
+ for (vector<SUrlEntry>::const_iterator iter = m_url.begin(); iter != m_url.end(); ++iter)
+ {
+ if (iter->m_aspect == type || type.empty() || type == "thumb" || iter->m_aspect.empty())
+ {
+ if ((iter->m_type == CScraperUrl::URL_TYPE_GENERAL && season == -1)
+ || (iter->m_type == CScraperUrl::URL_TYPE_SEASON && iter->m_season == season))
+ {
+ thumbs.push_back(GetThumbURL(*iter));
+ }
+ }
+ }
+}
diff --git a/src/utils/ScraperUrl.h b/src/utils/ScraperUrl.h
new file mode 100644
index 0000000000..d9871e8f8f
--- /dev/null
+++ b/src/utils/ScraperUrl.h
@@ -0,0 +1,92 @@
+#ifndef SCRAPER_URL_H
+#define SCRAPER_URL_H
+
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <vector>
+#include <map>
+#include <string>
+
+class TiXmlElement;
+namespace XFILE { class CCurlFile; }
+
+class CScraperUrl
+{
+public:
+ CScraperUrl(const std::string&);
+ CScraperUrl(const TiXmlElement*);
+ CScraperUrl();
+ ~CScraperUrl();
+
+ enum URLTYPES
+ {
+ URL_TYPE_GENERAL = 1,
+ URL_TYPE_SEASON = 2
+ };
+
+ struct SUrlEntry
+ {
+ std::string m_spoof;
+ std::string m_url;
+ std::string m_cache;
+ std::string m_aspect;
+ URLTYPES m_type;
+ bool m_post;
+ bool m_isgz;
+ int m_season;
+ };
+
+ bool Parse();
+ bool ParseString(std::string); // copies by intention
+ bool ParseElement(const TiXmlElement*);
+ bool ParseEpisodeGuide(std::string strUrls); // copies by intention
+
+ const SUrlEntry GetFirstThumb(const std::string &type = "") const;
+ const SUrlEntry GetSeasonThumb(int season, const std::string &type = "") const;
+ unsigned int GetMaxSeasonThumb() const;
+
+ /*! \brief fetch the full URL (including referrer) of a thumb
+ \param URL entry to use to create the full URL
+ \return the full URL, including referrer
+ */
+ static std::string GetThumbURL(const CScraperUrl::SUrlEntry &entry);
+
+ /*! \brief fetch the full URL (including referrer) of thumbs
+ \param thumbs [out] vector of thumb URLs to fill
+ \param type the type of thumb URLs to fetch, if empty (the default) picks any
+ \param season number of season that we want thumbs for, -1 indicates no season (the default)
+ */
+ void GetThumbURLs(std::vector<std::string> &thumbs, const std::string &type = "", int season = -1) const;
+ void Clear();
+ static bool Get(const SUrlEntry&, std::string&, XFILE::CCurlFile& http,
+ const std::string& cacheContext);
+
+ std::string m_xml;
+ std::string m_spoof; // for backwards compatibility only!
+ std::string strTitle;
+ std::string strId;
+ double relevance;
+ std::vector<SUrlEntry> m_url;
+};
+
+#endif
+
+
diff --git a/src/utils/Screenshot.cpp b/src/utils/Screenshot.cpp
new file mode 100644
index 0000000000..db73d4aa0b
--- /dev/null
+++ b/src/utils/Screenshot.cpp
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "Screenshot.h"
+
+#include "system.h"
+#include <vector>
+
+#include "Util.h"
+
+#include "Application.h"
+#include "windowing/WindowingFactory.h"
+#include "pictures/Picture.h"
+
+#ifdef TARGET_RASPBERRY_PI
+#include "xbmc/linux/RBP.h"
+#endif
+
+#ifdef HAS_VIDEO_PLAYBACK
+#include "cores/VideoRenderers/RenderManager.h"
+#endif
+
+#include "filesystem/File.h"
+#include "guilib/GraphicContext.h"
+
+#include "utils/JobManager.h"
+#include "utils/URIUtils.h"
+#include "utils/log.h"
+#include "settings/SettingPath.h"
+#include "settings/Settings.h"
+#include "settings/windows/GUIControlSettings.h"
+
+using namespace std;
+using namespace XFILE;
+
+CScreenshotSurface::CScreenshotSurface()
+{
+ m_width = 0;
+ m_height = 0;
+ m_stride = 0;
+ m_buffer = NULL;
+}
+
+CScreenshotSurface::~CScreenshotSurface()
+{
+ delete m_buffer;
+}
+
+bool CScreenshotSurface::capture()
+{
+#if defined(TARGET_RASPBERRY_PI)
+ g_RBP.GetDisplaySize(m_width, m_height);
+ m_buffer = g_RBP.CaptureDisplay(m_width, m_height, &m_stride, true, false);
+ if (!m_buffer)
+ return false;
+#elif defined(HAS_DX)
+ LPDIRECT3DSURFACE9 lpSurface = NULL, lpBackbuffer = NULL;
+ g_graphicsContext.Lock();
+ if (g_application.m_pPlayer->IsPlayingVideo())
+ {
+#ifdef HAS_VIDEO_PLAYBACK
+ g_renderManager.SetupScreenshot();
+#endif
+ }
+ g_application.RenderNoPresent();
+
+ if (FAILED(g_Windowing.Get3DDevice()->CreateOffscreenPlainSurface(g_Windowing.GetWidth(), g_Windowing.GetHeight(), D3DFMT_X8R8G8B8, D3DPOOL_SYSTEMMEM, &lpSurface, NULL)))
+ return false;
+
+ if (FAILED(g_Windowing.Get3DDevice()->GetRenderTarget(0, &lpBackbuffer)))
+ return false;
+
+ // now take screenshot
+ if (SUCCEEDED(g_Windowing.Get3DDevice()->GetRenderTargetData(lpBackbuffer, lpSurface)))
+ {
+ D3DLOCKED_RECT lr;
+ D3DSURFACE_DESC desc;
+ lpSurface->GetDesc(&desc);
+ if (SUCCEEDED(lpSurface->LockRect(&lr, NULL, D3DLOCK_READONLY)))
+ {
+ m_width = desc.Width;
+ m_height = desc.Height;
+ m_stride = lr.Pitch;
+ m_buffer = new unsigned char[m_height * m_stride];
+ memcpy(m_buffer, lr.pBits, m_height * m_stride);
+ lpSurface->UnlockRect();
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "%s LockRect failed", __FUNCTION__);
+ }
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "%s GetBackBuffer failed", __FUNCTION__);
+ }
+ lpSurface->Release();
+ lpBackbuffer->Release();
+
+ g_graphicsContext.Unlock();
+
+#elif defined(HAS_GL) || defined(HAS_GLES)
+
+ g_graphicsContext.BeginPaint();
+ if (g_application.m_pPlayer->IsPlayingVideo())
+ {
+#ifdef HAS_VIDEO_PLAYBACK
+ g_renderManager.SetupScreenshot();
+#endif
+ }
+ g_application.RenderNoPresent();
+#ifndef HAS_GLES
+ glReadBuffer(GL_BACK);
+#endif
+ //get current viewport
+ GLint viewport[4];
+ glGetIntegerv(GL_VIEWPORT, viewport);
+
+ m_width = viewport[2] - viewport[0];
+ m_height = viewport[3] - viewport[1];
+ m_stride = m_width * 4;
+ unsigned char* surface = new unsigned char[m_stride * m_height];
+
+ //read pixels from the backbuffer
+#if HAS_GLES == 2
+ glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3], GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)surface);
+#else
+ glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3], GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid*)surface);
+#endif
+ g_graphicsContext.EndPaint();
+
+ //make a new buffer and copy the read image to it with the Y axis inverted
+ m_buffer = new unsigned char[m_stride * m_height];
+ for (int y = 0; y < m_height; y++)
+ {
+#ifdef HAS_GLES
+ // we need to save in BGRA order so XOR Swap RGBA -> BGRA
+ unsigned char* swap_pixels = surface + (m_height - y - 1) * m_stride;
+ for (int x = 0; x < m_width; x++, swap_pixels+=4)
+ {
+ std::swap(swap_pixels[0], swap_pixels[2]);
+ }
+#endif
+ memcpy(m_buffer + y * m_stride, surface + (m_height - y - 1) *m_stride, m_stride);
+ }
+
+ delete [] surface;
+
+#else
+ //nothing to take a screenshot from
+ return false;
+#endif
+
+ return true;
+}
+
+void CScreenShot::TakeScreenshot(const std::string &filename, bool sync)
+{
+
+ CScreenshotSurface surface;
+ if (!surface.capture())
+ {
+ CLog::Log(LOGERROR, "Screenshot %s failed", filename.c_str());
+ return;
+ }
+
+ CLog::Log(LOGDEBUG, "Saving screenshot %s", filename.c_str());
+
+ //set alpha byte to 0xFF
+ for (int y = 0; y < surface.m_height; y++)
+ {
+ unsigned char* alphaptr = surface.m_buffer - 1 + y * surface.m_stride;
+ for (int x = 0; x < surface.m_width; x++)
+ *(alphaptr += 4) = 0xFF;
+ }
+
+ //if sync is true, the png file needs to be completely written when this function returns
+ if (sync)
+ {
+ if (!CPicture::CreateThumbnailFromSurface(surface.m_buffer, surface.m_width, surface.m_height, surface.m_stride, filename))
+ CLog::Log(LOGERROR, "Unable to write screenshot %s", filename.c_str());
+
+ delete [] surface.m_buffer;
+ surface.m_buffer = NULL;
+ }
+ else
+ {
+ //make sure the file exists to avoid concurrency issues
+ FILE* fp = fopen(filename.c_str(), "w");
+ if (fp)
+ fclose(fp);
+ else
+ CLog::Log(LOGERROR, "Unable to create file %s", filename.c_str());
+
+ //write .png file asynchronous with CThumbnailWriter, prevents stalling of the render thread
+ //buffer is deleted from CThumbnailWriter
+ CThumbnailWriter* thumbnailwriter = new CThumbnailWriter(surface.m_buffer, surface.m_width, surface.m_height, surface.m_stride, filename);
+ CJobManager::GetInstance().AddJob(thumbnailwriter, NULL);
+ surface.m_buffer = NULL;
+ }
+}
+
+void CScreenShot::TakeScreenshot()
+{
+ static bool savingScreenshots = false;
+ static vector<std::string> screenShots;
+ bool promptUser = false;
+ std::string strDir;
+
+ // check to see if we have a screenshot folder yet
+ CSettingPath *screenshotSetting = (CSettingPath*)CSettings::Get().GetSetting("debug.screenshotpath");
+ if (screenshotSetting != NULL)
+ {
+ strDir = screenshotSetting->GetValue();
+ if (strDir.empty())
+ {
+ if (CGUIControlButtonSetting::GetPath(screenshotSetting))
+ strDir = screenshotSetting->GetValue();
+ }
+ }
+
+ if (strDir.empty())
+ {
+ strDir = "special://temp/";
+ if (!savingScreenshots)
+ {
+ promptUser = true;
+ savingScreenshots = true;
+ screenShots.clear();
+ }
+ }
+ URIUtils::RemoveSlashAtEnd(strDir);
+
+ if (!strDir.empty())
+ {
+ std::string file = CUtil::GetNextFilename(URIUtils::AddFileToFolder(strDir, "screenshot%03d.png"), 999);
+
+ if (!file.empty())
+ {
+ TakeScreenshot(file, false);
+ if (savingScreenshots)
+ screenShots.push_back(file);
+ if (promptUser)
+ { // grab the real directory
+ std::string newDir;
+ if (screenshotSetting != NULL)
+ {
+ newDir = screenshotSetting->GetValue();
+ if (newDir.empty())
+ {
+ if (CGUIControlButtonSetting::GetPath(screenshotSetting))
+ newDir = screenshotSetting->GetValue();
+ }
+ }
+
+ if (!newDir.empty())
+ {
+ for (unsigned int i = 0; i < screenShots.size(); i++)
+ {
+ std::string file = CUtil::GetNextFilename(URIUtils::AddFileToFolder(newDir, "screenshot%03d.png"), 999);
+ CFile::Copy(screenShots[i], file);
+ }
+ screenShots.clear();
+ }
+ savingScreenshots = false;
+ }
+ }
+ else
+ {
+ CLog::Log(LOGWARNING, "Too many screen shots or invalid folder");
+ }
+ }
+}
diff --git a/src/utils/Screenshot.h b/src/utils/Screenshot.h
new file mode 100644
index 0000000000..200688c93d
--- /dev/null
+++ b/src/utils/Screenshot.h
@@ -0,0 +1,44 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string>
+
+class CScreenshotSurface
+{
+
+public:
+ int m_width;
+ int m_height;
+ int m_stride;
+ unsigned char* m_buffer;
+
+ CScreenshotSurface(void);
+ ~CScreenshotSurface();
+ bool capture( void );
+};
+
+class CScreenShot
+{
+
+public:
+ static void TakeScreenshot();
+ static void TakeScreenshot(const std::string &filename, bool sync);
+};
diff --git a/src/utils/SeekHandler.cpp b/src/utils/SeekHandler.cpp
new file mode 100644
index 0000000000..9c4e101800
--- /dev/null
+++ b/src/utils/SeekHandler.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2012-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "SeekHandler.h"
+#include "GUIInfoManager.h"
+#include "Application.h"
+
+CSeekHandler::CSeekHandler()
+: m_requireSeek(false),
+ m_percent(0.0f)
+{
+}
+
+void CSeekHandler::Reset()
+{
+ m_requireSeek = false;
+ m_percent = 0;
+}
+
+void CSeekHandler::Seek(bool forward, float amount, float duration)
+{
+ if (!m_requireSeek)
+ { // not yet seeking
+ if (g_infoManager.GetTotalPlayTime())
+ m_percent = (float)g_infoManager.GetPlayTime() / g_infoManager.GetTotalPlayTime() * 0.1f;
+ else
+ m_percent = 0.0f;
+
+ // tell info manager that we have started a seek operation
+ m_requireSeek = true;
+ g_infoManager.SetSeeking(true);
+ }
+ // calculate our seek amount
+ if (!g_infoManager.m_performingSeek)
+ {
+ //100% over 1 second.
+ float speed = 100.0f;
+ if( duration )
+ speed *= duration;
+ else
+ speed /= g_infoManager.GetFPS();
+
+ if (forward)
+ m_percent += amount * amount * speed;
+ else
+ m_percent -= amount * amount * speed;
+ if (m_percent > 100.0f) m_percent = 100.0f;
+ if (m_percent < 0.0f) m_percent = 0.0f;
+ }
+ m_timer.StartZero();
+}
+
+float CSeekHandler::GetPercent() const
+{
+ return m_percent;
+}
+
+bool CSeekHandler::InProgress() const
+{
+ return m_requireSeek;
+}
+
+void CSeekHandler::Process()
+{
+ if (m_timer.GetElapsedMilliseconds() > time_before_seek)
+ {
+ if (!g_infoManager.m_performingSeek && m_timer.GetElapsedMilliseconds() > time_for_display) // TODO: Why?
+ g_infoManager.SetSeeking(false);
+ if (m_requireSeek)
+ {
+ g_infoManager.m_performingSeek = true;
+ double time = g_infoManager.GetTotalPlayTime() * m_percent * 0.01;
+ g_application.SeekTime(time);
+ m_requireSeek = false;
+ }
+ }
+}
diff --git a/src/utils/SeekHandler.h b/src/utils/SeekHandler.h
new file mode 100644
index 0000000000..68e7820c84
--- /dev/null
+++ b/src/utils/SeekHandler.h
@@ -0,0 +1,41 @@
+#pragma once
+/*
+ * Copyright (C) 2012-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/Stopwatch.h"
+
+class CSeekHandler
+{
+public:
+ CSeekHandler();
+
+ void Seek(bool forward, float amount, float duration = 0);
+ void Process();
+ void Reset();
+
+ float GetPercent() const;
+ bool InProgress() const;
+private:
+ static const int time_before_seek = 500;
+ static const int time_for_display = 2000; // TODO: WTF?
+ bool m_requireSeek;
+ float m_percent;
+ CStopWatch m_timer;
+};
diff --git a/src/utils/SortUtils.cpp b/src/utils/SortUtils.cpp
new file mode 100644
index 0000000000..a2601ddd50
--- /dev/null
+++ b/src/utils/SortUtils.cpp
@@ -0,0 +1,906 @@
+/*
+ * Copyright (C) 2012-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "SortUtils.h"
+#include "URL.h"
+#include "Util.h"
+#include "XBDateTime.h"
+#include "settings/AdvancedSettings.h"
+#include "utils/CharsetConverter.h"
+#include "utils/StdString.h"
+#include "utils/StringUtils.h"
+#include "utils/Variant.h"
+
+using namespace std;
+
+string ArrayToString(SortAttribute attributes, const CVariant &variant, const string &seperator = " / ")
+{
+ vector<string> strArray;
+ if (variant.isArray())
+ {
+ for (CVariant::const_iterator_array it = variant.begin_array(); it != variant.end_array(); it++)
+ {
+ if (attributes & SortAttributeIgnoreArticle)
+ strArray.push_back(SortUtils::RemoveArticles(it->asString()));
+ else
+ strArray.push_back(it->asString());
+ }
+
+ return StringUtils::Join(strArray, seperator);
+ }
+ else if (variant.isString())
+ {
+ if (attributes & SortAttributeIgnoreArticle)
+ return SortUtils::RemoveArticles(variant.asString());
+ else
+ return variant.asString();
+ }
+
+ return "";
+}
+
+string ByLabel(SortAttribute attributes, const SortItem &values)
+{
+ if (attributes & SortAttributeIgnoreArticle)
+ return SortUtils::RemoveArticles(values.at(FieldLabel).asString());
+
+ return values.at(FieldLabel).asString();
+}
+
+string ByFile(SortAttribute attributes, const SortItem &values)
+{
+ CURL url(values.at(FieldPath).asString());
+
+ return StringUtils::Format("%s %" PRId64, url.GetFileNameWithoutPath().c_str(), values.at(FieldStartOffset).asInteger());
+}
+
+string ByPath(SortAttribute attributes, const SortItem &values)
+{
+ return StringUtils::Format("%s %" PRId64, values.at(FieldPath).asString().c_str(), values.at(FieldStartOffset).asInteger());
+}
+
+string ByLastPlayed(SortAttribute attributes, const SortItem &values)
+{
+ return StringUtils::Format("%s %s", values.at(FieldLastPlayed).asString().c_str(), ByLabel(attributes, values).c_str());
+}
+
+string ByPlaycount(SortAttribute attributes, const SortItem &values)
+{
+ return StringUtils::Format("%i %s", (int)values.at(FieldPlaycount).asInteger(), ByLabel(attributes, values).c_str());
+}
+
+string ByDate(SortAttribute attributes, const SortItem &values)
+{
+ return values.at(FieldDate).asString() + " " + ByLabel(attributes, values);
+}
+
+string ByDateAdded(SortAttribute attributes, const SortItem &values)
+{
+ return StringUtils::Format("%s %d", values.at(FieldDateAdded).asString().c_str(), (int)values.at(FieldId).asInteger());;
+}
+
+string BySize(SortAttribute attributes, const SortItem &values)
+{
+ return StringUtils::Format("%" PRId64, values.at(FieldSize).asInteger());
+}
+
+string ByDriveType(SortAttribute attributes, const SortItem &values)
+{
+ return StringUtils::Format("%d %s", (int)values.at(FieldDriveType).asInteger(), ByLabel(attributes, values).c_str());
+}
+
+string ByTitle(SortAttribute attributes, const SortItem &values)
+{
+ if (attributes & SortAttributeIgnoreArticle)
+ return SortUtils::RemoveArticles(values.at(FieldTitle).asString());
+
+ return values.at(FieldTitle).asString();
+}
+
+string ByAlbum(SortAttribute attributes, const SortItem &values)
+{
+ string album = values.at(FieldAlbum).asString();
+ if (attributes & SortAttributeIgnoreArticle)
+ album = SortUtils::RemoveArticles(album);
+
+ CStdString label = StringUtils::Format("%s %s", album.c_str(), ArrayToString(attributes, values.at(FieldArtist)).c_str());
+
+ const CVariant &track = values.at(FieldTrackNumber);
+ if (!track.isNull())
+ label += StringUtils::Format(" %i", (int)track.asInteger());
+
+ return label;
+}
+
+string ByAlbumType(SortAttribute attributes, const SortItem &values)
+{
+ return values.at(FieldAlbumType).asString() + " " + ByLabel(attributes, values);
+}
+
+string ByArtist(SortAttribute attributes, const SortItem &values)
+{
+ CStdString label = ArrayToString(attributes, values.at(FieldArtist));
+
+ const CVariant &year = values.at(FieldYear);
+ if (g_advancedSettings.m_bMusicLibraryAlbumsSortByArtistThenYear &&
+ !year.isNull())
+ label += StringUtils::Format(" %i", (int)year.asInteger());
+
+ const CVariant &album = values.at(FieldAlbum);
+ if (!album.isNull())
+ label += " " + SortUtils::RemoveArticles(album.asString());
+
+ const CVariant &track = values.at(FieldTrackNumber);
+ if (!track.isNull())
+ label += StringUtils::Format(" %i", (int)track.asInteger());
+
+ return label;
+}
+
+string ByTrackNumber(SortAttribute attributes, const SortItem &values)
+{
+ return StringUtils::Format("%i", (int)values.at(FieldTrackNumber).asInteger());
+}
+
+string ByTime(SortAttribute attributes, const SortItem &values)
+{
+ CStdString label;
+ const CVariant &time = values.at(FieldTime);
+ if (time.isInteger())
+ label = StringUtils::Format("%i", (int)time.asInteger());
+ else
+ label = StringUtils::Format("%s", time.asString().c_str());
+ return label;
+}
+
+string ByProgramCount(SortAttribute attributes, const SortItem &values)
+{
+ return StringUtils::Format("%i", (int)values.at(FieldProgramCount).asInteger());
+}
+
+string ByPlaylistOrder(SortAttribute attributes, const SortItem &values)
+{
+ // TODO: Playlist order is hacked into program count variable (not nice, but ok until 2.0)
+ return ByProgramCount(attributes, values);
+}
+
+string ByGenre(SortAttribute attributes, const SortItem &values)
+{
+ return ArrayToString(attributes, values.at(FieldGenre));
+}
+
+string ByCountry(SortAttribute attributes, const SortItem &values)
+{
+ return ArrayToString(attributes, values.at(FieldCountry));
+}
+
+string ByYear(SortAttribute attributes, const SortItem &values)
+{
+ CStdString label;
+ const CVariant &airDate = values.at(FieldAirDate);
+ if (!airDate.isNull() && !airDate.asString().empty())
+ label = airDate.asString() + " ";
+
+ label += StringUtils::Format("%i", (int)values.at(FieldYear).asInteger());
+
+ const CVariant &album = values.at(FieldAlbum);
+ if (!album.isNull())
+ label += " " + SortUtils::RemoveArticles(album.asString());
+
+ const CVariant &track = values.at(FieldTrackNumber);
+ if (!track.isNull())
+ label += StringUtils::Format(" %i", (int)track.asInteger());
+
+ label += " " + ByLabel(attributes, values);
+
+ return label;
+}
+
+string BySortTitle(SortAttribute attributes, const SortItem &values)
+{
+ string title = values.at(FieldSortTitle).asString();
+ if (title.empty())
+ title = values.at(FieldTitle).asString();
+
+ if (attributes & SortAttributeIgnoreArticle)
+ title = SortUtils::RemoveArticles(title);
+
+ return title;
+}
+
+string ByRating(SortAttribute attributes, const SortItem &values)
+{
+ return StringUtils::Format("%f %s", values.at(FieldRating).asFloat(), ByLabel(attributes, values).c_str());
+}
+
+string ByVotes(SortAttribute attributes, const SortItem &values)
+{
+ return StringUtils::Format("%d %s", (int)values.at(FieldVotes).asInteger(), ByLabel(attributes, values).c_str());
+}
+
+string ByTop250(SortAttribute attributes, const SortItem &values)
+{
+ return StringUtils::Format("%d %s", (int)values.at(FieldTop250).asInteger(), ByLabel(attributes, values).c_str());
+}
+
+string ByMPAA(SortAttribute attributes, const SortItem &values)
+{
+ return values.at(FieldMPAA).asString() + " " + ByLabel(attributes, values);
+}
+
+string ByStudio(SortAttribute attributes, const SortItem &values)
+{
+ return ArrayToString(attributes, values.at(FieldStudio));
+}
+
+string ByEpisodeNumber(SortAttribute attributes, const SortItem &values)
+{
+ // we calculate an offset number based on the episode's
+ // sort season and episode values. in addition
+ // we include specials 'episode' numbers to get proper
+ // sorting of multiple specials in a row. each
+ // of these are given their particular ranges to semi-ensure uniqueness.
+ // theoretical problem: if a show has > 2^15 specials and two of these are placed
+ // after each other they will sort backwards. if a show has > 2^32-1 seasons
+ // or if a season has > 2^16-1 episodes strange things will happen (overflow)
+ uint64_t num;
+ const CVariant &episodeSpecial = values.at(FieldEpisodeNumberSpecialSort);
+ const CVariant &seasonSpecial = values.at(FieldSeasonSpecialSort);
+ if (!episodeSpecial.isNull() && !seasonSpecial.isNull() &&
+ (episodeSpecial.asInteger() > 0 || seasonSpecial.asInteger() > 0))
+ num = ((uint64_t)seasonSpecial.asInteger() << 32) + (episodeSpecial.asInteger() << 16) - ((2 << 15) - values.at(FieldEpisodeNumber).asInteger());
+ else
+ num = ((uint64_t)values.at(FieldSeason).asInteger() << 32) + (values.at(FieldEpisodeNumber).asInteger() << 16);
+
+ std::string title;
+ if (values.find(FieldMediaType) != values.end() && values.at(FieldMediaType).asString() == MediaTypeMovie)
+ title = BySortTitle(attributes, values);
+ if (title.empty())
+ title = ByLabel(attributes, values);
+
+ return StringUtils::Format("%" PRIu64" %s", num, title.c_str());
+}
+
+string BySeason(SortAttribute attributes, const SortItem &values)
+{
+ int season = (int)values.at(FieldSeason).asInteger();
+ const CVariant &specialSeason = values.at(FieldSeasonSpecialSort);
+ if (!specialSeason.isNull())
+ season = (int)specialSeason.asInteger();
+
+ return StringUtils::Format("%i %s", season, ByLabel(attributes, values).c_str());
+}
+
+string ByNumberOfEpisodes(SortAttribute attributes, const SortItem &values)
+{
+ return StringUtils::Format("%i %s", (int)values.at(FieldNumberOfEpisodes).asInteger(), ByLabel(attributes, values).c_str());
+}
+
+string ByNumberOfWatchedEpisodes(SortAttribute attributes, const SortItem &values)
+{
+ return StringUtils::Format("%i %s", (int)values.at(FieldNumberOfWatchedEpisodes).asInteger(), ByLabel(attributes, values).c_str());
+}
+
+string ByTvShowStatus(SortAttribute attributes, const SortItem &values)
+{
+ return values.at(FieldTvShowStatus).asString() + " " + ByLabel(attributes, values);
+}
+
+string ByTvShowTitle(SortAttribute attributes, const SortItem &values)
+{
+ return values.at(FieldTvShowTitle).asString() + " " + ByLabel(attributes, values);
+}
+
+string ByProductionCode(SortAttribute attributes, const SortItem &values)
+{
+ return values.at(FieldProductionCode).asString();
+}
+
+string ByVideoResolution(SortAttribute attributes, const SortItem &values)
+{
+ return StringUtils::Format("%i %s", (int)values.at(FieldVideoResolution).asInteger(), ByLabel(attributes, values).c_str());
+}
+
+string ByVideoCodec(SortAttribute attributes, const SortItem &values)
+{
+ return StringUtils::Format("%s %s", values.at(FieldVideoCodec).asString().c_str(), ByLabel(attributes, values).c_str());
+}
+
+string ByVideoAspectRatio(SortAttribute attributes, const SortItem &values)
+{
+ return StringUtils::Format("%.03f %s", values.at(FieldVideoAspectRatio).asFloat(), ByLabel(attributes, values).c_str());
+}
+
+string ByAudioChannels(SortAttribute attributes, const SortItem &values)
+{
+ return StringUtils::Format("%i %s", (int)values.at(FieldAudioChannels).asInteger(), ByLabel(attributes, values).c_str());
+}
+
+string ByAudioCodec(SortAttribute attributes, const SortItem &values)
+{
+ return StringUtils::Format("%s %s", values.at(FieldAudioCodec).asString().c_str(), ByLabel(attributes, values).c_str());
+}
+
+string ByAudioLanguage(SortAttribute attributes, const SortItem &values)
+{
+ return StringUtils::Format("%s %s", values.at(FieldAudioLanguage).asString().c_str(), ByLabel(attributes, values).c_str());;
+}
+
+string BySubtitleLanguage(SortAttribute attributes, const SortItem &values)
+{
+ return StringUtils::Format("%s %s", values.at(FieldSubtitleLanguage).asString().c_str(), ByLabel(attributes, values).c_str());
+}
+
+string ByBitrate(SortAttribute attributes, const SortItem &values)
+{
+ return StringUtils::Format("%" PRId64, values.at(FieldBitrate).asInteger());
+}
+
+string ByListeners(SortAttribute attributes, const SortItem &values)
+{
+ return StringUtils::Format("%" PRId64, values.at(FieldListeners).asInteger());;
+}
+
+string ByRandom(SortAttribute attributes, const SortItem &values)
+{
+ return StringUtils::Format("%i", CUtil::GetRandomNumber());;
+}
+
+string ByChannel(SortAttribute attributes, const SortItem &values)
+{
+ return values.at(FieldChannelName).asString();
+}
+
+string ByChannelNumber(SortAttribute attributes, const SortItem &values)
+{
+ return StringUtils::Format("%i", (int)values.at(FieldChannelNumber).asInteger());
+}
+
+string ByDateTaken(SortAttribute attributes, const SortItem &values)
+{
+ return values.at(FieldDateTaken).asString();
+}
+
+bool preliminarySort(const SortItem &left, const SortItem &right, bool handleFolder, bool &result, std::wstring &labelLeft, std::wstring &labelRight)
+{
+ // make sure both items have the necessary data to do the sorting
+ SortItem::const_iterator itLeftSort, itRightSort;
+ if ((itLeftSort = left.find(FieldSort)) == left.end())
+ {
+ result = false;
+ return true;
+ }
+ if ((itRightSort = right.find(FieldSort)) == right.end())
+ {
+ result = true;
+ return true;
+ }
+
+ // look at special sorting behaviour
+ SortItem::const_iterator itLeft, itRight;
+ SortSpecial leftSortSpecial = SortSpecialNone;
+ SortSpecial rightSortSpecial = SortSpecialNone;
+ if ((itLeft = left.find(FieldSortSpecial)) != left.end() && itLeft->second.asInteger() <= (int64_t)SortSpecialOnBottom)
+ leftSortSpecial = (SortSpecial)itLeft->second.asInteger();
+ if ((itRight = right.find(FieldSortSpecial)) != right.end() && itRight->second.asInteger() <= (int64_t)SortSpecialOnBottom)
+ rightSortSpecial = (SortSpecial)itRight->second.asInteger();
+
+ // one has a special sort
+ if (leftSortSpecial != rightSortSpecial)
+ {
+ // left should be sorted on top
+ // or right should be sorted on bottom
+ // => left is sorted above right
+ if (leftSortSpecial == SortSpecialOnTop ||
+ rightSortSpecial == SortSpecialOnBottom)
+ {
+ result = true;
+ return true;
+ }
+
+ // otherwise right is sorted above left
+ result = false;
+ return true;
+ }
+ // both have either sort on top or sort on bottom -> leave as-is
+ else if (leftSortSpecial != SortSpecialNone && leftSortSpecial == rightSortSpecial)
+ {
+ result = false;
+ return true;
+ }
+
+ if (handleFolder)
+ {
+ itLeft = left.find(FieldFolder);
+ itRight = right.find(FieldFolder);
+ if (itLeft != left.end() && itRight != right.end() &&
+ itLeft->second.asBoolean() != itRight->second.asBoolean())
+ {
+ result = itLeft->second.asBoolean();
+ return true;
+ }
+ }
+
+ labelLeft = itLeftSort->second.asWideString();
+ labelRight = itRightSort->second.asWideString();
+
+ return false;
+}
+
+bool SorterAscending(const SortItem &left, const SortItem &right)
+{
+ bool result;
+ std::wstring labelLeft, labelRight;
+ if (preliminarySort(left, right, true, result, labelLeft, labelRight))
+ return result;
+
+ return StringUtils::AlphaNumericCompare(labelLeft.c_str(), labelRight.c_str()) < 0;
+}
+
+bool SorterDescending(const SortItem &left, const SortItem &right)
+{
+ bool result;
+ std::wstring labelLeft, labelRight;
+ if (preliminarySort(left, right, true, result, labelLeft, labelRight))
+ return result;
+
+ return StringUtils::AlphaNumericCompare(labelLeft.c_str(), labelRight.c_str()) > 0;
+}
+
+bool SorterIgnoreFoldersAscending(const SortItem &left, const SortItem &right)
+{
+ bool result;
+ std::wstring labelLeft, labelRight;
+ if (preliminarySort(left, right, false, result, labelLeft, labelRight))
+ return result;
+
+ return StringUtils::AlphaNumericCompare(labelLeft.c_str(), labelRight.c_str()) < 0;
+}
+
+bool SorterIgnoreFoldersDescending(const SortItem &left, const SortItem &right)
+{
+ bool result;
+ std::wstring labelLeft, labelRight;
+ if (preliminarySort(left, right, false, result, labelLeft, labelRight))
+ return result;
+
+ return StringUtils::AlphaNumericCompare(labelLeft.c_str(), labelRight.c_str()) > 0;
+}
+
+bool SorterIndirectAscending(const SortItemPtr &left, const SortItemPtr &right)
+{
+ return SorterAscending(*left, *right);
+}
+
+bool SorterIndirectDescending(const SortItemPtr &left, const SortItemPtr &right)
+{
+ return SorterDescending(*left, *right);
+}
+
+bool SorterIndirectIgnoreFoldersAscending(const SortItemPtr &left, const SortItemPtr &right)
+{
+ return SorterIgnoreFoldersAscending(*left, *right);
+}
+
+bool SorterIndirectIgnoreFoldersDescending(const SortItemPtr &left, const SortItemPtr &right)
+{
+ return SorterIgnoreFoldersDescending(*left, *right);
+}
+
+map<SortBy, SortUtils::SortPreparator> fillPreparators()
+{
+ map<SortBy, SortUtils::SortPreparator> preparators;
+
+ preparators[SortByNone] = NULL;
+ preparators[SortByLabel] = ByLabel;
+ preparators[SortByDate] = ByDate;
+ preparators[SortBySize] = BySize;
+ preparators[SortByFile] = ByFile;
+ preparators[SortByPath] = ByPath;
+ preparators[SortByDriveType] = ByDriveType;
+ preparators[SortByTitle] = ByTitle;
+ preparators[SortByTrackNumber] = ByTrackNumber;
+ preparators[SortByTime] = ByTime;
+ preparators[SortByArtist] = ByArtist;
+ preparators[SortByAlbum] = ByAlbum;
+ preparators[SortByAlbumType] = ByAlbumType;
+ preparators[SortByGenre] = ByGenre;
+ preparators[SortByCountry] = ByCountry;
+ preparators[SortByYear] = ByYear;
+ preparators[SortByRating] = ByRating;
+ preparators[SortByVotes] = ByVotes;
+ preparators[SortByTop250] = ByTop250;
+ preparators[SortByProgramCount] = ByProgramCount;
+ preparators[SortByPlaylistOrder] = ByPlaylistOrder;
+ preparators[SortByEpisodeNumber] = ByEpisodeNumber;
+ preparators[SortBySeason] = BySeason;
+ preparators[SortByNumberOfEpisodes] = ByNumberOfEpisodes;
+ preparators[SortByNumberOfWatchedEpisodes] = ByNumberOfWatchedEpisodes;
+ preparators[SortByTvShowStatus] = ByTvShowStatus;
+ preparators[SortByTvShowTitle] = ByTvShowTitle;
+ preparators[SortBySortTitle] = BySortTitle;
+ preparators[SortByProductionCode] = ByProductionCode;
+ preparators[SortByMPAA] = ByMPAA;
+ preparators[SortByVideoResolution] = ByVideoResolution;
+ preparators[SortByVideoCodec] = ByVideoCodec;
+ preparators[SortByVideoAspectRatio] = ByVideoAspectRatio;
+ preparators[SortByAudioChannels] = ByAudioChannels;
+ preparators[SortByAudioCodec] = ByAudioCodec;
+ preparators[SortByAudioLanguage] = ByAudioLanguage;
+ preparators[SortBySubtitleLanguage] = BySubtitleLanguage;
+ preparators[SortByStudio] = ByStudio;
+ preparators[SortByDateAdded] = ByDateAdded;
+ preparators[SortByLastPlayed] = ByLastPlayed;
+ preparators[SortByPlaycount] = ByPlaycount;
+ preparators[SortByListeners] = ByListeners;
+ preparators[SortByBitrate] = ByBitrate;
+ preparators[SortByRandom] = ByRandom;
+ preparators[SortByChannel] = ByChannel;
+ preparators[SortByChannelNumber] = ByChannelNumber;
+ preparators[SortByDateTaken] = ByDateTaken;
+
+ return preparators;
+}
+
+map<SortBy, Fields> fillSortingFields()
+{
+ map<SortBy, Fields> sortingFields;
+
+ sortingFields.insert(pair<SortBy, Fields>(SortByNone, Fields()));
+
+ sortingFields[SortByLabel].insert(FieldLabel);
+ sortingFields[SortByDate].insert(FieldDate);
+ sortingFields[SortBySize].insert(FieldSize);
+ sortingFields[SortByFile].insert(FieldPath);
+ sortingFields[SortByFile].insert(FieldStartOffset);
+ sortingFields[SortByPath].insert(FieldPath);
+ sortingFields[SortByPath].insert(FieldStartOffset);
+ sortingFields[SortByDriveType].insert(FieldDriveType);
+ sortingFields[SortByTitle].insert(FieldTitle);
+ sortingFields[SortByTrackNumber].insert(FieldTrackNumber);
+ sortingFields[SortByTime].insert(FieldTime);
+ sortingFields[SortByArtist].insert(FieldArtist);
+ sortingFields[SortByArtist].insert(FieldYear);
+ sortingFields[SortByArtist].insert(FieldAlbum);
+ sortingFields[SortByArtist].insert(FieldTrackNumber);
+ sortingFields[SortByAlbum].insert(FieldAlbum);
+ sortingFields[SortByAlbum].insert(FieldArtist);
+ sortingFields[SortByAlbum].insert(FieldTrackNumber);
+ sortingFields[SortByAlbumType].insert(FieldAlbumType);
+ sortingFields[SortByGenre].insert(FieldGenre);
+ sortingFields[SortByCountry].insert(FieldCountry);
+ sortingFields[SortByYear].insert(FieldYear);
+ sortingFields[SortByYear].insert(FieldAirDate);
+ sortingFields[SortByYear].insert(FieldAlbum);
+ sortingFields[SortByYear].insert(FieldTrackNumber);
+ sortingFields[SortByRating].insert(FieldRating);
+ sortingFields[SortByVotes].insert(FieldVotes);
+ sortingFields[SortByTop250].insert(FieldTop250);
+ sortingFields[SortByProgramCount].insert(FieldProgramCount);
+ sortingFields[SortByPlaylistOrder].insert(FieldProgramCount);
+ sortingFields[SortByEpisodeNumber].insert(FieldEpisodeNumber);
+ sortingFields[SortByEpisodeNumber].insert(FieldSeason);
+ sortingFields[SortByEpisodeNumber].insert(FieldEpisodeNumberSpecialSort);
+ sortingFields[SortByEpisodeNumber].insert(FieldSeasonSpecialSort);
+ sortingFields[SortByEpisodeNumber].insert(FieldTitle);
+ sortingFields[SortByEpisodeNumber].insert(FieldSortTitle);
+ sortingFields[SortBySeason].insert(FieldSeason);
+ sortingFields[SortBySeason].insert(FieldSeasonSpecialSort);
+ sortingFields[SortByNumberOfEpisodes].insert(FieldNumberOfEpisodes);
+ sortingFields[SortByNumberOfWatchedEpisodes].insert(FieldNumberOfWatchedEpisodes);
+ sortingFields[SortByTvShowStatus].insert(FieldTvShowStatus);
+ sortingFields[SortByTvShowTitle].insert(FieldTvShowTitle);
+ sortingFields[SortBySortTitle].insert(FieldSortTitle);
+ sortingFields[SortBySortTitle].insert(FieldTitle);
+ sortingFields[SortByProductionCode].insert(FieldProductionCode);
+ sortingFields[SortByMPAA].insert(FieldMPAA);
+ sortingFields[SortByVideoResolution].insert(FieldVideoResolution);
+ sortingFields[SortByVideoCodec].insert(FieldVideoCodec);
+ sortingFields[SortByVideoAspectRatio].insert(FieldVideoAspectRatio);
+ sortingFields[SortByAudioChannels].insert(FieldAudioChannels);
+ sortingFields[SortByAudioCodec].insert(FieldAudioCodec);
+ sortingFields[SortByAudioLanguage].insert(FieldAudioLanguage);
+ sortingFields[SortBySubtitleLanguage].insert(FieldSubtitleLanguage);
+ sortingFields[SortByStudio].insert(FieldStudio);
+ sortingFields[SortByDateAdded].insert(FieldDateAdded);
+ sortingFields[SortByDateAdded].insert(FieldId);
+ sortingFields[SortByLastPlayed].insert(FieldLastPlayed);
+ sortingFields[SortByPlaycount].insert(FieldPlaycount);
+ sortingFields[SortByListeners].insert(FieldListeners);
+ sortingFields[SortByBitrate].insert(FieldBitrate);
+ sortingFields[SortByChannel].insert(FieldChannelName);
+ sortingFields[SortByChannelNumber].insert(FieldChannelNumber);
+ sortingFields[SortByDateTaken].insert(FieldDateTaken);
+ sortingFields.insert(pair<SortBy, Fields>(SortByRandom, Fields()));
+
+ return sortingFields;
+}
+
+map<SortBy, SortUtils::SortPreparator> SortUtils::m_preparators = fillPreparators();
+map<SortBy, Fields> SortUtils::m_sortingFields = fillSortingFields();
+
+void SortUtils::Sort(SortBy sortBy, SortOrder sortOrder, SortAttribute attributes, DatabaseResults& items, int limitEnd /* = -1 */, int limitStart /* = 0 */)
+{
+ if (sortBy != SortByNone)
+ {
+ // get the matching SortPreparator
+ SortPreparator preparator = getPreparator(sortBy);
+ if (preparator != NULL)
+ {
+ Fields sortingFields = GetFieldsForSorting(sortBy);
+
+ // Prepare the string used for sorting and store it under FieldSort
+ for (DatabaseResults::iterator item = items.begin(); item != items.end(); ++item)
+ {
+ // add all fields to the item that are required for sorting if they are currently missing
+ for (Fields::const_iterator field = sortingFields.begin(); field != sortingFields.end(); ++field)
+ {
+ if (item->find(*field) == item->end())
+ item->insert(pair<Field, CVariant>(*field, CVariant::ConstNullVariant));
+ }
+
+ CStdStringW sortLabel;
+ g_charsetConverter.utf8ToW(preparator(attributes, *item), sortLabel, false);
+ item->insert(pair<Field, CVariant>(FieldSort, CVariant(sortLabel)));
+ }
+
+ // Do the sorting
+ std::stable_sort(items.begin(), items.end(), getSorter(sortOrder, attributes));
+ }
+ }
+
+ if (limitStart > 0 && (size_t)limitStart < items.size())
+ {
+ items.erase(items.begin(), items.begin() + limitStart);
+ limitEnd -= limitStart;
+ }
+ if (limitEnd > 0 && (size_t)limitEnd < items.size())
+ items.erase(items.begin() + limitEnd, items.end());
+}
+
+void SortUtils::Sort(SortBy sortBy, SortOrder sortOrder, SortAttribute attributes, SortItems& items, int limitEnd /* = -1 */, int limitStart /* = 0 */)
+{
+ if (sortBy != SortByNone)
+ {
+ // get the matching SortPreparator
+ SortPreparator preparator = getPreparator(sortBy);
+ if (preparator != NULL)
+ {
+ Fields sortingFields = GetFieldsForSorting(sortBy);
+
+ // Prepare the string used for sorting and store it under FieldSort
+ for (SortItems::iterator item = items.begin(); item != items.end(); ++item)
+ {
+ // add all fields to the item that are required for sorting if they are currently missing
+ for (Fields::const_iterator field = sortingFields.begin(); field != sortingFields.end(); ++field)
+ {
+ if ((*item)->find(*field) == (*item)->end())
+ (*item)->insert(pair<Field, CVariant>(*field, CVariant::ConstNullVariant));
+ }
+
+ CStdStringW sortLabel;
+ g_charsetConverter.utf8ToW(preparator(attributes, **item), sortLabel, false);
+ (*item)->insert(pair<Field, CVariant>(FieldSort, CVariant(sortLabel)));
+ }
+
+ // Do the sorting
+ std::stable_sort(items.begin(), items.end(), getSorterIndirect(sortOrder, attributes));
+ }
+ }
+
+ if (limitStart > 0 && (size_t)limitStart < items.size())
+ {
+ items.erase(items.begin(), items.begin() + limitStart);
+ limitEnd -= limitStart;
+ }
+ if (limitEnd > 0 && (size_t)limitEnd < items.size())
+ items.erase(items.begin() + limitEnd, items.end());
+}
+
+void SortUtils::Sort(const SortDescription &sortDescription, DatabaseResults& items)
+{
+ Sort(sortDescription.sortBy, sortDescription.sortOrder, sortDescription.sortAttributes, items, sortDescription.limitEnd, sortDescription.limitStart);
+}
+
+void SortUtils::Sort(const SortDescription &sortDescription, SortItems& items)
+{
+ Sort(sortDescription.sortBy, sortDescription.sortOrder, sortDescription.sortAttributes, items, sortDescription.limitEnd, sortDescription.limitStart);
+}
+
+bool SortUtils::SortFromDataset(const SortDescription &sortDescription, const MediaType &mediaType, const std::auto_ptr<dbiplus::Dataset> &dataset, DatabaseResults &results)
+{
+ FieldList fields;
+ if (!DatabaseUtils::GetSelectFields(SortUtils::GetFieldsForSorting(sortDescription.sortBy), mediaType, fields))
+ fields.clear();
+
+ if (!DatabaseUtils::GetDatabaseResults(mediaType, fields, dataset, results))
+ return false;
+
+ SortDescription sorting = sortDescription;
+ if (sortDescription.sortBy == SortByNone)
+ {
+ sorting.limitStart = 0;
+ sorting.limitEnd = -1;
+ }
+
+ Sort(sorting, results);
+
+ return true;
+}
+
+const SortUtils::SortPreparator& SortUtils::getPreparator(SortBy sortBy)
+{
+ map<SortBy, SortPreparator>::const_iterator it = m_preparators.find(sortBy);
+ if (it != m_preparators.end())
+ return it->second;
+
+ return m_preparators[SortByNone];
+}
+
+SortUtils::Sorter SortUtils::getSorter(SortOrder sortOrder, SortAttribute attributes)
+{
+ if (attributes & SortAttributeIgnoreFolders)
+ return sortOrder == SortOrderDescending ? SorterIgnoreFoldersDescending : SorterIgnoreFoldersAscending;
+
+ return sortOrder == SortOrderDescending ? SorterDescending : SorterAscending;
+}
+
+SortUtils::SorterIndirect SortUtils::getSorterIndirect(SortOrder sortOrder, SortAttribute attributes)
+{
+ if (attributes & SortAttributeIgnoreFolders)
+ return sortOrder == SortOrderDescending ? SorterIndirectIgnoreFoldersDescending : SorterIndirectIgnoreFoldersAscending;
+
+ return sortOrder == SortOrderDescending ? SorterIndirectDescending : SorterIndirectAscending;
+}
+
+const Fields& SortUtils::GetFieldsForSorting(SortBy sortBy)
+{
+ map<SortBy, Fields>::const_iterator it = m_sortingFields.find(sortBy);
+ if (it != m_sortingFields.end())
+ return it->second;
+
+ return m_sortingFields[SortByNone];
+}
+
+string SortUtils::RemoveArticles(const string &label)
+{
+ for (unsigned int i = 0; i < g_advancedSettings.m_vecTokens.size(); ++i)
+ {
+ if (g_advancedSettings.m_vecTokens[i].size() < label.size() &&
+ strnicmp(g_advancedSettings.m_vecTokens[i].c_str(), label.c_str(), g_advancedSettings.m_vecTokens[i].size()) == 0)
+ return label.substr(g_advancedSettings.m_vecTokens[i].size());
+ }
+
+ return label;
+}
+
+typedef struct
+{
+ SortBy sort;
+ SORT_METHOD old;
+ SortAttribute flags;
+ int label;
+} sort_map;
+
+const sort_map table[] = {
+ { SortByLabel, SORT_METHOD_LABEL, SortAttributeNone, 551 },
+ { SortByLabel, SORT_METHOD_LABEL_IGNORE_THE, SortAttributeIgnoreArticle, 551 },
+ { SortByLabel, SORT_METHOD_LABEL_IGNORE_FOLDERS, SortAttributeIgnoreFolders, 551 },
+ { SortByDate, SORT_METHOD_DATE, SortAttributeNone, 552 },
+ { SortBySize, SORT_METHOD_SIZE, SortAttributeNone, 553 },
+ { SortByBitrate, SORT_METHOD_BITRATE, SortAttributeNone, 623 },
+ { SortByDriveType, SORT_METHOD_DRIVE_TYPE, SortAttributeNone, 564 },
+ { SortByTrackNumber, SORT_METHOD_TRACKNUM, SortAttributeNone, 554 },
+ { SortByEpisodeNumber, SORT_METHOD_EPISODE, SortAttributeNone, 20359 },// 20360 "Episodes" used for SORT_METHOD_EPISODE for sorting tvshows by episode count
+ { SortByTime, SORT_METHOD_DURATION, SortAttributeNone, 180 },
+ { SortByTime, SORT_METHOD_VIDEO_RUNTIME, SortAttributeNone, 180 },
+ { SortByTitle, SORT_METHOD_TITLE, SortAttributeNone, 556 },
+ { SortByTitle, SORT_METHOD_TITLE_IGNORE_THE, SortAttributeIgnoreArticle, 556 },
+ { SortByTitle, SORT_METHOD_VIDEO_TITLE, SortAttributeNone, 556 },
+ { SortByArtist, SORT_METHOD_ARTIST, SortAttributeNone, 557 },
+ { SortByArtist, SORT_METHOD_ARTIST_IGNORE_THE, SortAttributeIgnoreArticle, 557 },
+ { SortByAlbum, SORT_METHOD_ALBUM, SortAttributeNone, 558 },
+ { SortByAlbum, SORT_METHOD_ALBUM_IGNORE_THE, SortAttributeIgnoreArticle, 558 },
+ { SortByGenre, SORT_METHOD_GENRE, SortAttributeNone, 515 },
+ { SortByCountry, SORT_METHOD_COUNTRY, SortAttributeNone, 574 },
+ { SortByDateAdded, SORT_METHOD_DATEADDED, SortAttributeIgnoreFolders, 570 },
+ { SortByFile, SORT_METHOD_FILE, SortAttributeIgnoreFolders, 561 },
+ { SortByRating, SORT_METHOD_SONG_RATING, SortAttributeNone, 563 },
+ { SortByRating, SORT_METHOD_VIDEO_RATING, SortAttributeIgnoreFolders, 563 },
+ { SortBySortTitle, SORT_METHOD_VIDEO_SORT_TITLE, SortAttributeIgnoreFolders, 171 },
+ { SortBySortTitle, SORT_METHOD_VIDEO_SORT_TITLE_IGNORE_THE, (SortAttribute)(SortAttributeIgnoreFolders | SortAttributeIgnoreArticle), 171 },
+ { SortByYear, SORT_METHOD_YEAR, SortAttributeIgnoreFolders, 562 },
+ { SortByProductionCode, SORT_METHOD_PRODUCTIONCODE, SortAttributeNone, 20368 },
+ { SortByProgramCount, SORT_METHOD_PROGRAM_COUNT, SortAttributeNone, 567 }, // label is "play count"
+ { SortByPlaylistOrder, SORT_METHOD_PLAYLIST_ORDER, SortAttributeIgnoreFolders, 559 },
+ { SortByMPAA, SORT_METHOD_MPAA_RATING, SortAttributeNone, 20074 },
+ { SortByStudio, SORT_METHOD_STUDIO, SortAttributeNone, 572 },
+ { SortByStudio, SORT_METHOD_STUDIO_IGNORE_THE, SortAttributeIgnoreArticle, 572 },
+ { SortByPath, SORT_METHOD_FULLPATH, SortAttributeNone, 573 },
+ { SortByLastPlayed, SORT_METHOD_LASTPLAYED, SortAttributeIgnoreFolders, 568 },
+ { SortByPlaycount, SORT_METHOD_PLAYCOUNT, SortAttributeIgnoreFolders, 567 },
+ { SortByListeners, SORT_METHOD_LISTENERS, SortAttributeNone, 20455 },
+ { SortByChannel, SORT_METHOD_CHANNEL, SortAttributeNone, 19029 },
+ { SortByChannel, SORT_METHOD_CHANNEL_NUMBER, SortAttributeNone, 549 },
+ { SortByDateTaken, SORT_METHOD_DATE_TAKEN, SortAttributeIgnoreFolders, 577 },
+ { SortByNone, SORT_METHOD_NONE, SortAttributeNone, 16018 },
+ // the following have no corresponding SORT_METHOD_*
+ { SortByAlbumType, SORT_METHOD_NONE, SortAttributeNone, 564 },
+ { SortByVotes, SORT_METHOD_NONE, SortAttributeNone, 205 },
+ { SortByTop250, SORT_METHOD_NONE, SortAttributeNone, 13409 },
+ { SortByMPAA, SORT_METHOD_NONE, SortAttributeNone, 20074 },
+ { SortByDateAdded, SORT_METHOD_NONE, SortAttributeNone, 570 },
+ { SortByTvShowTitle, SORT_METHOD_NONE, SortAttributeNone, 20364 },
+ { SortByTvShowStatus, SORT_METHOD_NONE, SortAttributeNone, 126 },
+ { SortBySeason, SORT_METHOD_NONE, SortAttributeNone, 20373 },
+ { SortByNumberOfEpisodes, SORT_METHOD_NONE, SortAttributeNone, 20360 },
+ { SortByNumberOfWatchedEpisodes, SORT_METHOD_NONE, SortAttributeNone, 21441 },
+ { SortByVideoResolution, SORT_METHOD_NONE, SortAttributeNone, 21443 },
+ { SortByVideoCodec, SORT_METHOD_NONE, SortAttributeNone, 21445 },
+ { SortByVideoAspectRatio, SORT_METHOD_NONE, SortAttributeNone, 21374 },
+ { SortByAudioChannels, SORT_METHOD_NONE, SortAttributeNone, 21444 },
+ { SortByAudioCodec, SORT_METHOD_NONE, SortAttributeNone, 21446 },
+ { SortByAudioLanguage, SORT_METHOD_NONE, SortAttributeNone, 21447 },
+ { SortBySubtitleLanguage, SORT_METHOD_NONE, SortAttributeNone, 21448 },
+ { SortByRandom, SORT_METHOD_NONE, SortAttributeNone, 590 }
+};
+
+SORT_METHOD SortUtils::TranslateOldSortMethod(SortBy sortBy, bool ignoreArticle)
+{
+ for (size_t i = 0; i < sizeof(table) / sizeof(sort_map); i++)
+ {
+ if (table[i].sort == sortBy)
+ {
+ if (ignoreArticle == ((table[i].flags & SortAttributeIgnoreArticle) == SortAttributeIgnoreArticle))
+ return table[i].old;
+ }
+ }
+ for (size_t i = 0; i < sizeof(table) / sizeof(sort_map); i++)
+ {
+ if (table[i].sort == sortBy)
+ return table[i].old;
+ }
+ return SORT_METHOD_NONE;
+}
+
+SortDescription SortUtils::TranslateOldSortMethod(SORT_METHOD sortBy)
+{
+ SortDescription description;
+ for (size_t i = 0; i < sizeof(table) / sizeof(sort_map); i++)
+ {
+ if (table[i].old == sortBy)
+ {
+ description.sortBy = table[i].sort;
+ description.sortAttributes = table[i].flags;
+ break;
+ }
+ }
+ return description;
+}
+
+int SortUtils::GetSortLabel(SortBy sortBy)
+{
+ for (size_t i = 0; i < sizeof(table) / sizeof(sort_map); i++)
+ {
+ if (table[i].sort == sortBy)
+ return table[i].label;
+ }
+ return 16018; // None
+}
diff --git a/src/utils/SortUtils.h b/src/utils/SortUtils.h
new file mode 100644
index 0000000000..0984af0e4b
--- /dev/null
+++ b/src/utils/SortUtils.h
@@ -0,0 +1,154 @@
+#pragma once
+/*
+ * Copyright (C) 2012-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <map>
+#include <string>
+#include "boost/shared_ptr.hpp"
+
+#include "DatabaseUtils.h"
+#include "SortFileItem.h"
+#include "LabelFormatter.h"
+
+typedef enum {
+ SortOrderNone = 0,
+ SortOrderAscending,
+ SortOrderDescending
+} SortOrder;
+
+typedef enum {
+ SortAttributeNone = 0x0,
+ SortAttributeIgnoreArticle = 0x1,
+ SortAttributeIgnoreFolders = 0x2
+} SortAttribute;
+
+typedef enum {
+ SortSpecialNone = 0,
+ SortSpecialOnTop = 1,
+ SortSpecialOnBottom = 2
+} SortSpecial;
+
+typedef enum {
+ SortByNone = 0,
+ SortByLabel,
+ SortByDate,
+ SortBySize,
+ SortByFile,
+ SortByPath,
+ SortByDriveType,
+ SortByTitle,
+ SortByTrackNumber,
+ SortByTime,
+ SortByArtist,
+ SortByAlbum,
+ SortByAlbumType,
+ SortByGenre,
+ SortByCountry,
+ SortByYear,
+ SortByRating,
+ SortByVotes,
+ SortByTop250,
+ SortByProgramCount,
+ SortByPlaylistOrder,
+ SortByEpisodeNumber,
+ SortBySeason,
+ SortByNumberOfEpisodes,
+ SortByNumberOfWatchedEpisodes,
+ SortByTvShowStatus,
+ SortByTvShowTitle,
+ SortBySortTitle,
+ SortByProductionCode,
+ SortByMPAA,
+ SortByVideoResolution,
+ SortByVideoCodec,
+ SortByVideoAspectRatio,
+ SortByAudioChannels,
+ SortByAudioCodec,
+ SortByAudioLanguage,
+ SortBySubtitleLanguage,
+ SortByStudio,
+ SortByDateAdded,
+ SortByLastPlayed,
+ SortByPlaycount,
+ SortByListeners,
+ SortByBitrate,
+ SortByRandom,
+ SortByChannel,
+ SortByChannelNumber,
+ SortByDateTaken
+} SortBy;
+
+typedef struct SortDescription {
+ SortBy sortBy;
+ SortOrder sortOrder;
+ SortAttribute sortAttributes;
+ int limitStart;
+ int limitEnd;
+
+ SortDescription()
+ : sortBy(SortByNone), sortOrder(SortOrderAscending), sortAttributes(SortAttributeNone),
+ limitStart(0), limitEnd(-1)
+ { }
+} SortDescription;
+
+typedef struct
+{
+ SortDescription m_sortDescription;
+ int m_buttonLabel;
+ LABEL_MASKS m_labelMasks;
+} SORT_METHOD_DETAILS;
+
+typedef DatabaseResult SortItem;
+typedef boost::shared_ptr<SortItem> SortItemPtr;
+typedef std::vector<SortItemPtr> SortItems;
+
+class SortUtils
+{
+public:
+ static SORT_METHOD TranslateOldSortMethod(SortBy sortBy, bool ignoreArticle);
+ static SortDescription TranslateOldSortMethod(SORT_METHOD sortBy);
+
+ /*! \brief retrieve the label id associated with a sort method for displaying in the UI.
+ \param sortBy the sort method in question.
+ \return the label id of the sort method.
+ */
+ static int GetSortLabel(SortBy sortBy);
+
+ static void Sort(SortBy sortBy, SortOrder sortOrder, SortAttribute attributes, DatabaseResults& items, int limitEnd = -1, int limitStart = 0);
+ static void Sort(SortBy sortBy, SortOrder sortOrder, SortAttribute attributes, SortItems& items, int limitEnd = -1, int limitStart = 0);
+ static void Sort(const SortDescription &sortDescription, DatabaseResults& items);
+ static void Sort(const SortDescription &sortDescription, SortItems& items);
+ static bool SortFromDataset(const SortDescription &sortDescription, const MediaType &mediaType, const std::auto_ptr<dbiplus::Dataset> &dataset, DatabaseResults &results);
+
+ static const Fields& GetFieldsForSorting(SortBy sortBy);
+ static std::string RemoveArticles(const std::string &label);
+
+ typedef std::string (*SortPreparator) (SortAttribute, const SortItem&);
+ typedef bool (*Sorter) (const DatabaseResult &, const DatabaseResult &);
+ typedef bool (*SorterIndirect) (const SortItemPtr &, const SortItemPtr &);
+
+private:
+ static const SortPreparator& getPreparator(SortBy sortBy);
+ static Sorter getSorter(SortOrder sortOrder, SortAttribute attributes);
+ static SorterIndirect getSorterIndirect(SortOrder sortOrder, SortAttribute attributes);
+
+ static std::map<SortBy, SortPreparator> m_preparators;
+ static std::map<SortBy, Fields> m_sortingFields;
+};
diff --git a/src/utils/Splash.cpp b/src/utils/Splash.cpp
new file mode 100644
index 0000000000..764f64ab66
--- /dev/null
+++ b/src/utils/Splash.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "system.h"
+#include "Splash.h"
+#include "guilib/GUIImage.h"
+#include "guilib/GUILabelControl.h"
+#include "guilib/GUIFontManager.h"
+#include "filesystem/File.h"
+#include "windowing/WindowingFactory.h"
+#include "rendering/RenderSystem.h"
+#include "log.h"
+
+using namespace XFILE;
+
+CSplash::CSplash(const std::string& imageName) : CThread("Splash"), m_ImageName(imageName)
+{
+ fade = 0.5;
+ m_messageLayout = NULL;
+ m_image = NULL;
+ m_layoutWasLoading = false;
+}
+
+
+CSplash::~CSplash()
+{
+ Stop();
+ delete m_image;
+ delete m_messageLayout;
+}
+
+void CSplash::OnStartup()
+{}
+
+void CSplash::OnExit()
+{}
+
+void CSplash::Show()
+{
+ Show("");
+}
+
+void CSplash::Show(const std::string& message)
+{
+ g_graphicsContext.Lock();
+ g_graphicsContext.Clear();
+
+ RESOLUTION_INFO res(1280,720,0);
+ g_graphicsContext.SetRenderingResolution(res, true);
+ if (!m_image)
+ {
+ m_image = new CGUIImage(0, 0, 0, 0, 1280, 720, CTextureInfo(m_ImageName));
+ m_image->SetAspectRatio(CAspectRatio::AR_CENTER);
+ }
+
+ //render splash image
+ g_Windowing.BeginRender();
+
+ m_image->AllocResources();
+ m_image->Render();
+ m_image->FreeResources();
+
+ // render message
+ if (!message.empty())
+ {
+ if (!m_layoutWasLoading)
+ {
+ // load arial font, white body, no shadow, size: 20, no additional styling
+ CGUIFont *messageFont = g_fontManager.LoadTTF("__splash__", "arial.ttf", 0xFFFFFFFF, 0, 20, FONT_STYLE_NORMAL, false, 1.0f, 1.0f, &res);
+ if (messageFont)
+ m_messageLayout = new CGUITextLayout(messageFont, true, 0);
+ m_layoutWasLoading = true;
+ }
+ if (m_messageLayout)
+ {
+ m_messageLayout->Update(message, 1150, false, true);
+
+ float textWidth, textHeight;
+ m_messageLayout->GetTextExtent(textWidth, textHeight);
+ // ideally place text in center of empty area below splash image
+ float y = 540 + m_image->GetTextureHeight() / 4 - textHeight / 2;
+ if (y + textHeight > 720) // make sure entire text is visible
+ y = 720 - textHeight;
+
+ m_messageLayout->RenderOutline(640, y, 0, 0xFF000000, XBFONT_CENTER_X, 1280);
+ }
+ }
+
+ //show it on screen
+ g_Windowing.EndRender();
+ CDirtyRegionList dirty;
+ g_graphicsContext.Flip(dirty);
+ g_graphicsContext.Unlock();
+}
+
+void CSplash::Hide()
+{
+}
+
+void CSplash::Process()
+{
+ Show();
+}
+
+bool CSplash::Start()
+{
+ if (m_ImageName.empty() || !CFile::Exists(m_ImageName))
+ {
+ CLog::Log(LOGDEBUG, "Splash image %s not found", m_ImageName.c_str());
+ return false;
+ }
+ Create();
+ return true;
+}
+
+void CSplash::Stop()
+{
+ StopThread();
+}
diff --git a/src/utils/Splash.h b/src/utils/Splash.h
new file mode 100644
index 0000000000..2cd7a4b13c
--- /dev/null
+++ b/src/utils/Splash.h
@@ -0,0 +1,59 @@
+#pragma once
+
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string>
+#include "threads/Thread.h"
+
+class CGUITextLayout;
+class CGUIImage;
+
+class CSplash : public CThread
+{
+public:
+ CSplash(const std::string& imageName);
+ virtual ~CSplash();
+
+ bool Start();
+ void Stop();
+
+ // In case you don't want to use another thread
+ void Show();
+ void Show(const std::string& message);
+ void Hide();
+
+private:
+ virtual void Process();
+ virtual void OnStartup();
+ virtual void OnExit();
+
+ float fade;
+ std::string m_ImageName;
+
+ CGUITextLayout* m_messageLayout;
+ CGUIImage* m_image;
+ bool m_layoutWasLoading;
+#ifdef HAS_DX
+ D3DGAMMARAMP newRamp;
+ D3DGAMMARAMP oldRamp;
+
+#endif
+};
diff --git a/src/utils/StdString.h b/src/utils/StdString.h
new file mode 100644
index 0000000000..6430f1008a
--- /dev/null
+++ b/src/utils/StdString.h
@@ -0,0 +1,3154 @@
+#pragma once
+#include <string>
+#include <stdint.h>
+#include <vector>
+
+#if defined(TARGET_WINDOWS) && !defined(va_copy)
+#define va_copy(dst, src) ((dst) = (src))
+#endif
+
+// =============================================================================
+// FILE: StdString.h
+// AUTHOR: Joe O'Leary (with outside help noted in comments)
+//
+// If you find any bugs in this code, please let me know:
+//
+// jmoleary@earthlink.net
+// http://www.joeo.net/stdstring.htm (a bit outdated)
+//
+// The latest version of this code should always be available at the
+// following link:
+//
+// http://www.joeo.net/code/StdString.zip (Dec 6, 2003)
+//
+//
+// REMARKS:
+// This header file declares the CStdStr template. This template derives
+// the Standard C++ Library basic_string<> template and add to it the
+// the following conveniences:
+// - The full MFC CString set of functions (including implicit cast)
+// - writing to/reading from COM IStream interfaces
+// - Functional objects for use in STL algorithms
+//
+// From this template, we intstantiate two classes: CStdStringA and
+// CStdStringW. The name "CStdString" is just a #define of one of these,
+// based upone the UNICODE macro setting
+//
+// This header also declares our own version of the MFC/ATL UNICODE-MBCS
+// conversion macros. Our version looks exactly like the Microsoft's to
+// facilitate portability.
+//
+// NOTE:
+// If you you use this in an MFC or ATL build, you should include either
+// afx.h or atlbase.h first, as appropriate.
+//
+// PEOPLE WHO HAVE CONTRIBUTED TO THIS CLASS:
+//
+// Several people have helped me iron out problems and othewise improve
+// this class. OK, this is a long list but in my own defense, this code
+// has undergone two major rewrites. Many of the improvements became
+// necessary after I rewrote the code as a template. Others helped me
+// improve the CString facade.
+//
+// Anyway, these people are (in chronological order):
+//
+// - Pete the Plumber (???)
+// - Julian Selman
+// - Chris (of Melbsys)
+// - Dave Plummer
+// - John C Sipos
+// - Chris Sells
+// - Nigel Nunn
+// - Fan Xia
+// - Matthew Williams
+// - Carl Engman
+// - Mark Zeren
+// - Craig Watson
+// - Rich Zuris
+// - Karim Ratib
+// - Chris Conti
+// - Baptiste Lepilleur
+// - Greg Pickles
+// - Jim Cline
+// - Jeff Kohn
+// - Todd Heckel
+// - Ullrich Poll�hne
+// - Joe Vitaterna
+// - Joe Woodbury
+// - Aaron (no last name)
+// - Joldakowski (???)
+// - Scott Hathaway
+// - Eric Nitzche
+// - Pablo Presedo
+// - Farrokh Nejadlotfi
+// - Jason Mills
+// - Igor Kholodov
+// - Mike Crusader
+// - John James
+// - Wang Haifeng
+// - Tim Dowty
+// - Arnt Witteveen
+// - Glen Maynard
+// - Paul DeMarco
+// - Bagira (full name?)
+// - Ronny Schulz
+// - Jakko Van Hunen
+// - Charles Godwin
+// - Henk Demper
+// - Greg Marr
+// - Bill Carducci
+// - Brian Groose
+// - MKingman
+// - Don Beusee
+//
+// REVISION HISTORY
+//
+// 2005-JAN-10 - Thanks to Don Beusee for pointing out the danger in mapping
+// length-checked formatting functions to non-length-checked
+// CRT equivalents. Also thanks to him for motivating me to
+// optimize my implementation of Replace()
+//
+// 2004-APR-22 - A big, big thank you to "MKingman" (whoever you are) for
+// finally spotting a silly little error in StdCodeCvt that
+// has been causing me (and users of CStdString) problems for
+// years in some relatively rare conversions. I had reversed
+// two length arguments.
+//
+// 2003-NOV-24 - Thanks to a bunch of people for helping me clean up many
+// compiler warnings (and yes, even a couple of actual compiler
+// errors). These include Henk Demper for figuring out how
+// to make the Intellisense work on with CStdString on VC6,
+// something I was never able to do. Greg Marr pointed out
+// a compiler warning about an unreferenced symbol and a
+// problem with my version of Load in MFC builds. Bill
+// Carducci took a lot of time with me to help me figure out
+// why some implementations of the Standard C++ Library were
+// returning error codes for apparently successful conversions
+// between ASCII and UNICODE. Finally thanks to Brian Groose
+// for helping me fix compiler signed unsigned warnings in
+// several functions.
+//
+// 2003-JUL-10 - Thanks to Charles Godwin for making me realize my 'FmtArg'
+// fixes had inadvertently broken the DLL-export code (which is
+// normally commented out. I had to move it up higher. Also
+// this helped me catch a bug in ssicoll that would prevent
+// compilation, otherwise.
+//
+// 2003-MAR-14 - Thanks to Jakko Van Hunen for pointing out a copy-and-paste
+// bug in one of the overloads of FmtArg.
+//
+// 2003-MAR-10 - Thanks to Ronny Schulz for (twice!) sending me some changes
+// to help CStdString build on SGI and for pointing out an
+// error in placement of my preprocessor macros for ssfmtmsg.
+//
+// 2002-NOV-26 - Thanks to Bagira for pointing out that my implementation of
+// SpanExcluding was not properly handling the case in which
+// the string did NOT contain any of the given characters
+//
+// 2002-OCT-21 - Many thanks to Paul DeMarco who was invaluable in helping me
+// get this code working with Borland's free compiler as well
+// as the Dev-C++ compiler (available free at SourceForge).
+//
+// 2002-SEP-13 - Thanks to Glen Maynard who helped me get rid of some loud
+// but harmless warnings that were showing up on g++. Glen
+// also pointed out that some pre-declarations of FmtArg<>
+// specializations were unnecessary (and no good on G++)
+//
+// 2002-JUN-26 - Thanks to Arnt Witteveen for pointing out that I was using
+// static_cast<> in a place in which I should have been using
+// reinterpret_cast<> (the ctor for unsigned char strings).
+// That's what happens when I don't unit-test properly!
+// Arnt also noticed that CString was silently correcting the
+// 'nCount' argument to Left() and Right() where CStdString was
+// not (and crashing if it was bad). That is also now fixed!
+//
+// 2002-FEB-25 - Thanks to Tim Dowty for pointing out (and giving me the fix
+// for) a conversion problem with non-ASCII MBCS characters.
+// CStdString is now used in my favorite commercial MP3 player!
+//
+// 2001-DEC-06 - Thanks to Wang Haifeng for spotting a problem in one of the
+// assignment operators (for _bstr_t) that would cause compiler
+// errors when refcounting protection was turned off.
+//
+// 2001-NOV-27 - Remove calls to operator!= which involve reverse_iterators
+// due to a conflict with the rel_ops operator!=. Thanks to
+// John James for pointing this out.
+//
+// 2001-OCT-29 - Added a minor range checking fix for the Mid function to
+// make it as forgiving as CString's version is. Thanks to
+// Igor Kholodov for noticing this.
+// - Added a specialization of std::swap for CStdString. Thanks
+// to Mike Crusader for suggesting this! It's commented out
+// because you're not supposed to inject your own code into the
+// 'std' namespace. But if you don't care about that, it's
+// there if you want it
+// - Thanks to Jason Mills for catching a case where CString was
+// more forgiving in the Delete() function than I was.
+//
+// 2001-JUN-06 - I was violating the Standard name lookup rules stated
+// in [14.6.2(3)]. None of the compilers I've tried so
+// far apparently caught this but HP-UX aCC 3.30 did. The
+// fix was to add 'this->' prefixes in many places.
+// Thanks to Farrokh Nejadlotfi for this!
+//
+// 2001-APR-27 - StreamLoad was calculating the number of BYTES in one
+// case, not characters. Thanks to Pablo Presedo for this.
+//
+// 2001-FEB-23 - Replace() had a bug which caused infinite loops if the
+// source string was empty. Fixed thanks to Eric Nitzsche.
+//
+// 2001-FEB-23 - Scott Hathaway was a huge help in providing me with the
+// ability to build CStdString on Sun Unix systems. He
+// sent me detailed build reports about what works and what
+// does not. If CStdString compiles on your Unix box, you
+// can thank Scott for it.
+//
+// 2000-DEC-29 - Joldakowski noticed one overload of Insert failed to do a
+// range check as CString's does. Now fixed -- thanks!
+//
+// 2000-NOV-07 - Aaron pointed out that I was calling static member
+// functions of char_traits via a temporary. This was not
+// technically wrong, but it was unnecessary and caused
+// problems for poor old buggy VC5. Thanks Aaron!
+//
+// 2000-JUL-11 - Joe Woodbury noted that the CString::Find docs don't match
+// what the CString::Find code really ends up doing. I was
+// trying to match the docs. Now I match the CString code
+// - Joe also caught me truncating strings for GetBuffer() calls
+// when the supplied length was less than the current length.
+//
+// 2000-MAY-25 - Better support for STLPORT's Standard library distribution
+// - Got rid of the NSP macro - it interfered with Koenig lookup
+// - Thanks to Joe Woodbury for catching a TrimLeft() bug that
+// I introduced in January. Empty strings were not getting
+// trimmed
+//
+// 2000-APR-17 - Thanks to Joe Vitaterna for pointing out that ReverseFind
+// is supposed to be a const function.
+//
+// 2000-MAR-07 - Thanks to Ullrich Poll�hne for catching a range bug in one
+// of the overloads of assign.
+//
+// 2000-FEB-01 - You can now use CStdString on the Mac with CodeWarrior!
+// Thanks to Todd Heckel for helping out with this.
+//
+// 2000-JAN-23 - Thanks to Jim Cline for pointing out how I could make the
+// Trim() function more efficient.
+// - Thanks to Jeff Kohn for prompting me to find and fix a typo
+// in one of the addition operators that takes _bstr_t.
+// - Got rid of the .CPP file - you only need StdString.h now!
+//
+// 1999-DEC-22 - Thanks to Greg Pickles for helping me identify a problem
+// with my implementation of CStdString::FormatV in which
+// resulting string might not be properly NULL terminated.
+//
+// 1999-DEC-06 - Chris Conti pointed yet another basic_string<> assignment
+// bug that MS has not fixed. CStdString did nothing to fix
+// it either but it does now! The bug was: create a string
+// longer than 31 characters, get a pointer to it (via c_str())
+// and then assign that pointer to the original string object.
+// The resulting string would be empty. Not with CStdString!
+//
+// 1999-OCT-06 - BufferSet was erasing the string even when it was merely
+// supposed to shrink it. Fixed. Thanks to Chris Conti.
+// - Some of the Q172398 fixes were not checking for assignment-
+// to-self. Fixed. Thanks to Baptiste Lepilleur.
+//
+// 1999-AUG-20 - Improved Load() function to be more efficient by using
+// SizeOfResource(). Thanks to Rich Zuris for this.
+// - Corrected resource ID constructor, again thanks to Rich.
+// - Fixed a bug that occurred with UNICODE characters above
+// the first 255 ANSI ones. Thanks to Craig Watson.
+// - Added missing overloads of TrimLeft() and TrimRight().
+// Thanks to Karim Ratib for pointing them out
+//
+// 1999-JUL-21 - Made all calls to GetBuf() with no args check length first.
+//
+// 1999-JUL-10 - Improved MFC/ATL independence of conversion macros
+// - Added SS_NO_REFCOUNT macro to allow you to disable any
+// reference-counting your basic_string<> impl. may do.
+// - Improved ReleaseBuffer() to be as forgiving as CString.
+// Thanks for Fan Xia for helping me find this and to
+// Matthew Williams for pointing it out directly.
+//
+// 1999-JUL-06 - Thanks to Nigel Nunn for catching a very sneaky bug in
+// ToLower/ToUpper. They should call GetBuf() instead of
+// data() in order to ensure the changed string buffer is not
+// reference-counted (in those implementations that refcount).
+//
+// 1999-JUL-01 - Added a true CString facade. Now you can use CStdString as
+// a drop-in replacement for CString. If you find this useful,
+// you can thank Chris Sells for finally convincing me to give
+// in and implement it.
+// - Changed operators << and >> (for MFC CArchive) to serialize
+// EXACTLY as CString's do. So now you can send a CString out
+// to a CArchive and later read it in as a CStdString. I have
+// no idea why you would want to do this but you can.
+//
+// 1999-JUN-21 - Changed the CStdString class into the CStdStr template.
+// - Fixed FormatV() to correctly decrement the loop counter.
+// This was harmless bug but a bug nevertheless. Thanks to
+// Chris (of Melbsys) for pointing it out
+// - Changed Format() to try a normal stack-based array before
+// using to _alloca().
+// - Updated the text conversion macros to properly use code
+// pages and to fit in better in MFC/ATL builds. In other
+// words, I copied Microsoft's conversion stuff again.
+// - Added equivalents of CString::GetBuffer, GetBufferSetLength
+// - new sscpy() replacement of CStdString::CopyString()
+// - a Trim() function that combines TrimRight() and TrimLeft().
+//
+// 1999-MAR-13 - Corrected the "NotSpace" functional object to use _istpace()
+// instead of _isspace() Thanks to Dave Plummer for this.
+//
+// 1999-FEB-26 - Removed errant line (left over from testing) that #defined
+// _MFC_VER. Thanks to John C Sipos for noticing this.
+//
+// 1999-FEB-03 - Fixed a bug in a rarely-used overload of operator+() that
+// caused infinite recursion and stack overflow
+// - Added member functions to simplify the process of
+// persisting CStdStrings to/from DCOM IStream interfaces
+// - Added functional objects (e.g. StdStringLessNoCase) that
+// allow CStdStrings to be used as keys STL map objects with
+// case-insensitive comparison
+// - Added array indexing operators (i.e. operator[]). I
+// originally assumed that these were unnecessary and would be
+// inherited from basic_string. However, without them, Visual
+// C++ complains about ambiguous overloads when you try to use
+// them. Thanks to Julian Selman to pointing this out.
+//
+// 1998-FEB-?? - Added overloads of assign() function to completely account
+// for Q172398 bug. Thanks to "Pete the Plumber" for this
+//
+// 1998-FEB-?? - Initial submission
+//
+// COPYRIGHT:
+// 2002 Joseph M. O'Leary. This code is 100% free. Use it anywhere you
+// want. Rewrite it, restructure it, whatever. If you can write software
+// that makes money off of it, good for you. I kinda like capitalism.
+// Please don't blame me if it causes your $30 billion dollar satellite
+// explode in orbit. If you redistribute it in any form, I'd appreciate it
+// if you would leave this notice here.
+// =============================================================================
+
+// Avoid multiple inclusion
+
+#ifndef STDSTRING_H
+#define STDSTRING_H
+
+// When using VC, turn off browser references
+// Turn off unavoidable compiler warnings
+
+#if defined(_MSC_VER) && (_MSC_VER > 1100)
+ #pragma component(browser, off, references, "CStdString")
+ #pragma warning (disable : 4290) // C++ Exception Specification ignored
+ #pragma warning (disable : 4127) // Conditional expression is constant
+ #pragma warning (disable : 4097) // typedef name used as synonym for class name
+#endif
+
+// Borland warnings to turn off
+
+#ifdef __BORLANDC__
+ #pragma option push -w-inl
+// #pragma warn -inl // Turn off inline function warnings
+#endif
+
+// SS_IS_INTRESOURCE
+// -----------------
+// A copy of IS_INTRESOURCE from VC7. Because old VC6 version of winuser.h
+// doesn't have this.
+
+#define SS_IS_INTRESOURCE(_r) (false)
+
+#if !defined (SS_ANSI) && defined(_MSC_VER)
+ #undef SS_IS_INTRESOURCE
+ #if defined(_WIN64)
+ #define SS_IS_INTRESOURCE(_r) (((uint64_t)(_r) >> 16) == 0)
+ #else
+ #define SS_IS_INTRESOURCE(_r) (((unsigned long)(_r) >> 16) == 0)
+ #endif
+#endif
+
+
+// MACRO: SS_UNSIGNED
+// ------------------
+// This macro causes the addition of a constructor and assignment operator
+// which take unsigned characters. CString has such functions and in order
+// to provide maximum CString-compatability, this code needs them as well.
+// In practice you will likely never need these functions...
+
+//#define SS_UNSIGNED
+
+#ifdef SS_ALLOW_UNSIGNED_CHARS
+ #define SS_UNSIGNED
+#endif
+
+// MACRO: SS_SAFE_FORMAT
+// ---------------------
+// This macro provides limited compatability with a questionable CString
+// "feature". You can define it in order to avoid a common problem that
+// people encounter when switching from CString to CStdString.
+//
+// To illustrate the problem -- With CString, you can do this:
+//
+// CString sName("Joe");
+// CString sTmp;
+// sTmp.Format("My name is %s", sName); // WORKS!
+//
+// However if you were to try this with CStdString, your program would
+// crash.
+//
+// CStdString sName("Joe");
+// CStdString sTmp;
+// sTmp.Format("My name is %s", sName); // CRASHES!
+//
+// You must explicitly call c_str() or cast the object to the proper type
+//
+// sTmp.Format("My name is %s", sName.c_str()); // WORKS!
+// sTmp.Format("My name is %s", static_cast<PCSTR>(sName));// WORKS!
+// sTmp.Format("My name is %s", (PCSTR)sName); // WORKS!
+//
+// This is because it is illegal to pass anything but a POD type as a
+// variadic argument to a variadic function (i.e. as one of the "..."
+// arguments). The type const char* is a POD type. The type CStdString
+// is not. Of course, neither is the type CString, but CString lets you do
+// it anyway due to the way they laid out the class in binary. I have no
+// control over this in CStdString since I derive from whatever
+// implementation of basic_string is available.
+//
+// However if you have legacy code (which does this) that you want to take
+// out of the MFC world and you don't want to rewrite all your calls to
+// Format(), then you can define this flag and it will no longer crash.
+//
+// Note however that this ONLY works for Format(), not sprintf, fprintf,
+// etc. If you pass a CStdString object to one of those functions, your
+// program will crash. Not much I can do to get around this, short of
+// writing substitutes for those functions as well.
+
+#define SS_SAFE_FORMAT // use new template style Format() function
+
+
+// MACRO: SS_NO_IMPLICIT_CAST
+// --------------------------
+// Some people don't like the implicit cast to const char* (or rather to
+// const CT*) that CStdString (and MFC's CString) provide. That was the
+// whole reason I created this class in the first place, but hey, whatever
+// bakes your cake. Just #define this macro to get rid of the the implicit
+// cast.
+
+//#define SS_NO_IMPLICIT_CAST // gets rid of operator const CT*()
+
+
+// MACRO: SS_NO_REFCOUNT
+// ---------------------
+// turns off reference counting at the assignment level. Only needed
+// for the version of basic_string<> that comes with Visual C++ versions
+// 6.0 or earlier, and only then in some heavily multithreaded scenarios.
+// Uncomment it if you feel you need it.
+
+//#define SS_NO_REFCOUNT
+
+// MACRO: SS_WIN32
+// ---------------
+// When this flag is set, we are building code for the Win32 platform and
+// may use Win32 specific functions (such as LoadString). This gives us
+// a couple of nice extras for the code.
+//
+// Obviously, Microsoft's is not the only compiler available for Win32 out
+// there. So I can't just check to see if _MSC_VER is defined to detect
+// if I'm building on Win32. So for now, if you use MS Visual C++ or
+// Borland's compiler, I turn this on. Otherwise you may turn it on
+// yourself, if you prefer
+
+#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(_WIN32)
+ #define SS_WIN32
+#endif
+
+// MACRO: SS_ANSI
+// --------------
+// When this macro is defined, the code attempts only to use ANSI/ISO
+// standard library functions to do it's work. It will NOT attempt to use
+// any Win32 of Visual C++ specific functions -- even if they are
+// available. You may define this flag yourself to prevent any Win32
+// of VC++ specific functions from being called.
+
+// If we're not on Win32, we MUST use an ANSI build
+
+#ifndef SS_WIN32
+ #if !defined(SS_NO_ANSI)
+ #define SS_ANSI
+ #endif
+#endif
+
+// MACRO: SS_ALLOCA
+// ----------------
+// Some implementations of the Standard C Library have a non-standard
+// function known as alloca(). This functions allows one to allocate a
+// variable amount of memory on the stack. It is needed to implement
+// the ASCII/MBCS conversion macros.
+//
+// I wanted to find some way to determine automatically if alloca() is
+// available on this platform via compiler flags but that is asking for
+// trouble. The crude test presented here will likely need fixing on
+// other platforms. Therefore I'll leave it up to you to fiddle with
+// this test to determine if it exists. Just make sure SS_ALLOCA is or
+// is not defined as appropriate and you control this feature.
+
+#if defined(_MSC_VER) && !defined(SS_ANSI)
+ #define SS_ALLOCA
+#endif
+
+
+// MACRO: SS_MBCS
+// --------------
+// Setting this macro means you are using MBCS characters. In MSVC builds,
+// this macro gets set automatically by detection of the preprocessor flag
+// _MBCS. For other platforms you may set it manually if you wish. The
+// only effect it currently has is to cause the allocation of more space
+// for wchar_t --> char conversions.
+// Note that MBCS does not mean UNICODE.
+//
+// #define SS_MBCS
+//
+
+#ifdef _MBCS
+ #define SS_MBCS
+#endif
+
+
+// MACRO SS_NO_LOCALE
+// ------------------
+// If your implementation of the Standard C++ Library lacks the <locale> header,
+// you can #define this macro to make your code build properly. Note that this
+// is some of my newest code and frankly I'm not very sure of it, though it does
+// pass my unit tests.
+
+// #define SS_NO_LOCALE
+
+
+// Compiler Error regarding _UNICODE and UNICODE
+// -----------------------------------------------
+// Microsoft header files are screwy. Sometimes they depend on a preprocessor
+// flag named "_UNICODE". Other times they check "UNICODE" (note the lack of
+// leading underscore in the second version". In several places, they silently
+// "synchronize" these two flags this by defining one of the other was defined.
+// In older version of this header, I used to try to do the same thing.
+//
+// However experience has taught me that this is a bad idea. You get weird
+// compiler errors that seem to indicate things like LPWSTR and LPTSTR not being
+// equivalent in UNICODE builds, stuff like that (when they MUST be in a proper
+// UNICODE build). You end up scratching your head and saying, "But that HAS
+// to compile!".
+//
+// So what should you do if you get this error?
+//
+// Make sure that both macros (_UNICODE and UNICODE) are defined before this
+// file is included. You can do that by either
+//
+// a) defining both yourself before any files get included
+// b) including the proper MS headers in the proper order
+// c) including this file before any other file, uncommenting
+// the #defines below, and commenting out the #errors
+//
+// Personally I recommend solution a) but it's your call.
+
+#ifdef _MSC_VER
+ #if defined (_UNICODE) && !defined (UNICODE)
+ #error UNICODE defined but not UNICODE
+ // #define UNICODE // no longer silently fix this
+ #endif
+ #if defined (UNICODE) && !defined (_UNICODE)
+ #error Warning, UNICODE defined but not _UNICODE
+ // #define _UNICODE // no longer silently fix this
+ #endif
+#endif
+
+
+// -----------------------------------------------------------------------------
+// MIN and MAX. The Standard C++ template versions go by so many names (at
+// at least in the MS implementation) that you never know what's available
+// -----------------------------------------------------------------------------
+template<class Type>
+inline const Type& SSMIN(const Type& arg1, const Type& arg2)
+{
+ return arg2 < arg1 ? arg2 : arg1;
+}
+template<class Type>
+inline const Type& SSMAX(const Type& arg1, const Type& arg2)
+{
+ return arg2 > arg1 ? arg2 : arg1;
+}
+
+// If they have not #included W32Base.h (part of my W32 utility library) then
+// we need to define some stuff. Otherwise, this is all defined there.
+
+#if !defined(W32BASE_H)
+
+ // If they want us to use only standard C++ stuff (no Win32 stuff)
+
+ #ifdef SS_ANSI
+
+ // On Win32 we have TCHAR.H so just include it. This is NOT violating
+ // the spirit of SS_ANSI as we are not calling any Win32 functions here.
+
+ #ifdef SS_WIN32
+
+ #include <TCHAR.H>
+ #include <WTYPES.H>
+ #ifndef STRICT
+ #define STRICT
+ #endif
+
+ // ... but on non-Win32 platforms, we must #define the types we need.
+
+ #else
+
+ typedef const char* PCSTR;
+ typedef char* PSTR;
+ typedef const wchar_t* PCWSTR;
+ typedef wchar_t* PWSTR;
+ #ifdef UNICODE
+ typedef wchar_t TCHAR;
+ #else
+ typedef char TCHAR;
+ #endif
+ typedef wchar_t OLECHAR;
+
+ #endif // #ifndef _WIN32
+
+
+ // Make sure ASSERT and verify are defined using only ANSI stuff
+
+ #ifndef ASSERT
+ #include <assert.h>
+ #define ASSERT(f) assert((f))
+ #endif
+ #ifndef VERIFY
+ #ifdef _DEBUG
+ #define VERIFY(x) ASSERT((x))
+ #else
+ #define VERIFY(x) x
+ #endif
+ #endif
+
+ #else // ...else SS_ANSI is NOT defined
+
+ #include <TCHAR.H>
+ #include <WTYPES.H>
+ #ifndef STRICT
+ #define STRICT
+ #endif
+
+ // Make sure ASSERT and verify are defined
+
+ #ifndef ASSERT
+ #include <crtdbg.h>
+ #define ASSERT(f) _ASSERTE((f))
+ #endif
+ #ifndef VERIFY
+ #ifdef _DEBUG
+ #define VERIFY(x) ASSERT((x))
+ #else
+ #define VERIFY(x) x
+ #endif
+ #endif
+
+ #endif // #ifdef SS_ANSI
+
+ #ifndef UNUSED
+ #define UNUSED(x) x
+ #endif
+
+#endif // #ifndef W32BASE_H
+
+// Standard headers needed
+
+#include <string> // basic_string
+#include <algorithm> // for_each, etc.
+#include <functional> // for StdStringLessNoCase, et al
+#ifndef SS_NO_LOCALE
+ #include <locale> // for various facets
+#endif
+
+// If this is a recent enough version of VC include comdef.h, so we can write
+// member functions to deal with COM types & compiler support classes e.g.
+// _bstr_t
+
+#if defined (_MSC_VER) && (_MSC_VER >= 1100)
+ #include <comdef.h>
+ #define SS_INC_COMDEF // signal that we #included MS comdef.h file
+ #define STDSTRING_INC_COMDEF
+ #define SS_NOTHROW __declspec(nothrow)
+#else
+ #define SS_NOTHROW
+#endif
+
+#ifndef TRACE
+ #define TRACE_DEFINED_HERE
+ #define TRACE
+#endif
+
+// Microsoft defines PCSTR, PCWSTR, etc, but no PCTSTR. I hate to use the
+// versions with the "L" in front of them because that's a leftover from Win 16
+// days, even though it evaluates to the same thing. Therefore, Define a PCSTR
+// as an LPCTSTR.
+
+#if !defined(PCTSTR) && !defined(PCTSTR_DEFINED)
+ typedef const TCHAR* PCTSTR;
+ #define PCTSTR_DEFINED
+#endif
+
+#if !defined(PCOLESTR) && !defined(PCOLESTR_DEFINED)
+ typedef const OLECHAR* PCOLESTR;
+ #define PCOLESTR_DEFINED
+#endif
+
+#if !defined(POLESTR) && !defined(POLESTR_DEFINED)
+ typedef OLECHAR* POLESTR;
+ #define POLESTR_DEFINED
+#endif
+
+#if !defined(PCUSTR) && !defined(PCUSTR_DEFINED)
+ typedef const unsigned char* PCUSTR;
+ typedef unsigned char* PUSTR;
+ #define PCUSTR_DEFINED
+#endif
+
+
+// SGI compiler 7.3 doesnt know these types - oh and btw, remember to use
+// -LANG:std in the CXX Flags
+#if defined(__sgi)
+ typedef unsigned long DWORD;
+ typedef void * LPCVOID;
+#endif
+
+
+// SS_USE_FACET macro and why we need it:
+//
+// Since I'm a good little Standard C++ programmer, I use locales. Thus, I
+// need to make use of the use_facet<> template function here. Unfortunately,
+// this need is complicated by the fact the MS' implementation of the Standard
+// C++ Library has a non-standard version of use_facet that takes more
+// arguments than the standard dictates. Since I'm trying to write CStdString
+// to work with any version of the Standard library, this presents a problem.
+//
+// The upshot of this is that I can't do 'use_facet' directly. The MS' docs
+// tell me that I have to use a macro, _USE() instead. Since _USE obviously
+// won't be available in other implementations, this means that I have to write
+// my OWN macro -- SS_USE_FACET -- that evaluates either to _USE or to the
+// standard, use_facet.
+//
+// If you are having trouble with the SS_USE_FACET macro, in your implementation
+// of the Standard C++ Library, you can define your own version of SS_USE_FACET.
+
+#ifndef schMSG
+ #define schSTR(x) #x
+ #define schSTR2(x) schSTR(x)
+ #define schMSG(desc) message(__FILE__ "(" schSTR2(__LINE__) "):" #desc)
+#endif
+
+#ifndef SS_USE_FACET
+
+ // STLPort #defines a macro (__STL_NO_EXPLICIT_FUNCTION_TMPL_ARGS) for
+ // all MSVC builds, erroneously in my opinion. It causes problems for
+ // my SS_ANSI builds. In my code, I always comment out that line. You'll
+ // find it in \stlport\config\stl_msvc.h
+
+ #if defined(__SGI_STL_PORT) && (__SGI_STL_PORT >= 0x400 )
+
+ #if defined(__STL_NO_EXPLICIT_FUNCTION_TMPL_ARGS) && defined(_MSC_VER)
+ #ifdef SS_ANSI
+ #pragma schMSG(__STL_NO_EXPLICIT_FUNCTION_TMPL_ARGS defined!!)
+ #endif
+ #endif
+ #define SS_USE_FACET(loc, fac) std::use_facet<fac >(loc)
+
+ #elif defined(_MSC_VER )
+
+ #define SS_USE_FACET(loc, fac) std::_USE(loc, fac)
+
+ // ...and
+ #elif defined(_RWSTD_NO_TEMPLATE_ON_RETURN_TYPE)
+
+ #define SS_USE_FACET(loc, fac) std::use_facet(loc, (fac*)0)
+
+ #else
+
+ #define SS_USE_FACET(loc, fac) std::use_facet<fac >(loc)
+
+ #endif
+
+#endif
+
+// =============================================================================
+// UNICODE/MBCS conversion macros. Made to work just like the MFC/ATL ones.
+// =============================================================================
+
+#include <wchar.h> // Added to Std Library with Amendment #1.
+
+// First define the conversion helper functions. We define these regardless of
+// any preprocessor macro settings since their names won't collide.
+
+// Not sure if we need all these headers. I believe ANSI says we do.
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <wctype.h>
+#include <ctype.h>
+#include <stdlib.h>
+#ifndef va_start
+ #include <varargs.h>
+#endif
+
+
+#ifdef SS_NO_LOCALE
+
+ #if defined(_WIN32) || defined (_WIN32_WCE)
+
+ inline PWSTR StdCodeCvt(PWSTR pDstW, int nDst, PCSTR pSrcA, int nSrc,
+ UINT acp=CP_ACP)
+ {
+ ASSERT(0 != pSrcA);
+ ASSERT(0 != pDstW);
+ pDstW[0] = '\0';
+ MultiByteToWideChar(acp, 0, pSrcA, nSrc, pDstW, nDst);
+ return pDstW;
+ }
+ inline PWSTR StdCodeCvt(PWSTR pDstW, int nDst, PCUSTR pSrcA, int nSrc,
+ UINT acp=CP_ACP)
+ {
+ return StdCodeCvt(pDstW, nDst, (PCSTR)pSrcA, nSrc, acp);
+ }
+
+ inline PSTR StdCodeCvt(PSTR pDstA, int nDst, PCWSTR pSrcW, int nSrc,
+ UINT acp=CP_ACP)
+ {
+ ASSERT(0 != pDstA);
+ ASSERT(0 != pSrcW);
+ pDstA[0] = '\0';
+ WideCharToMultiByte(acp, 0, pSrcW, nSrc, pDstA, nDst, 0, 0);
+ return pDstA;
+ }
+ inline PUSTR StdCodeCvt(PUSTR pDstA, int nDst, PCWSTR pSrcW, int nSrc,
+ UINT acp=CP_ACP)
+ {
+ return (PUSTR)StdCodeCvt((PSTR)pDstA, nDst, pSrcW, nSrc, acp);
+ }
+ #else
+ #endif
+
+#else
+
+ // StdCodeCvt - made to look like Win32 functions WideCharToMultiByte
+ // and MultiByteToWideChar but uses locales in SS_ANSI
+ // builds. There are a number of overloads.
+ // First argument is the destination buffer.
+ // Second argument is the source buffer
+ //#if defined (SS_ANSI) || !defined (SS_WIN32)
+
+ // 'SSCodeCvt' - shorthand name for the codecvt facet we use
+
+ typedef std::codecvt<wchar_t, char, mbstate_t> SSCodeCvt;
+
+ inline PWSTR StdCodeCvt(PWSTR pDstW, int nDst, PCSTR pSrcA, int nSrc,
+ const std::locale& loc=std::locale())
+ {
+ ASSERT(0 != pSrcA);
+ ASSERT(0 != pDstW);
+
+ pDstW[0] = '\0';
+
+ if ( nSrc > 0 )
+ {
+ PCSTR pNextSrcA = pSrcA;
+ PWSTR pNextDstW = pDstW;
+ const SSCodeCvt& conv = SS_USE_FACET(loc, SSCodeCvt);
+ SSCodeCvt::state_type st= {};
+ SSCodeCvt::result res = conv.in(st,
+ pSrcA, pSrcA + nSrc, pNextSrcA,
+ pDstW, pDstW + nDst, pNextDstW);
+#ifdef TARGET_POSIX
+#define ASSERT2(a) if (!(a)) {fprintf(stderr, "StdString: Assertion Failed on line %d\n", __LINE__);}
+#else
+#define ASSERT2 ASSERT
+#endif
+ ASSERT2(SSCodeCvt::ok == res);
+ ASSERT2(SSCodeCvt::error != res);
+ ASSERT2(pNextDstW >= pDstW);
+ ASSERT2(pNextSrcA >= pSrcA);
+#undef ASSERT2
+ // Null terminate the converted string
+
+ if ( pNextDstW - pDstW > nDst )
+ *(pDstW + nDst) = '\0';
+ else
+ *pNextDstW = '\0';
+ }
+ return pDstW;
+ }
+ inline PWSTR StdCodeCvt(PWSTR pDstW, int nDst, PCUSTR pSrcA, int nSrc,
+ const std::locale& loc=std::locale())
+ {
+ return StdCodeCvt(pDstW, nDst, (PCSTR)pSrcA, nSrc, loc);
+ }
+
+ inline PSTR StdCodeCvt(PSTR pDstA, int nDst, PCWSTR pSrcW, int nSrc,
+ const std::locale& loc=std::locale())
+ {
+ ASSERT(0 != pDstA);
+ ASSERT(0 != pSrcW);
+
+ pDstA[0] = '\0';
+
+ if ( nSrc > 0 )
+ {
+ PSTR pNextDstA = pDstA;
+ PCWSTR pNextSrcW = pSrcW;
+ const SSCodeCvt& conv = SS_USE_FACET(loc, SSCodeCvt);
+ SSCodeCvt::state_type st= {};
+ SSCodeCvt::result res = conv.out(st,
+ pSrcW, pSrcW + nSrc, pNextSrcW,
+ pDstA, pDstA + nDst, pNextDstA);
+#ifdef TARGET_POSIX
+#define ASSERT2(a) if (!(a)) {fprintf(stderr, "StdString: Assertion Failed on line %d\n", __LINE__);}
+#else
+#define ASSERT2 ASSERT
+#endif
+ ASSERT2(SSCodeCvt::error != res);
+ ASSERT2(SSCodeCvt::ok == res); // strict, comment out for sanity
+ ASSERT2(pNextDstA >= pDstA);
+ ASSERT2(pNextSrcW >= pSrcW);
+#undef ASSERT2
+
+ // Null terminate the converted string
+
+ if ( pNextDstA - pDstA > nDst )
+ *(pDstA + nDst) = '\0';
+ else
+ *pNextDstA = '\0';
+ }
+ return pDstA;
+ }
+
+ inline PUSTR StdCodeCvt(PUSTR pDstA, int nDst, PCWSTR pSrcW, int nSrc,
+ const std::locale& loc=std::locale())
+ {
+ return (PUSTR)StdCodeCvt((PSTR)pDstA, nDst, pSrcW, nSrc, loc);
+ }
+
+#endif
+
+
+
+// Unicode/MBCS conversion macros are only available on implementations of
+// the "C" library that have the non-standard _alloca function. As far as I
+// know that's only Microsoft's though I've heard that the function exists
+// elsewhere.
+
+#if defined(SS_ALLOCA) && !defined SS_NO_CONVERSION
+
+ #include <malloc.h> // needed for _alloca
+
+ // Define our conversion macros to look exactly like Microsoft's to
+ // facilitate using this stuff both with and without MFC/ATL
+
+ #ifdef _CONVERSION_USES_THREAD_LOCALE
+
+ #ifndef _DEBUG
+ #define SSCVT int _cvt; _cvt; UINT _acp=GetACP(); \
+ _acp; PCWSTR _pw; _pw; PCSTR _pa; _pa
+ #else
+ #define SSCVT int _cvt = 0; _cvt; UINT _acp=GetACP();\
+ _acp; PCWSTR _pw=0; _pw; PCSTR _pa=0; _pa
+ #endif
+ #define SSA2W(pa) (\
+ ((_pa = pa) == 0) ? 0 : (\
+ _cvt = (sslen(_pa)),\
+ StdCodeCvt((PWSTR) _alloca((_cvt+1)*2), (_cvt+1)*2, \
+ _pa, _cvt, _acp)))
+ #define SSW2A(pw) (\
+ ((_pw = pw) == 0) ? 0 : (\
+ _cvt = sslen(_pw), \
+ StdCodeCvt((LPSTR) _alloca((_cvt+1)*2), (_cvt+1)*2, \
+ _pw, _cvt, _acp)))
+ #else
+
+ #ifndef _DEBUG
+ #define SSCVT int _cvt; _cvt; UINT _acp=CP_ACP; _acp;\
+ PCWSTR _pw; _pw; PCSTR _pa; _pa
+ #else
+ #define SSCVT int _cvt = 0; _cvt; UINT _acp=CP_ACP; \
+ _acp; PCWSTR _pw=0; _pw; PCSTR _pa=0; _pa
+ #endif
+ #define SSA2W(pa) (\
+ ((_pa = pa) == 0) ? 0 : (\
+ _cvt = (sslen(_pa)),\
+ StdCodeCvt((PWSTR) _alloca((_cvt+1)*2), (_cvt+1)*2, \
+ _pa, _cvt)))
+ #define SSW2A(pw) (\
+ ((_pw = pw) == 0) ? 0 : (\
+ _cvt = (sslen(_pw)),\
+ StdCodeCvt((LPSTR) _alloca((_cvt+1)*2), (_cvt+1)*2, \
+ _pw, _cvt)))
+ #endif
+
+ #define SSA2CW(pa) ((PCWSTR)SSA2W((pa)))
+ #define SSW2CA(pw) ((PCSTR)SSW2A((pw)))
+
+ #ifdef UNICODE
+ #define SST2A SSW2A
+ #define SSA2T SSA2W
+ #define SST2CA SSW2CA
+ #define SSA2CT SSA2CW
+ // (Did you get a compiler error here about not being able to convert
+ // PTSTR into PWSTR? Then your _UNICODE and UNICODE flags are messed
+ // up. Best bet: #define BOTH macros before including any MS headers.)
+ inline PWSTR SST2W(PTSTR p) { return p; }
+ inline PTSTR SSW2T(PWSTR p) { return p; }
+ inline PCWSTR SST2CW(PCTSTR p) { return p; }
+ inline PCTSTR SSW2CT(PCWSTR p) { return p; }
+ #else
+ #define SST2W SSA2W
+ #define SSW2T SSW2A
+ #define SST2CW SSA2CW
+ #define SSW2CT SSW2CA
+ inline PSTR SST2A(PTSTR p) { return p; }
+ inline PTSTR SSA2T(PSTR p) { return p; }
+ inline PCSTR SST2CA(PCTSTR p) { return p; }
+ inline PCTSTR SSA2CT(PCSTR p) { return p; }
+ #endif // #ifdef UNICODE
+
+ #if defined(UNICODE)
+ // in these cases the default (TCHAR) is the same as OLECHAR
+ inline PCOLESTR SST2COLE(PCTSTR p) { return p; }
+ inline PCTSTR SSOLE2CT(PCOLESTR p) { return p; }
+ inline POLESTR SST2OLE(PTSTR p) { return p; }
+ inline PTSTR SSOLE2T(POLESTR p) { return p; }
+ #elif defined(OLE2ANSI)
+ // in these cases the default (TCHAR) is the same as OLECHAR
+ inline PCOLESTR SST2COLE(PCTSTR p) { return p; }
+ inline PCTSTR SSOLE2CT(PCOLESTR p) { return p; }
+ inline POLESTR SST2OLE(PTSTR p) { return p; }
+ inline PTSTR SSOLE2T(POLESTR p) { return p; }
+ #else
+ //CharNextW doesn't work on Win95 so we use this
+ #define SST2COLE(pa) SSA2CW((pa))
+ #define SST2OLE(pa) SSA2W((pa))
+ #define SSOLE2CT(po) SSW2CA((po))
+ #define SSOLE2T(po) SSW2A((po))
+ #endif
+
+ #ifdef OLE2ANSI
+ #define SSW2OLE SSW2A
+ #define SSOLE2W SSA2W
+ #define SSW2COLE SSW2CA
+ #define SSOLE2CW SSA2CW
+ inline POLESTR SSA2OLE(PSTR p) { return p; }
+ inline PSTR SSOLE2A(POLESTR p) { return p; }
+ inline PCOLESTR SSA2COLE(PCSTR p) { return p; }
+ inline PCSTR SSOLE2CA(PCOLESTR p){ return p; }
+ #else
+ #define SSA2OLE SSA2W
+ #define SSOLE2A SSW2A
+ #define SSA2COLE SSA2CW
+ #define SSOLE2CA SSW2CA
+ inline POLESTR SSW2OLE(PWSTR p) { return p; }
+ inline PWSTR SSOLE2W(POLESTR p) { return p; }
+ inline PCOLESTR SSW2COLE(PCWSTR p) { return p; }
+ inline PCWSTR SSOLE2CW(PCOLESTR p){ return p; }
+ #endif
+
+ // Above we've defined macros that look like MS' but all have
+ // an 'SS' prefix. Now we need the real macros. We'll either
+ // get them from the macros above or from MFC/ATL.
+
+ #if defined (USES_CONVERSION)
+
+ #define _NO_STDCONVERSION // just to be consistent
+
+ #else
+
+ #ifdef _MFC_VER
+
+ #include <afxconv.h>
+ #define _NO_STDCONVERSION // just to be consistent
+
+ #else
+
+ #define USES_CONVERSION SSCVT
+ #define A2CW SSA2CW
+ #define W2CA SSW2CA
+ #define T2A SST2A
+ #define A2T SSA2T
+ #define T2W SST2W
+ #define W2T SSW2T
+ #define T2CA SST2CA
+ #define A2CT SSA2CT
+ #define T2CW SST2CW
+ #define W2CT SSW2CT
+ #define ocslen sslen
+ #define ocscpy sscpy
+ #define T2COLE SST2COLE
+ #define OLE2CT SSOLE2CT
+ #define T2OLE SST2COLE
+ #define OLE2T SSOLE2CT
+ #define A2OLE SSA2OLE
+ #define OLE2A SSOLE2A
+ #define W2OLE SSW2OLE
+ #define OLE2W SSOLE2W
+ #define A2COLE SSA2COLE
+ #define OLE2CA SSOLE2CA
+ #define W2COLE SSW2COLE
+ #define OLE2CW SSOLE2CW
+
+ #endif // #ifdef _MFC_VER
+ #endif // #ifndef USES_CONVERSION
+#endif // #ifndef SS_NO_CONVERSION
+
+// Define ostring - generic name for std::basic_string<OLECHAR>
+
+#if !defined(ostring) && !defined(OSTRING_DEFINED)
+ typedef std::basic_string<OLECHAR> ostring;
+ #define OSTRING_DEFINED
+#endif
+
+// StdCodeCvt when there's no conversion to be done
+template <typename T>
+inline T* StdCodeCvt(T* pDst, int nDst, const T* pSrc, int nSrc)
+{
+ int nChars = SSMIN(nSrc, nDst);
+
+ if ( nChars > 0 )
+ {
+ pDst[0] = '\0';
+ std::basic_string<T>::traits_type::copy(pDst, pSrc, nChars);
+// std::char_traits<T>::copy(pDst, pSrc, nChars);
+ pDst[nChars] = '\0';
+ }
+
+ return pDst;
+}
+inline PSTR StdCodeCvt(PSTR pDst, int nDst, PCUSTR pSrc, int nSrc)
+{
+ return StdCodeCvt(pDst, nDst, (PCSTR)pSrc, nSrc);
+}
+inline PUSTR StdCodeCvt(PUSTR pDst, int nDst, PCSTR pSrc, int nSrc)
+{
+ return (PUSTR)StdCodeCvt((PSTR)pDst, nDst, pSrc, nSrc);
+}
+
+// Define tstring -- generic name for std::basic_string<TCHAR>
+
+#if !defined(tstring) && !defined(TSTRING_DEFINED)
+ typedef std::basic_string<TCHAR> tstring;
+ #define TSTRING_DEFINED
+#endif
+
+// a very shorthand way of applying the fix for KB problem Q172398
+// (basic_string assignment bug)
+
+#if defined ( _MSC_VER ) && ( _MSC_VER < 1200 )
+ #define Q172398(x) (x).erase()
+#else
+ #define Q172398(x)
+#endif
+
+// =============================================================================
+// INLINE FUNCTIONS ON WHICH CSTDSTRING RELIES
+//
+// Usually for generic text mapping, we rely on preprocessor macro definitions
+// to map to string functions. However the CStdStr<> template cannot use
+// macro-based generic text mappings because its character types do not get
+// resolved until template processing which comes AFTER macro processing. In
+// other words, the preprocessor macro UNICODE is of little help to us in the
+// CStdStr template
+//
+// Therefore, to keep the CStdStr declaration simple, we have these inline
+// functions. The template calls them often. Since they are inline (and NOT
+// exported when this is built as a DLL), they will probably be resolved away
+// to nothing.
+//
+// Without these functions, the CStdStr<> template would probably have to broken
+// out into two, almost identical classes. Either that or it would be a huge,
+// convoluted mess, with tons of "if" statements all over the place checking the
+// size of template parameter CT.
+// =============================================================================
+
+#ifdef SS_NO_LOCALE
+
+ // --------------------------------------------------------------------------
+ // Win32 GetStringTypeEx wrappers
+ // --------------------------------------------------------------------------
+ inline bool wsGetStringType(LCID lc, DWORD dwT, PCSTR pS, int nSize,
+ WORD* pWd)
+ {
+ return FALSE != GetStringTypeExA(lc, dwT, pS, nSize, pWd);
+ }
+ inline bool wsGetStringType(LCID lc, DWORD dwT, PCWSTR pS, int nSize,
+ WORD* pWd)
+ {
+ return FALSE != GetStringTypeExW(lc, dwT, pS, nSize, pWd);
+ }
+
+
+ template<typename CT>
+ inline bool ssisspace (CT t)
+ {
+ WORD toYourMother;
+ return wsGetStringType(GetThreadLocale(), CT_CTYPE1, &t, 1, &toYourMother)
+ && 0 != (C1_BLANK & toYourMother);
+ }
+
+#endif
+
+// If they defined SS_NO_REFCOUNT, then we must convert all assignments
+
+#if defined (_MSC_VER) && (_MSC_VER < 1300)
+ #ifdef SS_NO_REFCOUNT
+ #define SSREF(x) (x).c_str()
+ #else
+ #define SSREF(x) (x)
+ #endif
+#else
+ #define SSREF(x) (x)
+#endif
+
+// -----------------------------------------------------------------------------
+// sslen: strlen/wcslen wrappers
+// -----------------------------------------------------------------------------
+template<typename CT> inline int sslen(const CT* pT)
+{
+ return 0 == pT ? 0 : (int)std::basic_string<CT>::traits_type::length(pT);
+// return 0 == pT ? 0 : std::char_traits<CT>::length(pT);
+}
+inline SS_NOTHROW int sslen(const std::string& s)
+{
+ return static_cast<int>(s.length());
+}
+inline SS_NOTHROW int sslen(const std::wstring& s)
+{
+ return static_cast<int>(s.length());
+}
+
+// -----------------------------------------------------------------------------
+// sstolower/sstoupper -- convert characters to upper/lower case
+// -----------------------------------------------------------------------------
+
+#ifdef SS_NO_LOCALE
+ inline char sstoupper(char ch) { return (char)::toupper(ch); }
+ inline wchar_t sstoupper(wchar_t ch){ return (wchar_t)::towupper(ch); }
+ inline char sstolower(char ch) { return (char)::tolower(ch); }
+ inline wchar_t sstolower(wchar_t ch){ return (wchar_t)::tolower(ch); }
+#else
+ template<typename CT>
+ inline CT sstolower(const CT& t, const std::locale& loc = std::locale())
+ {
+ return std::tolower<CT>(t, loc);
+ }
+ template<typename CT>
+ inline CT sstoupper(const CT& t, const std::locale& loc = std::locale())
+ {
+ return std::toupper<CT>(t, loc);
+ }
+#endif
+
+// -----------------------------------------------------------------------------
+// ssasn: assignment functions -- assign "sSrc" to "sDst"
+// -----------------------------------------------------------------------------
+typedef std::string::size_type SS_SIZETYPE; // just for shorthand, really
+typedef std::string::pointer SS_PTRTYPE;
+typedef std::wstring::size_type SW_SIZETYPE;
+typedef std::wstring::pointer SW_PTRTYPE;
+
+
+template <typename T>
+inline void ssasn(std::basic_string<T>& sDst, const std::basic_string<T>& sSrc)
+{
+ if ( sDst.c_str() != sSrc.c_str() )
+ {
+ sDst.erase();
+ sDst.assign(SSREF(sSrc));
+ }
+}
+template <typename T>
+inline void ssasn(std::basic_string<T>& sDst, const T *pA)
+{
+ // Watch out for NULLs, as always.
+
+ if ( 0 == pA )
+ {
+ sDst.erase();
+ }
+
+ // If pA actually points to part of sDst, we must NOT erase(), but
+ // rather take a substring
+
+ else if ( pA >= sDst.c_str() && pA <= sDst.c_str() + sDst.size() )
+ {
+ sDst =sDst.substr(static_cast<typename std::basic_string<T>::size_type>(pA-sDst.c_str()));
+ }
+
+ // Otherwise (most cases) apply the assignment bug fix, if applicable
+ // and do the assignment
+
+ else
+ {
+ Q172398(sDst);
+ sDst.assign(pA);
+ }
+}
+inline void ssasn(std::string& sDst, const std::wstring& sSrc)
+{
+ if ( sSrc.empty() )
+ {
+ sDst.erase();
+ }
+ else
+ {
+ int nDst = static_cast<int>(sSrc.size());
+
+ // In MBCS builds, pad the buffer to account for the possibility of
+ // some 3 byte characters. Not perfect but should get most cases.
+
+#ifdef SS_MBCS
+ // In MBCS builds, we don't know how long the destination string will be.
+ nDst = static_cast<int>(static_cast<double>(nDst) * 1.3);
+ sDst.resize(nDst+1);
+ PCSTR szCvt = StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), nDst,
+ sSrc.c_str(), static_cast<int>(sSrc.size()));
+ sDst.resize(sslen(szCvt));
+#else
+ sDst.resize(nDst+1);
+ StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), nDst,
+ sSrc.c_str(), static_cast<int>(sSrc.size()));
+ sDst.resize(sSrc.size());
+#endif
+ }
+}
+inline void ssasn(std::string& sDst, PCWSTR pW)
+{
+ int nSrc = sslen(pW);
+ if ( nSrc > 0 )
+ {
+ int nSrc = sslen(pW);
+ int nDst = nSrc;
+
+ // In MBCS builds, pad the buffer to account for the possibility of
+ // some 3 byte characters. Not perfect but should get most cases.
+
+#ifdef SS_MBCS
+ nDst = static_cast<int>(static_cast<double>(nDst) * 1.3);
+ // In MBCS builds, we don't know how long the destination string will be.
+ sDst.resize(nDst + 1);
+ PCSTR szCvt = StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), nDst,
+ pW, nSrc);
+ sDst.resize(sslen(szCvt));
+#else
+ sDst.resize(nDst + 1);
+ StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), nDst, pW, nSrc);
+ sDst.resize(nDst);
+#endif
+ }
+ else
+ {
+ sDst.erase();
+ }
+}
+inline void ssasn(std::string& sDst, const int nNull)
+{
+ //UNUSED(nNull);
+ ASSERT(nNull==0);
+ sDst.assign("");
+}
+#undef StrSizeType
+inline void ssasn(std::wstring& sDst, const std::string& sSrc)
+{
+ if ( sSrc.empty() )
+ {
+ sDst.erase();
+ }
+ else
+ {
+ int nSrc = static_cast<int>(sSrc.size());
+ int nDst = nSrc;
+
+ sDst.resize(nSrc+1);
+ PCWSTR szCvt = StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()), nDst,
+ sSrc.c_str(), nSrc);
+
+ sDst.resize(sslen(szCvt));
+ }
+}
+inline void ssasn(std::wstring& sDst, PCSTR pA)
+{
+ int nSrc = sslen(pA);
+
+ if ( 0 == nSrc )
+ {
+ sDst.erase();
+ }
+ else
+ {
+ int nDst = nSrc;
+ sDst.resize(nDst+1);
+ PCWSTR szCvt = StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()), nDst, pA,
+ nSrc);
+
+ sDst.resize(sslen(szCvt));
+ }
+}
+inline void ssasn(std::wstring& sDst, const int nNull)
+{
+ //UNUSED(nNull);
+ ASSERT(nNull==0);
+ sDst.assign(L"");
+}
+
+// -----------------------------------------------------------------------------
+// ssadd: string object concatenation -- add second argument to first
+// -----------------------------------------------------------------------------
+inline void ssadd(std::string& sDst, const std::wstring& sSrc)
+{
+ int nSrc = static_cast<int>(sSrc.size());
+
+ if ( nSrc > 0 )
+ {
+ int nDst = static_cast<int>(sDst.size());
+ int nAdd = nSrc;
+
+ // In MBCS builds, pad the buffer to account for the possibility of
+ // some 3 byte characters. Not perfect but should get most cases.
+
+#ifdef SS_MBCS
+ nAdd = static_cast<int>(static_cast<double>(nAdd) * 1.3);
+ sDst.resize(nDst+nAdd+1);
+ PCSTR szCvt = StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDst),
+ nAdd, sSrc.c_str(), nSrc);
+ sDst.resize(nDst + sslen(szCvt));
+#else
+ sDst.resize(nDst+nAdd+1);
+ StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDst), nAdd, sSrc.c_str(), nSrc);
+ sDst.resize(nDst + nAdd);
+#endif
+ }
+}
+template <typename T>
+inline void ssadd(typename std::basic_string<T>& sDst, const typename std::basic_string<T>& sSrc)
+{
+ sDst += sSrc;
+}
+inline void ssadd(std::string& sDst, PCWSTR pW)
+{
+ int nSrc = sslen(pW);
+ if ( nSrc > 0 )
+ {
+ int nDst = static_cast<int>(sDst.size());
+ int nAdd = nSrc;
+
+#ifdef SS_MBCS
+ nAdd = static_cast<int>(static_cast<double>(nAdd) * 1.3);
+ sDst.resize(nDst + nAdd + 1);
+ PCSTR szCvt = StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDst),
+ nAdd, pW, nSrc);
+ sDst.resize(nDst + sslen(szCvt));
+#else
+ sDst.resize(nDst + nAdd + 1);
+ StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDst), nAdd, pW, nSrc);
+ sDst.resize(nDst + nSrc);
+#endif
+ }
+}
+template <typename T>
+inline void ssadd(typename std::basic_string<T>& sDst, const T *pA)
+{
+ if ( pA )
+ {
+ // If the string being added is our internal string or a part of our
+ // internal string, then we must NOT do any reallocation without
+ // first copying that string to another object (since we're using a
+ // direct pointer)
+
+ if ( pA >= sDst.c_str() && pA <= sDst.c_str()+sDst.length())
+ {
+ if ( sDst.capacity() <= sDst.size()+sslen(pA) )
+ sDst.append(std::basic_string<T>(pA));
+ else
+ sDst.append(pA);
+ }
+ else
+ {
+ sDst.append(pA);
+ }
+ }
+}
+inline void ssadd(std::wstring& sDst, const std::string& sSrc)
+{
+ if ( !sSrc.empty() )
+ {
+ int nSrc = static_cast<int>(sSrc.size());
+ int nDst = static_cast<int>(sDst.size());
+
+ sDst.resize(nDst + nSrc + 1);
+#ifdef SS_MBCS
+ PCWSTR szCvt = StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDst),
+ nSrc, sSrc.c_str(), nSrc+1);
+ sDst.resize(nDst + sslen(szCvt));
+#else
+ StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDst), nSrc, sSrc.c_str(), nSrc+1);
+ sDst.resize(nDst + nSrc);
+#endif
+ }
+}
+inline void ssadd(std::wstring& sDst, PCSTR pA)
+{
+ int nSrc = sslen(pA);
+
+ if ( nSrc > 0 )
+ {
+ int nDst = static_cast<int>(sDst.size());
+
+ sDst.resize(nDst + nSrc + 1);
+#ifdef SS_MBCS
+ PCWSTR szCvt = StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDst),
+ nSrc, pA, nSrc+1);
+ sDst.resize(nDst + sslen(szCvt));
+#else
+ StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDst), nSrc, pA, nSrc+1);
+ sDst.resize(nDst + nSrc);
+#endif
+ }
+}
+
+// -----------------------------------------------------------------------------
+// sscmp: comparison (case sensitive, not affected by locale)
+// -----------------------------------------------------------------------------
+template<typename CT>
+inline int sscmp(const CT* pA1, const CT* pA2)
+{
+ CT f;
+ CT l;
+
+ do
+ {
+ f = *(pA1++);
+ l = *(pA2++);
+ } while ( (f) && (f == l) );
+
+ return (int)(f - l);
+}
+
+// -----------------------------------------------------------------------------
+// ssicmp: comparison (case INsensitive, not affected by locale)
+// -----------------------------------------------------------------------------
+template<typename CT>
+inline int ssicmp(const CT* pA1, const CT* pA2)
+{
+ // Using the "C" locale = "not affected by locale"
+
+ std::locale loc = std::locale::classic();
+ const std::ctype<CT>& ct = SS_USE_FACET(loc, std::ctype<CT>);
+ CT f;
+ CT l;
+
+ do
+ {
+ f = ct.tolower(*(pA1++));
+ l = ct.tolower(*(pA2++));
+ } while ( (f) && (f == l) );
+
+ return (int)(f - l);
+}
+
+// -----------------------------------------------------------------------------
+// ssupr/sslwr: Uppercase/Lowercase conversion functions
+// -----------------------------------------------------------------------------
+
+template<typename CT>
+inline void sslwr(CT* pT, size_t nLen, const std::locale& loc=std::locale())
+{
+ SS_USE_FACET(loc, std::ctype<CT>).tolower(pT, pT+nLen);
+}
+template<typename CT>
+inline void ssupr(CT* pT, size_t nLen, const std::locale& loc=std::locale())
+{
+ SS_USE_FACET(loc, std::ctype<CT>).toupper(pT, pT+nLen);
+}
+
+// -----------------------------------------------------------------------------
+// vsprintf/vswprintf or _vsnprintf/_vsnwprintf equivalents. In standard
+// builds we can't use _vsnprintf/_vsnwsprintf because they're MS extensions.
+//
+// -----------------------------------------------------------------------------
+// Borland's headers put some ANSI "C" functions in the 'std' namespace.
+// Promote them to the global namespace so we can use them here.
+
+#if defined(__BORLANDC__)
+ using std::vsprintf;
+ using std::vswprintf;
+#endif
+
+ // GNU is supposed to have vsnprintf and vsnwprintf. But only the newer
+ // distributions do.
+
+#if defined(__GNUC__)
+
+ inline int ssvsprintf(PSTR pA, size_t nCount, PCSTR pFmtA, va_list vl)
+ {
+ return vsnprintf(pA, nCount, pFmtA, vl);
+ }
+ inline int ssvsprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl)
+ {
+ return vswprintf(pW, nCount, pFmtW, vl);
+ }
+
+ // Microsofties can use
+#elif defined(_MSC_VER) && !defined(SS_ANSI)
+
+ inline int ssvsprintf(PSTR pA, size_t nCount, PCSTR pFmtA, va_list vl)
+ {
+ return _vsnprintf(pA, nCount, pFmtA, vl);
+ }
+ inline int ssvsprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl)
+ {
+ return _vsnwprintf(pW, nCount, pFmtW, vl);
+ }
+
+#elif defined (SS_DANGEROUS_FORMAT) // ignore buffer size parameter if needed?
+
+ inline int ssvsprintf(PSTR pA, size_t /*nCount*/, PCSTR pFmtA, va_list vl)
+ {
+ return vsprintf(pA, pFmtA, vl);
+ }
+
+ inline int ssvsprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl)
+ {
+ // JMO: Some distributions of the "C" have a version of vswprintf that
+ // takes 3 arguments (e.g. Microsoft, Borland, GNU). Others have a
+ // version which takes 4 arguments (an extra "count" argument in the
+ // second position. The best stab I can take at this so far is that if
+ // you are NOT running with MS, Borland, or GNU, then I'll assume you
+ // have the version that takes 4 arguments.
+ //
+ // I'm sure that these checks don't catch every platform correctly so if
+ // you get compiler errors on one of the lines immediately below, it's
+ // probably because your implemntation takes a different number of
+ // arguments. You can comment out the offending line (and use the
+ // alternate version) or you can figure out what compiler flag to check
+ // and add that preprocessor check in. Regardless, if you get an error
+ // on these lines, I'd sure like to hear from you about it.
+ //
+ // Thanks to Ronny Schulz for the SGI-specific checks here.
+
+// #if !defined(__MWERKS__) && !defined(__SUNPRO_CC_COMPAT) && !defined(__SUNPRO_CC)
+ #if !defined(_MSC_VER) \
+ && !defined (__BORLANDC__) \
+ && !defined(__GNUC__) \
+ && !defined(__sgi)
+
+ return vswprintf(pW, nCount, pFmtW, vl);
+
+ // suddenly with the current SGI 7.3 compiler there is no such function as
+ // vswprintf and the substitute needs explicit casts to compile
+
+ #elif defined(__sgi)
+
+ nCount;
+ return vsprintf( (char *)pW, (char *)pFmtW, vl);
+
+ #else
+
+ nCount;
+ return vswprintf(pW, pFmtW, vl);
+
+ #endif
+
+ }
+
+#endif
+
+ // GOT COMPILER PROBLEMS HERE?
+ // ---------------------------
+ // Does your compiler choke on one or more of the following 2 functions? It
+ // probably means that you don't have have either vsnprintf or vsnwprintf in
+ // your version of the CRT. This is understandable since neither is an ANSI
+ // "C" function. However it still leaves you in a dilemma. In order to make
+ // this code build, you're going to have to to use some non-length-checked
+ // formatting functions that every CRT has: vsprintf and vswprintf.
+ //
+ // This is very dangerous. With the proper erroneous (or malicious) code, it
+ // can lead to buffer overlows and crashing your PC. Use at your own risk
+ // In order to use them, just #define SS_DANGEROUS_FORMAT at the top of
+ // this file.
+ //
+ // Even THEN you might not be all the way home due to some non-conforming
+ // distributions. More on this in the comments below.
+
+ inline int ssnprintf(PSTR pA, size_t nCount, PCSTR pFmtA, va_list vl)
+ {
+ #ifdef _MSC_VER
+ return _vsnprintf(pA, nCount, pFmtA, vl);
+ #else
+ return vsnprintf(pA, nCount, pFmtA, vl);
+ #endif
+ }
+ inline int ssnprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl)
+ {
+ #ifdef _MSC_VER
+ return _vsnwprintf(pW, nCount, pFmtW, vl);
+ #else
+ return vswprintf(pW, nCount, pFmtW, vl);
+ #endif
+ }
+
+
+
+
+// -----------------------------------------------------------------------------
+// ssload: Type safe, overloaded ::LoadString wrappers
+// There is no equivalent of these in non-Win32-specific builds. However, I'm
+// thinking that with the message facet, there might eventually be one
+// -----------------------------------------------------------------------------
+#if defined (SS_WIN32) && !defined(SS_ANSI)
+ inline int ssload(HMODULE hInst, UINT uId, PSTR pBuf, int nMax)
+ {
+ return ::LoadStringA(hInst, uId, pBuf, nMax);
+ }
+ inline int ssload(HMODULE hInst, UINT uId, PWSTR pBuf, int nMax)
+ {
+ return ::LoadStringW(hInst, uId, pBuf, nMax);
+ }
+#if defined ( _MSC_VER ) && ( _MSC_VER >= 1500 )
+ inline int ssload(HMODULE hInst, UINT uId, uint16_t *pBuf, int nMax)
+ {
+ return 0;
+ }
+ inline int ssload(HMODULE hInst, UINT uId, uint32_t *pBuf, int nMax)
+ {
+ return 0;
+ }
+#endif
+#endif
+
+
+// -----------------------------------------------------------------------------
+// sscoll/ssicoll: Collation wrappers
+// Note -- with MSVC I have reversed the arguments order here because the
+// functions appear to return the opposite of what they should
+// -----------------------------------------------------------------------------
+#ifndef SS_NO_LOCALE
+template <typename CT>
+inline int sscoll(const CT* sz1, int nLen1, const CT* sz2, int nLen2)
+{
+ const std::collate<CT>& coll =
+ SS_USE_FACET(std::locale(), std::collate<CT>);
+
+ return coll.compare(sz2, sz2+nLen2, sz1, sz1+nLen1);
+}
+template <typename CT>
+inline int ssicoll(const CT* sz1, int nLen1, const CT* sz2, int nLen2)
+{
+ const std::locale loc;
+ const std::collate<CT>& coll = SS_USE_FACET(loc, std::collate<CT>);
+
+ // Some implementations seem to have trouble using the collate<>
+ // facet typedefs so we'll just default to basic_string and hope
+ // that's what the collate facet uses (which it generally should)
+
+// std::collate<CT>::string_type s1(sz1);
+// std::collate<CT>::string_type s2(sz2);
+ const std::basic_string<CT> sEmpty;
+ std::basic_string<CT> s1(sz1 ? sz1 : sEmpty.c_str());
+ std::basic_string<CT> s2(sz2 ? sz2 : sEmpty.c_str());
+
+ sslwr(const_cast<CT*>(s1.c_str()), nLen1, loc);
+ sslwr(const_cast<CT*>(s2.c_str()), nLen2, loc);
+ return coll.compare(s2.c_str(), s2.c_str()+nLen2,
+ s1.c_str(), s1.c_str()+nLen1);
+}
+#endif
+
+
+// -----------------------------------------------------------------------------
+// ssfmtmsg: FormatMessage equivalents. Needed because I added a CString facade
+// Again -- no equivalent of these on non-Win32 builds but their might one day
+// be one if the message facet gets implemented
+// -----------------------------------------------------------------------------
+#if defined (SS_WIN32) && !defined(SS_ANSI)
+ inline DWORD ssfmtmsg(DWORD dwFlags, LPCVOID pSrc, DWORD dwMsgId,
+ DWORD dwLangId, PSTR pBuf, DWORD nSize,
+ va_list* vlArgs)
+ {
+ return FormatMessageA(dwFlags, pSrc, dwMsgId, dwLangId,
+ pBuf, nSize,vlArgs);
+ }
+ inline DWORD ssfmtmsg(DWORD dwFlags, LPCVOID pSrc, DWORD dwMsgId,
+ DWORD dwLangId, PWSTR pBuf, DWORD nSize,
+ va_list* vlArgs)
+ {
+ return FormatMessageW(dwFlags, pSrc, dwMsgId, dwLangId,
+ pBuf, nSize,vlArgs);
+ }
+#else
+#endif
+
+
+
+// FUNCTION: sscpy. Copies up to 'nMax' characters from pSrc to pDst.
+// -----------------------------------------------------------------------------
+// FUNCTION: sscpy
+// inline int sscpy(PSTR pDst, PCSTR pSrc, int nMax=-1);
+// inline int sscpy(PUSTR pDst, PCSTR pSrc, int nMax=-1)
+// inline int sscpy(PSTR pDst, PCWSTR pSrc, int nMax=-1);
+// inline int sscpy(PWSTR pDst, PCWSTR pSrc, int nMax=-1);
+// inline int sscpy(PWSTR pDst, PCSTR pSrc, int nMax=-1);
+//
+// DESCRIPTION:
+// This function is very much (but not exactly) like strcpy. These
+// overloads simplify copying one C-style string into another by allowing
+// the caller to specify two different types of strings if necessary.
+//
+// The strings must NOT overlap
+//
+// "Character" is expressed in terms of the destination string, not
+// the source. If no 'nMax' argument is supplied, then the number of
+// characters copied will be sslen(pSrc). A NULL terminator will
+// also be added so pDst must actually be big enough to hold nMax+1
+// characters. The return value is the number of characters copied,
+// not including the NULL terminator.
+//
+// PARAMETERS:
+// pSrc - the string to be copied FROM. May be a char based string, an
+// MBCS string (in Win32 builds) or a wide string (wchar_t).
+// pSrc - the string to be copied TO. Also may be either MBCS or wide
+// nMax - the maximum number of characters to be copied into szDest. Note
+// that this is expressed in whatever a "character" means to pDst.
+// If pDst is a wchar_t type string than this will be the maximum
+// number of wchar_ts that my be copied. The pDst string must be
+// large enough to hold least nMaxChars+1 characters.
+// If the caller supplies no argument for nMax this is a signal to
+// the routine to copy all the characters in pSrc, regardless of
+// how long it is.
+//
+// RETURN VALUE: none
+// -----------------------------------------------------------------------------
+
+template<typename CT1, typename CT2>
+inline int sscpycvt(CT1* pDst, const CT2* pSrc, int nMax)
+{
+ // Note -- we assume pDst is big enough to hold pSrc. If not, we're in
+ // big trouble. No bounds checking. Caveat emptor.
+
+ int nSrc = sslen(pSrc);
+
+ const CT1* szCvt = StdCodeCvt(pDst, nMax, pSrc, nSrc);
+
+ // If we're copying the same size characters, then all the "code convert"
+ // just did was basically memcpy so the #of characters copied is the same
+ // as the number requested. I should probably specialize this function
+ // template to achieve this purpose as it is silly to do a runtime check
+ // of a fact known at compile time. I'll get around to it.
+
+ return sslen(szCvt);
+}
+
+template<typename T>
+inline int sscpycvt(T* pDst, const T* pSrc, int nMax)
+{
+ int nCount = nMax;
+ for (; nCount > 0 && *pSrc; ++pSrc, ++pDst, --nCount)
+ std::basic_string<T>::traits_type::assign(*pDst, *pSrc);
+
+ *pDst = 0;
+ return nMax - nCount;
+}
+
+inline int sscpycvt(PWSTR pDst, PCSTR pSrc, int nMax)
+{
+ // Note -- we assume pDst is big enough to hold pSrc. If not, we're in
+ // big trouble. No bounds checking. Caveat emptor.
+
+ const PWSTR szCvt = StdCodeCvt(pDst, nMax, pSrc, nMax);
+ return sslen(szCvt);
+}
+
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const CT2* pSrc, int nMax, int nLen)
+{
+ return sscpycvt(pDst, pSrc, SSMIN(nMax, nLen));
+}
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const CT2* pSrc, int nMax)
+{
+ return sscpycvt(pDst, pSrc, SSMIN(nMax, sslen(pSrc)));
+}
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const CT2* pSrc)
+{
+ return sscpycvt(pDst, pSrc, sslen(pSrc));
+}
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const std::basic_string<CT2>& sSrc, int nMax)
+{
+ return sscpycvt(pDst, sSrc.c_str(), SSMIN(nMax, (int)sSrc.length()));
+}
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const std::basic_string<CT2>& sSrc)
+{
+ return sscpycvt(pDst, sSrc.c_str(), (int)sSrc.length());
+}
+
+#ifdef SS_INC_COMDEF
+ template<typename CT1>
+ inline int sscpy(CT1* pDst, const _bstr_t& bs, int nMax)
+ {
+ return sscpycvt(pDst, static_cast<PCOLESTR>(bs),
+ SSMIN(nMax, static_cast<int>(bs.length())));
+ }
+ template<typename CT1>
+ inline int sscpy(CT1* pDst, const _bstr_t& bs)
+ {
+ return sscpy(pDst, bs, static_cast<int>(bs.length()));
+ }
+#endif
+
+
+// -----------------------------------------------------------------------------
+// Functional objects for changing case. They also let you pass locales
+// -----------------------------------------------------------------------------
+
+#ifdef SS_NO_LOCALE
+ template<typename CT>
+ struct SSToUpper : public std::unary_function<CT, CT>
+ {
+ inline CT operator()(const CT& t) const
+ {
+ return sstoupper(t);
+ }
+ };
+ template<typename CT>
+ struct SSToLower : public std::unary_function<CT, CT>
+ {
+ inline CT operator()(const CT& t) const
+ {
+ return sstolower(t);
+ }
+ };
+#else
+ template<typename CT>
+ struct SSToUpper : public std::binary_function<CT, std::locale, CT>
+ {
+ inline CT operator()(const CT& t, const std::locale& loc) const
+ {
+ return sstoupper<CT>(t, loc);
+ }
+ };
+ template<typename CT>
+ struct SSToLower : public std::binary_function<CT, std::locale, CT>
+ {
+ inline CT operator()(const CT& t, const std::locale& loc) const
+ {
+ return sstolower<CT>(t, loc);
+ }
+ };
+#endif
+
+// This struct is used for TrimRight() and TrimLeft() function implementations.
+//template<typename CT>
+//struct NotSpace : public std::unary_function<CT, bool>
+//{
+// const std::locale& loc;
+// inline NotSpace(const std::locale& locArg) : loc(locArg) {}
+// inline bool operator() (CT t) { return !std::isspace(t, loc); }
+//};
+template<typename CT>
+struct NotSpace : public std::unary_function<CT, bool>
+{
+ // DINKUMWARE BUG:
+ // Note -- using std::isspace in a COM DLL gives us access violations
+ // because it causes the dynamic addition of a function to be called
+ // when the library shuts down. Unfortunately the list is maintained
+ // in DLL memory but the function is in static memory. So the COM DLL
+ // goes away along with the function that was supposed to be called,
+ // and then later when the DLL CRT shuts down it unloads the list and
+ // tries to call the long-gone function.
+ // This is DinkumWare's implementation problem. If you encounter this
+ // problem, you may replace the calls here with good old isspace() and
+ // iswspace() from the CRT unless they specify SS_ANSI
+
+#ifdef SS_NO_LOCALE
+
+ bool operator() (CT t) const { return !ssisspace(t); }
+
+#else
+ const std::locale loc;
+ NotSpace(const std::locale& locArg=std::locale()) : loc(locArg) {}
+ bool operator() (CT t) const { return !std::isspace(t, loc); }
+#endif
+};
+
+
+
+
+// Now we can define the template (finally!)
+// =============================================================================
+// TEMPLATE: CStdStr
+// template<typename CT> class CStdStr : public std::basic_string<CT>
+//
+// REMARKS:
+// This template derives from basic_string<CT> and adds some MFC CString-
+// like functionality
+//
+// Basically, this is my attempt to make Standard C++ library strings as
+// easy to use as the MFC CString class.
+//
+// Note that although this is a template, it makes the assumption that the
+// template argument (CT, the character type) is either char or wchar_t.
+// =============================================================================
+
+//#define CStdStr _SS // avoid compiler warning 4786
+
+// template<typename ARG> ARG& FmtArg(ARG& arg) { return arg; }
+// PCSTR FmtArg(const std::string& arg) { return arg.c_str(); }
+// PCWSTR FmtArg(const std::wstring& arg) { return arg.c_str(); }
+
+template<typename ARG>
+struct FmtArg
+{
+ explicit FmtArg(const ARG& arg) : a_(arg) {}
+ const ARG& operator()() const { return a_; }
+ const ARG& a_;
+private:
+ FmtArg& operator=(const FmtArg&) { return *this; }
+};
+
+template<typename CT>
+class CStdStr : public std::basic_string<CT>
+{
+ // Typedefs for shorter names. Using these names also appears to help
+ // us avoid some ambiguities that otherwise arise on some platforms
+
+ #define MYBASE std::basic_string<CT> // my base class
+ //typedef typename std::basic_string<CT> MYBASE; // my base class
+ typedef CStdStr<CT> MYTYPE; // myself
+ typedef typename MYBASE::const_pointer PCMYSTR; // PCSTR or PCWSTR
+ typedef typename MYBASE::pointer PMYSTR; // PSTR or PWSTR
+ typedef typename MYBASE::iterator MYITER; // my iterator type
+ typedef typename MYBASE::const_iterator MYCITER; // you get the idea...
+ typedef typename MYBASE::reverse_iterator MYRITER;
+ typedef typename MYBASE::size_type MYSIZE;
+ typedef typename MYBASE::value_type MYVAL;
+ typedef typename MYBASE::allocator_type MYALLOC;
+
+public:
+ // shorthand conversion from PCTSTR to string resource ID
+ #define SSRES(pctstr) LOWORD(reinterpret_cast<unsigned long>(pctstr))
+
+ bool TryLoad(const void* pT)
+ {
+ bool bLoaded = false;
+
+#if defined(SS_WIN32) && !defined(SS_ANSI)
+ if ( ( pT != NULL ) && SS_IS_INTRESOURCE(pT) )
+ {
+ UINT nId = LOWORD(reinterpret_cast<unsigned long>(pT));
+ if ( !LoadString(nId) )
+ {
+ TRACE(_T("Can't load string %u\n"), SSRES(pT));
+ }
+ bLoaded = true;
+ }
+#endif
+
+ return bLoaded;
+ }
+
+
+ // CStdStr inline constructors
+ CStdStr()
+ {
+ }
+
+ CStdStr(const MYTYPE& str) : MYBASE(SSREF(str))
+ {
+ }
+
+ CStdStr(const std::string& str)
+ {
+ ssasn(*this, SSREF(str));
+ }
+
+ CStdStr(const std::wstring& str)
+ {
+ ssasn(*this, SSREF(str));
+ }
+
+ CStdStr(PCMYSTR pT, MYSIZE n) : MYBASE(pT, n)
+ {
+ }
+
+ CStdStr(PCSTR pA)
+ {
+ #ifdef SS_ANSI
+ *this = pA;
+ #else
+ if ( !TryLoad(pA) )
+ *this = pA;
+ #endif
+ }
+
+ CStdStr(PCWSTR pW)
+ {
+ #ifdef SS_ANSI
+ *this = pW;
+ #else
+ if ( !TryLoad(pW) )
+ *this = pW;
+ #endif
+ }
+
+ CStdStr(MYCITER first, MYCITER last)
+ : MYBASE(first, last)
+ {
+ }
+
+ CStdStr(MYSIZE nSize, MYVAL ch, const MYALLOC& al=MYALLOC())
+ : MYBASE(nSize, ch, al)
+ {
+ }
+
+ #ifdef SS_INC_COMDEF
+ CStdStr(const _bstr_t& bstr)
+ {
+ if ( bstr.length() > 0 )
+ this->append(static_cast<PCMYSTR>(bstr), bstr.length());
+ }
+ #endif
+
+ // CStdStr inline assignment operators -- the ssasn function now takes care
+ // of fixing the MSVC assignment bug (see knowledge base article Q172398).
+ MYTYPE& operator=(const MYTYPE& str)
+ {
+ ssasn(*this, str);
+ return *this;
+ }
+
+ MYTYPE& operator=(const std::string& str)
+ {
+ ssasn(*this, str);
+ return *this;
+ }
+
+ MYTYPE& operator=(const std::wstring& str)
+ {
+ ssasn(*this, str);
+ return *this;
+ }
+
+ MYTYPE& operator=(PCSTR pA)
+ {
+ ssasn(*this, pA);
+ return *this;
+ }
+
+ MYTYPE& operator=(PCWSTR pW)
+ {
+ ssasn(*this, pW);
+ return *this;
+ }
+
+ // Overloads also needed to fix the MSVC assignment bug (KB: Q172398)
+ // *** Thanks to Pete The Plumber for catching this one ***
+ // They also are compiled if you have explicitly turned off refcounting
+ #if ( defined(_MSC_VER) && ( _MSC_VER < 1200 ) ) || defined(SS_NO_REFCOUNT)
+
+ MYTYPE& assign(const MYTYPE& str)
+ {
+ Q172398(*this);
+ sscpy(GetBuffer(str.size()+1), SSREF(str));
+ this->ReleaseBuffer(str.size());
+ return *this;
+ }
+
+ MYTYPE& assign(const MYTYPE& str, MYSIZE nStart, MYSIZE nChars)
+ {
+ // This overload of basic_string::assign is supposed to assign up to
+ // <nChars> or the NULL terminator, whichever comes first. Since we
+ // are about to call a less forgiving overload (in which <nChars>
+ // must be a valid length), we must adjust the length here to a safe
+ // value. Thanks to Ullrich Poll�hne for catching this bug
+
+ nChars = SSMIN(nChars, str.length() - nStart);
+ MYTYPE strTemp(str.c_str()+nStart, nChars);
+ Q172398(*this);
+ this->assign(strTemp);
+ return *this;
+ }
+
+ MYTYPE& assign(const MYBASE& str)
+ {
+ ssasn(*this, str);
+ return *this;
+ }
+
+ MYTYPE& assign(const MYBASE& str, MYSIZE nStart, MYSIZE nChars)
+ {
+ // This overload of basic_string::assign is supposed to assign up to
+ // <nChars> or the NULL terminator, whichever comes first. Since we
+ // are about to call a less forgiving overload (in which <nChars>
+ // must be a valid length), we must adjust the length here to a safe
+ // value. Thanks to Ullrich Poll�hne for catching this bug
+
+ nChars = SSMIN(nChars, str.length() - nStart);
+
+ // Watch out for assignment to self
+
+ if ( this == &str )
+ {
+ MYTYPE strTemp(str.c_str() + nStart, nChars);
+ static_cast<MYBASE*>(this)->assign(strTemp);
+ }
+ else
+ {
+ Q172398(*this);
+ static_cast<MYBASE*>(this)->assign(str.c_str()+nStart, nChars);
+ }
+ return *this;
+ }
+
+ MYTYPE& assign(const CT* pC, MYSIZE nChars)
+ {
+ // Q172398 only fix -- erase before assigning, but not if we're
+ // assigning from our own buffer
+
+ #if defined ( _MSC_VER ) && ( _MSC_VER < 1200 )
+ if ( !this->empty() &&
+ ( pC < this->data() || pC > this->data() + this->capacity() ) )
+ {
+ this->erase();
+ }
+ #endif
+ Q172398(*this);
+ static_cast<MYBASE*>(this)->assign(pC, nChars);
+ return *this;
+ }
+
+ MYTYPE& assign(MYSIZE nChars, MYVAL val)
+ {
+ Q172398(*this);
+ static_cast<MYBASE*>(this)->assign(nChars, val);
+ return *this;
+ }
+
+ MYTYPE& assign(const CT* pT)
+ {
+ return this->assign(pT, MYBASE::traits_type::length(pT));
+ }
+
+ MYTYPE& assign(MYCITER iterFirst, MYCITER iterLast)
+ {
+ #if defined ( _MSC_VER ) && ( _MSC_VER < 1200 )
+ // Q172398 fix. don't call erase() if we're assigning from ourself
+ if ( iterFirst < this->begin() ||
+ iterFirst > this->begin() + this->size() )
+ {
+ this->erase()
+ }
+ #endif
+ this->replace(this->begin(), this->end(), iterFirst, iterLast);
+ return *this;
+ }
+ #endif
+
+
+ // -------------------------------------------------------------------------
+ // CStdStr inline concatenation.
+ // -------------------------------------------------------------------------
+ MYTYPE& operator+=(const MYTYPE& str)
+ {
+ ssadd(*this, str);
+ return *this;
+ }
+
+ MYTYPE& operator+=(const std::string& str)
+ {
+ ssadd(*this, str);
+ return *this;
+ }
+
+ MYTYPE& operator+=(const std::wstring& str)
+ {
+ ssadd(*this, str);
+ return *this;
+ }
+
+ MYTYPE& operator+=(PCSTR pA)
+ {
+ ssadd(*this, pA);
+ return *this;
+ }
+
+ MYTYPE& operator+=(PCWSTR pW)
+ {
+ ssadd(*this, pW);
+ return *this;
+ }
+
+ MYTYPE& operator+=(CT t)
+ {
+ this->append(1, t);
+ return *this;
+ }
+
+ // -------------------------------------------------------------------------
+ // CStdStr -- Direct access to character buffer. In the MS' implementation,
+ // the at() function that we use here also calls _Freeze() providing us some
+ // protection from multithreading problems associated with ref-counting.
+ // In VC 7 and later, of course, the ref-counting stuff is gone.
+ // -------------------------------------------------------------------------
+
+ CT* GetBuf(int nMinLen=-1)
+ {
+ if ( static_cast<int>(this->size()) < nMinLen )
+ this->resize(static_cast<MYSIZE>(nMinLen));
+
+ return this->empty() ? const_cast<CT*>(this->data()) : &(this->at(0));
+ }
+
+ CT* SetBuf(int nLen)
+ {
+ nLen = ( nLen > 0 ? nLen : 0 );
+ if ( this->capacity() < 1 && nLen == 0 )
+ this->resize(1);
+
+ this->resize(static_cast<MYSIZE>(nLen));
+ return const_cast<CT*>(this->data());
+ }
+ void RelBuf(int nNewLen=-1)
+ {
+ this->resize(static_cast<MYSIZE>(nNewLen > -1 ? nNewLen :
+ sslen(this->c_str())));
+ }
+
+ void BufferRel() { RelBuf(); } // backwards compatability
+ CT* Buffer() { return GetBuf(); } // backwards compatability
+ CT* BufferSet(int nLen) { return SetBuf(nLen);}// backwards compatability
+
+ bool Equals(const CT* pT, bool bUseCase=false) const
+ {
+ return 0 == (bUseCase ? this->compare(pT) : ssicmp(this->c_str(), pT));
+ }
+
+ // -------------------------------------------------------------------------
+ // FUNCTION: CStdStr::Load
+ // REMARKS:
+ // Loads string from resource specified by nID
+ //
+ // PARAMETERS:
+ // nID - resource Identifier. Purely a Win32 thing in this case
+ //
+ // RETURN VALUE:
+ // true if successful, false otherwise
+ // -------------------------------------------------------------------------
+
+#ifndef SS_ANSI
+
+ bool Load(UINT nId, HMODULE hModule=NULL)
+ {
+ bool bLoaded = false; // set to true of we succeed.
+
+ #ifdef _MFC_VER // When in Rome (or MFC land)...
+
+ // If they gave a resource handle, use it. Note - this is archaic
+ // and not really what I would recommend. But then again, in MFC
+ // land, you ought to be using CString for resources anyway since
+ // it walks the resource chain for you.
+
+ HMODULE hModuleOld = NULL;
+
+ if ( NULL != hModule )
+ {
+ hModuleOld = AfxGetResourceHandle();
+ AfxSetResourceHandle(hModule);
+ }
+
+ // ...load the string
+
+ CString strRes;
+ bLoaded = FALSE != strRes.LoadString(nId);
+
+ // ...and if we set the resource handle, restore it.
+
+ if ( NULL != hModuleOld )
+ AfxSetResourceHandle(hModule);
+
+ if ( bLoaded )
+ *this = strRes;
+
+ #else // otherwise make our own hackneyed version of CString's Load
+
+ // Get the resource name and module handle
+
+ if ( NULL == hModule )
+ hModule = GetResourceHandle();
+
+ PCTSTR szName = MAKEINTRESOURCE((nId>>4)+1); // lifted
+ DWORD dwSize = 0;
+
+ // No sense continuing if we can't find the resource
+
+ HRSRC hrsrc = ::FindResource(hModule, szName, RT_STRING);
+
+ if ( NULL == hrsrc )
+ {
+ TRACE(_T("Cannot find resource %d: 0x%X"), nId, ::GetLastError());
+ }
+ else if ( 0 == (dwSize = ::SizeofResource(hModule, hrsrc) / sizeof(CT)))
+ {
+ TRACE(_T("Cant get size of resource %d 0x%X\n"),nId,GetLastError());
+ }
+ else
+ {
+ bLoaded = 0 != ssload(hModule, nId, GetBuf(dwSize), dwSize);
+ ReleaseBuffer();
+ }
+
+ #endif // #ifdef _MFC_VER
+
+ if ( !bLoaded )
+ TRACE(_T("String not loaded 0x%X\n"), ::GetLastError());
+
+ return bLoaded;
+ }
+
+#endif // #ifdef SS_ANSI
+
+ // -------------------------------------------------------------------------
+ // CString Facade Functions:
+ //
+ // The following methods are intended to allow you to use this class as a
+ // near drop-in replacement for CString.
+ // -------------------------------------------------------------------------
+ #ifdef SS_WIN32
+ BSTR AllocSysString() const
+ {
+ ostring os;
+ ssasn(os, *this);
+ return ::SysAllocString(os.c_str());
+ }
+ #endif
+
+#ifndef SS_NO_LOCALE
+ int Collate(PCMYSTR szThat) const
+ {
+ return sscoll(this->c_str(), this->length(), szThat, sslen(szThat));
+ }
+
+ int CollateNoCase(PCMYSTR szThat) const
+ {
+ return ssicoll(this->c_str(), this->length(), szThat, sslen(szThat));
+ }
+#endif
+ int FindOneOf(PCMYSTR szCharSet) const
+ {
+ MYSIZE nIdx = this->find_first_of(szCharSet);
+ return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+ }
+
+#ifndef SS_ANSI
+ void FormatMessage(PCMYSTR szFormat, ...) throw(std::exception)
+ {
+ va_list argList;
+ va_start(argList, szFormat);
+ PMYSTR szTemp;
+ if ( ssfmtmsg(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ szFormat, 0, 0,
+ reinterpret_cast<PMYSTR>(&szTemp), 0, &argList) == 0 ||
+ szTemp == 0 )
+ {
+ throw std::runtime_error("out of memory");
+ }
+ *this = szTemp;
+ LocalFree(szTemp);
+ va_end(argList);
+ }
+
+ void FormatMessage(UINT nFormatId, ...) throw(std::exception)
+ {
+ MYTYPE sFormat;
+ VERIFY(sFormat.LoadString(nFormatId));
+ va_list argList;
+ va_start(argList, nFormatId);
+ PMYSTR szTemp;
+ if ( ssfmtmsg(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ sFormat, 0, 0,
+ reinterpret_cast<PMYSTR>(&szTemp), 0, &argList) == 0 ||
+ szTemp == 0)
+ {
+ throw std::runtime_error("out of memory");
+ }
+ *this = szTemp;
+ LocalFree(szTemp);
+ va_end(argList);
+ }
+#endif
+
+ // GetAllocLength -- an MSVC7 function but it costs us nothing to add it.
+
+ int GetAllocLength()
+ {
+ return static_cast<int>(this->capacity());
+ }
+
+ // -------------------------------------------------------------------------
+ // GetXXXX -- Direct access to character buffer
+ // -------------------------------------------------------------------------
+ CT* GetBuffer(int nMinLen=-1)
+ {
+ return GetBuf(nMinLen);
+ }
+
+ CT* GetBufferSetLength(int nLen)
+ {
+ return BufferSet(nLen);
+ }
+
+#ifndef SS_ANSI
+ bool LoadString(UINT nId)
+ {
+ return this->Load(nId);
+ }
+#endif
+
+ void MakeReverse()
+ {
+ std::reverse(this->begin(), this->end());
+ }
+
+ void ReleaseBuffer(int nNewLen=-1)
+ {
+ RelBuf(nNewLen);
+ }
+
+#ifndef SS_ANSI
+ BSTR SetSysString(BSTR* pbstr) const
+ {
+ ostring os;
+ ssasn(os, *this);
+ if ( !::SysReAllocStringLen(pbstr, os.c_str(), os.length()) )
+ throw std::runtime_error("out of memory");
+
+ ASSERT(*pbstr != 0);
+ return *pbstr;
+ }
+#endif
+
+ MYTYPE SpanExcluding(PCMYSTR szCharSet) const
+ {
+ MYSIZE pos = this->find_first_of(szCharSet);
+ return pos == MYBASE::npos ? *this : Left(pos);
+ }
+
+ MYTYPE SpanIncluding(PCMYSTR szCharSet) const
+ {
+ MYSIZE pos = this->find_first_not_of(szCharSet);
+ return pos == MYBASE::npos ? *this : Left(pos);
+ }
+
+#if defined SS_WIN32 && !defined(UNICODE) && !defined(SS_ANSI)
+
+ // CString's OemToAnsi and AnsiToOem functions are available only in
+ // Unicode builds. However since we're a template we also need a
+ // runtime check of CT and a reinterpret_cast to account for the fact
+ // that CStdStringW gets instantiated even in non-Unicode builds.
+
+ void AnsiToOem()
+ {
+ if ( sizeof(CT) == sizeof(char) && !empty() )
+ {
+ ::CharToOem(reinterpret_cast<PCSTR>(this->c_str()),
+ reinterpret_cast<PSTR>(GetBuf()));
+ }
+ else
+ {
+ ASSERT(false);
+ }
+ }
+
+ void OemToAnsi()
+ {
+ if ( sizeof(CT) == sizeof(char) && !empty() )
+ {
+ ::OemToChar(reinterpret_cast<PCSTR>(this->c_str()),
+ reinterpret_cast<PSTR>(GetBuf()));
+ }
+ else
+ {
+ ASSERT(false);
+ }
+ }
+
+#endif
+
+ void FreeExtra()
+ {
+ MYTYPE mt;
+ this->swap(mt);
+ if ( !mt.empty() )
+ this->assign(mt.c_str(), mt.size());
+ }
+
+ // I have intentionally not implemented the following CString
+ // functions. You cannot make them work without taking advantage
+ // of implementation specific behavior. However if you absolutely
+ // MUST have them, uncomment out these lines for "sort-of-like"
+ // their behavior. You're on your own.
+
+// CT* LockBuffer() { return GetBuf(); }// won't really lock
+// void UnlockBuffer(); { } // why have UnlockBuffer w/o LockBuffer?
+
+ // Array-indexing operators. Required because we defined an implicit cast
+ // to operator const CT* (Thanks to Julian Selman for pointing this out)
+
+ CT& operator[](int nIdx)
+ {
+ return static_cast<MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+ }
+
+ const CT& operator[](int nIdx) const
+ {
+ return static_cast<const MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+ }
+
+ CT& operator[](unsigned int nIdx)
+ {
+ return static_cast<MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+ }
+
+ const CT& operator[](unsigned int nIdx) const
+ {
+ return static_cast<const MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+ }
+
+ CT& operator[](unsigned long nIdx)
+ {
+ return static_cast<MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+ }
+
+ const CT& operator[](unsigned long nIdx) const
+ {
+ return static_cast<const MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+ }
+
+#ifndef SS_NO_IMPLICIT_CAST
+ operator const CT*() const
+ {
+ return this->c_str();
+ }
+#endif
+
+ // IStream related functions. Useful in IPersistStream implementations
+
+#ifdef SS_INC_COMDEF
+
+ // struct SSSHDR - useful for non Std C++ persistence schemes.
+ typedef struct SSSHDR
+ {
+ BYTE byCtrl;
+ ULONG nChars;
+ } SSSHDR; // as in "Standard String Stream Header"
+
+ #define SSSO_UNICODE 0x01 // the string is a wide string
+ #define SSSO_COMPRESS 0x02 // the string is compressed
+
+ // -------------------------------------------------------------------------
+ // FUNCTION: StreamSize
+ // REMARKS:
+ // Returns how many bytes it will take to StreamSave() this CStdString
+ // object to an IStream.
+ // -------------------------------------------------------------------------
+ ULONG StreamSize() const
+ {
+ // Control header plus string
+ ASSERT(this->size()*sizeof(CT) < 0xffffffffUL - sizeof(SSSHDR));
+ return (this->size() * sizeof(CT)) + sizeof(SSSHDR);
+ }
+
+ // -------------------------------------------------------------------------
+ // FUNCTION: StreamSave
+ // REMARKS:
+ // Saves this CStdString object to a COM IStream.
+ // -------------------------------------------------------------------------
+ HRESULT StreamSave(IStream* pStream) const
+ {
+ ASSERT(this->size()*sizeof(CT) < 0xffffffffUL - sizeof(SSSHDR));
+ HRESULT hr = E_FAIL;
+ ASSERT(pStream != 0);
+ SSSHDR hdr;
+ hdr.byCtrl = sizeof(CT) == 2 ? SSSO_UNICODE : 0;
+ hdr.nChars = this->size();
+
+
+ if ( FAILED(hr=pStream->Write(&hdr, sizeof(SSSHDR), 0)) )
+ {
+ TRACE(_T("StreamSave: Cannot write control header, ERR=0x%X\n"),hr);
+ }
+ else if ( empty() )
+ {
+ ; // nothing to write
+ }
+ else if ( FAILED(hr=pStream->Write(this->c_str(),
+ this->size()*sizeof(CT), 0)) )
+ {
+ TRACE(_T("StreamSave: Cannot write string to stream 0x%X\n"), hr);
+ }
+
+ return hr;
+ }
+
+
+ // -------------------------------------------------------------------------
+ // FUNCTION: StreamLoad
+ // REMARKS:
+ // This method loads the object from an IStream.
+ // -------------------------------------------------------------------------
+ HRESULT StreamLoad(IStream* pStream)
+ {
+ ASSERT(pStream != 0);
+ SSSHDR hdr;
+ HRESULT hr = E_FAIL;
+
+ if ( FAILED(hr=pStream->Read(&hdr, sizeof(SSSHDR), 0)) )
+ {
+ TRACE(_T("StreamLoad: Cant read control header, ERR=0x%X\n"), hr);
+ }
+ else if ( hdr.nChars > 0 )
+ {
+ ULONG nRead = 0;
+ PMYSTR pMyBuf = BufferSet(hdr.nChars);
+
+ // If our character size matches the character size of the string
+ // we're trying to read, then we can read it directly into our
+ // buffer. Otherwise, we have to read into an intermediate buffer
+ // and convert.
+
+ if ( (hdr.byCtrl & SSSO_UNICODE) != 0 )
+ {
+ ULONG nBytes = hdr.nChars * sizeof(wchar_t);
+ if ( sizeof(CT) == sizeof(wchar_t) )
+ {
+ if ( FAILED(hr=pStream->Read(pMyBuf, nBytes, &nRead)) )
+ TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);
+ }
+ else
+ {
+ PWSTR pBufW = reinterpret_cast<PWSTR>(_alloca((nBytes)+1));
+ if ( FAILED(hr=pStream->Read(pBufW, nBytes, &nRead)) )
+ TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);
+ else
+ sscpy(pMyBuf, pBufW, hdr.nChars);
+ }
+ }
+ else
+ {
+ ULONG nBytes = hdr.nChars * sizeof(char);
+ if ( sizeof(CT) == sizeof(char) )
+ {
+ if ( FAILED(hr=pStream->Read(pMyBuf, nBytes, &nRead)) )
+ TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);
+ }
+ else
+ {
+ PSTR pBufA = reinterpret_cast<PSTR>(_alloca(nBytes));
+ if ( FAILED(hr=pStream->Read(pBufA, hdr.nChars, &nRead)) )
+ TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);
+ else
+ sscpy(pMyBuf, pBufA, hdr.nChars);
+ }
+ }
+ }
+ else
+ {
+ this->erase();
+ }
+ return hr;
+ }
+#endif // #ifdef SS_INC_COMDEF
+
+#ifndef SS_ANSI
+
+ // SetResourceHandle/GetResourceHandle. In MFC builds, these map directly
+ // to AfxSetResourceHandle and AfxGetResourceHandle. In non-MFC builds they
+ // point to a single static HINST so that those who call the member
+ // functions that take resource IDs can provide an alternate HINST of a DLL
+ // to search. This is not exactly the list of HMODULES that MFC provides
+ // but it's better than nothing.
+
+ #ifdef _MFC_VER
+ static void SetResourceHandle(HMODULE hNew)
+ {
+ AfxSetResourceHandle(hNew);
+ }
+ static HMODULE GetResourceHandle()
+ {
+ return AfxGetResourceHandle();
+ }
+ #else
+ static void SetResourceHandle(HMODULE hNew)
+ {
+ SSResourceHandle() = hNew;
+ }
+ static HMODULE GetResourceHandle()
+ {
+ return SSResourceHandle();
+ }
+ #endif
+
+#endif
+};
+
+// -----------------------------------------------------------------------------
+// MSVC USERS: HOW TO EXPORT CSTDSTRING FROM A DLL
+//
+// If you are using MS Visual C++ and you want to export CStdStringA and
+// CStdStringW from a DLL, then all you need to
+//
+// 1. make sure that all components link to the same DLL version
+// of the CRT (not the static one).
+// 2. Uncomment the 3 lines of code below
+// 3. #define 2 macros per the instructions in MS KnowledgeBase
+// article Q168958. The macros are:
+//
+// MACRO DEFINTION WHEN EXPORTING DEFINITION WHEN IMPORTING
+// ----- ------------------------ -------------------------
+// SSDLLEXP (nothing, just #define it) extern
+// SSDLLSPEC __declspec(dllexport) __declspec(dllimport)
+//
+// Note that these macros must be available to ALL clients who want to
+// link to the DLL and use the class. If they
+//
+// A word of advice: Don't bother.
+//
+// Really, it is not necessary to export CStdString functions from a DLL. I
+// never do. In my projects, I do generally link to the DLL version of the
+// Standard C++ Library, but I do NOT attempt to export CStdString functions.
+// I simply include the header where it is needed and allow for the code
+// redundancy.
+//
+// That redundancy is a lot less than you think. This class does most of its
+// work via the Standard C++ Library, particularly the base_class basic_string<>
+// member functions. Most of the functions here are small enough to be inlined
+// anyway. Besides, you'll find that in actual practice you use less than 1/2
+// of the code here, even in big projects and different modules will use as
+// little as 10% of it. That means a lot less functions actually get linked
+// your binaries. If you export this code from a DLL, it ALL gets linked in.
+//
+// I've compared the size of the binaries from exporting vs NOT exporting. Take
+// my word for it -- exporting this code is not worth the hassle.
+//
+// -----------------------------------------------------------------------------
+//#pragma warning(disable:4231) // non-standard extension ("extern template")
+// SSDLLEXP template class SSDLLSPEC CStdStr<char>;
+// SSDLLEXP template class SSDLLSPEC CStdStr<wchar_t>;
+
+
+// =============================================================================
+// END OF CStdStr INLINE FUNCTION DEFINITIONS
+// =============================================================================
+
+// Now typedef our class names based upon this humongous template
+
+typedef CStdStr<char> CStdStringA; // a better std::string
+typedef CStdStr<wchar_t> CStdStringW; // a better std::wstring
+typedef CStdStr<uint16_t> CStdString16; // a 16bit char string
+typedef CStdStr<uint32_t> CStdString32; // a 32bit char string
+typedef CStdStr<OLECHAR> CStdStringO; // almost always CStdStringW
+
+// -----------------------------------------------------------------------------
+// CStdStr addition functions defined as inline
+// -----------------------------------------------------------------------------
+
+
+inline CStdStringA operator+(const CStdStringA& s1, const CStdStringA& s2)
+{
+ CStdStringA sRet(SSREF(s1));
+ sRet.append(s2);
+ return sRet;
+}
+inline CStdStringA operator+(const CStdStringA& s1, CStdStringA::value_type t)
+{
+ CStdStringA sRet(SSREF(s1));
+ sRet.append(1, t);
+ return sRet;
+}
+inline CStdStringA operator+(const CStdStringA& s1, PCSTR pA)
+{
+ CStdStringA sRet(SSREF(s1));
+ sRet.append(pA);
+ return sRet;
+}
+inline CStdStringA operator+(PCSTR pA, const CStdStringA& sA)
+{
+ CStdStringA sRet;
+ CStdStringA::size_type nObjSize = sA.size();
+ CStdStringA::size_type nLitSize =
+ static_cast<CStdStringA::size_type>(sslen(pA));
+
+ sRet.reserve(nLitSize + nObjSize);
+ sRet.assign(pA);
+ sRet.append(sA);
+ return sRet;
+}
+
+
+inline CStdStringA operator+(const CStdStringA& s1, const CStdStringW& s2)
+{
+ return s1 + CStdStringA(s2);
+}
+inline CStdStringW operator+(const CStdStringW& s1, const CStdStringW& s2)
+{
+ CStdStringW sRet(SSREF(s1));
+ sRet.append(s2);
+ return sRet;
+}
+inline CStdStringA operator+(const CStdStringA& s1, PCWSTR pW)
+{
+ return s1 + CStdStringA(pW);
+}
+
+#ifdef UNICODE
+ inline CStdStringW operator+(PCWSTR pW, const CStdStringA& sA)
+ {
+ return CStdStringW(pW) + CStdStringW(SSREF(sA));
+ }
+ inline CStdStringW operator+(PCSTR pA, const CStdStringW& sW)
+ {
+ return CStdStringW(pA) + sW;
+ }
+#else
+ inline CStdStringA operator+(PCWSTR pW, const CStdStringA& sA)
+ {
+ return CStdStringA(pW) + sA;
+ }
+ inline CStdStringA operator+(PCSTR pA, const CStdStringW& sW)
+ {
+ return pA + CStdStringA(sW);
+ }
+#endif
+
+// ...Now the wide string versions.
+inline CStdStringW operator+(const CStdStringW& s1, CStdStringW::value_type t)
+{
+ CStdStringW sRet(SSREF(s1));
+ sRet.append(1, t);
+ return sRet;
+}
+inline CStdStringW operator+(const CStdStringW& s1, PCWSTR pW)
+{
+ CStdStringW sRet(SSREF(s1));
+ sRet.append(pW);
+ return sRet;
+}
+inline CStdStringW operator+(PCWSTR pW, const CStdStringW& sW)
+{
+ CStdStringW sRet;
+ CStdStringW::size_type nObjSize = sW.size();
+ CStdStringA::size_type nLitSize =
+ static_cast<CStdStringW::size_type>(sslen(pW));
+
+ sRet.reserve(nLitSize + nObjSize);
+ sRet.assign(pW);
+ sRet.append(sW);
+ return sRet;
+}
+
+inline CStdStringW operator+(const CStdStringW& s1, const CStdStringA& s2)
+{
+ return s1 + CStdStringW(s2);
+}
+inline CStdStringW operator+(const CStdStringW& s1, PCSTR pA)
+{
+ return s1 + CStdStringW(pA);
+}
+
+
+// New-style format function is a template
+
+#ifdef SS_SAFE_FORMAT
+
+template<>
+struct FmtArg<CStdStringA>
+{
+ explicit FmtArg(const CStdStringA& arg) : a_(arg) {}
+ PCSTR operator()() const { return a_.c_str(); }
+ const CStdStringA& a_;
+private:
+ FmtArg<CStdStringA>& operator=(const FmtArg<CStdStringA>&) { return *this; }
+};
+template<>
+struct FmtArg<CStdStringW>
+{
+ explicit FmtArg(const CStdStringW& arg) : a_(arg) {}
+ PCWSTR operator()() const { return a_.c_str(); }
+ const CStdStringW& a_;
+private:
+ FmtArg<CStdStringW>& operator=(const FmtArg<CStdStringW>&) { return *this; }
+};
+
+template<>
+struct FmtArg<std::string>
+{
+ explicit FmtArg(const std::string& arg) : a_(arg) {}
+ PCSTR operator()() const { return a_.c_str(); }
+ const std::string& a_;
+private:
+ FmtArg<std::string>& operator=(const FmtArg<std::string>&) { return *this; }
+};
+template<>
+struct FmtArg<std::wstring>
+{
+ explicit FmtArg(const std::wstring& arg) : a_(arg) {}
+ PCWSTR operator()() const { return a_.c_str(); }
+ const std::wstring& a_;
+private:
+ FmtArg<std::wstring>& operator=(const FmtArg<std::wstring>&) {return *this;}
+};
+#endif // #ifdef SS_SAFEFORMAT
+
+#ifndef SS_ANSI
+ // SSResourceHandle: our MFC-like resource handle
+ inline HMODULE& SSResourceHandle()
+ {
+ static HMODULE hModuleSS = GetModuleHandle(0);
+ return hModuleSS;
+ }
+#endif
+
+
+// In MFC builds, define some global serialization operators
+// Special operators that allow us to serialize CStdStrings to CArchives.
+// Note that we use an intermediate CString object in order to ensure that
+// we use the exact same format.
+
+#ifdef _MFC_VER
+ inline CArchive& AFXAPI operator<<(CArchive& ar, const CStdStringA& strA)
+ {
+ CString strTemp = strA;
+ return ar << strTemp;
+ }
+ inline CArchive& AFXAPI operator<<(CArchive& ar, const CStdStringW& strW)
+ {
+ CString strTemp = strW;
+ return ar << strTemp;
+ }
+
+ inline CArchive& AFXAPI operator>>(CArchive& ar, CStdStringA& strA)
+ {
+ CString strTemp;
+ ar >> strTemp;
+ strA = strTemp;
+ return ar;
+ }
+ inline CArchive& AFXAPI operator>>(CArchive& ar, CStdStringW& strW)
+ {
+ CString strTemp;
+ ar >> strTemp;
+ strW = strTemp;
+ return ar;
+ }
+#endif // #ifdef _MFC_VER -- (i.e. is this MFC?)
+
+
+// Define TCHAR based friendly names for some of these functions
+
+#ifdef UNICODE
+ //#define CStdString CStdStringW
+ typedef CStdStringW CStdString;
+#else
+ //#define CStdString CStdStringA
+ typedef CStdStringA CStdString;
+#endif
+
+// ...and some shorter names for the space-efficient
+
+// -----------------------------------------------------------------------------
+// FUNCTIONAL COMPARATORS:
+// REMARKS:
+// These structs are derived from the std::binary_function template. They
+// give us functional classes (which may be used in Standard C++ Library
+// collections and algorithms) that perform case-insensitive comparisons of
+// CStdString objects. This is useful for maps in which the key may be the
+// proper string but in the wrong case.
+// -----------------------------------------------------------------------------
+#define StdStringLessNoCaseW SSLNCW // avoid VC compiler warning 4786
+#define StdStringEqualsNoCaseW SSENCW
+#define StdStringLessNoCaseA SSLNCA
+#define StdStringEqualsNoCaseA SSENCA
+
+#ifdef UNICODE
+ #define StdStringLessNoCase SSLNCW
+ #define StdStringEqualsNoCase SSENCW
+#else
+ #define StdStringLessNoCase SSLNCA
+ #define StdStringEqualsNoCase SSENCA
+#endif
+
+struct StdStringLessNoCaseW
+ : std::binary_function<CStdStringW, CStdStringW, bool>
+{
+ inline
+ bool operator()(const CStdStringW& sLeft, const CStdStringW& sRight) const
+ { return ssicmp(sLeft.c_str(), sRight.c_str()) < 0; }
+};
+struct StdStringEqualsNoCaseW
+ : std::binary_function<CStdStringW, CStdStringW, bool>
+{
+ inline
+ bool operator()(const CStdStringW& sLeft, const CStdStringW& sRight) const
+ { return ssicmp(sLeft.c_str(), sRight.c_str()) == 0; }
+};
+struct StdStringLessNoCaseA
+ : std::binary_function<CStdStringA, CStdStringA, bool>
+{
+ inline
+ bool operator()(const CStdStringA& sLeft, const CStdStringA& sRight) const
+ { return ssicmp(sLeft.c_str(), sRight.c_str()) < 0; }
+};
+struct StdStringEqualsNoCaseA
+ : std::binary_function<CStdStringA, CStdStringA, bool>
+{
+ inline
+ bool operator()(const CStdStringA& sLeft, const CStdStringA& sRight) const
+ { return ssicmp(sLeft.c_str(), sRight.c_str()) == 0; }
+};
+
+// If we had to define our own version of TRACE above, get rid of it now
+
+#ifdef TRACE_DEFINED_HERE
+ #undef TRACE
+ #undef TRACE_DEFINED_HERE
+#endif
+
+
+// These std::swap specializations come courtesy of Mike Crusader.
+
+//namespace std
+//{
+// inline void swap(CStdStringA& s1, CStdStringA& s2) throw()
+// {
+// s1.swap(s2);
+// }
+// template<>
+// inline void swap(CStdStringW& s1, CStdStringW& s2) throw()
+// {
+// s1.swap(s2);
+// }
+//}
+
+// Turn back on any Borland warnings we turned off.
+
+#ifdef __BORLANDC__
+ #pragma option pop // Turn back on inline function warnings
+// #pragma warn +inl // Turn back on inline function warnings
+#endif
+
+#endif // #ifndef STDSTRING_H
diff --git a/src/utils/Stopwatch.cpp b/src/utils/Stopwatch.cpp
new file mode 100644
index 0000000000..6e41eedec3
--- /dev/null
+++ b/src/utils/Stopwatch.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "threads/SystemClock.h"
+#include "Stopwatch.h"
+#if defined(TARGET_POSIX) && !defined(TARGET_DARWIN) && !defined(TARGET_FREEBSD)
+#include <sys/sysinfo.h>
+#endif
+#include "utils/TimeUtils.h"
+
+CStopWatch::CStopWatch(bool useFrameTime /*=false*/)
+{
+ m_timerPeriod = 0.0f;
+ m_startTick = 0;
+ m_stopTick = 0;
+ m_isRunning = false;
+ m_useFrameTime = useFrameTime;
+
+#ifdef TARGET_POSIX
+ m_timerPeriod = 1.0f / 1000.0f; // we want seconds
+#else
+ if (m_useFrameTime)
+ m_timerPeriod = 1.0f / 1000.0f; //frametime is in milliseconds
+ else
+ m_timerPeriod = 1.0f / (float)CurrentHostFrequency();
+#endif
+}
+
+CStopWatch::~CStopWatch()
+{
+}
+
+int64_t CStopWatch::GetTicks() const
+{
+ if (m_useFrameTime)
+ return CTimeUtils::GetFrameTime();
+#ifndef TARGET_POSIX
+ return CurrentHostCounter();
+#else
+ return XbmcThreads::SystemClockMillis();
+#endif
+}
diff --git a/src/utils/Stopwatch.h b/src/utils/Stopwatch.h
new file mode 100644
index 0000000000..a3bcb8a0c7
--- /dev/null
+++ b/src/utils/Stopwatch.h
@@ -0,0 +1,112 @@
+#pragma once
+
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdint.h>
+
+class CStopWatch
+{
+public:
+ CStopWatch(bool useFrameTime=false);
+ ~CStopWatch();
+
+ /*!
+ \brief Retrieve the running state of the stopwatch.
+
+ \return True if stopwatch has been started but not stopped.
+ */
+ inline bool IsRunning() const
+ {
+ return m_isRunning;
+ }
+
+ /*!
+ \brief Record start time and change state to running.
+ */
+ inline void StartZero()
+ {
+ m_startTick = GetTicks();
+ m_isRunning = true;
+ }
+
+ /*!
+ \brief Record start time and change state to running, only if the stopwatch is stopped.
+ */
+ inline void Start()
+ {
+ if (!m_isRunning)
+ StartZero();
+ }
+
+ /*!
+ \brief Record stop time and change state to not running.
+ */
+ inline void Stop()
+ {
+ if(m_isRunning)
+ {
+ m_stopTick = GetTicks();
+ m_isRunning = false;
+ }
+ }
+
+ /*!
+ \brief Set the start time such that time elapsed is now zero.
+ */
+ void Reset()
+ {
+ if (m_isRunning)
+ m_startTick = GetTicks();
+ else
+ m_startTick = m_stopTick;
+ }
+
+ /*!
+ \brief Retrieve time elapsed between the last call to Start(), StartZero()
+ or Reset() and; if running, now; if stopped, the last call to Stop().
+
+ \return Elapsed time, in seconds, as a float.
+ */
+ float GetElapsedSeconds() const
+ {
+ int64_t totalTicks = (m_isRunning ? GetTicks() : m_stopTick) - m_startTick;
+ return (float)totalTicks * m_timerPeriod;
+ }
+
+ /*!
+ \brief Retrieve time elapsed between the last call to Start(), StartZero()
+ or Reset() and; if running, now; if stopped, the last call to Stop().
+
+ \return Elapsed time, in milliseconds, as a float.
+ */
+ float GetElapsedMilliseconds() const
+ {
+ return GetElapsedSeconds() * 1000.0f;
+ }
+
+private:
+ int64_t GetTicks() const;
+ float m_timerPeriod; // to save division in GetElapsed...()
+ int64_t m_startTick;
+ int64_t m_stopTick;
+ bool m_isRunning;
+ bool m_useFrameTime;
+};
diff --git a/src/utils/StreamDetails.cpp b/src/utils/StreamDetails.cpp
new file mode 100644
index 0000000000..28b413d249
--- /dev/null
+++ b/src/utils/StreamDetails.cpp
@@ -0,0 +1,598 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <math.h>
+#include "StreamDetails.h"
+#include "StreamUtils.h"
+#include "Variant.h"
+#include "LangInfo.h"
+#include "utils/LangCodeExpander.h"
+#include "utils/Archive.h"
+
+const float VIDEOASPECT_EPSILON = 0.025f;
+
+void CStreamDetail::Archive(CArchive &ar)
+{
+ // there's nothing to do here, the type is stored externally and parent isn't stored
+}
+void CStreamDetail::Serialize(CVariant &value) const
+{
+ // there's nothing to do here, the type is stored externally and parent isn't stored
+}
+
+CStreamDetailVideo::CStreamDetailVideo() :
+ CStreamDetail(CStreamDetail::VIDEO), m_iWidth(0), m_iHeight(0), m_fAspect(0.0), m_iDuration(0)
+{
+}
+
+void CStreamDetailVideo::Archive(CArchive& ar)
+{
+ CStreamDetail::Archive(ar);
+ if (ar.IsStoring())
+ {
+ ar << m_strCodec;
+ ar << m_fAspect;
+ ar << m_iHeight;
+ ar << m_iWidth;
+ ar << m_iDuration;
+ ar << m_strStereoMode;
+ }
+ else
+ {
+ ar >> m_strCodec;
+ ar >> m_fAspect;
+ ar >> m_iHeight;
+ ar >> m_iWidth;
+ ar >> m_iDuration;
+ ar >> m_strStereoMode;
+ }
+}
+void CStreamDetailVideo::Serialize(CVariant& value) const
+{
+ value["codec"] = m_strCodec;
+ value["aspect"] = m_fAspect;
+ value["height"] = m_iHeight;
+ value["width"] = m_iWidth;
+ value["duration"] = m_iDuration;
+ value["stereomode"] = m_strStereoMode;
+}
+
+bool CStreamDetailVideo::IsWorseThan(CStreamDetail *that)
+{
+ if (that->m_eType != CStreamDetail::VIDEO)
+ return true;
+
+ // Best video stream is that with the most pixels
+ CStreamDetailVideo *sdv = (CStreamDetailVideo *)that;
+ return (sdv->m_iWidth * sdv->m_iHeight) > (m_iWidth * m_iHeight);
+}
+
+CStreamDetailAudio::CStreamDetailAudio() :
+ CStreamDetail(CStreamDetail::AUDIO), m_iChannels(-1)
+{
+}
+
+void CStreamDetailAudio::Archive(CArchive& ar)
+{
+ CStreamDetail::Archive(ar);
+ if (ar.IsStoring())
+ {
+ ar << m_strCodec;
+ ar << m_strLanguage;
+ ar << m_iChannels;
+ }
+ else
+ {
+ ar >> m_strCodec;
+ ar >> m_strLanguage;
+ ar >> m_iChannels;
+ }
+}
+void CStreamDetailAudio::Serialize(CVariant& value) const
+{
+ value["codec"] = m_strCodec;
+ value["language"] = m_strLanguage;
+ value["channels"] = m_iChannels;
+}
+
+bool CStreamDetailAudio::IsWorseThan(CStreamDetail *that)
+{
+ if (that->m_eType != CStreamDetail::AUDIO)
+ return true;
+
+ CStreamDetailAudio *sda = (CStreamDetailAudio *)that;
+ // First choice is the thing with the most channels
+ if (sda->m_iChannels > m_iChannels)
+ return true;
+ if (m_iChannels > sda->m_iChannels)
+ return false;
+
+ // In case of a tie, revert to codec priority
+ return StreamUtils::GetCodecPriority(sda->m_strCodec) > StreamUtils::GetCodecPriority(m_strCodec);
+}
+
+CStreamDetailSubtitle::CStreamDetailSubtitle() :
+ CStreamDetail(CStreamDetail::SUBTITLE)
+{
+}
+
+void CStreamDetailSubtitle::Archive(CArchive& ar)
+{
+ CStreamDetail::Archive(ar);
+ if (ar.IsStoring())
+ {
+ ar << m_strLanguage;
+ }
+ else
+ {
+ ar >> m_strLanguage;
+ }
+}
+void CStreamDetailSubtitle::Serialize(CVariant& value) const
+{
+ value["language"] = m_strLanguage;
+}
+
+bool CStreamDetailSubtitle::IsWorseThan(CStreamDetail *that)
+{
+ if (that->m_eType != CStreamDetail::SUBTITLE)
+ return true;
+
+ if (g_LangCodeExpander.CompareLangCodes(m_strLanguage, ((CStreamDetailSubtitle *)that)->m_strLanguage))
+ return false;
+
+ // the best subtitle should be the one in the user's preferred language
+ // If preferred language is set to "original" this is "eng"
+ return m_strLanguage.empty() ||
+ g_LangCodeExpander.CompareLangCodes(((CStreamDetailSubtitle *)that)->m_strLanguage, g_langInfo.GetSubtitleLanguage());
+}
+
+CStreamDetailSubtitle& CStreamDetailSubtitle::operator=(const CStreamDetailSubtitle &that)
+{
+ if (this != &that)
+ {
+ this->m_pParent = that.m_pParent;
+ this->m_strLanguage = that.m_strLanguage;
+ }
+ return *this;
+}
+
+CStreamDetails& CStreamDetails::operator=(const CStreamDetails &that)
+{
+ if (this != &that)
+ {
+ Reset();
+ std::vector<CStreamDetail *>::const_iterator iter;
+ for (iter = that.m_vecItems.begin(); iter != that.m_vecItems.end(); ++iter)
+ {
+ switch ((*iter)->m_eType)
+ {
+ case CStreamDetail::VIDEO:
+ AddStream(new CStreamDetailVideo((const CStreamDetailVideo &)(**iter)));
+ break;
+ case CStreamDetail::AUDIO:
+ AddStream(new CStreamDetailAudio((const CStreamDetailAudio &)(**iter)));
+ break;
+ case CStreamDetail::SUBTITLE:
+ AddStream(new CStreamDetailSubtitle((const CStreamDetailSubtitle &)(**iter)));
+ break;
+ }
+ }
+
+ DetermineBestStreams();
+ } /* if this != that */
+
+ return *this;
+}
+
+bool CStreamDetails::operator ==(const CStreamDetails &right) const
+{
+ if (this == &right) return true;
+
+ if (GetVideoStreamCount() != right.GetVideoStreamCount() ||
+ GetAudioStreamCount() != right.GetAudioStreamCount() ||
+ GetSubtitleStreamCount() != right.GetSubtitleStreamCount())
+ return false;
+
+ for (int iStream=1; iStream<=GetVideoStreamCount(); iStream++)
+ {
+ if (GetVideoCodec(iStream) != right.GetVideoCodec(iStream) ||
+ GetVideoWidth(iStream) != right.GetVideoWidth(iStream) ||
+ GetVideoHeight(iStream) != right.GetVideoHeight(iStream) ||
+ GetVideoDuration(iStream) != right.GetVideoDuration(iStream) ||
+ fabs(GetVideoAspect(iStream) - right.GetVideoAspect(iStream)) > VIDEOASPECT_EPSILON)
+ return false;
+ }
+
+ for (int iStream=1; iStream<=GetAudioStreamCount(); iStream++)
+ {
+ if (GetAudioCodec(iStream) != right.GetAudioCodec(iStream) ||
+ GetAudioLanguage(iStream) != right.GetAudioLanguage(iStream) ||
+ GetAudioChannels(iStream) != right.GetAudioChannels(iStream) )
+ return false;
+ }
+
+ for (int iStream=1; iStream<=GetSubtitleStreamCount(); iStream++)
+ {
+ if (GetSubtitleLanguage(iStream) != right.GetSubtitleLanguage(iStream) )
+ return false;
+ }
+
+ return true;
+}
+
+bool CStreamDetails::operator !=(const CStreamDetails &right) const
+{
+ if (this == &right) return false;
+
+ return !(*this == right);
+}
+
+CStreamDetail *CStreamDetails::NewStream(CStreamDetail::StreamType type)
+{
+ CStreamDetail *retVal = NULL;
+ switch (type)
+ {
+ case CStreamDetail::VIDEO:
+ retVal = new CStreamDetailVideo();
+ break;
+ case CStreamDetail::AUDIO:
+ retVal = new CStreamDetailAudio();
+ break;
+ case CStreamDetail::SUBTITLE:
+ retVal = new CStreamDetailSubtitle();
+ break;
+ }
+
+ if (retVal)
+ AddStream(retVal);
+
+ return retVal;
+}
+
+int CStreamDetails::GetStreamCount(CStreamDetail::StreamType type) const
+{
+ int retVal = 0;
+ std::vector<CStreamDetail *>::const_iterator iter;
+ for (iter = m_vecItems.begin(); iter != m_vecItems.end(); ++iter)
+ if ((*iter)->m_eType == type)
+ retVal++;
+ return retVal;
+}
+
+int CStreamDetails::GetVideoStreamCount(void) const
+{
+ return GetStreamCount(CStreamDetail::VIDEO);
+}
+
+int CStreamDetails::GetAudioStreamCount(void) const
+{
+ return GetStreamCount(CStreamDetail::AUDIO);
+}
+
+int CStreamDetails::GetSubtitleStreamCount(void) const
+{
+ return GetStreamCount(CStreamDetail::SUBTITLE);
+}
+
+CStreamDetails::CStreamDetails(const CStreamDetails &that)
+{
+ *this = that;
+}
+
+void CStreamDetails::AddStream(CStreamDetail *item)
+{
+ item->m_pParent = this;
+ m_vecItems.push_back(item);
+}
+
+void CStreamDetails::Reset(void)
+{
+ m_pBestVideo = NULL;
+ m_pBestAudio = NULL;
+ m_pBestSubtitle = NULL;
+
+ std::vector<CStreamDetail *>::iterator iter;
+ for (iter = m_vecItems.begin(); iter != m_vecItems.end(); ++iter)
+ delete *iter;
+ m_vecItems.clear();
+}
+
+const CStreamDetail* CStreamDetails::GetNthStream(CStreamDetail::StreamType type, int idx) const
+{
+ if (idx == 0)
+ {
+ switch (type)
+ {
+ case CStreamDetail::VIDEO:
+ return m_pBestVideo;
+ break;
+ case CStreamDetail::AUDIO:
+ return m_pBestAudio;
+ break;
+ case CStreamDetail::SUBTITLE:
+ return m_pBestSubtitle;
+ break;
+ default:
+ return NULL;
+ break;
+ }
+ }
+
+ std::vector<CStreamDetail *>::const_iterator iter;
+ for (iter = m_vecItems.begin(); iter != m_vecItems.end(); ++iter)
+ if ((*iter)->m_eType == type)
+ {
+ idx--;
+ if (idx < 1)
+ return *iter;
+ }
+
+ return NULL;
+}
+
+std::string CStreamDetails::GetVideoCodec(int idx) const
+{
+ CStreamDetailVideo *item = (CStreamDetailVideo *)GetNthStream(CStreamDetail::VIDEO, idx);
+ if (item)
+ return item->m_strCodec;
+ else
+ return "";
+}
+
+float CStreamDetails::GetVideoAspect(int idx) const
+{
+ CStreamDetailVideo *item = (CStreamDetailVideo *)GetNthStream(CStreamDetail::VIDEO, idx);
+ if (item)
+ return item->m_fAspect;
+ else
+ return 0.0;
+}
+
+int CStreamDetails::GetVideoWidth(int idx) const
+{
+ CStreamDetailVideo *item = (CStreamDetailVideo *)GetNthStream(CStreamDetail::VIDEO, idx);
+ if (item)
+ return item->m_iWidth;
+ else
+ return 0;
+}
+
+int CStreamDetails::GetVideoHeight(int idx) const
+{
+ CStreamDetailVideo *item = (CStreamDetailVideo *)GetNthStream(CStreamDetail::VIDEO, idx);
+ if (item)
+ return item->m_iHeight;
+ else
+ return 0;
+}
+
+int CStreamDetails::GetVideoDuration(int idx) const
+{
+ CStreamDetailVideo *item = (CStreamDetailVideo *)GetNthStream(CStreamDetail::VIDEO, idx);
+ if (item)
+ return item->m_iDuration;
+ else
+ return 0;
+}
+
+void CStreamDetails::SetVideoDuration(int idx, const int duration)
+{
+ CStreamDetailVideo *item = (CStreamDetailVideo *)GetNthStream(CStreamDetail::VIDEO, idx);
+ if (item)
+ item->m_iDuration = duration;
+}
+
+std::string CStreamDetails::GetStereoMode(int idx) const
+{
+ CStreamDetailVideo *item = (CStreamDetailVideo *)GetNthStream(CStreamDetail::VIDEO, idx);
+ if (item)
+ return item->m_strStereoMode;
+ else
+ return "";
+}
+
+std::string CStreamDetails::GetAudioCodec(int idx) const
+{
+ CStreamDetailAudio *item = (CStreamDetailAudio *)GetNthStream(CStreamDetail::AUDIO, idx);
+ if (item)
+ return item->m_strCodec;
+ else
+ return "";
+}
+
+std::string CStreamDetails::GetAudioLanguage(int idx) const
+{
+ CStreamDetailAudio *item = (CStreamDetailAudio *)GetNthStream(CStreamDetail::AUDIO, idx);
+ if (item)
+ return item->m_strLanguage;
+ else
+ return "";
+}
+
+int CStreamDetails::GetAudioChannels(int idx) const
+{
+ CStreamDetailAudio *item = (CStreamDetailAudio *)GetNthStream(CStreamDetail::AUDIO, idx);
+ if (item)
+ return item->m_iChannels;
+ else
+ return -1;
+}
+
+std::string CStreamDetails::GetSubtitleLanguage(int idx) const
+{
+ CStreamDetailSubtitle *item = (CStreamDetailSubtitle *)GetNthStream(CStreamDetail::SUBTITLE, idx);
+ if (item)
+ return item->m_strLanguage;
+ else
+ return "";
+}
+
+void CStreamDetails::Archive(CArchive& ar)
+{
+ if (ar.IsStoring())
+ {
+ ar << (int)m_vecItems.size();
+
+ std::vector<CStreamDetail *>::const_iterator iter;
+ for (iter = m_vecItems.begin(); iter != m_vecItems.end(); ++iter)
+ {
+ // the type goes before the actual item. When loading we need
+ // to know the type before we can construct an instance to serialize
+ ar << (int)(*iter)->m_eType;
+ ar << (**iter);
+ }
+ }
+ else
+ {
+ int count;
+ ar >> count;
+
+ Reset();
+ for (int i=0; i<count; i++)
+ {
+ int type;
+ CStreamDetail *p = NULL;
+
+ ar >> type;
+ p = NewStream(CStreamDetail::StreamType(type));
+ if (p)
+ ar >> (*p);
+ }
+
+ DetermineBestStreams();
+ }
+}
+void CStreamDetails::Serialize(CVariant& value) const
+{
+ // make sure these properties are always present
+ value["audio"] = CVariant(CVariant::VariantTypeArray);
+ value["video"] = CVariant(CVariant::VariantTypeArray);
+ value["subtitle"] = CVariant(CVariant::VariantTypeArray);
+
+ std::vector<CStreamDetail *>::const_iterator iter;
+ CVariant v;
+ for (iter = m_vecItems.begin(); iter != m_vecItems.end(); ++iter)
+ {
+ v.clear();
+ (*iter)->Serialize(v);
+ switch ((*iter)->m_eType)
+ {
+ case CStreamDetail::AUDIO:
+ value["audio"].push_back(v);
+ break;
+ case CStreamDetail::VIDEO:
+ value["video"].push_back(v);
+ break;
+ case CStreamDetail::SUBTITLE:
+ value["subtitle"].push_back(v);
+ break;
+ }
+ }
+}
+
+void CStreamDetails::DetermineBestStreams(void)
+{
+ m_pBestVideo = NULL;
+ m_pBestAudio = NULL;
+ m_pBestSubtitle = NULL;
+
+ std::vector<CStreamDetail *>::const_iterator iter;
+ for (iter = m_vecItems.begin(); iter != m_vecItems.end(); ++iter)
+ {
+ CStreamDetail **champion;
+ switch ((*iter)->m_eType)
+ {
+ case CStreamDetail::VIDEO:
+ champion = (CStreamDetail **)&m_pBestVideo;
+ break;
+ case CStreamDetail::AUDIO:
+ champion = (CStreamDetail **)&m_pBestAudio;
+ break;
+ case CStreamDetail::SUBTITLE:
+ champion = (CStreamDetail **)&m_pBestSubtitle;
+ break;
+ default:
+ champion = NULL;
+ } /* switch type */
+
+ if (!champion)
+ continue;
+
+ if ((*champion == NULL) || (*champion)->IsWorseThan(*iter))
+ *champion = *iter;
+ } /* for each */
+}
+
+std::string CStreamDetails::VideoDimsToResolutionDescription(int iWidth, int iHeight)
+{
+ if (iWidth == 0 || iHeight == 0)
+ return "";
+
+ else if (iWidth <= 720 && iHeight <= 480)
+ return "480";
+ // 720x576 (PAL) (768 when rescaled for square pixels)
+ else if (iWidth <= 768 && iHeight <= 576)
+ return "576";
+ // 960x540 (sometimes 544 which is multiple of 16)
+ else if (iWidth <= 960 && iHeight <= 544)
+ return "540";
+ // 1280x720
+ else if (iWidth <= 1280 && iHeight <= 720)
+ return "720";
+ // 1920x1080
+ else if (iWidth <= 1920 && iHeight <= 1080)
+ return "1080";
+ // 4K
+ else if (iWidth * iHeight >= 6000000)
+ return "4K";
+ else
+ return "";
+}
+
+std::string CStreamDetails::VideoAspectToAspectDescription(float fAspect)
+{
+ if (fAspect == 0.0f)
+ return "";
+
+ // Given that we're never going to be able to handle every single possibility in
+ // aspect ratios, particularly when cropping prior to video encoding is taken into account
+ // the best we can do is take the "common" aspect ratios, and return the closest one available.
+ // The cutoffs are the geometric mean of the two aspect ratios either side.
+ if (fAspect < 1.3499f) // sqrt(1.33*1.37)
+ return "1.33";
+ else if (fAspect < 1.5080f) // sqrt(1.37*1.66)
+ return "1.37";
+ else if (fAspect < 1.7190f) // sqrt(1.66*1.78)
+ return "1.66";
+ else if (fAspect < 1.8147f) // sqrt(1.78*1.85)
+ return "1.78";
+ else if (fAspect < 2.0174f) // sqrt(1.85*2.20)
+ return "1.85";
+ else if (fAspect < 2.2738f) // sqrt(2.20*2.35)
+ return "2.20";
+ else if (fAspect < 2.3749f) // sqrt(2.35*2.40)
+ return "2.35";
+ else if (fAspect < 2.4739f) // sqrt(2.40*2.55)
+ return "2.40";
+ else if (fAspect < 2.6529f) // sqrt(2.55*2.76)
+ return "2.55";
+ return "2.76";
+}
diff --git a/src/utils/StreamDetails.h b/src/utils/StreamDetails.h
new file mode 100644
index 0000000000..fdda28eeab
--- /dev/null
+++ b/src/utils/StreamDetails.h
@@ -0,0 +1,138 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/IArchivable.h"
+#include "ISerializable.h"
+#include <string>
+#include <vector>
+
+class CStreamDetails;
+
+class CStreamDetail : public IArchivable, public ISerializable
+{
+public:
+ enum StreamType {
+ VIDEO,
+ AUDIO,
+ SUBTITLE
+ };
+
+ CStreamDetail(StreamType type) : m_eType(type), m_pParent(NULL) {};
+ virtual void Archive(CArchive& ar);
+ virtual void Serialize(CVariant& value) const;
+ virtual bool IsWorseThan(CStreamDetail *that) { return true; };
+
+ const StreamType m_eType;
+
+protected:
+ CStreamDetails *m_pParent;
+ friend class CStreamDetails;
+};
+
+class CStreamDetailVideo : public CStreamDetail
+{
+public:
+ CStreamDetailVideo();
+ virtual void Archive(CArchive& ar);
+ virtual void Serialize(CVariant& value) const;
+ virtual bool IsWorseThan(CStreamDetail *that);
+
+ int m_iWidth;
+ int m_iHeight;
+ float m_fAspect;
+ int m_iDuration;
+ std::string m_strCodec;
+ std::string m_strStereoMode;
+};
+
+class CStreamDetailAudio : public CStreamDetail
+{
+public:
+ CStreamDetailAudio();
+ virtual void Archive(CArchive& ar);
+ virtual void Serialize(CVariant& value) const;
+ virtual bool IsWorseThan(CStreamDetail *that);
+
+ int m_iChannels;
+ std::string m_strCodec;
+ std::string m_strLanguage;
+};
+
+class CStreamDetailSubtitle : public CStreamDetail
+{
+public:
+ CStreamDetailSubtitle();
+ CStreamDetailSubtitle& operator=(const CStreamDetailSubtitle &that);
+ virtual void Archive(CArchive& ar);
+ virtual void Serialize(CVariant& value) const;
+ virtual bool IsWorseThan(CStreamDetail *that);
+
+ std::string m_strLanguage;
+};
+
+class CStreamDetails : public IArchivable, public ISerializable
+{
+public:
+ CStreamDetails() { Reset(); };
+ CStreamDetails(const CStreamDetails &that);
+ ~CStreamDetails() { Reset(); };
+ CStreamDetails& operator=(const CStreamDetails &that);
+ bool operator ==(const CStreamDetails &that) const;
+ bool operator !=(const CStreamDetails &that) const;
+
+ static std::string VideoDimsToResolutionDescription(int iWidth, int iHeight);
+ static std::string VideoAspectToAspectDescription(float fAspect);
+
+ bool HasItems(void) const { return m_vecItems.size() > 0; };
+ int GetStreamCount(CStreamDetail::StreamType type) const;
+ int GetVideoStreamCount(void) const;
+ int GetAudioStreamCount(void) const;
+ int GetSubtitleStreamCount(void) const;
+ const CStreamDetail* GetNthStream(CStreamDetail::StreamType type, int idx) const;
+
+ std::string GetVideoCodec(int idx = 0) const;
+ float GetVideoAspect(int idx = 0) const;
+ int GetVideoWidth(int idx = 0) const;
+ int GetVideoHeight(int idx = 0) const;
+ int GetVideoDuration(int idx = 0) const;
+ void SetVideoDuration(int idx, const int duration);
+ std::string GetStereoMode(int idx = 0) const;
+
+ std::string GetAudioCodec(int idx = 0) const;
+ std::string GetAudioLanguage(int idx = 0) const;
+ int GetAudioChannels(int idx = 0) const;
+
+ std::string GetSubtitleLanguage(int idx = 0) const;
+
+ void AddStream(CStreamDetail *item);
+ void Reset(void);
+ void DetermineBestStreams(void);
+
+ virtual void Archive(CArchive& ar);
+ virtual void Serialize(CVariant& value) const;
+
+private:
+ CStreamDetail *NewStream(CStreamDetail::StreamType type);
+ std::vector<CStreamDetail *> m_vecItems;
+ CStreamDetailVideo *m_pBestVideo;
+ CStreamDetailAudio *m_pBestAudio;
+ CStreamDetailSubtitle *m_pBestSubtitle;
+};
diff --git a/src/utils/StreamUtils.cpp b/src/utils/StreamUtils.cpp
new file mode 100644
index 0000000000..e7ef9a149a
--- /dev/null
+++ b/src/utils/StreamUtils.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "StreamUtils.h"
+
+int StreamUtils::GetCodecPriority(const std::string &codec)
+{
+ /*
+ * Technically flac, truehd, and dtshd_ma are equivalently good as they're all lossless. However,
+ * ffmpeg can't decode dtshd_ma losslessy yet.
+ */
+ if (codec == "flac") // Lossless FLAC
+ return 7;
+ if (codec == "truehd") // Dolby TrueHD
+ return 6;
+ if (codec == "dtshd_ma") // DTS-HD Master Audio (previously known as DTS++)
+ return 5;
+ if (codec == "dtshd_hra") // DTS-HD High Resolution Audio
+ return 4;
+ if (codec == "eac3") // Dolby Digital Plus
+ return 3;
+ if (codec == "dca") // DTS
+ return 2;
+ if (codec == "ac3") // Dolby Digital
+ return 1;
+ return 0;
+}
diff --git a/src/utils/StreamUtils.h b/src/utils/StreamUtils.h
new file mode 100644
index 0000000000..46edebab12
--- /dev/null
+++ b/src/utils/StreamUtils.h
@@ -0,0 +1,28 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string>
+
+class StreamUtils
+{
+public:
+ static int GetCodecPriority(const std::string &codec);
+};
diff --git a/src/utils/StringUtils.cpp b/src/utils/StringUtils.cpp
new file mode 100644
index 0000000000..56bb28ef99
--- /dev/null
+++ b/src/utils/StringUtils.cpp
@@ -0,0 +1,1197 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+//-----------------------------------------------------------------------
+//
+// File: StringUtils.cpp
+//
+// Purpose: ATL split string utility
+// Author: Paul J. Weiss
+//
+// Modified to use J O'Leary's CStdString class by kraqh3d
+//
+//------------------------------------------------------------------------
+
+
+#include "StringUtils.h"
+#include "utils/RegExp.h"
+#include "utils/fstrcmp.h"
+#include "Util.h"
+#include <locale>
+
+#include <math.h>
+#include <sstream>
+#include <time.h>
+#include <stdlib.h>
+
+#define FORMAT_BLOCK_SIZE 512 // # of bytes for initial allocation for printf
+
+using namespace std;
+
+const char* ADDON_GUID_RE = "^(\\{){0,1}[0-9a-fA-F]{8}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{12}(\\}){0,1}$";
+
+/* empty string for use in returns by ref */
+const CStdString StringUtils::EmptyString = "";
+const std::string StringUtils::Empty = "";
+CStdString StringUtils::m_lastUUID = "";
+
+// Copyright (c) Leigh Brasington 2012. All rights reserved.
+// This code may be used and reproduced without written permission.
+// http://www.leighb.com/tounicupper.htm
+//
+// The tables were constructed from
+// http://publib.boulder.ibm.com/infocenter/iseries/v7r1m0/index.jsp?topic=%2Fnls%2Frbagslowtoupmaptable.htm
+
+static wchar_t unicode_lowers[] = {
+ (wchar_t)0x0061, (wchar_t)0x0062, (wchar_t)0x0063, (wchar_t)0x0064, (wchar_t)0x0065, (wchar_t)0x0066, (wchar_t)0x0067, (wchar_t)0x0068, (wchar_t)0x0069,
+ (wchar_t)0x006A, (wchar_t)0x006B, (wchar_t)0x006C, (wchar_t)0x006D, (wchar_t)0x006E, (wchar_t)0x006F, (wchar_t)0x0070, (wchar_t)0x0071, (wchar_t)0x0072,
+ (wchar_t)0x0073, (wchar_t)0x0074, (wchar_t)0x0075, (wchar_t)0x0076, (wchar_t)0x0077, (wchar_t)0x0078, (wchar_t)0x0079, (wchar_t)0x007A, (wchar_t)0x00E0,
+ (wchar_t)0x00E1, (wchar_t)0x00E2, (wchar_t)0x00E3, (wchar_t)0x00E4, (wchar_t)0x00E5, (wchar_t)0x00E6, (wchar_t)0x00E7, (wchar_t)0x00E8, (wchar_t)0x00E9,
+ (wchar_t)0x00EA, (wchar_t)0x00EB, (wchar_t)0x00EC, (wchar_t)0x00ED, (wchar_t)0x00EE, (wchar_t)0x00EF, (wchar_t)0x00F0, (wchar_t)0x00F1, (wchar_t)0x00F2,
+ (wchar_t)0x00F3, (wchar_t)0x00F4, (wchar_t)0x00F5, (wchar_t)0x00F6, (wchar_t)0x00F8, (wchar_t)0x00F9, (wchar_t)0x00FA, (wchar_t)0x00FB, (wchar_t)0x00FC,
+ (wchar_t)0x00FD, (wchar_t)0x00FE, (wchar_t)0x00FF, (wchar_t)0x0101, (wchar_t)0x0103, (wchar_t)0x0105, (wchar_t)0x0107, (wchar_t)0x0109, (wchar_t)0x010B,
+ (wchar_t)0x010D, (wchar_t)0x010F, (wchar_t)0x0111, (wchar_t)0x0113, (wchar_t)0x0115, (wchar_t)0x0117, (wchar_t)0x0119, (wchar_t)0x011B, (wchar_t)0x011D,
+ (wchar_t)0x011F, (wchar_t)0x0121, (wchar_t)0x0123, (wchar_t)0x0125, (wchar_t)0x0127, (wchar_t)0x0129, (wchar_t)0x012B, (wchar_t)0x012D, (wchar_t)0x012F,
+ (wchar_t)0x0131, (wchar_t)0x0133, (wchar_t)0x0135, (wchar_t)0x0137, (wchar_t)0x013A, (wchar_t)0x013C, (wchar_t)0x013E, (wchar_t)0x0140, (wchar_t)0x0142,
+ (wchar_t)0x0144, (wchar_t)0x0146, (wchar_t)0x0148, (wchar_t)0x014B, (wchar_t)0x014D, (wchar_t)0x014F, (wchar_t)0x0151, (wchar_t)0x0153, (wchar_t)0x0155,
+ (wchar_t)0x0157, (wchar_t)0x0159, (wchar_t)0x015B, (wchar_t)0x015D, (wchar_t)0x015F, (wchar_t)0x0161, (wchar_t)0x0163, (wchar_t)0x0165, (wchar_t)0x0167,
+ (wchar_t)0x0169, (wchar_t)0x016B, (wchar_t)0x016D, (wchar_t)0x016F, (wchar_t)0x0171, (wchar_t)0x0173, (wchar_t)0x0175, (wchar_t)0x0177, (wchar_t)0x017A,
+ (wchar_t)0x017C, (wchar_t)0x017E, (wchar_t)0x0183, (wchar_t)0x0185, (wchar_t)0x0188, (wchar_t)0x018C, (wchar_t)0x0192, (wchar_t)0x0199, (wchar_t)0x01A1,
+ (wchar_t)0x01A3, (wchar_t)0x01A5, (wchar_t)0x01A8, (wchar_t)0x01AD, (wchar_t)0x01B0, (wchar_t)0x01B4, (wchar_t)0x01B6, (wchar_t)0x01B9, (wchar_t)0x01BD,
+ (wchar_t)0x01C6, (wchar_t)0x01C9, (wchar_t)0x01CC, (wchar_t)0x01CE, (wchar_t)0x01D0, (wchar_t)0x01D2, (wchar_t)0x01D4, (wchar_t)0x01D6, (wchar_t)0x01D8,
+ (wchar_t)0x01DA, (wchar_t)0x01DC, (wchar_t)0x01DF, (wchar_t)0x01E1, (wchar_t)0x01E3, (wchar_t)0x01E5, (wchar_t)0x01E7, (wchar_t)0x01E9, (wchar_t)0x01EB,
+ (wchar_t)0x01ED, (wchar_t)0x01EF, (wchar_t)0x01F3, (wchar_t)0x01F5, (wchar_t)0x01FB, (wchar_t)0x01FD, (wchar_t)0x01FF, (wchar_t)0x0201, (wchar_t)0x0203,
+ (wchar_t)0x0205, (wchar_t)0x0207, (wchar_t)0x0209, (wchar_t)0x020B, (wchar_t)0x020D, (wchar_t)0x020F, (wchar_t)0x0211, (wchar_t)0x0213, (wchar_t)0x0215,
+ (wchar_t)0x0217, (wchar_t)0x0253, (wchar_t)0x0254, (wchar_t)0x0257, (wchar_t)0x0258, (wchar_t)0x0259, (wchar_t)0x025B, (wchar_t)0x0260, (wchar_t)0x0263,
+ (wchar_t)0x0268, (wchar_t)0x0269, (wchar_t)0x026F, (wchar_t)0x0272, (wchar_t)0x0275, (wchar_t)0x0283, (wchar_t)0x0288, (wchar_t)0x028A, (wchar_t)0x028B,
+ (wchar_t)0x0292, (wchar_t)0x03AC, (wchar_t)0x03AD, (wchar_t)0x03AE, (wchar_t)0x03AF, (wchar_t)0x03B1, (wchar_t)0x03B2, (wchar_t)0x03B3, (wchar_t)0x03B4,
+ (wchar_t)0x03B5, (wchar_t)0x03B6, (wchar_t)0x03B7, (wchar_t)0x03B8, (wchar_t)0x03B9, (wchar_t)0x03BA, (wchar_t)0x03BB, (wchar_t)0x03BC, (wchar_t)0x03BD,
+ (wchar_t)0x03BE, (wchar_t)0x03BF, (wchar_t)0x03C0, (wchar_t)0x03C1, (wchar_t)0x03C3, (wchar_t)0x03C4, (wchar_t)0x03C5, (wchar_t)0x03C6, (wchar_t)0x03C7,
+ (wchar_t)0x03C8, (wchar_t)0x03C9, (wchar_t)0x03CA, (wchar_t)0x03CB, (wchar_t)0x03CC, (wchar_t)0x03CD, (wchar_t)0x03CE, (wchar_t)0x03E3, (wchar_t)0x03E5,
+ (wchar_t)0x03E7, (wchar_t)0x03E9, (wchar_t)0x03EB, (wchar_t)0x03ED, (wchar_t)0x03EF, (wchar_t)0x0430, (wchar_t)0x0431, (wchar_t)0x0432, (wchar_t)0x0433,
+ (wchar_t)0x0434, (wchar_t)0x0435, (wchar_t)0x0436, (wchar_t)0x0437, (wchar_t)0x0438, (wchar_t)0x0439, (wchar_t)0x043A, (wchar_t)0x043B, (wchar_t)0x043C,
+ (wchar_t)0x043D, (wchar_t)0x043E, (wchar_t)0x043F, (wchar_t)0x0440, (wchar_t)0x0441, (wchar_t)0x0442, (wchar_t)0x0443, (wchar_t)0x0444, (wchar_t)0x0445,
+ (wchar_t)0x0446, (wchar_t)0x0447, (wchar_t)0x0448, (wchar_t)0x0449, (wchar_t)0x044A, (wchar_t)0x044B, (wchar_t)0x044C, (wchar_t)0x044D, (wchar_t)0x044E,
+ (wchar_t)0x044F, (wchar_t)0x0451, (wchar_t)0x0452, (wchar_t)0x0453, (wchar_t)0x0454, (wchar_t)0x0455, (wchar_t)0x0456, (wchar_t)0x0457, (wchar_t)0x0458,
+ (wchar_t)0x0459, (wchar_t)0x045A, (wchar_t)0x045B, (wchar_t)0x045C, (wchar_t)0x045E, (wchar_t)0x045F, (wchar_t)0x0461, (wchar_t)0x0463, (wchar_t)0x0465,
+ (wchar_t)0x0467, (wchar_t)0x0469, (wchar_t)0x046B, (wchar_t)0x046D, (wchar_t)0x046F, (wchar_t)0x0471, (wchar_t)0x0473, (wchar_t)0x0475, (wchar_t)0x0477,
+ (wchar_t)0x0479, (wchar_t)0x047B, (wchar_t)0x047D, (wchar_t)0x047F, (wchar_t)0x0481, (wchar_t)0x0491, (wchar_t)0x0493, (wchar_t)0x0495, (wchar_t)0x0497,
+ (wchar_t)0x0499, (wchar_t)0x049B, (wchar_t)0x049D, (wchar_t)0x049F, (wchar_t)0x04A1, (wchar_t)0x04A3, (wchar_t)0x04A5, (wchar_t)0x04A7, (wchar_t)0x04A9,
+ (wchar_t)0x04AB, (wchar_t)0x04AD, (wchar_t)0x04AF, (wchar_t)0x04B1, (wchar_t)0x04B3, (wchar_t)0x04B5, (wchar_t)0x04B7, (wchar_t)0x04B9, (wchar_t)0x04BB,
+ (wchar_t)0x04BD, (wchar_t)0x04BF, (wchar_t)0x04C2, (wchar_t)0x04C4, (wchar_t)0x04C8, (wchar_t)0x04CC, (wchar_t)0x04D1, (wchar_t)0x04D3, (wchar_t)0x04D5,
+ (wchar_t)0x04D7, (wchar_t)0x04D9, (wchar_t)0x04DB, (wchar_t)0x04DD, (wchar_t)0x04DF, (wchar_t)0x04E1, (wchar_t)0x04E3, (wchar_t)0x04E5, (wchar_t)0x04E7,
+ (wchar_t)0x04E9, (wchar_t)0x04EB, (wchar_t)0x04EF, (wchar_t)0x04F1, (wchar_t)0x04F3, (wchar_t)0x04F5, (wchar_t)0x04F9, (wchar_t)0x0561, (wchar_t)0x0562,
+ (wchar_t)0x0563, (wchar_t)0x0564, (wchar_t)0x0565, (wchar_t)0x0566, (wchar_t)0x0567, (wchar_t)0x0568, (wchar_t)0x0569, (wchar_t)0x056A, (wchar_t)0x056B,
+ (wchar_t)0x056C, (wchar_t)0x056D, (wchar_t)0x056E, (wchar_t)0x056F, (wchar_t)0x0570, (wchar_t)0x0571, (wchar_t)0x0572, (wchar_t)0x0573, (wchar_t)0x0574,
+ (wchar_t)0x0575, (wchar_t)0x0576, (wchar_t)0x0577, (wchar_t)0x0578, (wchar_t)0x0579, (wchar_t)0x057A, (wchar_t)0x057B, (wchar_t)0x057C, (wchar_t)0x057D,
+ (wchar_t)0x057E, (wchar_t)0x057F, (wchar_t)0x0580, (wchar_t)0x0581, (wchar_t)0x0582, (wchar_t)0x0583, (wchar_t)0x0584, (wchar_t)0x0585, (wchar_t)0x0586,
+ (wchar_t)0x10D0, (wchar_t)0x10D1, (wchar_t)0x10D2, (wchar_t)0x10D3, (wchar_t)0x10D4, (wchar_t)0x10D5, (wchar_t)0x10D6, (wchar_t)0x10D7, (wchar_t)0x10D8,
+ (wchar_t)0x10D9, (wchar_t)0x10DA, (wchar_t)0x10DB, (wchar_t)0x10DC, (wchar_t)0x10DD, (wchar_t)0x10DE, (wchar_t)0x10DF, (wchar_t)0x10E0, (wchar_t)0x10E1,
+ (wchar_t)0x10E2, (wchar_t)0x10E3, (wchar_t)0x10E4, (wchar_t)0x10E5, (wchar_t)0x10E6, (wchar_t)0x10E7, (wchar_t)0x10E8, (wchar_t)0x10E9, (wchar_t)0x10EA,
+ (wchar_t)0x10EB, (wchar_t)0x10EC, (wchar_t)0x10ED, (wchar_t)0x10EE, (wchar_t)0x10EF, (wchar_t)0x10F0, (wchar_t)0x10F1, (wchar_t)0x10F2, (wchar_t)0x10F3,
+ (wchar_t)0x10F4, (wchar_t)0x10F5, (wchar_t)0x1E01, (wchar_t)0x1E03, (wchar_t)0x1E05, (wchar_t)0x1E07, (wchar_t)0x1E09, (wchar_t)0x1E0B, (wchar_t)0x1E0D,
+ (wchar_t)0x1E0F, (wchar_t)0x1E11, (wchar_t)0x1E13, (wchar_t)0x1E15, (wchar_t)0x1E17, (wchar_t)0x1E19, (wchar_t)0x1E1B, (wchar_t)0x1E1D, (wchar_t)0x1E1F,
+ (wchar_t)0x1E21, (wchar_t)0x1E23, (wchar_t)0x1E25, (wchar_t)0x1E27, (wchar_t)0x1E29, (wchar_t)0x1E2B, (wchar_t)0x1E2D, (wchar_t)0x1E2F, (wchar_t)0x1E31,
+ (wchar_t)0x1E33, (wchar_t)0x1E35, (wchar_t)0x1E37, (wchar_t)0x1E39, (wchar_t)0x1E3B, (wchar_t)0x1E3D, (wchar_t)0x1E3F, (wchar_t)0x1E41, (wchar_t)0x1E43,
+ (wchar_t)0x1E45, (wchar_t)0x1E47, (wchar_t)0x1E49, (wchar_t)0x1E4B, (wchar_t)0x1E4D, (wchar_t)0x1E4F, (wchar_t)0x1E51, (wchar_t)0x1E53, (wchar_t)0x1E55,
+ (wchar_t)0x1E57, (wchar_t)0x1E59, (wchar_t)0x1E5B, (wchar_t)0x1E5D, (wchar_t)0x1E5F, (wchar_t)0x1E61, (wchar_t)0x1E63, (wchar_t)0x1E65, (wchar_t)0x1E67,
+ (wchar_t)0x1E69, (wchar_t)0x1E6B, (wchar_t)0x1E6D, (wchar_t)0x1E6F, (wchar_t)0x1E71, (wchar_t)0x1E73, (wchar_t)0x1E75, (wchar_t)0x1E77, (wchar_t)0x1E79,
+ (wchar_t)0x1E7B, (wchar_t)0x1E7D, (wchar_t)0x1E7F, (wchar_t)0x1E81, (wchar_t)0x1E83, (wchar_t)0x1E85, (wchar_t)0x1E87, (wchar_t)0x1E89, (wchar_t)0x1E8B,
+ (wchar_t)0x1E8D, (wchar_t)0x1E8F, (wchar_t)0x1E91, (wchar_t)0x1E93, (wchar_t)0x1E95, (wchar_t)0x1EA1, (wchar_t)0x1EA3, (wchar_t)0x1EA5, (wchar_t)0x1EA7,
+ (wchar_t)0x1EA9, (wchar_t)0x1EAB, (wchar_t)0x1EAD, (wchar_t)0x1EAF, (wchar_t)0x1EB1, (wchar_t)0x1EB3, (wchar_t)0x1EB5, (wchar_t)0x1EB7, (wchar_t)0x1EB9,
+ (wchar_t)0x1EBB, (wchar_t)0x1EBD, (wchar_t)0x1EBF, (wchar_t)0x1EC1, (wchar_t)0x1EC3, (wchar_t)0x1EC5, (wchar_t)0x1EC7, (wchar_t)0x1EC9, (wchar_t)0x1ECB,
+ (wchar_t)0x1ECD, (wchar_t)0x1ECF, (wchar_t)0x1ED1, (wchar_t)0x1ED3, (wchar_t)0x1ED5, (wchar_t)0x1ED7, (wchar_t)0x1ED9, (wchar_t)0x1EDB, (wchar_t)0x1EDD,
+ (wchar_t)0x1EDF, (wchar_t)0x1EE1, (wchar_t)0x1EE3, (wchar_t)0x1EE5, (wchar_t)0x1EE7, (wchar_t)0x1EE9, (wchar_t)0x1EEB, (wchar_t)0x1EED, (wchar_t)0x1EEF,
+ (wchar_t)0x1EF1, (wchar_t)0x1EF3, (wchar_t)0x1EF5, (wchar_t)0x1EF7, (wchar_t)0x1EF9, (wchar_t)0x1F00, (wchar_t)0x1F01, (wchar_t)0x1F02, (wchar_t)0x1F03,
+ (wchar_t)0x1F04, (wchar_t)0x1F05, (wchar_t)0x1F06, (wchar_t)0x1F07, (wchar_t)0x1F10, (wchar_t)0x1F11, (wchar_t)0x1F12, (wchar_t)0x1F13, (wchar_t)0x1F14,
+ (wchar_t)0x1F15, (wchar_t)0x1F20, (wchar_t)0x1F21, (wchar_t)0x1F22, (wchar_t)0x1F23, (wchar_t)0x1F24, (wchar_t)0x1F25, (wchar_t)0x1F26, (wchar_t)0x1F27,
+ (wchar_t)0x1F30, (wchar_t)0x1F31, (wchar_t)0x1F32, (wchar_t)0x1F33, (wchar_t)0x1F34, (wchar_t)0x1F35, (wchar_t)0x1F36, (wchar_t)0x1F37, (wchar_t)0x1F40,
+ (wchar_t)0x1F41, (wchar_t)0x1F42, (wchar_t)0x1F43, (wchar_t)0x1F44, (wchar_t)0x1F45, (wchar_t)0x1F51, (wchar_t)0x1F53, (wchar_t)0x1F55, (wchar_t)0x1F57,
+ (wchar_t)0x1F60, (wchar_t)0x1F61, (wchar_t)0x1F62, (wchar_t)0x1F63, (wchar_t)0x1F64, (wchar_t)0x1F65, (wchar_t)0x1F66, (wchar_t)0x1F67, (wchar_t)0x1F80,
+ (wchar_t)0x1F81, (wchar_t)0x1F82, (wchar_t)0x1F83, (wchar_t)0x1F84, (wchar_t)0x1F85, (wchar_t)0x1F86, (wchar_t)0x1F87, (wchar_t)0x1F90, (wchar_t)0x1F91,
+ (wchar_t)0x1F92, (wchar_t)0x1F93, (wchar_t)0x1F94, (wchar_t)0x1F95, (wchar_t)0x1F96, (wchar_t)0x1F97, (wchar_t)0x1FA0, (wchar_t)0x1FA1, (wchar_t)0x1FA2,
+ (wchar_t)0x1FA3, (wchar_t)0x1FA4, (wchar_t)0x1FA5, (wchar_t)0x1FA6, (wchar_t)0x1FA7, (wchar_t)0x1FB0, (wchar_t)0x1FB1, (wchar_t)0x1FD0, (wchar_t)0x1FD1,
+ (wchar_t)0x1FE0, (wchar_t)0x1FE1, (wchar_t)0x24D0, (wchar_t)0x24D1, (wchar_t)0x24D2, (wchar_t)0x24D3, (wchar_t)0x24D4, (wchar_t)0x24D5, (wchar_t)0x24D6,
+ (wchar_t)0x24D7, (wchar_t)0x24D8, (wchar_t)0x24D9, (wchar_t)0x24DA, (wchar_t)0x24DB, (wchar_t)0x24DC, (wchar_t)0x24DD, (wchar_t)0x24DE, (wchar_t)0x24DF,
+ (wchar_t)0x24E0, (wchar_t)0x24E1, (wchar_t)0x24E2, (wchar_t)0x24E3, (wchar_t)0x24E4, (wchar_t)0x24E5, (wchar_t)0x24E6, (wchar_t)0x24E7, (wchar_t)0x24E8,
+ (wchar_t)0x24E9, (wchar_t)0xFF41, (wchar_t)0xFF42, (wchar_t)0xFF43, (wchar_t)0xFF44, (wchar_t)0xFF45, (wchar_t)0xFF46, (wchar_t)0xFF47, (wchar_t)0xFF48,
+ (wchar_t)0xFF49, (wchar_t)0xFF4A, (wchar_t)0xFF4B, (wchar_t)0xFF4C, (wchar_t)0xFF4D, (wchar_t)0xFF4E, (wchar_t)0xFF4F, (wchar_t)0xFF50, (wchar_t)0xFF51,
+ (wchar_t)0xFF52, (wchar_t)0xFF53, (wchar_t)0xFF54, (wchar_t)0xFF55, (wchar_t)0xFF56, (wchar_t)0xFF57, (wchar_t)0xFF58, (wchar_t)0xFF59, (wchar_t)0xFF5A
+};
+
+static const wchar_t unicode_uppers[] = {
+ (wchar_t)0x0041, (wchar_t)0x0042, (wchar_t)0x0043, (wchar_t)0x0044, (wchar_t)0x0045, (wchar_t)0x0046, (wchar_t)0x0047, (wchar_t)0x0048, (wchar_t)0x0049,
+ (wchar_t)0x004A, (wchar_t)0x004B, (wchar_t)0x004C, (wchar_t)0x004D, (wchar_t)0x004E, (wchar_t)0x004F, (wchar_t)0x0050, (wchar_t)0x0051, (wchar_t)0x0052,
+ (wchar_t)0x0053, (wchar_t)0x0054, (wchar_t)0x0055, (wchar_t)0x0056, (wchar_t)0x0057, (wchar_t)0x0058, (wchar_t)0x0059, (wchar_t)0x005A, (wchar_t)0x00C0,
+ (wchar_t)0x00C1, (wchar_t)0x00C2, (wchar_t)0x00C3, (wchar_t)0x00C4, (wchar_t)0x00C5, (wchar_t)0x00C6, (wchar_t)0x00C7, (wchar_t)0x00C8, (wchar_t)0x00C9,
+ (wchar_t)0x00CA, (wchar_t)0x00CB, (wchar_t)0x00CC, (wchar_t)0x00CD, (wchar_t)0x00CE, (wchar_t)0x00CF, (wchar_t)0x00D0, (wchar_t)0x00D1, (wchar_t)0x00D2,
+ (wchar_t)0x00D3, (wchar_t)0x00D4, (wchar_t)0x00D5, (wchar_t)0x00D6, (wchar_t)0x00D8, (wchar_t)0x00D9, (wchar_t)0x00DA, (wchar_t)0x00DB, (wchar_t)0x00DC,
+ (wchar_t)0x00DD, (wchar_t)0x00DE, (wchar_t)0x0178, (wchar_t)0x0100, (wchar_t)0x0102, (wchar_t)0x0104, (wchar_t)0x0106, (wchar_t)0x0108, (wchar_t)0x010A,
+ (wchar_t)0x010C, (wchar_t)0x010E, (wchar_t)0x0110, (wchar_t)0x0112, (wchar_t)0x0114, (wchar_t)0x0116, (wchar_t)0x0118, (wchar_t)0x011A, (wchar_t)0x011C,
+ (wchar_t)0x011E, (wchar_t)0x0120, (wchar_t)0x0122, (wchar_t)0x0124, (wchar_t)0x0126, (wchar_t)0x0128, (wchar_t)0x012A, (wchar_t)0x012C, (wchar_t)0x012E,
+ (wchar_t)0x0049, (wchar_t)0x0132, (wchar_t)0x0134, (wchar_t)0x0136, (wchar_t)0x0139, (wchar_t)0x013B, (wchar_t)0x013D, (wchar_t)0x013F, (wchar_t)0x0141,
+ (wchar_t)0x0143, (wchar_t)0x0145, (wchar_t)0x0147, (wchar_t)0x014A, (wchar_t)0x014C, (wchar_t)0x014E, (wchar_t)0x0150, (wchar_t)0x0152, (wchar_t)0x0154,
+ (wchar_t)0x0156, (wchar_t)0x0158, (wchar_t)0x015A, (wchar_t)0x015C, (wchar_t)0x015E, (wchar_t)0x0160, (wchar_t)0x0162, (wchar_t)0x0164, (wchar_t)0x0166,
+ (wchar_t)0x0168, (wchar_t)0x016A, (wchar_t)0x016C, (wchar_t)0x016E, (wchar_t)0x0170, (wchar_t)0x0172, (wchar_t)0x0174, (wchar_t)0x0176, (wchar_t)0x0179,
+ (wchar_t)0x017B, (wchar_t)0x017D, (wchar_t)0x0182, (wchar_t)0x0184, (wchar_t)0x0187, (wchar_t)0x018B, (wchar_t)0x0191, (wchar_t)0x0198, (wchar_t)0x01A0,
+ (wchar_t)0x01A2, (wchar_t)0x01A4, (wchar_t)0x01A7, (wchar_t)0x01AC, (wchar_t)0x01AF, (wchar_t)0x01B3, (wchar_t)0x01B5, (wchar_t)0x01B8, (wchar_t)0x01BC,
+ (wchar_t)0x01C4, (wchar_t)0x01C7, (wchar_t)0x01CA, (wchar_t)0x01CD, (wchar_t)0x01CF, (wchar_t)0x01D1, (wchar_t)0x01D3, (wchar_t)0x01D5, (wchar_t)0x01D7,
+ (wchar_t)0x01D9, (wchar_t)0x01DB, (wchar_t)0x01DE, (wchar_t)0x01E0, (wchar_t)0x01E2, (wchar_t)0x01E4, (wchar_t)0x01E6, (wchar_t)0x01E8, (wchar_t)0x01EA,
+ (wchar_t)0x01EC, (wchar_t)0x01EE, (wchar_t)0x01F1, (wchar_t)0x01F4, (wchar_t)0x01FA, (wchar_t)0x01FC, (wchar_t)0x01FE, (wchar_t)0x0200, (wchar_t)0x0202,
+ (wchar_t)0x0204, (wchar_t)0x0206, (wchar_t)0x0208, (wchar_t)0x020A, (wchar_t)0x020C, (wchar_t)0x020E, (wchar_t)0x0210, (wchar_t)0x0212, (wchar_t)0x0214,
+ (wchar_t)0x0216, (wchar_t)0x0181, (wchar_t)0x0186, (wchar_t)0x018A, (wchar_t)0x018E, (wchar_t)0x018F, (wchar_t)0x0190, (wchar_t)0x0193, (wchar_t)0x0194,
+ (wchar_t)0x0197, (wchar_t)0x0196, (wchar_t)0x019C, (wchar_t)0x019D, (wchar_t)0x019F, (wchar_t)0x01A9, (wchar_t)0x01AE, (wchar_t)0x01B1, (wchar_t)0x01B2,
+ (wchar_t)0x01B7, (wchar_t)0x0386, (wchar_t)0x0388, (wchar_t)0x0389, (wchar_t)0x038A, (wchar_t)0x0391, (wchar_t)0x0392, (wchar_t)0x0393, (wchar_t)0x0394,
+ (wchar_t)0x0395, (wchar_t)0x0396, (wchar_t)0x0397, (wchar_t)0x0398, (wchar_t)0x0399, (wchar_t)0x039A, (wchar_t)0x039B, (wchar_t)0x039C, (wchar_t)0x039D,
+ (wchar_t)0x039E, (wchar_t)0x039F, (wchar_t)0x03A0, (wchar_t)0x03A1, (wchar_t)0x03A3, (wchar_t)0x03A4, (wchar_t)0x03A5, (wchar_t)0x03A6, (wchar_t)0x03A7,
+ (wchar_t)0x03A8, (wchar_t)0x03A9, (wchar_t)0x03AA, (wchar_t)0x03AB, (wchar_t)0x038C, (wchar_t)0x038E, (wchar_t)0x038F, (wchar_t)0x03E2, (wchar_t)0x03E4,
+ (wchar_t)0x03E6, (wchar_t)0x03E8, (wchar_t)0x03EA, (wchar_t)0x03EC, (wchar_t)0x03EE, (wchar_t)0x0410, (wchar_t)0x0411, (wchar_t)0x0412, (wchar_t)0x0413,
+ (wchar_t)0x0414, (wchar_t)0x0415, (wchar_t)0x0416, (wchar_t)0x0417, (wchar_t)0x0418, (wchar_t)0x0419, (wchar_t)0x041A, (wchar_t)0x041B, (wchar_t)0x041C,
+ (wchar_t)0x041D, (wchar_t)0x041E, (wchar_t)0x041F, (wchar_t)0x0420, (wchar_t)0x0421, (wchar_t)0x0422, (wchar_t)0x0423, (wchar_t)0x0424, (wchar_t)0x0425,
+ (wchar_t)0x0426, (wchar_t)0x0427, (wchar_t)0x0428, (wchar_t)0x0429, (wchar_t)0x042A, (wchar_t)0x042B, (wchar_t)0x042C, (wchar_t)0x042D, (wchar_t)0x042E,
+ (wchar_t)0x042F, (wchar_t)0x0401, (wchar_t)0x0402, (wchar_t)0x0403, (wchar_t)0x0404, (wchar_t)0x0405, (wchar_t)0x0406, (wchar_t)0x0407, (wchar_t)0x0408,
+ (wchar_t)0x0409, (wchar_t)0x040A, (wchar_t)0x040B, (wchar_t)0x040C, (wchar_t)0x040E, (wchar_t)0x040F, (wchar_t)0x0460, (wchar_t)0x0462, (wchar_t)0x0464,
+ (wchar_t)0x0466, (wchar_t)0x0468, (wchar_t)0x046A, (wchar_t)0x046C, (wchar_t)0x046E, (wchar_t)0x0470, (wchar_t)0x0472, (wchar_t)0x0474, (wchar_t)0x0476,
+ (wchar_t)0x0478, (wchar_t)0x047A, (wchar_t)0x047C, (wchar_t)0x047E, (wchar_t)0x0480, (wchar_t)0x0490, (wchar_t)0x0492, (wchar_t)0x0494, (wchar_t)0x0496,
+ (wchar_t)0x0498, (wchar_t)0x049A, (wchar_t)0x049C, (wchar_t)0x049E, (wchar_t)0x04A0, (wchar_t)0x04A2, (wchar_t)0x04A4, (wchar_t)0x04A6, (wchar_t)0x04A8,
+ (wchar_t)0x04AA, (wchar_t)0x04AC, (wchar_t)0x04AE, (wchar_t)0x04B0, (wchar_t)0x04B2, (wchar_t)0x04B4, (wchar_t)0x04B6, (wchar_t)0x04B8, (wchar_t)0x04BA,
+ (wchar_t)0x04BC, (wchar_t)0x04BE, (wchar_t)0x04C1, (wchar_t)0x04C3, (wchar_t)0x04C7, (wchar_t)0x04CB, (wchar_t)0x04D0, (wchar_t)0x04D2, (wchar_t)0x04D4,
+ (wchar_t)0x04D6, (wchar_t)0x04D8, (wchar_t)0x04DA, (wchar_t)0x04DC, (wchar_t)0x04DE, (wchar_t)0x04E0, (wchar_t)0x04E2, (wchar_t)0x04E4, (wchar_t)0x04E6,
+ (wchar_t)0x04E8, (wchar_t)0x04EA, (wchar_t)0x04EE, (wchar_t)0x04F0, (wchar_t)0x04F2, (wchar_t)0x04F4, (wchar_t)0x04F8, (wchar_t)0x0531, (wchar_t)0x0532,
+ (wchar_t)0x0533, (wchar_t)0x0534, (wchar_t)0x0535, (wchar_t)0x0536, (wchar_t)0x0537, (wchar_t)0x0538, (wchar_t)0x0539, (wchar_t)0x053A, (wchar_t)0x053B,
+ (wchar_t)0x053C, (wchar_t)0x053D, (wchar_t)0x053E, (wchar_t)0x053F, (wchar_t)0x0540, (wchar_t)0x0541, (wchar_t)0x0542, (wchar_t)0x0543, (wchar_t)0x0544,
+ (wchar_t)0x0545, (wchar_t)0x0546, (wchar_t)0x0547, (wchar_t)0x0548, (wchar_t)0x0549, (wchar_t)0x054A, (wchar_t)0x054B, (wchar_t)0x054C, (wchar_t)0x054D,
+ (wchar_t)0x054E, (wchar_t)0x054F, (wchar_t)0x0550, (wchar_t)0x0551, (wchar_t)0x0552, (wchar_t)0x0553, (wchar_t)0x0554, (wchar_t)0x0555, (wchar_t)0x0556,
+ (wchar_t)0x10A0, (wchar_t)0x10A1, (wchar_t)0x10A2, (wchar_t)0x10A3, (wchar_t)0x10A4, (wchar_t)0x10A5, (wchar_t)0x10A6, (wchar_t)0x10A7, (wchar_t)0x10A8,
+ (wchar_t)0x10A9, (wchar_t)0x10AA, (wchar_t)0x10AB, (wchar_t)0x10AC, (wchar_t)0x10AD, (wchar_t)0x10AE, (wchar_t)0x10AF, (wchar_t)0x10B0, (wchar_t)0x10B1,
+ (wchar_t)0x10B2, (wchar_t)0x10B3, (wchar_t)0x10B4, (wchar_t)0x10B5, (wchar_t)0x10B6, (wchar_t)0x10B7, (wchar_t)0x10B8, (wchar_t)0x10B9, (wchar_t)0x10BA,
+ (wchar_t)0x10BB, (wchar_t)0x10BC, (wchar_t)0x10BD, (wchar_t)0x10BE, (wchar_t)0x10BF, (wchar_t)0x10C0, (wchar_t)0x10C1, (wchar_t)0x10C2, (wchar_t)0x10C3,
+ (wchar_t)0x10C4, (wchar_t)0x10C5, (wchar_t)0x1E00, (wchar_t)0x1E02, (wchar_t)0x1E04, (wchar_t)0x1E06, (wchar_t)0x1E08, (wchar_t)0x1E0A, (wchar_t)0x1E0C,
+ (wchar_t)0x1E0E, (wchar_t)0x1E10, (wchar_t)0x1E12, (wchar_t)0x1E14, (wchar_t)0x1E16, (wchar_t)0x1E18, (wchar_t)0x1E1A, (wchar_t)0x1E1C, (wchar_t)0x1E1E,
+ (wchar_t)0x1E20, (wchar_t)0x1E22, (wchar_t)0x1E24, (wchar_t)0x1E26, (wchar_t)0x1E28, (wchar_t)0x1E2A, (wchar_t)0x1E2C, (wchar_t)0x1E2E, (wchar_t)0x1E30,
+ (wchar_t)0x1E32, (wchar_t)0x1E34, (wchar_t)0x1E36, (wchar_t)0x1E38, (wchar_t)0x1E3A, (wchar_t)0x1E3C, (wchar_t)0x1E3E, (wchar_t)0x1E40, (wchar_t)0x1E42,
+ (wchar_t)0x1E44, (wchar_t)0x1E46, (wchar_t)0x1E48, (wchar_t)0x1E4A, (wchar_t)0x1E4C, (wchar_t)0x1E4E, (wchar_t)0x1E50, (wchar_t)0x1E52, (wchar_t)0x1E54,
+ (wchar_t)0x1E56, (wchar_t)0x1E58, (wchar_t)0x1E5A, (wchar_t)0x1E5C, (wchar_t)0x1E5E, (wchar_t)0x1E60, (wchar_t)0x1E62, (wchar_t)0x1E64, (wchar_t)0x1E66,
+ (wchar_t)0x1E68, (wchar_t)0x1E6A, (wchar_t)0x1E6C, (wchar_t)0x1E6E, (wchar_t)0x1E70, (wchar_t)0x1E72, (wchar_t)0x1E74, (wchar_t)0x1E76, (wchar_t)0x1E78,
+ (wchar_t)0x1E7A, (wchar_t)0x1E7C, (wchar_t)0x1E7E, (wchar_t)0x1E80, (wchar_t)0x1E82, (wchar_t)0x1E84, (wchar_t)0x1E86, (wchar_t)0x1E88, (wchar_t)0x1E8A,
+ (wchar_t)0x1E8C, (wchar_t)0x1E8E, (wchar_t)0x1E90, (wchar_t)0x1E92, (wchar_t)0x1E94, (wchar_t)0x1EA0, (wchar_t)0x1EA2, (wchar_t)0x1EA4, (wchar_t)0x1EA6,
+ (wchar_t)0x1EA8, (wchar_t)0x1EAA, (wchar_t)0x1EAC, (wchar_t)0x1EAE, (wchar_t)0x1EB0, (wchar_t)0x1EB2, (wchar_t)0x1EB4, (wchar_t)0x1EB6, (wchar_t)0x1EB8,
+ (wchar_t)0x1EBA, (wchar_t)0x1EBC, (wchar_t)0x1EBE, (wchar_t)0x1EC0, (wchar_t)0x1EC2, (wchar_t)0x1EC4, (wchar_t)0x1EC6, (wchar_t)0x1EC8, (wchar_t)0x1ECA,
+ (wchar_t)0x1ECC, (wchar_t)0x1ECE, (wchar_t)0x1ED0, (wchar_t)0x1ED2, (wchar_t)0x1ED4, (wchar_t)0x1ED6, (wchar_t)0x1ED8, (wchar_t)0x1EDA, (wchar_t)0x1EDC,
+ (wchar_t)0x1EDE, (wchar_t)0x1EE0, (wchar_t)0x1EE2, (wchar_t)0x1EE4, (wchar_t)0x1EE6, (wchar_t)0x1EE8, (wchar_t)0x1EEA, (wchar_t)0x1EEC, (wchar_t)0x1EEE,
+ (wchar_t)0x1EF0, (wchar_t)0x1EF2, (wchar_t)0x1EF4, (wchar_t)0x1EF6, (wchar_t)0x1EF8, (wchar_t)0x1F08, (wchar_t)0x1F09, (wchar_t)0x1F0A, (wchar_t)0x1F0B,
+ (wchar_t)0x1F0C, (wchar_t)0x1F0D, (wchar_t)0x1F0E, (wchar_t)0x1F0F, (wchar_t)0x1F18, (wchar_t)0x1F19, (wchar_t)0x1F1A, (wchar_t)0x1F1B, (wchar_t)0x1F1C,
+ (wchar_t)0x1F1D, (wchar_t)0x1F28, (wchar_t)0x1F29, (wchar_t)0x1F2A, (wchar_t)0x1F2B, (wchar_t)0x1F2C, (wchar_t)0x1F2D, (wchar_t)0x1F2E, (wchar_t)0x1F2F,
+ (wchar_t)0x1F38, (wchar_t)0x1F39, (wchar_t)0x1F3A, (wchar_t)0x1F3B, (wchar_t)0x1F3C, (wchar_t)0x1F3D, (wchar_t)0x1F3E, (wchar_t)0x1F3F, (wchar_t)0x1F48,
+ (wchar_t)0x1F49, (wchar_t)0x1F4A, (wchar_t)0x1F4B, (wchar_t)0x1F4C, (wchar_t)0x1F4D, (wchar_t)0x1F59, (wchar_t)0x1F5B, (wchar_t)0x1F5D, (wchar_t)0x1F5F,
+ (wchar_t)0x1F68, (wchar_t)0x1F69, (wchar_t)0x1F6A, (wchar_t)0x1F6B, (wchar_t)0x1F6C, (wchar_t)0x1F6D, (wchar_t)0x1F6E, (wchar_t)0x1F6F, (wchar_t)0x1F88,
+ (wchar_t)0x1F89, (wchar_t)0x1F8A, (wchar_t)0x1F8B, (wchar_t)0x1F8C, (wchar_t)0x1F8D, (wchar_t)0x1F8E, (wchar_t)0x1F8F, (wchar_t)0x1F98, (wchar_t)0x1F99,
+ (wchar_t)0x1F9A, (wchar_t)0x1F9B, (wchar_t)0x1F9C, (wchar_t)0x1F9D, (wchar_t)0x1F9E, (wchar_t)0x1F9F, (wchar_t)0x1FA8, (wchar_t)0x1FA9, (wchar_t)0x1FAA,
+ (wchar_t)0x1FAB, (wchar_t)0x1FAC, (wchar_t)0x1FAD, (wchar_t)0x1FAE, (wchar_t)0x1FAF, (wchar_t)0x1FB8, (wchar_t)0x1FB9, (wchar_t)0x1FD8, (wchar_t)0x1FD9,
+ (wchar_t)0x1FE8, (wchar_t)0x1FE9, (wchar_t)0x24B6, (wchar_t)0x24B7, (wchar_t)0x24B8, (wchar_t)0x24B9, (wchar_t)0x24BA, (wchar_t)0x24BB, (wchar_t)0x24BC,
+ (wchar_t)0x24BD, (wchar_t)0x24BE, (wchar_t)0x24BF, (wchar_t)0x24C0, (wchar_t)0x24C1, (wchar_t)0x24C2, (wchar_t)0x24C3, (wchar_t)0x24C4, (wchar_t)0x24C5,
+ (wchar_t)0x24C6, (wchar_t)0x24C7, (wchar_t)0x24C8, (wchar_t)0x24C9, (wchar_t)0x24CA, (wchar_t)0x24CB, (wchar_t)0x24CC, (wchar_t)0x24CD, (wchar_t)0x24CE,
+ (wchar_t)0x24CF, (wchar_t)0xFF21, (wchar_t)0xFF22, (wchar_t)0xFF23, (wchar_t)0xFF24, (wchar_t)0xFF25, (wchar_t)0xFF26, (wchar_t)0xFF27, (wchar_t)0xFF28,
+ (wchar_t)0xFF29, (wchar_t)0xFF2A, (wchar_t)0xFF2B, (wchar_t)0xFF2C, (wchar_t)0xFF2D, (wchar_t)0xFF2E, (wchar_t)0xFF2F, (wchar_t)0xFF30, (wchar_t)0xFF31,
+ (wchar_t)0xFF32, (wchar_t)0xFF33, (wchar_t)0xFF34, (wchar_t)0xFF35, (wchar_t)0xFF36, (wchar_t)0xFF37, (wchar_t)0xFF38, (wchar_t)0xFF39, (wchar_t)0xFF3A
+};
+
+string StringUtils::Format(const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ string str = FormatV(fmt, args);
+ va_end(args);
+
+ return str;
+}
+
+string StringUtils::FormatV(const char *fmt, va_list args)
+{
+ if (!fmt || !fmt[0])
+ return "";
+
+ int size = FORMAT_BLOCK_SIZE;
+ va_list argCopy;
+
+ while (1)
+ {
+ char *cstr = reinterpret_cast<char*>(malloc(sizeof(char) * size));
+ if (!cstr)
+ return "";
+
+ va_copy(argCopy, args);
+ int nActual = vsnprintf(cstr, size, fmt, argCopy);
+ va_end(argCopy);
+
+ if (nActual > -1 && nActual < size) // We got a valid result
+ {
+ std::string str(cstr, nActual);
+ free(cstr);
+ return str;
+ }
+ free(cstr);
+#ifndef TARGET_WINDOWS
+ if (nActual > -1) // Exactly what we will need (glibc 2.1)
+ size = nActual + 1;
+ else // Let's try to double the size (glibc 2.0)
+ size *= 2;
+#else // TARGET_WINDOWS
+ va_copy(argCopy, args);
+ size = _vscprintf(fmt, argCopy);
+ va_end(argCopy);
+ if (size < 0)
+ return "";
+ else
+ size++; // increment for null-termination
+#endif // TARGET_WINDOWS
+ }
+
+ return ""; // unreachable
+}
+
+wstring StringUtils::Format(const wchar_t *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ wstring str = FormatV(fmt, args);
+ va_end(args);
+
+ return str;
+}
+
+wstring StringUtils::FormatV(const wchar_t *fmt, va_list args)
+{
+ if (!fmt || !fmt[0])
+ return L"";
+
+ int size = FORMAT_BLOCK_SIZE;
+ va_list argCopy;
+
+ while (1)
+ {
+ wchar_t *cstr = reinterpret_cast<wchar_t*>(malloc(sizeof(wchar_t) * size));
+ if (!cstr)
+ return L"";
+
+ va_copy(argCopy, args);
+ int nActual = vswprintf(cstr, size, fmt, argCopy);
+ va_end(argCopy);
+
+ if (nActual > -1 && nActual < size) // We got a valid result
+ {
+ std::wstring str(cstr, nActual);
+ free(cstr);
+ return str;
+ }
+ free(cstr);
+
+#ifndef TARGET_WINDOWS
+ if (nActual > -1) // Exactly what we will need (glibc 2.1)
+ size = nActual + 1;
+ else // Let's try to double the size (glibc 2.0)
+ size *= 2;
+#else // TARGET_WINDOWS
+ va_copy(argCopy, args);
+ size = _vscwprintf(fmt, argCopy);
+ va_end(argCopy);
+ if (size < 0)
+ return L"";
+ else
+ size++; // increment for null-termination
+#endif // TARGET_WINDOWS
+ }
+
+ return L"";
+}
+
+int compareWchar (const void* a, const void* b)
+{
+ if (*(wchar_t*)a < *(wchar_t*)b)
+ return -1;
+ else if (*(wchar_t*)a > *(wchar_t*)b)
+ return 1;
+ return 0;
+}
+
+wchar_t tolowerUnicode(const wchar_t& c)
+{
+ wchar_t* p = (wchar_t*) bsearch (&c, unicode_uppers, sizeof(unicode_uppers) / sizeof(wchar_t), sizeof(wchar_t), compareWchar);
+ if (p)
+ return *(unicode_lowers + (p - unicode_uppers));
+
+ return c;
+}
+
+wchar_t toupperUnicode(const wchar_t& c)
+{
+ wchar_t* p = (wchar_t*) bsearch (&c, unicode_lowers, sizeof(unicode_lowers) / sizeof(wchar_t), sizeof(wchar_t), compareWchar);
+ if (p)
+ return *(unicode_uppers + (p - unicode_lowers));
+
+ return c;
+}
+
+void StringUtils::ToUpper(string &str)
+{
+ transform(str.begin(), str.end(), str.begin(), ::toupper);
+}
+
+void StringUtils::ToUpper(wstring &str)
+{
+ transform(str.begin(), str.end(), str.begin(), toupperUnicode);
+}
+
+void StringUtils::ToLower(string &str)
+{
+ transform(str.begin(), str.end(), str.begin(), ::tolower);
+}
+
+void StringUtils::ToLower(wstring &str)
+{
+ transform(str.begin(), str.end(), str.begin(), tolowerUnicode);
+}
+
+bool StringUtils::EqualsNoCase(const std::string &str1, const std::string &str2)
+{
+ return EqualsNoCase(str1.c_str(), str2.c_str());
+}
+
+bool StringUtils::EqualsNoCase(const std::string &str1, const char *s2)
+{
+ return EqualsNoCase(str1.c_str(), s2);
+}
+
+bool StringUtils::EqualsNoCase(const char *s1, const char *s2)
+{
+ char c2; // we need only one char outside the loop
+ do
+ {
+ const char c1 = *s1++; // const local variable should help compiler to optimize
+ c2 = *s2++;
+ if (c1 != c2 && ::tolower(c1) != ::tolower(c2)) // This includes the possibility that one of the characters is the null-terminator, which implies a string mismatch.
+ return false;
+ } while (c2 != '\0'); // At this point, we know c1 == c2, so there's no need to test them both.
+ return true;
+}
+
+int StringUtils::CompareNoCase(const std::string &str1, const std::string &str2)
+{
+ return CompareNoCase(str1.c_str(), str2.c_str());
+}
+
+int StringUtils::CompareNoCase(const char *s1, const char *s2)
+{
+ char c2; // we need only one char outside the loop
+ do
+ {
+ const char c1 = *s1++; // const local variable should help compiler to optimize
+ c2 = *s2++;
+ if (c1 != c2 && ::tolower(c1) != ::tolower(c2)) // This includes the possibility that one of the characters is the null-terminator, which implies a string mismatch.
+ return ::tolower(c1) - ::tolower(c2);
+ } while (c2 != '\0'); // At this point, we know c1 == c2, so there's no need to test them both.
+ return 0;
+}
+
+string StringUtils::Left(const string &str, size_t count)
+{
+ count = max((size_t)0, min(count, str.size()));
+ return str.substr(0, count);
+}
+
+string StringUtils::Mid(const string &str, size_t first, size_t count /* = string::npos */)
+{
+ if (first + count > str.size())
+ count = str.size() - first;
+
+ if (first > str.size())
+ return string();
+
+ ASSERT(first + count <= str.size());
+
+ return str.substr(first, count);
+}
+
+string StringUtils::Right(const string &str, size_t count)
+{
+ count = max((size_t)0, min(count, str.size()));
+ return str.substr(str.size() - count);
+}
+
+std::string& StringUtils::Trim(std::string &str)
+{
+ TrimLeft(str);
+ return TrimRight(str);
+}
+
+std::string& StringUtils::Trim(std::string &str, const char* const chars)
+{
+ TrimLeft(str, chars);
+ return TrimRight(str, chars);
+}
+
+// hack to check only first byte of UTF-8 character
+// without this hack "TrimX" functions failed on Win32 and OS X with UTF-8 strings
+static int isspace_c(char c)
+{
+ return (c & 0x80) == 0 && ::isspace(c);
+}
+
+std::string& StringUtils::TrimLeft(std::string &str)
+{
+ str.erase(str.begin(), ::find_if(str.begin(), str.end(), ::not1(::ptr_fun(isspace_c))));
+ return str;
+}
+
+std::string& StringUtils::TrimLeft(std::string &str, const char* const chars)
+{
+ size_t nidx = str.find_first_not_of(chars);
+ str.erase(0, nidx);
+ return str;
+}
+
+std::string& StringUtils::TrimRight(std::string &str)
+{
+ str.erase(::find_if(str.rbegin(), str.rend(), ::not1(::ptr_fun(isspace_c))).base(), str.end());
+ return str;
+}
+
+std::string& StringUtils::TrimRight(std::string &str, const char* const chars)
+{
+ size_t nidx = str.find_last_not_of(chars);
+ str.erase(str.npos == nidx ? 0 : ++nidx);
+ return str;
+}
+
+std::string& StringUtils::RemoveDuplicatedSpacesAndTabs(std::string& str)
+{
+ std::string::iterator it = str.begin();
+ bool onSpace = false;
+ while(it != str.end())
+ {
+ if (*it == '\t')
+ *it = ' ';
+
+ if (*it == ' ')
+ {
+ if (onSpace)
+ {
+ it = str.erase(it);
+ continue;
+ }
+ else
+ onSpace = true;
+ }
+ else
+ onSpace = false;
+
+ ++it;
+ }
+ return str;
+}
+
+int StringUtils::Replace(string &str, char oldChar, char newChar)
+{
+ int replacedChars = 0;
+ for (string::iterator it = str.begin(); it != str.end(); ++it)
+ {
+ if (*it == oldChar)
+ {
+ *it = newChar;
+ replacedChars++;
+ }
+ }
+
+ return replacedChars;
+}
+
+int StringUtils::Replace(std::string &str, const std::string &oldStr, const std::string &newStr)
+{
+ if (oldStr.empty())
+ return 0;
+
+ int replacedChars = 0;
+ size_t index = 0;
+
+ while (index < str.size() && (index = str.find(oldStr, index)) != string::npos)
+ {
+ str.replace(index, oldStr.size(), newStr);
+ index += newStr.size();
+ replacedChars++;
+ }
+
+ return replacedChars;
+}
+
+int StringUtils::Replace(std::wstring &str, const std::wstring &oldStr, const std::wstring &newStr)
+{
+ if (oldStr.empty())
+ return 0;
+
+ int replacedChars = 0;
+ size_t index = 0;
+
+ while (index < str.size() && (index = str.find(oldStr, index)) != string::npos)
+ {
+ str.replace(index, oldStr.size(), newStr);
+ index += newStr.size();
+ replacedChars++;
+ }
+
+ return replacedChars;
+}
+
+bool StringUtils::StartsWith(const std::string &str1, const std::string &str2)
+{
+ return str1.compare(0, str2.size(), str2) == 0;
+}
+
+bool StringUtils::StartsWith(const std::string &str1, const char *s2)
+{
+ return StartsWith(str1.c_str(), s2);
+}
+
+bool StringUtils::StartsWith(const char *s1, const char *s2)
+{
+ while (*s2 != '\0')
+ {
+ if (*s1 != *s2)
+ return false;
+ s1++;
+ s2++;
+ }
+ return true;
+}
+
+bool StringUtils::StartsWithNoCase(const std::string &str1, const std::string &str2)
+{
+ return StartsWithNoCase(str1.c_str(), str2.c_str());
+}
+
+bool StringUtils::StartsWithNoCase(const std::string &str1, const char *s2)
+{
+ return StartsWithNoCase(str1.c_str(), s2);
+}
+
+bool StringUtils::StartsWithNoCase(const char *s1, const char *s2)
+{
+ while (*s2 != '\0')
+ {
+ if (::tolower(*s1) != ::tolower(*s2))
+ return false;
+ s1++;
+ s2++;
+ }
+ return true;
+}
+
+bool StringUtils::EndsWith(const std::string &str1, const std::string &str2)
+{
+ if (str1.size() < str2.size())
+ return false;
+ return str1.compare(str1.size() - str2.size(), str2.size(), str2) == 0;
+}
+
+bool StringUtils::EndsWith(const std::string &str1, const char *s2)
+{
+ size_t len2 = strlen(s2);
+ if (str1.size() < len2)
+ return false;
+ return str1.compare(str1.size() - len2, len2, s2) == 0;
+}
+
+bool StringUtils::EndsWithNoCase(const std::string &str1, const std::string &str2)
+{
+ if (str1.size() < str2.size())
+ return false;
+ const char *s1 = str1.c_str() + str1.size() - str2.size();
+ const char *s2 = str2.c_str();
+ while (*s2 != '\0')
+ {
+ if (::tolower(*s1) != ::tolower(*s2))
+ return false;
+ s1++;
+ s2++;
+ }
+ return true;
+}
+
+bool StringUtils::EndsWithNoCase(const std::string &str1, const char *s2)
+{
+ size_t len2 = strlen(s2);
+ if (str1.size() < len2)
+ return false;
+ const char *s1 = str1.c_str() + str1.size() - len2;
+ while (*s2 != '\0')
+ {
+ if (::tolower(*s1) != ::tolower(*s2))
+ return false;
+ s1++;
+ s2++;
+ }
+ return true;
+}
+
+std::string StringUtils::Join(const vector<string> &strings, const std::string& delimiter)
+{
+ std::string result;
+ for(vector<string>::const_iterator it = strings.begin(); it != strings.end(); it++ )
+ result += (*it) + delimiter;
+
+ if (!result.empty())
+ result.erase(result.size() - delimiter.size());
+ return result;
+}
+
+vector<string> StringUtils::Split(const std::string& input, const std::string& delimiter, unsigned int iMaxStrings /* = 0 */)
+{
+ std::vector<std::string> results;
+ if (input.empty())
+ return results;
+ if (delimiter.empty())
+ {
+ results.push_back(input);
+ return results;
+ }
+
+ const size_t delimLen = delimiter.length();
+ size_t nextDelim;
+ size_t textPos = 0;
+ do
+ {
+ if (--iMaxStrings == 0)
+ {
+ results.push_back(input.substr(textPos));
+ break;
+ }
+ nextDelim = input.find(delimiter, textPos);
+ results.push_back(input.substr(textPos, nextDelim - textPos));
+ textPos = nextDelim + delimLen;
+ } while (nextDelim != std::string::npos);
+
+ return results;
+}
+
+std::vector<std::string> StringUtils::Split(const std::string& input, const char delimiter, size_t iMaxStrings /*= 0*/)
+{
+ std::vector<std::string> results;
+ if (input.empty())
+ return results;
+
+ size_t nextDelim;
+ size_t textPos = 0;
+ do
+ {
+ if (--iMaxStrings == 0)
+ {
+ results.push_back(input.substr(textPos));
+ break;
+ }
+ nextDelim = input.find(delimiter, textPos);
+ results.push_back(input.substr(textPos, nextDelim - textPos));
+ textPos = nextDelim + 1;
+ } while (nextDelim != std::string::npos);
+
+ return results;
+}
+
+
+// returns the number of occurrences of strFind in strInput.
+int StringUtils::FindNumber(const CStdString& strInput, const CStdString &strFind)
+{
+ size_t pos = strInput.find(strFind, 0);
+ int numfound = 0;
+ while (pos != std::string::npos)
+ {
+ numfound++;
+ pos = strInput.find(strFind, pos + 1);
+ }
+ return numfound;
+}
+
+// Compares separately the numeric and alphabetic parts of a string.
+// returns negative if left < right, positive if left > right
+// and 0 if they are identical (essentially calculates left - right)
+int64_t StringUtils::AlphaNumericCompare(const wchar_t *left, const wchar_t *right)
+{
+ wchar_t *l = (wchar_t *)left;
+ wchar_t *r = (wchar_t *)right;
+ wchar_t *ld, *rd;
+ wchar_t lc, rc;
+ int64_t lnum, rnum;
+ const collate<wchar_t>& coll = use_facet< collate<wchar_t> >( locale() );
+ int cmp_res = 0;
+ while (*l != 0 && *r != 0)
+ {
+ // check if we have a numerical value
+ if (*l >= L'0' && *l <= L'9' && *r >= L'0' && *r <= L'9')
+ {
+ ld = l;
+ lnum = 0;
+ while (*ld >= L'0' && *ld <= L'9' && ld < l + 15)
+ { // compare only up to 15 digits
+ lnum *= 10;
+ lnum += *ld++ - '0';
+ }
+ rd = r;
+ rnum = 0;
+ while (*rd >= L'0' && *rd <= L'9' && rd < r + 15)
+ { // compare only up to 15 digits
+ rnum *= 10;
+ rnum += *rd++ - L'0';
+ }
+ // do we have numbers?
+ if (lnum != rnum)
+ { // yes - and they're different!
+ return lnum - rnum;
+ }
+ l = ld;
+ r = rd;
+ continue;
+ }
+ // do case less comparison
+ lc = *l;
+ if (lc >= L'A' && lc <= L'Z')
+ lc += L'a'-L'A';
+ rc = *r;
+ if (rc >= L'A' && rc <= L'Z')
+ rc += L'a'- L'A';
+
+ // ok, do a normal comparison, taking current locale into account. Add special case stuff (eg '(' characters)) in here later
+ if ((cmp_res = coll.compare(&lc, &lc + 1, &rc, &rc + 1)) != 0)
+ {
+ return cmp_res;
+ }
+ l++; r++;
+ }
+ if (*r)
+ { // r is longer
+ return -1;
+ }
+ else if (*l)
+ { // l is longer
+ return 1;
+ }
+ return 0; // files are the same
+}
+
+int StringUtils::DateStringToYYYYMMDD(const CStdString &dateString)
+{
+ vector<string> days = StringUtils::Split(dateString, '-');
+ if (days.size() == 1)
+ return atoi(days[0].c_str());
+ else if (days.size() == 2)
+ return atoi(days[0].c_str())*100+atoi(days[1].c_str());
+ else if (days.size() == 3)
+ return atoi(days[0].c_str())*10000+atoi(days[1].c_str())*100+atoi(days[2].c_str());
+ else
+ return -1;
+}
+
+long StringUtils::TimeStringToSeconds(const CStdString &timeString)
+{
+ CStdString strCopy(timeString);
+ StringUtils::Trim(strCopy);
+ if(StringUtils::EndsWithNoCase(strCopy, " min"))
+ {
+ // this is imdb format of "XXX min"
+ return 60 * atoi(strCopy.c_str());
+ }
+ else
+ {
+ vector<string> secs = StringUtils::Split(strCopy, ':');
+ int timeInSecs = 0;
+ for (unsigned int i = 0; i < 3 && i < secs.size(); i++)
+ {
+ timeInSecs *= 60;
+ timeInSecs += atoi(secs[i].c_str());
+ }
+ return timeInSecs;
+ }
+}
+
+CStdString StringUtils::SecondsToTimeString(long lSeconds, TIME_FORMAT format)
+{
+ int hh = lSeconds / 3600;
+ lSeconds = lSeconds % 3600;
+ int mm = lSeconds / 60;
+ int ss = lSeconds % 60;
+
+ if (format == TIME_FORMAT_GUESS)
+ format = (hh >= 1) ? TIME_FORMAT_HH_MM_SS : TIME_FORMAT_MM_SS;
+ CStdString strHMS;
+ if (format & TIME_FORMAT_HH)
+ strHMS += StringUtils::Format("%02.2i", hh);
+ else if (format & TIME_FORMAT_H)
+ strHMS += StringUtils::Format("%i", hh);
+ if (format & TIME_FORMAT_MM)
+ strHMS += StringUtils::Format(strHMS.empty() ? "%02.2i" : ":%02.2i", mm);
+ if (format & TIME_FORMAT_SS)
+ strHMS += StringUtils::Format(strHMS.empty() ? "%02.2i" : ":%02.2i", ss);
+ return strHMS;
+}
+
+bool StringUtils::IsNaturalNumber(const CStdString& str)
+{
+ size_t i = 0, n = 0;
+ // allow whitespace,digits,whitespace
+ while (i < str.size() && isspace((unsigned char) str[i]))
+ i++;
+ while (i < str.size() && isdigit((unsigned char) str[i]))
+ {
+ i++; n++;
+ }
+ while (i < str.size() && isspace((unsigned char) str[i]))
+ i++;
+ return i == str.size() && n > 0;
+}
+
+bool StringUtils::IsInteger(const CStdString& str)
+{
+ size_t i = 0, n = 0;
+ // allow whitespace,-,digits,whitespace
+ while (i < str.size() && isspace((unsigned char) str[i]))
+ i++;
+ if (i < str.size() && str[i] == '-')
+ i++;
+ while (i < str.size() && isdigit((unsigned char) str[i]))
+ {
+ i++; n++;
+ }
+ while (i < str.size() && isspace((unsigned char) str[i]))
+ i++;
+ return i == str.size() && n > 0;
+}
+
+int StringUtils::asciidigitvalue(char chr)
+{
+ if (!isasciidigit(chr))
+ return -1;
+
+ return chr - '0';
+}
+
+int StringUtils::asciixdigitvalue(char chr)
+{
+ int v = asciidigitvalue(chr);
+ if (v >= 0)
+ return v;
+ if (chr >= 'a' && chr <= 'f')
+ return chr - 'a' + 10;
+ if (chr >= 'A' && chr <= 'F')
+ return chr - 'A' + 10;
+
+ return -1;
+}
+
+
+void StringUtils::RemoveCRLF(std::string& strLine)
+{
+ StringUtils::TrimRight(strLine, "\n\r");
+}
+
+CStdString StringUtils::SizeToString(int64_t size)
+{
+ CStdString strLabel;
+ const char prefixes[] = {' ','k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'};
+ unsigned int i = 0;
+ double s = (double)size;
+ while (i < ARRAY_SIZE(prefixes) && s >= 1000.0)
+ {
+ s /= 1024.0;
+ i++;
+ }
+
+ if (!i)
+ strLabel = StringUtils::Format("%.0lf %cB ", s, prefixes[i]);
+ else if (s >= 100.0)
+ strLabel = StringUtils::Format("%.1lf %cB", s, prefixes[i]);
+ else
+ strLabel = StringUtils::Format("%.2lf %cB", s, prefixes[i]);
+
+ return strLabel;
+}
+
+// return -1 if not, else return the utf8 char length.
+int IsUTF8Letter(const unsigned char *str)
+{
+ // reference:
+ // unicode -> utf8 table: http://www.utf8-chartable.de/
+ // latin characters in unicode: http://en.wikipedia.org/wiki/Latin_characters_in_Unicode
+ unsigned char ch = str[0];
+ if (!ch)
+ return -1;
+ if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
+ return 1;
+ if (!(ch & 0x80))
+ return -1;
+ unsigned char ch2 = str[1];
+ if (!ch2)
+ return -1;
+ // check latin 1 letter table: http://en.wikipedia.org/wiki/C1_Controls_and_Latin-1_Supplement
+ if (ch == 0xC3 && ch2 >= 0x80 && ch2 <= 0xBF && ch2 != 0x97 && ch2 != 0xB7)
+ return 2;
+ // check latin extended A table: http://en.wikipedia.org/wiki/Latin_Extended-A
+ if (ch >= 0xC4 && ch <= 0xC7 && ch2 >= 0x80 && ch2 <= 0xBF)
+ return 2;
+ // check latin extended B table: http://en.wikipedia.org/wiki/Latin_Extended-B
+ // and International Phonetic Alphabet: http://en.wikipedia.org/wiki/IPA_Extensions_(Unicode_block)
+ if (((ch == 0xC8 || ch == 0xC9) && ch2 >= 0x80 && ch2 <= 0xBF)
+ || (ch == 0xCA && ch2 >= 0x80 && ch2 <= 0xAF))
+ return 2;
+ return -1;
+}
+
+size_t StringUtils::FindWords(const char *str, const char *wordLowerCase)
+{
+ // NOTE: This assumes word is lowercase!
+ unsigned char *s = (unsigned char *)str;
+ do
+ {
+ // start with a compare
+ unsigned char *c = s;
+ unsigned char *w = (unsigned char *)wordLowerCase;
+ bool same = true;
+ while (same && *c && *w)
+ {
+ unsigned char lc = *c++;
+ if (lc >= 'A' && lc <= 'Z')
+ lc += 'a'-'A';
+
+ if (lc != *w++) // different
+ same = false;
+ }
+ if (same && *w == 0) // only the same if word has been exhausted
+ return (const char *)s - str;
+
+ // otherwise, skip current word (composed by latin letters) or number
+ int l;
+ if (*s >= '0' && *s <= '9')
+ {
+ ++s;
+ while (*s >= '0' && *s <= '9') ++s;
+ }
+ else if ((l = IsUTF8Letter(s)) > 0)
+ {
+ s += l;
+ while ((l = IsUTF8Letter(s)) > 0) s += l;
+ }
+ else
+ ++s;
+ while (*s && *s == ' ') s++;
+
+ // and repeat until we're done
+ } while (*s);
+
+ return CStdString::npos;
+}
+
+// assumes it is called from after the first open bracket is found
+int StringUtils::FindEndBracket(const CStdString &str, char opener, char closer, int startPos)
+{
+ int blocks = 1;
+ for (unsigned int i = startPos; i < str.size(); i++)
+ {
+ if (str[i] == opener)
+ blocks++;
+ else if (str[i] == closer)
+ {
+ blocks--;
+ if (!blocks)
+ return i;
+ }
+ }
+
+ return (int)CStdString::npos;
+}
+
+void StringUtils::WordToDigits(std::string &word)
+{
+ static const char word_to_letter[] = "22233344455566677778889999";
+ StringUtils::ToLower(word);
+ for (unsigned int i = 0; i < word.size(); ++i)
+ { // NB: This assumes ascii, which probably needs extending at some point.
+ char letter = word[i];
+ if ((letter >= 'a' && letter <= 'z')) // assume contiguous letter range
+ {
+ word[i] = word_to_letter[letter-'a'];
+ }
+ else if (letter < '0' || letter > '9') // We want to keep 0-9!
+ {
+ word[i] = ' '; // replace everything else with a space
+ }
+ }
+}
+
+CStdString StringUtils::CreateUUID()
+{
+ /* This function generate a DCE 1.1, ISO/IEC 11578:1996 and IETF RFC-4122
+ * Version 4 conform local unique UUID based upon random number generation.
+ */
+ char UuidStrTmp[40];
+ char *pUuidStr = UuidStrTmp;
+ int i;
+
+ static bool m_uuidInitialized = false;
+ if (!m_uuidInitialized)
+ {
+ /* use current time as the seed for rand()*/
+ srand(time(NULL));
+ m_uuidInitialized = true;
+ }
+
+ /*Data1 - 8 characters.*/
+ for(i = 0; i < 8; i++, pUuidStr++)
+ ((*pUuidStr = (rand() % 16)) < 10) ? *pUuidStr += 48 : *pUuidStr += 55;
+
+ /*Data2 - 4 characters.*/
+ *pUuidStr++ = '-';
+ for(i = 0; i < 4; i++, pUuidStr++)
+ ((*pUuidStr = (rand() % 16)) < 10) ? *pUuidStr += 48 : *pUuidStr += 55;
+
+ /*Data3 - 4 characters.*/
+ *pUuidStr++ = '-';
+ for(i = 0; i < 4; i++, pUuidStr++)
+ ((*pUuidStr = (rand() % 16)) < 10) ? *pUuidStr += 48 : *pUuidStr += 55;
+
+ /*Data4 - 4 characters.*/
+ *pUuidStr++ = '-';
+ for(i = 0; i < 4; i++, pUuidStr++)
+ ((*pUuidStr = (rand() % 16)) < 10) ? *pUuidStr += 48 : *pUuidStr += 55;
+
+ /*Data5 - 12 characters.*/
+ *pUuidStr++ = '-';
+ for(i = 0; i < 12; i++, pUuidStr++)
+ ((*pUuidStr = (rand() % 16)) < 10) ? *pUuidStr += 48 : *pUuidStr += 55;
+
+ *pUuidStr = '\0';
+
+ m_lastUUID = UuidStrTmp;
+ return UuidStrTmp;
+}
+
+bool StringUtils::ValidateUUID(const CStdString &uuid)
+{
+ CRegExp guidRE;
+ guidRE.RegComp(ADDON_GUID_RE);
+ return (guidRE.RegFind(uuid.c_str()) == 0);
+}
+
+double StringUtils::CompareFuzzy(const CStdString &left, const CStdString &right)
+{
+ return (0.5 + fstrcmp(left.c_str(), right.c_str(), 0.0) * (left.length() + right.length())) / 2.0;
+}
+
+int StringUtils::FindBestMatch(const CStdString &str, const vector<string> &strings, double &matchscore)
+{
+ int best = -1;
+ matchscore = 0;
+
+ int i = 0;
+ for (vector<string>::const_iterator it = strings.begin(); it != strings.end(); ++it, i++)
+ {
+ int maxlength = max(str.length(), it->length());
+ double score = StringUtils::CompareFuzzy(str, *it) / maxlength;
+ if (score > matchscore)
+ {
+ matchscore = score;
+ best = i;
+ }
+ }
+ return best;
+}
+
+bool StringUtils::ContainsKeyword(const CStdString &str, const vector<string> &keywords)
+{
+ for (vector<string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
+ {
+ if (str.find(*it) != str.npos)
+ return true;
+ }
+ return false;
+}
+
+size_t StringUtils::utf8_strlen(const char *s)
+{
+ size_t length = 0;
+ while (*s)
+ {
+ if ((*s++ & 0xC0) != 0x80)
+ length++;
+ }
+ return length;
+}
+
+std::string StringUtils::Paramify(const std::string &param)
+{
+ std::string result = param;
+ // escape backspaces
+ StringUtils::Replace(result, "\\", "\\\\");
+ // escape double quotes
+ StringUtils::Replace(result, "\"", "\\\"");
+
+ // add double quotes around the whole string
+ return "\"" + result + "\"";
+}
+
+std::vector<std::string> StringUtils::Tokenize(const std::string &input, const std::string &delimiters)
+{
+ std::vector<std::string> tokens;
+ Tokenize(input, tokens, delimiters);
+ return tokens;
+}
+
+void StringUtils::Tokenize(const std::string& input, std::vector<std::string>& tokens, const std::string& delimiters)
+{
+ tokens.clear();
+ // Skip delimiters at beginning.
+ std::string::size_type dataPos = input.find_first_not_of(delimiters);
+ while (dataPos != std::string::npos)
+ {
+ // Find next delimiter
+ const std::string::size_type nextDelimPos = input.find_first_of(delimiters, dataPos);
+ // Found a token, add it to the vector.
+ tokens.push_back(input.substr(dataPos, nextDelimPos - dataPos));
+ // Skip delimiters. Note the "not_of"
+ dataPos = input.find_first_not_of(delimiters, nextDelimPos);
+ }
+}
+
+std::vector<std::string> StringUtils::Tokenize(const std::string &input, const char delimiter)
+{
+ std::vector<std::string> tokens;
+ Tokenize(input, tokens, delimiter);
+ return tokens;
+}
+
+void StringUtils::Tokenize(const std::string& input, std::vector<std::string>& tokens, const char delimiter)
+{
+ tokens.clear();
+ // Skip delimiters at beginning.
+ std::string::size_type dataPos = input.find_first_not_of(delimiter);
+ while (dataPos != std::string::npos)
+ {
+ // Find next delimiter
+ const std::string::size_type nextDelimPos = input.find(delimiter, dataPos);
+ // Found a token, add it to the vector.
+ tokens.push_back(input.substr(dataPos, nextDelimPos - dataPos));
+ // Skip delimiters. Note the "not_of"
+ dataPos = input.find_first_not_of(delimiter, nextDelimPos);
+ }
+}
diff --git a/src/utils/StringUtils.h b/src/utils/StringUtils.h
new file mode 100644
index 0000000000..998fae2442
--- /dev/null
+++ b/src/utils/StringUtils.h
@@ -0,0 +1,197 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+//-----------------------------------------------------------------------
+//
+// File: StringUtils.h
+//
+// Purpose: ATL split string utility
+// Author: Paul J. Weiss
+//
+// Modified to support J O'Leary's CStdString class by kraqh3d
+//
+//------------------------------------------------------------------------
+
+#include <vector>
+#include <stdint.h>
+#include <string>
+
+#include "XBDateTime.h"
+#include "utils/StdString.h"
+#include "utils/params_check_macros.h"
+
+class StringUtils
+{
+public:
+ /*! \brief Get a formatted string similar to sprintf
+
+ Beware that this does not support directly passing in
+ std::string objects. You need to call c_str() to pass
+ the const char* buffer representing the value of the
+ std::string object.
+
+ \param fmt Format of the resulting string
+ \param ... variable number of value type arguments
+ \return Formatted string
+ */
+ static std::string Format(PRINTF_FORMAT_STRING const char *fmt, ...) PARAM1_PRINTF_FORMAT;
+ static std::string FormatV(PRINTF_FORMAT_STRING const char *fmt, va_list args);
+ static std::wstring Format(PRINTF_FORMAT_STRING const wchar_t *fmt, ...);
+ static std::wstring FormatV(PRINTF_FORMAT_STRING const wchar_t *fmt, va_list args);
+ static void ToUpper(std::string &str);
+ static void ToUpper(std::wstring &str);
+ static void ToLower(std::string &str);
+ static void ToLower(std::wstring &str);
+ static bool EqualsNoCase(const std::string &str1, const std::string &str2);
+ static bool EqualsNoCase(const std::string &str1, const char *s2);
+ static bool EqualsNoCase(const char *s1, const char *s2);
+ static int CompareNoCase(const std::string &str1, const std::string &str2);
+ static int CompareNoCase(const char *s1, const char *s2);
+ static std::string Left(const std::string &str, size_t count);
+ static std::string Mid(const std::string &str, size_t first, size_t count = std::string::npos);
+ static std::string Right(const std::string &str, size_t count);
+ static std::string& Trim(std::string &str);
+ static std::string& Trim(std::string &str, const char* const chars);
+ static std::string& TrimLeft(std::string &str);
+ static std::string& TrimLeft(std::string &str, const char* const chars);
+ static std::string& TrimRight(std::string &str);
+ static std::string& TrimRight(std::string &str, const char* const chars);
+ static std::string& RemoveDuplicatedSpacesAndTabs(std::string& str);
+ static int Replace(std::string &str, char oldChar, char newChar);
+ static int Replace(std::string &str, const std::string &oldStr, const std::string &newStr);
+ static int Replace(std::wstring &str, const std::wstring &oldStr, const std::wstring &newStr);
+ static bool StartsWith(const std::string &str1, const std::string &str2);
+ static bool StartsWith(const std::string &str1, const char *s2);
+ static bool StartsWith(const char *s1, const char *s2);
+ static bool StartsWithNoCase(const std::string &str1, const std::string &str2);
+ static bool StartsWithNoCase(const std::string &str1, const char *s2);
+ static bool StartsWithNoCase(const char *s1, const char *s2);
+ static bool EndsWith(const std::string &str1, const std::string &str2);
+ static bool EndsWith(const std::string &str1, const char *s2);
+ static bool EndsWithNoCase(const std::string &str1, const std::string &str2);
+ static bool EndsWithNoCase(const std::string &str1, const char *s2);
+
+ static std::string Join(const std::vector<std::string> &strings, const std::string& delimiter);
+ /*! \brief Splits the given input string using the given delimiter into separate strings.
+
+ If the given input string is empty the result will be an empty array (not
+ an array containing an empty string).
+
+ \param input Input string to be split
+ \param delimiter Delimiter to be used to split the input string
+ \param iMaxStrings (optional) Maximum number of splitted strings
+ */
+ static std::vector<std::string> Split(const std::string& input, const std::string& delimiter, unsigned int iMaxStrings = 0);
+ static std::vector<std::string> Split(const std::string& input, const char delimiter, size_t iMaxStrings = 0);
+ static int FindNumber(const CStdString& strInput, const CStdString &strFind);
+ static int64_t AlphaNumericCompare(const wchar_t *left, const wchar_t *right);
+ static long TimeStringToSeconds(const CStdString &timeString);
+ static void RemoveCRLF(std::string& strLine);
+
+ /*! \brief utf8 version of strlen - skips any non-starting bytes in the count, thus returning the number of utf8 characters
+ \param s c-string to find the length of.
+ \return the number of utf8 characters in the string.
+ */
+ static size_t utf8_strlen(const char *s);
+
+ /*! \brief convert a time in seconds to a string based on the given time format
+ \param seconds time in seconds
+ \param format the format we want the time in.
+ \return the formatted time
+ \sa TIME_FORMAT
+ */
+ static CStdString SecondsToTimeString(long seconds, TIME_FORMAT format = TIME_FORMAT_GUESS);
+
+ /*! \brief check whether a string is a natural number.
+ Matches [ \t]*[0-9]+[ \t]*
+ \param str the string to check
+ \return true if the string is a natural number, false otherwise.
+ */
+ static bool IsNaturalNumber(const CStdString& str);
+
+ /*! \brief check whether a string is an integer.
+ Matches [ \t]*[\-]*[0-9]+[ \t]*
+ \param str the string to check
+ \return true if the string is an integer, false otherwise.
+ */
+ static bool IsInteger(const CStdString& str);
+
+ /* The next several isasciiXX and asciiXXvalue functions are locale independent (US-ASCII only),
+ * as opposed to standard ::isXX (::isalpha, ::isdigit...) which are locale dependent.
+ * Next functions get parameter as char and don't need double cast ((int)(unsigned char) is required for standard functions). */
+ inline static bool isasciidigit(char chr) // locale independent
+ {
+ return chr >= '0' && chr <= '9';
+ }
+ inline static bool isasciixdigit(char chr) // locale independent
+ {
+ return (chr >= '0' && chr <= '9') || (chr >= 'a' && chr <= 'f') || (chr >= 'A' && chr <= 'F');
+ }
+ static int asciidigitvalue(char chr); // locale independent
+ static int asciixdigitvalue(char chr); // locale independent
+ inline static bool isasciiuppercaseletter(char chr) // locale independent
+ {
+ return (chr >= 'A' && chr <= 'Z');
+ }
+ inline static bool isasciilowercaseletter(char chr) // locale independent
+ {
+ return (chr >= 'a' && chr <= 'z');
+ }
+ inline static bool isasciialphanum(char chr) // locale independent
+ {
+ return isasciiuppercaseletter(chr) || isasciilowercaseletter(chr) || isasciidigit(chr);
+ }
+ static CStdString SizeToString(int64_t size);
+ static const CStdString EmptyString;
+ static const std::string Empty;
+ static size_t FindWords(const char *str, const char *wordLowerCase);
+ static int FindEndBracket(const CStdString &str, char opener, char closer, int startPos = 0);
+ static int DateStringToYYYYMMDD(const CStdString &dateString);
+ static void WordToDigits(std::string &word);
+ static CStdString CreateUUID();
+ static bool ValidateUUID(const CStdString &uuid); // NB only validates syntax
+ static double CompareFuzzy(const CStdString &left, const CStdString &right);
+ static int FindBestMatch(const CStdString &str, const std::vector<std::string> &strings, double &matchscore);
+ static bool ContainsKeyword(const CStdString &str, const std::vector<std::string> &keywords);
+
+ /*! \brief Escapes the given string to be able to be used as a parameter.
+
+ Escapes backslashes and double-quotes with an additional backslash and
+ adds double-quotes around the whole string.
+
+ \param param String to escape/paramify
+ \return Escaped/Paramified string
+ */
+ static std::string Paramify(const std::string &param);
+
+ /*! \brief Split a string by the specified delimiters.
+ Splits a string using one or more delimiting characters, ignoring empty tokens.
+ Differs from Split() in two ways:
+ 1. The delimiters are treated as individual characters, rather than a single delimiting string.
+ 2. Empty tokens are ignored.
+ \return a vector of tokens
+ */
+ static std::vector<std::string> Tokenize(const std::string& input, const std::string& delimiters);
+ static void Tokenize(const std::string& input, std::vector<std::string>& tokens, const std::string& delimiters);
+ static std::vector<std::string> Tokenize(const std::string& input, const char delimiter);
+ static void Tokenize(const std::string& input, std::vector<std::string>& tokens, const char delimiter);
+private:
+ static CStdString m_lastUUID;
+};
diff --git a/src/utils/StringValidation.cpp b/src/utils/StringValidation.cpp
new file mode 100644
index 0000000000..3f60dc51c6
--- /dev/null
+++ b/src/utils/StringValidation.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "StringValidation.h"
+#include "utils/StringUtils.h"
+#include "utils/Variant.h"
+
+bool StringValidation::IsInteger(const std::string &input, void *data)
+{
+ return StringUtils::IsInteger(input);
+}
+
+bool StringValidation::IsPositiveInteger(const std::string &input, void *data)
+{
+ return StringUtils::IsNaturalNumber(input);
+}
+
+bool StringValidation::IsTime(const std::string &input, void *data)
+{
+ std::string strTime = input;
+ StringUtils::Trim(strTime);
+
+ if (StringUtils::EndsWithNoCase(strTime, " min"))
+ {
+ strTime = StringUtils::Left(strTime, strTime.size() - 4);
+ StringUtils::TrimRight(strTime);
+
+ return IsPositiveInteger(strTime, NULL);
+ }
+ else
+ {
+ // support [[HH:]MM:]SS
+ std::vector<std::string> bits = StringUtils::Split(input, ":");
+ if (bits.size() > 3)
+ return false;
+
+ for (std::vector<std::string>::const_iterator i = bits.begin(); i != bits.end(); ++i)
+ if (!IsPositiveInteger(*i, NULL))
+ return false;
+
+ return true;
+ }
+ return false;
+}
diff --git a/src/utils/StringValidation.h b/src/utils/StringValidation.h
new file mode 100644
index 0000000000..eaf279c836
--- /dev/null
+++ b/src/utils/StringValidation.h
@@ -0,0 +1,36 @@
+#pragma once
+/*
+ * Copyright (C) 2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string>
+
+class StringValidation
+{
+public:
+ typedef bool (*Validator)(const std::string &input, void *data);
+
+ static bool NonEmpty(const std::string &input, void *data) { return !input.empty(); }
+ static bool IsInteger(const std::string &input, void *data);
+ static bool IsPositiveInteger(const std::string &input, void *data);
+ static bool IsTime(const std::string &input, void *data);
+
+private:
+ StringValidation() { }
+};
diff --git a/src/utils/SystemInfo.cpp b/src/utils/SystemInfo.cpp
new file mode 100644
index 0000000000..b322191e45
--- /dev/null
+++ b/src/utils/SystemInfo.cpp
@@ -0,0 +1,1390 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <limits.h>
+
+#include "threads/SystemClock.h"
+#include "system.h"
+#include "SystemInfo.h"
+#ifndef TARGET_POSIX
+#include <conio.h>
+#else
+#include <sys/utsname.h>
+#endif
+#include "GUIInfoManager.h"
+#include "filesystem/CurlFile.h"
+#include "network/Network.h"
+#include "Application.h"
+#include "windowing/WindowingFactory.h"
+#include "guilib/LocalizeStrings.h"
+#include "CPUInfo.h"
+#include "utils/TimeUtils.h"
+#include "utils/log.h"
+#include "CompileInfo.h"
+
+#ifdef TARGET_WINDOWS
+#include "dwmapi.h"
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif // WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include "utils/CharsetConverter.h"
+#endif
+#if defined(TARGET_DARWIN)
+#include "osx/DarwinUtils.h"
+#include "osx/CocoaInterface.h"
+#endif
+#include "powermanagement/PowerManager.h"
+#include "utils/StringUtils.h"
+#include "utils/XMLUtils.h"
+#if defined(TARGET_ANDROID)
+#include "android/jni/Build.h"
+#include "utils/AMLUtils.h"
+#endif
+
+/* Platform identification */
+#if defined(TARGET_DARWIN)
+#include <Availability.h>
+#include <mach-o/arch.h>
+#include <sys/sysctl.h>
+#include "utils/auto_buffer.h"
+#elif defined(TARGET_ANDROID)
+#include <android/api-level.h>
+#include <sys/system_properties.h>
+#elif defined(TARGET_FREEBSD)
+#include <sys/param.h>
+#elif defined(TARGET_LINUX)
+#include <linux/version.h>
+#endif
+
+/* Expand macro before stringify */
+#define STR_MACRO(x) #x
+#define XSTR_MACRO(x) STR_MACRO(x)
+
+#ifdef TARGET_WINDOWS
+static bool sysGetVersionExWByRef(OSVERSIONINFOEXW& osVerInfo)
+{
+ ZeroMemory(&osVerInfo, sizeof(osVerInfo));
+ osVerInfo.dwOSVersionInfoSize = sizeof(osVerInfo);
+
+ typedef NTSTATUS(__stdcall *RtlGetVersionPtr)(RTL_OSVERSIONINFOEXW* pOsInfo);
+ static HMODULE hNtDll = GetModuleHandleW(L"ntdll.dll");
+ if (hNtDll != NULL)
+ {
+ static RtlGetVersionPtr RtlGetVer = (RtlGetVersionPtr) GetProcAddress(hNtDll, "RtlGetVersion");
+ if (RtlGetVer && RtlGetVer(&osVerInfo) == 0)
+ return true;
+ }
+ // failed to get OS information directly from ntdll.dll
+ // use GetVersionExW() as fallback
+ // note: starting from Windows 8.1 GetVersionExW() may return unfaithful information
+ if (GetVersionExW((OSVERSIONINFOW*) &osVerInfo) != 0)
+ return true;
+
+ ZeroMemory(&osVerInfo, sizeof(osVerInfo));
+ return false;
+}
+#endif // TARGET_WINDOWS
+
+#ifdef TARGET_LINUX
+static std::string getValueFromOs_release(std::string key)
+{
+ FILE* os_rel = fopen("/etc/os-release", "r");
+ if (!os_rel)
+ return "";
+
+ char* buf = new char[10 * 1024]; // more than enough
+ size_t len = fread(buf, 1, 10 * 1024, os_rel);
+ fclose(os_rel);
+ if (len == 0)
+ {
+ delete[] buf;
+ return "";
+ }
+
+ std::string content(buf, len);
+ delete[] buf;
+
+ // find begin of value string
+ size_t valStart = 0, seachPos;
+ key += '=';
+ if (content.compare(0, key.length(), key) == 0)
+ valStart = key.length();
+ else
+ {
+ key = "\n" + key;
+ seachPos = 0;
+ do
+ {
+ seachPos = content.find(key, seachPos);
+ if (seachPos == std::string::npos)
+ return "";
+ if (seachPos == 0 || content[seachPos - 1] != '\\')
+ valStart = seachPos + key.length();
+ else
+ seachPos++;
+ } while (valStart == 0);
+ }
+
+ if (content[valStart] == '\n')
+ return "";
+
+ // find end of value string
+ seachPos = valStart;
+ do
+ {
+ seachPos = content.find('\n', seachPos + 1);
+ } while (seachPos != std::string::npos && content[seachPos - 1] == '\\');
+ size_t const valEnd = seachPos;
+
+ std::string value(content, valStart, valEnd - valStart);
+ if (value.empty())
+ return value;
+
+ // remove quotes
+ if (value[0] == '\'' || value[0] == '"')
+ {
+ if (value.length() < 2)
+ return value;
+ size_t qEnd = value.rfind(value[0]);
+ if (qEnd != std::string::npos)
+ {
+ value.erase(qEnd);
+ value.erase(0, 1);
+ }
+ }
+
+ // unescape characters
+ for (size_t slashPos = value.find('\\'); slashPos < value.length() - 1; slashPos = value.find('\\', slashPos))
+ {
+ if (value[slashPos + 1] == '\n')
+ value.erase(slashPos, 2);
+ else
+ {
+ value.erase(slashPos, 1);
+ slashPos++; // skip unescaped character
+ }
+ }
+
+ return value;
+}
+
+enum lsb_rel_info_type
+{
+ lsb_rel_distributor,
+ lsb_rel_description,
+ lsb_rel_release,
+ lsb_rel_codename
+};
+
+static std::string getValueFromLsb_release(enum lsb_rel_info_type infoType)
+{
+ std::string key, command("unset PYTHONHOME; unset PYTHONPATH; lsb_release ");
+ switch (infoType)
+ {
+ case lsb_rel_distributor:
+ command += "-i";
+ key = "Distributor ID:\t";
+ break;
+ case lsb_rel_description:
+ command += "-d";
+ key = "Description:\t";
+ break;
+ case lsb_rel_release:
+ command += "-r";
+ key = "Release:\t";
+ break;
+ case lsb_rel_codename:
+ command += "-c";
+ key = "Codename:\t";
+ break;
+ default:
+ return "";
+ }
+ FILE* lsb_rel = popen(command.c_str(), "r");
+ if (lsb_rel == NULL)
+ return "";
+
+ char buf[300]; // more than enough
+ if (fgets(buf, 300, lsb_rel) == NULL)
+ {
+ pclose(lsb_rel);
+ return "";
+ }
+ pclose(lsb_rel);
+
+ std::string response(buf);
+ if (response.compare(0, key.length(), key) != 0)
+ return "";
+
+ return response.substr(key.length(), response.find('\n') - key.length());
+}
+#endif // TARGET_LINUX
+
+CSysInfo g_sysinfo;
+
+CSysInfoJob::CSysInfoJob()
+{
+}
+
+bool CSysInfoJob::DoWork()
+{
+ m_info.systemUptime = GetSystemUpTime(false);
+ m_info.systemTotalUptime = GetSystemUpTime(true);
+ m_info.internetState = GetInternetState();
+ m_info.videoEncoder = GetVideoEncoder();
+ m_info.cpuFrequency = GetCPUFreqInfo();
+ m_info.osVersionInfo = CSysInfo::GetOsPrettyNameWithVersion() + " (kernel: " + CSysInfo::GetKernelName() + " " + CSysInfo::GetKernelVersionFull() + ")";
+ m_info.macAddress = GetMACAddress();
+ m_info.batteryLevel = GetBatteryLevel();
+ return true;
+}
+
+const CSysData &CSysInfoJob::GetData() const
+{
+ return m_info;
+}
+
+std::string CSysInfoJob::GetCPUFreqInfo()
+{
+ double CPUFreq = GetCPUFrequency();
+ return StringUtils::Format("%4.0f MHz", CPUFreq);;
+}
+
+CSysData::INTERNET_STATE CSysInfoJob::GetInternetState()
+{
+ // Internet connection state!
+ XFILE::CCurlFile http;
+ if (http.IsInternet())
+ return CSysData::CONNECTED;
+ return CSysData::DISCONNECTED;
+}
+
+std::string CSysInfoJob::GetMACAddress()
+{
+#if defined(HAS_LINUX_NETWORK) || defined(HAS_WIN32_NETWORK)
+ CNetworkInterface* iface = g_application.getNetwork().GetFirstConnectedInterface();
+ if (iface)
+ return iface->GetMacAddress();
+#endif
+ return "";
+}
+
+std::string CSysInfoJob::GetVideoEncoder()
+{
+ return "GPU: " + g_Windowing.GetRenderRenderer();
+}
+
+std::string CSysInfoJob::GetBatteryLevel()
+{
+ return StringUtils::Format("%d%%", g_powerManager.BatteryLevel());
+}
+
+double CSysInfoJob::GetCPUFrequency()
+{
+#if defined (TARGET_POSIX) || defined(TARGET_WINDOWS)
+ return double (g_cpuInfo.getCPUFrequency());
+#else
+ return 0;
+#endif
+}
+
+bool CSysInfoJob::SystemUpTime(int iInputMinutes, int &iMinutes, int &iHours, int &iDays)
+{
+ iHours = 0; iDays = 0;
+ iMinutes = iInputMinutes;
+ if (iMinutes >= 60) // Hour's
+ {
+ iHours = iMinutes / 60;
+ iMinutes = iMinutes - (iHours *60);
+ }
+ if (iHours >= 24) // Days
+ {
+ iDays = iHours / 24;
+ iHours = iHours - (iDays * 24);
+ }
+ return true;
+}
+
+std::string CSysInfoJob::GetSystemUpTime(bool bTotalUptime)
+{
+ std::string strSystemUptime;
+ int iInputMinutes, iMinutes,iHours,iDays;
+
+ if(bTotalUptime)
+ {
+ //Total Uptime
+ iInputMinutes = g_sysinfo.GetTotalUptime() + ((int)(XbmcThreads::SystemClockMillis() / 60000));
+ }
+ else
+ {
+ //Current UpTime
+ iInputMinutes = (int)(XbmcThreads::SystemClockMillis() / 60000);
+ }
+
+ SystemUpTime(iInputMinutes,iMinutes, iHours, iDays);
+ if (iDays > 0)
+ {
+ strSystemUptime = StringUtils::Format("%i %s, %i %s, %i %s",
+ iDays, g_localizeStrings.Get(12393).c_str(),
+ iHours, g_localizeStrings.Get(12392).c_str(),
+ iMinutes, g_localizeStrings.Get(12391).c_str());
+ }
+ else if (iDays == 0 && iHours >= 1 )
+ {
+ strSystemUptime = StringUtils::Format("%i %s, %i %s",
+ iHours, g_localizeStrings.Get(12392).c_str(),
+ iMinutes, g_localizeStrings.Get(12391).c_str());
+ }
+ else if (iDays == 0 && iHours == 0 && iMinutes >= 0)
+ {
+ strSystemUptime = StringUtils::Format("%i %s",
+ iMinutes, g_localizeStrings.Get(12391).c_str());
+ }
+ return strSystemUptime;
+}
+
+std::string CSysInfo::TranslateInfo(int info) const
+{
+ switch(info)
+ {
+ case SYSTEM_VIDEO_ENCODER_INFO:
+ return m_info.videoEncoder;
+ case NETWORK_MAC_ADDRESS:
+ return m_info.macAddress;
+ case SYSTEM_OS_VERSION_INFO:
+ return m_info.osVersionInfo;
+ case SYSTEM_CPUFREQUENCY:
+ return m_info.cpuFrequency;
+ case SYSTEM_UPTIME:
+ return m_info.systemUptime;
+ case SYSTEM_TOTALUPTIME:
+ return m_info.systemTotalUptime;
+ case SYSTEM_INTERNET_STATE:
+ if (m_info.internetState == CSysData::CONNECTED)
+ return g_localizeStrings.Get(13296);
+ else
+ return g_localizeStrings.Get(13297);
+ case SYSTEM_BATTERY_LEVEL:
+ return m_info.batteryLevel;
+ default:
+ return "";
+ }
+}
+
+void CSysInfo::Reset()
+{
+ m_info.Reset();
+}
+
+CSysInfo::CSysInfo(void) : CInfoLoader(15 * 1000)
+{
+ memset(MD5_Sign, 0, sizeof(MD5_Sign));
+ m_iSystemTimeTotalUp = 0;
+}
+
+CSysInfo::~CSysInfo()
+{
+}
+
+bool CSysInfo::Load(const TiXmlNode *settings)
+{
+ if (settings == NULL)
+ return false;
+
+ const TiXmlElement *pElement = settings->FirstChildElement("general");
+ if (pElement)
+ XMLUtils::GetInt(pElement, "systemtotaluptime", m_iSystemTimeTotalUp, 0, INT_MAX);
+
+ return true;
+}
+
+bool CSysInfo::Save(TiXmlNode *settings) const
+{
+ if (settings == NULL)
+ return false;
+
+ TiXmlNode *generalNode = settings->FirstChild("general");
+ if (generalNode == NULL)
+ {
+ TiXmlElement generalNodeNew("general");
+ generalNode = settings->InsertEndChild(generalNodeNew);
+ if (generalNode == NULL)
+ return false;
+ }
+ XMLUtils::SetInt(generalNode, "systemtotaluptime", m_iSystemTimeTotalUp);
+
+ return true;
+}
+
+const std::string& CSysInfo::GetAppName(void)
+{
+ assert(CCompileInfo::GetAppName() != NULL);
+ static const std::string appName(CCompileInfo::GetAppName());
+
+ return appName;
+}
+
+bool CSysInfo::GetDiskSpace(const std::string& drive,int& iTotal, int& iTotalFree, int& iTotalUsed, int& iPercentFree, int& iPercentUsed)
+{
+ bool bRet= false;
+ ULARGE_INTEGER ULTotal= { { 0 } };
+ ULARGE_INTEGER ULTotalFree= { { 0 } };
+
+ if( !drive.empty() && drive != "*" )
+ {
+#ifdef TARGET_WINDOWS
+ UINT uidriveType = GetDriveType(( drive + ":\\" ).c_str());
+ if(uidriveType != DRIVE_UNKNOWN && uidriveType != DRIVE_NO_ROOT_DIR)
+ bRet= ( 0 != GetDiskFreeSpaceEx( ( drive + ":\\" ).c_str(), NULL, &ULTotal, &ULTotalFree) );
+#elif defined(TARGET_POSIX)
+ bRet = (0 != GetDiskFreeSpaceEx(drive.c_str(), NULL, &ULTotal, &ULTotalFree));
+#endif
+ }
+ else
+ {
+ ULARGE_INTEGER ULTotalTmp= { { 0 } };
+ ULARGE_INTEGER ULTotalFreeTmp= { { 0 } };
+#ifdef TARGET_WINDOWS
+ char* pcBuffer= NULL;
+ DWORD dwStrLength= GetLogicalDriveStrings( 0, pcBuffer );
+ if( dwStrLength != 0 )
+ {
+ dwStrLength+= 1;
+ pcBuffer= new char [dwStrLength];
+ GetLogicalDriveStrings( dwStrLength, pcBuffer );
+ int iPos= 0;
+ do {
+ if( DRIVE_FIXED == GetDriveType( pcBuffer + iPos ) &&
+ GetDiskFreeSpaceEx( ( pcBuffer + iPos ), NULL, &ULTotal, &ULTotalFree ) )
+ {
+ ULTotalTmp.QuadPart+= ULTotal.QuadPart;
+ ULTotalFreeTmp.QuadPart+= ULTotalFree.QuadPart;
+ }
+ iPos += (strlen( pcBuffer + iPos) + 1 );
+ }while( strlen( pcBuffer + iPos ) > 0 );
+ }
+ delete[] pcBuffer;
+#else // for linux and osx
+ if( GetDiskFreeSpaceEx( "/", NULL, &ULTotal, &ULTotalFree ) )
+ {
+ ULTotalTmp.QuadPart+= ULTotal.QuadPart;
+ ULTotalFreeTmp.QuadPart+= ULTotalFree.QuadPart;
+ }
+#endif
+ if( ULTotalTmp.QuadPart || ULTotalFreeTmp.QuadPart )
+ {
+ ULTotal.QuadPart= ULTotalTmp.QuadPart;
+ ULTotalFree.QuadPart= ULTotalFreeTmp.QuadPart;
+ bRet= true;
+ }
+ }
+
+ if( bRet )
+ {
+ iTotal = (int)( ULTotal.QuadPart / MB );
+ iTotalFree = (int)( ULTotalFree.QuadPart / MB );
+ iTotalUsed = iTotal - iTotalFree;
+ if( ULTotal.QuadPart > 0 )
+ {
+ iPercentUsed = (int)( 100.0f * ( ULTotal.QuadPart - ULTotalFree.QuadPart ) / ULTotal.QuadPart + 0.5f );
+ }
+ else
+ {
+ iPercentUsed = 0;
+ }
+ iPercentFree = 100 - iPercentUsed;
+ }
+
+ return bRet;
+}
+
+std::string CSysInfo::GetCPUModel()
+{
+ return "CPU: " + g_cpuInfo.getCPUModel();
+}
+
+std::string CSysInfo::GetCPUBogoMips()
+{
+ return "BogoMips: " + g_cpuInfo.getCPUBogoMips();
+}
+
+std::string CSysInfo::GetCPUHardware()
+{
+ return "Hardware: " + g_cpuInfo.getCPUHardware();
+}
+
+std::string CSysInfo::GetCPURevision()
+{
+ return "Revision: " + g_cpuInfo.getCPURevision();
+}
+
+std::string CSysInfo::GetCPUSerial()
+{
+ return "Serial: " + g_cpuInfo.getCPUSerial();
+}
+
+std::string CSysInfo::GetKernelName(bool emptyIfUnknown /*= false*/)
+{
+ static std::string kernelName;
+ if (kernelName.empty())
+ {
+#if defined(TARGET_WINDOWS)
+ OSVERSIONINFOEXW osvi;
+ if (sysGetVersionExWByRef(osvi) && osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
+ kernelName = "Windows NT";
+#elif defined(TARGET_POSIX)
+ struct utsname un;
+ if (uname(&un) == 0)
+ kernelName.assign(un.sysname);
+#endif // defined(TARGET_POSIX)
+
+ if (kernelName.empty())
+ kernelName = "Unknown kernel"; // can't detect
+ }
+
+ if (emptyIfUnknown && kernelName == "Unknown kernel")
+ return "";
+
+ return kernelName;
+}
+
+std::string CSysInfo::GetKernelVersionFull(void)
+{
+ static std::string kernelVersionFull;
+ if (!kernelVersionFull.empty())
+ return kernelVersionFull;
+
+#if defined(TARGET_WINDOWS)
+ OSVERSIONINFOEXW osvi;
+ if (sysGetVersionExWByRef(osvi))
+ kernelVersionFull = StringUtils::Format("%d.%d", osvi.dwMajorVersion, osvi.dwMinorVersion);
+#elif defined(TARGET_POSIX)
+ struct utsname un;
+ if (uname(&un) == 0)
+ kernelVersionFull.assign(un.release);
+#endif // defined(TARGET_POSIX)
+
+ if (kernelVersionFull.empty())
+ kernelVersionFull = "0.0.0"; // can't detect
+
+ return kernelVersionFull;
+}
+
+std::string CSysInfo::GetKernelVersion(void)
+{
+ static std::string kernelVersionClear;
+ if (kernelVersionClear.empty())
+ {
+ kernelVersionClear = GetKernelVersionFull();
+ const size_t erasePos = kernelVersionClear.find_first_not_of("0123456789.");
+ if (erasePos != std::string::npos)
+ kernelVersionClear.erase(erasePos);
+ }
+
+ return kernelVersionClear;
+}
+
+std::string CSysInfo::GetOsName(bool emptyIfUnknown /* = false*/)
+{
+ static std::string osName;
+ if (osName.empty())
+ {
+#if defined (TARGET_WINDOWS)
+ osName = GetKernelName() + "-based OS";
+#elif defined(TARGET_FREEBSD)
+ osName = GetKernelName(true); // FIXME: for FreeBSD OS name is a kernel name
+#elif defined(TARGET_DARWIN_IOS)
+ osName = "iOS";
+#elif defined(TARGET_DARWIN_OSX)
+ osName = "OS X";
+#elif defined (TARGET_ANDROID)
+ osName = "Android";
+#elif defined(TARGET_LINUX)
+ osName = getValueFromOs_release("NAME");
+ if (osName.empty())
+ osName = getValueFromLsb_release(lsb_rel_distributor);
+ if (osName.empty())
+ osName = getValueFromOs_release("ID");
+#endif // defined(TARGET_LINUX)
+
+ if (osName.empty())
+ osName = "Unknown OS";
+ }
+
+ if (emptyIfUnknown && osName == "Unknown OS")
+ return "";
+
+ return osName;
+}
+
+std::string CSysInfo::GetOsVersion(void)
+{
+ static std::string osVersion;
+ if (!osVersion.empty())
+ return osVersion;
+
+#if defined(TARGET_WINDOWS) || defined(TARGET_FREEBSD)
+ osVersion = GetKernelVersion(); // FIXME: for Win32 and FreeBSD OS version is a kernel version
+#elif defined(TARGET_DARWIN_IOS)
+ osVersion = CDarwinUtils::GetIOSVersionString();
+#elif defined(TARGET_DARWIN_OSX)
+ osVersion = CDarwinUtils::GetOSXVersionString();
+#elif defined(TARGET_ANDROID)
+ char versionCStr[PROP_VALUE_MAX];
+ int propLen = __system_property_get("ro.build.version.release", versionCStr);
+ osVersion.assign(versionCStr, (propLen > 0 && propLen <= PROP_VALUE_MAX) ? propLen : 0);
+
+ if (osVersion.empty() || std::string("0123456789").find(versionCStr[0]) == std::string::npos)
+ osVersion.clear(); // can't correctly detect Android version
+ else
+ {
+ size_t pointPos = osVersion.find('.');
+ if (pointPos == std::string::npos)
+ osVersion += ".0.0";
+ else if (osVersion.find('.', pointPos + 1) == std::string::npos)
+ osVersion += ".0";
+ }
+#elif defined(TARGET_LINUX)
+ osVersion = getValueFromOs_release("VERSION_ID");
+ if (osVersion.empty())
+ osVersion = getValueFromLsb_release(lsb_rel_release);
+#endif // defined(TARGET_LINUX)
+
+ if (osVersion.empty())
+ osVersion = "0.0";
+
+ return osVersion;
+}
+
+std::string CSysInfo::GetOsPrettyNameWithVersion(void)
+{
+ static std::string osNameVer;
+ if (!osNameVer.empty())
+ return osNameVer;
+
+#if defined (TARGET_WINDOWS)
+ OSVERSIONINFOEXW osvi = {};
+
+ osNameVer = "Windows ";
+ if (sysGetVersionExWByRef(osvi))
+ {
+ switch (GetWindowsVersion())
+ {
+ case WindowsVersionVista:
+ if (osvi.wProductType == VER_NT_WORKSTATION)
+ osNameVer.append("Vista");
+ else
+ osNameVer.append("Server 2008");
+ break;
+ case WindowsVersionWin7:
+ if (osvi.wProductType == VER_NT_WORKSTATION)
+ osNameVer.append("7");
+ else
+ osNameVer.append("Server 2008 R2");
+ break;
+ case WindowsVersionWin8:
+ if (osvi.wProductType == VER_NT_WORKSTATION)
+ osNameVer.append("8");
+ else
+ osNameVer.append("Server 2012");
+ break;
+ case WindowsVersionWin8_1:
+ if (osvi.wProductType == VER_NT_WORKSTATION)
+ osNameVer.append("8.1");
+ else
+ osNameVer.append("Server 2012 R2");
+ break;
+ case WindowsVersionFuture:
+ osNameVer.append("Unknown Future Version");
+ break;
+ default:
+ osNameVer.append("Unknown version");
+ break;
+ }
+
+ // Append Service Pack version if any
+ if (osvi.wServicePackMajor > 0 || osvi.wServicePackMinor > 0)
+ {
+ osNameVer.append(StringUtils::Format(" SP%d", osvi.wServicePackMajor));
+ if (osvi.wServicePackMinor > 0)
+ {
+ osNameVer.append(StringUtils::Format(".%d", osvi.wServicePackMinor));
+ }
+ }
+ }
+ else
+ osNameVer.append(" unknown");
+#elif defined(TARGET_FREEBSD) || defined(TARGET_DARWIN_IOS) || defined(TARGET_DARWIN_OSX)
+ osNameVer = GetOsName() + " " + GetOsVersion();
+#elif defined(TARGET_ANDROID)
+ osNameVer = GetOsName() + " " + GetOsVersion() + " API level " + StringUtils::Format("%d", CJNIBuild::SDK_INT);
+#elif defined(TARGET_LINUX)
+ osNameVer = getValueFromOs_release("PRETTY_NAME");
+ if (osNameVer.empty())
+ {
+ osNameVer = getValueFromLsb_release(lsb_rel_description);
+ std::string osName(GetOsName(true));
+ if (!osName.empty() && osNameVer.find(osName) == std::string::npos)
+ osNameVer = osName + osNameVer;
+ if (osNameVer.empty())
+ osNameVer = "Unknown Linux Distribution";
+ }
+
+ if (osNameVer.find(GetOsVersion()) == std::string::npos)
+ osNameVer += " " + GetOsVersion();
+#endif // defined(TARGET_LINUX)
+
+ if (osNameVer.empty())
+ osNameVer = "Unknown OS Unknown version";
+
+ return osNameVer;
+
+}
+
+std::string CSysInfo::GetManufacturerName(void)
+{
+ static std::string manufName;
+ static bool inited = false;
+ if (!inited)
+ {
+#if defined(TARGET_ANDROID)
+ char deviceCStr[PROP_VALUE_MAX];
+ int propLen = __system_property_get("ro.product.manufacturer", deviceCStr);
+ manufName.assign(deviceCStr, (propLen > 0 && propLen <= PROP_VALUE_MAX) ? propLen : 0);
+#elif defined(TARGET_DARWIN)
+ manufName = CDarwinUtils::GetManufacturer();
+#elif defined(TARGET_WINDOWS)
+ HKEY hKey;
+ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\BIOS", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
+ {
+ wchar_t buf[400]; // more than enough
+ DWORD bufSize = sizeof(buf);
+ DWORD valType;
+ if (RegQueryValueExW(hKey, L"SystemManufacturer", NULL, &valType, (LPBYTE)buf, &bufSize) == ERROR_SUCCESS && valType == REG_SZ)
+ {
+ g_charsetConverter.wToUTF8(std::wstring(buf, bufSize / sizeof(wchar_t)), manufName);
+ size_t zeroPos = manufName.find(char(0));
+ if (zeroPos != std::string::npos)
+ manufName.erase(zeroPos); // remove any extra zero-terminations
+ std::string lower(manufName);
+ StringUtils::ToLower(lower);
+ if (lower == "system manufacturer" || lower == "to be filled by o.e.m." || lower == "unknown" ||
+ lower == "unidentified")
+ manufName.clear();
+ }
+ RegCloseKey(hKey);
+ }
+#endif
+ inited = true;
+ }
+
+ return manufName;
+}
+
+std::string CSysInfo::GetModelName(void)
+{
+ static std::string modelName;
+ static bool inited = false;
+ if (!inited)
+ {
+#if defined(TARGET_ANDROID)
+ char deviceCStr[PROP_VALUE_MAX];
+ int propLen = __system_property_get("ro.product.model", deviceCStr);
+ modelName.assign(deviceCStr, (propLen > 0 && propLen <= PROP_VALUE_MAX) ? propLen : 0);
+#elif defined(TARGET_DARWIN_IOS)
+ modelName = CDarwinUtils::getIosPlatformString();
+#elif defined(TARGET_DARWIN_OSX)
+ size_t nameLen = 0; // 'nameLen' should include terminating null
+ if (sysctlbyname("hw.model", NULL, &nameLen, NULL, NULL) == 0 && nameLen > 1)
+ {
+ XUTILS::auto_buffer buf(nameLen);
+ if (sysctlbyname("hw.model", buf.get(), &nameLen, NULL, NULL) == 0 && nameLen == buf.size())
+ modelName.assign(buf.get(), nameLen - 1); // assign exactly 'nameLen-1' characters to 'modelName'
+ }
+#elif defined(TARGET_WINDOWS)
+ HKEY hKey;
+ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\BIOS", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
+ {
+ wchar_t buf[400]; // more than enough
+ DWORD bufSize = sizeof(buf);
+ DWORD valType;
+ if (RegQueryValueExW(hKey, L"SystemProductName", NULL, &valType, (LPBYTE)buf, &bufSize) == ERROR_SUCCESS && valType == REG_SZ)
+ {
+ g_charsetConverter.wToUTF8(std::wstring(buf, bufSize / sizeof(wchar_t)), modelName);
+ size_t zeroPos = modelName.find(char(0));
+ if (zeroPos != std::string::npos)
+ modelName.erase(zeroPos); // remove any extra zero-terminations
+ std::string lower(modelName);
+ StringUtils::ToLower(lower);
+ if (lower == "system product name" || lower == "to be filled by o.e.m." || lower == "unknown" ||
+ lower == "unidentified")
+ modelName.clear();
+ }
+ RegCloseKey(hKey);
+ }
+#endif
+ inited = true;
+ }
+
+ return modelName;
+}
+
+bool CSysInfo::IsAeroDisabled()
+{
+#ifdef TARGET_WINDOWS
+ BOOL aeroEnabled = FALSE;
+ HRESULT res = DwmIsCompositionEnabled(&aeroEnabled);
+ if (SUCCEEDED(res))
+ return !aeroEnabled;
+#endif
+ return false;
+}
+
+bool CSysInfo::HasHW3DInterlaced()
+{
+#if defined(TARGET_ANDROID)
+ if (aml_hw3d_present())
+ return true;
+#endif
+ return false;
+}
+
+CSysInfo::WindowsVersion CSysInfo::m_WinVer = WindowsVersionUnknown;
+
+bool CSysInfo::IsWindowsVersion(WindowsVersion ver)
+{
+ if (ver == WindowsVersionUnknown)
+ return false;
+ return GetWindowsVersion() == ver;
+}
+
+bool CSysInfo::IsWindowsVersionAtLeast(WindowsVersion ver)
+{
+ if (ver == WindowsVersionUnknown)
+ return false;
+ return GetWindowsVersion() >= ver;
+}
+
+CSysInfo::WindowsVersion CSysInfo::GetWindowsVersion()
+{
+#ifdef TARGET_WINDOWS
+ if (m_WinVer == WindowsVersionUnknown)
+ {
+ OSVERSIONINFOEXW osvi = {};
+ if (sysGetVersionExWByRef(osvi))
+ {
+ if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0)
+ m_WinVer = WindowsVersionVista;
+ else if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1)
+ m_WinVer = WindowsVersionWin7;
+ else if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 2)
+ m_WinVer = WindowsVersionWin8;
+ else if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 3)
+ m_WinVer = WindowsVersionWin8_1;
+ /* Insert checks for new Windows versions here */
+ else if ( (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion > 3) || osvi.dwMajorVersion > 6)
+ m_WinVer = WindowsVersionFuture;
+ }
+ }
+#endif // TARGET_WINDOWS
+ return m_WinVer;
+}
+
+int CSysInfo::GetKernelBitness(void)
+{
+ static int kernelBitness = -1;
+ if (kernelBitness == -1)
+ {
+#ifdef TARGET_WINDOWS
+ SYSTEM_INFO si;
+ GetNativeSystemInfo(&si);
+ if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL || si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM)
+ kernelBitness = 32;
+ else if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
+ kernelBitness = 64;
+ else
+ {
+ BOOL isWow64 = FALSE;
+ if (IsWow64Process(GetCurrentProcess(), &isWow64) && isWow64) // fallback
+ kernelBitness = 64;
+ }
+#elif defined(TARGET_DARWIN_IOS)
+ // Note: OS X return x86 CPU type without CPU_ARCH_ABI64 flag
+ const NXArchInfo* archInfo = NXGetLocalArchInfo();
+ if (archInfo)
+ kernelBitness = ((archInfo->cputype & CPU_ARCH_ABI64) != 0) ? 64 : 32;
+#elif defined(TARGET_POSIX)
+ struct utsname un;
+ if (uname(&un) == 0)
+ {
+ std::string machine(un.machine);
+ if (machine == "x86_64" || machine == "amd64" || machine == "arm64" || machine == "aarch64" || machine == "ppc64" ||
+ machine == "ia64" || machine == "mips64")
+ kernelBitness = 64;
+ else
+ kernelBitness = 32;
+ }
+#endif
+ if (kernelBitness == -1)
+ kernelBitness = 0; // can't detect
+ }
+
+ return kernelBitness;
+}
+
+const std::string& CSysInfo::GetKernelCpuFamily(void)
+{
+ static std::string kernelCpuFamily;
+ if (kernelCpuFamily.empty())
+ {
+#ifdef TARGET_WINDOWS
+ SYSTEM_INFO si;
+ GetNativeSystemInfo(&si);
+ if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL ||
+ si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
+ kernelCpuFamily = "x86";
+ else if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM)
+ kernelCpuFamily = "ARM";
+#elif defined(TARGET_DARWIN)
+ const NXArchInfo* archInfo = NXGetLocalArchInfo();
+ if (archInfo)
+ {
+ const cpu_type_t cpuType = (archInfo->cputype & ~CPU_ARCH_ABI64); // get CPU family without 64-bit ABI flag
+ if (cpuType == CPU_TYPE_I386)
+ kernelCpuFamily = "x86";
+ else if (cpuType == CPU_TYPE_ARM)
+ kernelCpuFamily = "ARM";
+ else if (cpuType == CPU_TYPE_POWERPC)
+ kernelCpuFamily = "PowerPC";
+#ifdef CPU_TYPE_MIPS
+ else if (cpuType == CPU_TYPE_MIPS)
+ kernelCpuFamily = "MIPS";
+#endif // CPU_TYPE_MIPS
+ }
+#elif defined(TARGET_POSIX)
+ struct utsname un;
+ if (uname(&un) == 0)
+ {
+ std::string machine(un.machine);
+ if (machine.compare(0, 3, "arm", 3) == 0)
+ kernelCpuFamily = "ARM";
+ else if (machine.compare(0, 4, "mips", 4) == 0)
+ kernelCpuFamily = "MIPS";
+ else if (machine.compare(0, 4, "i686", 4) == 0 || machine == "i386" || machine == "amd64" || machine.compare(0, 3, "x86", 3) == 0)
+ kernelCpuFamily = "x86";
+ else if (machine.compare(0, 3, "ppc", 3) == 0 || machine.compare(0, 5, "power", 5) == 0)
+ kernelCpuFamily = "PowerPC";
+ }
+#endif
+ if (kernelCpuFamily.empty())
+ kernelCpuFamily = "unknown CPU family";
+ }
+ return kernelCpuFamily;
+}
+
+int CSysInfo::GetXbmcBitness(void)
+{
+#if defined (__aarch64__) || defined(__arm64__) || defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || \
+ defined(_M_AMD64) || defined(__ppc64__) || defined(__mips64)
+ return 64;
+#elif defined(__thumb__) || defined(_M_ARMT) || defined(__arm__) || defined(_M_ARM) || defined(__mips__) || defined(mips) || defined(__mips) || defined(i386) || \
+ defined(__i386) || defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(_M_IX86) || defined(_X86_) || defined(__powerpc) || \
+ defined(__powerpc__) || defined(__ppc__) || defined(_M_PPC)
+ return 32;
+#else
+ return 0; // Unknown
+#endif
+}
+
+bool CSysInfo::HasInternet()
+{
+ if (m_info.internetState != CSysData::UNKNOWN)
+ return m_info.internetState == CSysData::CONNECTED;
+ return (m_info.internetState = CSysInfoJob::GetInternetState()) == CSysData::CONNECTED;
+}
+
+std::string CSysInfo::GetHddSpaceInfo(int drive, bool shortText)
+{
+ int percent;
+ return GetHddSpaceInfo( percent, drive, shortText);
+}
+
+std::string CSysInfo::GetHddSpaceInfo(int& percent, int drive, bool shortText)
+{
+ int total, totalFree, totalUsed, percentFree, percentused;
+ std::string strRet;
+ percent = 0;
+ if (g_sysinfo.GetDiskSpace("", total, totalFree, totalUsed, percentFree, percentused))
+ {
+ if (shortText)
+ {
+ switch(drive)
+ {
+ case SYSTEM_FREE_SPACE:
+ percent = percentFree;
+ break;
+ case SYSTEM_USED_SPACE:
+ percent = percentused;
+ break;
+ }
+ }
+ else
+ {
+ switch(drive)
+ {
+ case SYSTEM_FREE_SPACE:
+ strRet = StringUtils::Format("%i MB %s", totalFree, g_localizeStrings.Get(160).c_str());
+ break;
+ case SYSTEM_USED_SPACE:
+ strRet = StringUtils::Format("%i MB %s", totalUsed, g_localizeStrings.Get(20162).c_str());
+ break;
+ case SYSTEM_TOTAL_SPACE:
+ strRet = StringUtils::Format("%i MB %s", total, g_localizeStrings.Get(20161).c_str());
+ break;
+ case SYSTEM_FREE_SPACE_PERCENT:
+ strRet = StringUtils::Format("%i %% %s", percentFree, g_localizeStrings.Get(160).c_str());
+ break;
+ case SYSTEM_USED_SPACE_PERCENT:
+ strRet = StringUtils::Format("%i %% %s", percentused, g_localizeStrings.Get(20162).c_str());
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (shortText)
+ strRet = "N/A";
+ else
+ strRet = g_localizeStrings.Get(161);
+ }
+ return strRet;
+}
+
+std::string CSysInfo::GetUserAgent()
+{
+ static std::string result;
+ if (!result.empty())
+ return result;
+
+ result = GetAppName() + "/" + (std::string)g_infoManager.GetLabel(SYSTEM_BUILD_VERSION_SHORT) + " (";
+#if defined(TARGET_WINDOWS)
+ result += GetKernelName() + " " + GetKernelVersion();
+ BOOL bIsWow = FALSE;
+ if (IsWow64Process(GetCurrentProcess(), &bIsWow) && bIsWow)
+ result.append("; WOW64");
+ else
+ {
+ SYSTEM_INFO si = {};
+ GetSystemInfo(&si);
+ if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
+ result.append("; Win64; x64");
+ else if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64)
+ result.append("; Win64; IA64");
+ else if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM)
+ result.append("; ARM");
+ }
+#elif defined(TARGET_DARWIN)
+#if defined(TARGET_DARWIN_IOS)
+ std::string iDevStr(GetModelName()); // device model name with number of model version
+ size_t iDevStrDigit = iDevStr.find_first_of("0123456789");
+ std::string iDev(iDevStr, 0, iDevStrDigit); // device model name without number
+ if (iDevStrDigit == 0)
+ iDev = "unknown";
+ result += iDev + "; ";
+ std::string iOSVerison(GetOsVersion());
+ size_t lastDotPos = iOSVerison.rfind('.');
+ if (lastDotPos != std::string::npos && iOSVerison.find('.') != lastDotPos
+ && iOSVerison.find_first_not_of('0', lastDotPos + 1) == std::string::npos)
+ iOSVerison.erase(lastDotPos);
+ StringUtils::Replace(iOSVerison, '.', '_');
+ if (iDev == "iPad" || iDev == "AppleTV")
+ result += "CPU OS ";
+ else
+ result += "CPU iPhone OS ";
+ result += iOSVerison + " like Mac OS X";
+#else
+ result += "Macintosh; ";
+ std::string cpuFam(GetBuildTargetCpuFamily());
+ if (cpuFam == "x86")
+ result += "Intel ";
+ else if (cpuFam == "PowerPC")
+ result += "PPC ";
+ result += "Mac OS X ";
+ std::string OSXVersion(GetOsVersion());
+ StringUtils::Replace(OSXVersion, '.', '_');
+ result += OSXVersion;
+#endif
+#elif defined(TARGET_ANDROID)
+ result += "Linux; Android ";
+ std::string versionStr(GetOsVersion());
+ const size_t verLen = versionStr.length();
+ if (verLen >= 2 && versionStr.compare(verLen - 2, 2, ".0", 2) == 0)
+ versionStr.erase(verLen - 2); // remove last ".0" if any
+ result += versionStr;
+ std::string deviceInfo(GetModelName());
+
+ char buildId[PROP_VALUE_MAX];
+ int propLen = __system_property_get("ro.build.id", buildId);
+ if (propLen > 0 && propLen <= PROP_VALUE_MAX)
+ {
+ if (!deviceInfo.empty())
+ deviceInfo += " ";
+ deviceInfo += "Build/";
+ deviceInfo.append(buildId, propLen);
+ }
+
+ if (!deviceInfo.empty())
+ result += "; " + deviceInfo;
+#elif defined(TARGET_POSIX)
+ result += "X11; ";
+ struct utsname un;
+ if (uname(&un) == 0)
+ {
+ std::string cpuStr(un.machine);
+ if (cpuStr == "x86_64" && GetXbmcBitness() == 32)
+ cpuStr = "i686 (x86_64)";
+ result += un.sysname;
+ result += " ";
+ result += cpuStr;
+ }
+ else
+ result += "Unknown";
+#else
+ result += "Unknown";
+#endif
+ result += ")";
+
+ if (GetAppName() != "Kodi")
+ result += " Kodi_Fork_" + GetAppName() + "/1.0"; // default fork number is '1.0', replace it with actual number if necessary
+
+#ifdef TARGET_LINUX
+ // Add distribution name
+ std::string linuxOSName(GetOsName(true));
+ if (!linuxOSName.empty())
+ result += " " + linuxOSName + "/" + GetOsVersion();
+#endif
+
+#ifdef TARGET_RASPBERRY_PI
+ result += " HW_RaspberryPi/1.0";
+#elif defined (TARGET_DARWIN_IOS)
+ std::string iDevVer;
+ if (iDevStrDigit == std::string::npos)
+ iDevVer = "0.0";
+ else
+ iDevVer.assign(iDevStr, iDevStrDigit, std::string::npos);
+ StringUtils::Replace(iDevVer, ',', '.');
+ result += " HW_" + iDev + "/" + iDevVer;
+#endif
+ // add more device IDs here if needed.
+ // keep only one device ID in result! Form:
+ // result += " HW_" + "deviceID" + "/" + "1.0"; // '1.0' if device has no version
+
+#if defined(TARGET_ANDROID)
+ // Android has no CPU string by default, so add it as additional parameter
+ struct utsname un1;
+ if (uname(&un1) == 0)
+ {
+ std::string cpuStr(un1.machine);
+ StringUtils::Replace(cpuStr, ' ', '_');
+ result += " Sys_CPU/" + cpuStr;
+ }
+#endif
+
+ result += " App_Bitness/" + StringUtils::Format("%d", GetXbmcBitness());
+
+ std::string fullVer(g_infoManager.GetLabel(SYSTEM_BUILD_VERSION));
+ StringUtils::Replace(fullVer, ' ', '-');
+ result += " Version/" + fullVer;
+
+ return result;
+}
+
+bool CSysInfo::IsAppleTV2()
+{
+#if defined(TARGET_DARWIN)
+ return CDarwinUtils::IsAppleTV2();
+#else
+ return false;
+#endif
+}
+
+bool CSysInfo::HasVideoToolBoxDecoder()
+{
+#if defined(HAVE_VIDEOTOOLBOXDECODER)
+ return CDarwinUtils::HasVideoToolboxDecoder();
+#else
+ return false;
+#endif
+}
+
+std::string CSysInfo::GetBuildTargetPlatformName(void)
+{
+#if defined(TARGET_DARWIN_OSX)
+ return "OS X";
+#elif defined(TARGET_DARWIN_IOS_ATV2)
+ return "iOS ATV2";
+#elif defined(TARGET_DARWIN_IOS)
+ return "iOS";
+#elif defined(TARGET_FREEBSD)
+ return "FreeBSD";
+#elif defined(TARGET_ANDROID)
+ return "Android";
+#elif defined(TARGET_LINUX)
+ return "Linux";
+#elif defined(TARGET_WINDOWS)
+#ifdef NTDDI_VERSION
+ return "Windows NT";
+#else // !NTDDI_VERSION
+ return "unknown Win32 platform";
+#endif // !NTDDI_VERSION
+#else
+ return "unknown platform";
+#endif
+}
+
+std::string CSysInfo::GetBuildTargetPlatformVersion(void)
+{
+#if defined(TARGET_DARWIN_OSX)
+ return XSTR_MACRO(__MAC_OS_X_VERSION_MIN_REQUIRED);
+#elif defined(TARGET_DARWIN_IOS)
+ return XSTR_MACRO(__IPHONE_OS_VERSION_MIN_REQUIRED);
+#elif defined(TARGET_FREEBSD)
+ return XSTR_MACRO(__FreeBSD_version);
+#elif defined(TARGET_ANDROID)
+ return "API level " XSTR_MACRO(__ANDROID_API__);
+#elif defined(TARGET_LINUX)
+ return XSTR_MACRO(LINUX_VERSION_CODE);
+#elif defined(TARGET_WINDOWS)
+#ifdef NTDDI_VERSION
+ return XSTR_MACRO(NTDDI_VERSION);
+#else // !NTDDI_VERSION
+ return "(unknown Win32 platform)";
+#endif // !NTDDI_VERSION
+#else
+ return "(unknown platform)";
+#endif
+}
+
+std::string CSysInfo::GetBuildTargetPlatformVersionDecoded(void)
+{
+#if defined(TARGET_DARWIN_OSX)
+#if defined(MAC_OS_X_VERSION_10_10) && __MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10
+ if (__MAC_OS_X_VERSION_MIN_REQUIRED % 10)
+ return StringUtils::Format("version %d.%d", (__MAC_OS_X_VERSION_MIN_REQUIRED / 1000) % 100, (__MAC_OS_X_VERSION_MIN_REQUIRED / 10) % 100);
+ else
+ return StringUtils::Format("version %d.%d.%d", (__MAC_OS_X_VERSION_MIN_REQUIRED / 1000) % 100,
+ (__MAC_OS_X_VERSION_MIN_REQUIRED / 10) % 100, __MAC_OS_X_VERSION_MIN_REQUIRED % 10);
+#else // defined(MAC_OS_X_VERSION_10_10) && __MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10
+ if (__MAC_OS_X_VERSION_MIN_REQUIRED % 10)
+ return StringUtils::Format("version %d.%d", (__MAC_OS_X_VERSION_MIN_REQUIRED / 100) % 100, (__MAC_OS_X_VERSION_MIN_REQUIRED / 10) % 10);
+ else
+ return StringUtils::Format("version %d.%d.%d", (__MAC_OS_X_VERSION_MIN_REQUIRED / 100) % 100,
+ (__MAC_OS_X_VERSION_MIN_REQUIRED / 10) % 10, __MAC_OS_X_VERSION_MIN_REQUIRED % 10);
+#endif // defined(MAC_OS_X_VERSION_10_10) && __MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10
+#elif defined(TARGET_DARWIN_IOS)
+ return StringUtils::Format("version %d.%d.%d", (__IPHONE_OS_VERSION_MIN_REQUIRED / 10000) % 100,
+ (__IPHONE_OS_VERSION_MIN_REQUIRED / 100) % 100, __IPHONE_OS_VERSION_MIN_REQUIRED % 100);
+#elif defined(TARGET_FREEBSD)
+ // FIXME: should works well starting from FreeBSD 8.1
+ static const int major = (__FreeBSD_version / 100000) % 100;
+ static const int minor = (__FreeBSD_version / 1000) % 100;
+ static const int Rxx = __FreeBSD_version % 1000;
+ if ((major < 9 && Rxx == 0) ||
+ __FreeBSD_version == 900044 || __FreeBSD_version == 901000)
+ return StringUtils::Format("version %d.%d-RELEASE", major, minor);
+ if (Rxx >= 500)
+ return StringUtils::Format("version %d.%d-STABLE", major, minor);
+
+ return StringUtils::Format("version %d.%d-CURRENT", major, minor);
+#elif defined(TARGET_ANDROID)
+ return "API level " XSTR_MACRO(__ANDROID_API__);
+#elif defined(TARGET_LINUX)
+ return StringUtils::Format("version %d.%d.%d", (LINUX_VERSION_CODE >> 16) & 0xFF , (LINUX_VERSION_CODE >> 8) & 0xFF, LINUX_VERSION_CODE & 0xFF);
+#elif defined(TARGET_WINDOWS)
+#ifdef NTDDI_VERSION
+ std::string version(StringUtils::Format("version %d.%d", int(NTDDI_VERSION >> 24) & 0xFF, int(NTDDI_VERSION >> 16) & 0xFF));
+ if (SPVER(NTDDI_VERSION))
+ version += StringUtils::Format(" SP%d", int(SPVER(NTDDI_VERSION)));
+ return version;
+#else // !NTDDI_VERSION
+ return "(unknown Win32 platform)";
+#endif // !NTDDI_VERSION
+#else
+ return "(unknown platform)";
+#endif
+}
+
+std::string CSysInfo::GetBuildTargetCpuFamily(void)
+{
+#if defined(__thumb__) || defined(_M_ARMT)
+ return "ARM (Thumb)";
+#elif defined(__arm__) || defined(_M_ARM) || defined (__aarch64__)
+ return "ARM";
+#elif defined(__mips__) || defined(mips) || defined(__mips)
+ return "MIPS";
+#elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) || \
+ defined(i386) || defined(__i386) || defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(_M_IX86) || defined(_X86_)
+ return "x86";
+#elif defined(__powerpc) || defined(__powerpc__) || defined(__powerpc64__) || defined(__ppc__) || defined(__ppc64__) || defined(_M_PPC)
+ return "PowerPC";
+#else
+ return "unknown CPU family";
+#endif
+}
+
+std::string CSysInfo::GetUsedCompilerNameAndVer(void)
+{
+#if defined(__clang__)
+#ifdef __clang_version__
+ return "Clang " __clang_version__;
+#else // ! __clang_version__
+ return "Clang " XSTR_MACRO(__clang_major__) "." XSTR_MACRO(__clang_minor__) "." XSTR_MACRO(__clang_patchlevel__);
+#endif //! __clang_version__
+#elif defined (__INTEL_COMPILER)
+ return "Intel Compiler " XSTR_MACRO(__INTEL_COMPILER);
+#elif defined (__GNUC__)
+ std::string compilerStr;
+#ifdef __llvm__
+ /* Note: this will not detect GCC + DragonEgg */
+ compilerStr = "llvm-gcc ";
+#else // __llvm__
+ compilerStr = "GCC ";
+#endif // !__llvm__
+ compilerStr += XSTR_MACRO(__GNUC__) "." XSTR_MACRO(__GNUC_MINOR__) "." XSTR_MACRO(__GNUC_PATCHLEVEL__);
+ return compilerStr;
+#elif defined (_MSC_VER)
+ return "MSVC " XSTR_MACRO(_MSC_FULL_VER);
+#else
+ return "unknown compiler";
+#endif
+}
+
+
+CJob *CSysInfo::GetJob() const
+{
+ return new CSysInfoJob();
+}
+
+void CSysInfo::OnJobComplete(unsigned int jobID, bool success, CJob *job)
+{
+ m_info = ((CSysInfoJob *)job)->GetData();
+ CInfoLoader::OnJobComplete(jobID, success, job);
+}
diff --git a/src/utils/SystemInfo.h b/src/utils/SystemInfo.h
new file mode 100644
index 0000000000..0a96625802
--- /dev/null
+++ b/src/utils/SystemInfo.h
@@ -0,0 +1,157 @@
+#pragma once
+
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "md5.h"
+#include "InfoLoader.h"
+#include "settings/lib/ISubSettings.h"
+#include <string>
+
+#define KB (1024) // 1 KiloByte (1KB) 1024 Byte (2^10 Byte)
+#define MB (1024*KB) // 1 MegaByte (1MB) 1024 KB (2^10 KB)
+#define GB (1024*MB) // 1 GigaByte (1GB) 1024 MB (2^10 MB)
+#define TB (1024*GB) // 1 TerraByte (1TB) 1024 GB (2^10 GB)
+
+#define MAX_KNOWN_ATTRIBUTES 46
+
+
+class CSysData
+{
+public:
+ enum INTERNET_STATE { UNKNOWN, CONNECTED, DISCONNECTED };
+ CSysData()
+ {
+ Reset();
+ };
+
+ void Reset()
+ {
+ internetState = UNKNOWN;
+ };
+
+ std::string systemUptime;
+ std::string systemTotalUptime;
+ INTERNET_STATE internetState;
+ std::string videoEncoder;
+ std::string cpuFrequency;
+ std::string osVersionInfo;
+ std::string macAddress;
+ std::string batteryLevel;
+};
+
+class CSysInfoJob : public CJob
+{
+public:
+ CSysInfoJob();
+
+ virtual bool DoWork();
+ const CSysData &GetData() const;
+
+ static CSysData::INTERNET_STATE GetInternetState();
+private:
+ static bool SystemUpTime(int iInputMinutes, int &iMinutes, int &iHours, int &iDays);
+ static double GetCPUFrequency();
+ static std::string GetSystemUpTime(bool bTotalUptime);
+ static std::string GetCPUFreqInfo();
+ static std::string GetMACAddress();
+ static std::string GetVideoEncoder();
+ static std::string GetBatteryLevel();
+
+ CSysData m_info;
+};
+
+class CSysInfo : public CInfoLoader, public ISubSettings
+{
+public:
+ enum WindowsVersion
+ {
+ WindowsVersionUnknown = -1, // Undetected, unsupported Windows version or OS in not Windows
+ WindowsVersionVista, // Windows Vista, Windows Server 2008
+ WindowsVersionWin7, // Windows 7, Windows Server 2008 R2
+ WindowsVersionWin8, // Windows 8, Windows Server 2012
+ WindowsVersionWin8_1, // Windows 8.1
+ /* Insert new Windows versions here, when they'll be known */
+ WindowsVersionFuture = 100 // Future Windows version, not known to code
+ };
+
+ CSysInfo(void);
+ virtual ~CSysInfo();
+
+ virtual bool Load(const TiXmlNode *settings);
+ virtual bool Save(TiXmlNode *settings) const;
+
+ char MD5_Sign[32 + 1];
+
+ static const std::string& GetAppName(void); // the same name as CCompileInfo::GetAppName(), but const ref to std::string
+
+ static std::string GetKernelName(bool emptyIfUnknown = false);
+ static std::string GetKernelVersionFull(void); // full version string, including "-generic", "-RELEASE" etc.
+ static std::string GetKernelVersion(void); // only digits with dots
+ static std::string GetOsName(bool emptyIfUnknown = false);
+ static std::string GetOsVersion(void);
+ static std::string GetOsPrettyNameWithVersion(void);
+ static std::string GetUserAgent();
+ bool HasInternet();
+ bool IsAppleTV2();
+ bool HasVideoToolBoxDecoder();
+ bool IsAeroDisabled();
+ bool HasHW3DInterlaced();
+ static bool IsWindowsVersion(WindowsVersion ver);
+ static bool IsWindowsVersionAtLeast(WindowsVersion ver);
+ static WindowsVersion GetWindowsVersion();
+ static int GetKernelBitness(void);
+ static int GetXbmcBitness(void);
+ static const std::string& GetKernelCpuFamily(void);
+ std::string GetCPUModel();
+ std::string GetCPUBogoMips();
+ std::string GetCPUHardware();
+ std::string GetCPURevision();
+ std::string GetCPUSerial();
+ static std::string GetManufacturerName(void);
+ static std::string GetModelName(void);
+ bool GetDiskSpace(const std::string& drive,int& iTotal, int& iTotalFree, int& iTotalUsed, int& iPercentFree, int& iPercentUsed);
+ std::string GetHddSpaceInfo(int& percent, int drive, bool shortText=false);
+ std::string GetHddSpaceInfo(int drive, bool shortText=false);
+
+ int GetTotalUptime() const { return m_iSystemTimeTotalUp; }
+ void SetTotalUptime(int uptime) { m_iSystemTimeTotalUp = uptime; }
+
+ static std::string GetBuildTargetPlatformName(void);
+ static std::string GetBuildTargetPlatformVersion(void);
+ static std::string GetBuildTargetPlatformVersionDecoded(void);
+ static std::string GetBuildTargetCpuFamily(void);
+
+ static std::string GetUsedCompilerNameAndVer(void);
+
+protected:
+ virtual CJob *GetJob() const;
+ virtual std::string TranslateInfo(int info) const;
+ virtual void OnJobComplete(unsigned int jobID, bool success, CJob *job);
+
+private:
+ CSysData m_info;
+ static WindowsVersion m_WinVer;
+ int m_iSystemTimeTotalUp; // Uptime in minutes!
+ void Reset();
+};
+
+extern CSysInfo g_sysinfo;
+
diff --git a/src/utils/TextSearch.cpp b/src/utils/TextSearch.cpp
new file mode 100644
index 0000000000..ef0c6ae3aa
--- /dev/null
+++ b/src/utils/TextSearch.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "TextSearch.h"
+
+using namespace std;
+
+CTextSearch::CTextSearch(const CStdString &strSearchTerms, bool bCaseSensitive /* = false */, TextSearchDefault defaultSearchMode /* = SEARCH_DEFAULT_OR */)
+{
+ m_bCaseSensitive = bCaseSensitive;
+ ExtractSearchTerms(strSearchTerms, defaultSearchMode);
+}
+
+CTextSearch::~CTextSearch(void)
+{
+ m_AND.clear();
+ m_OR.clear();
+ m_NOT.clear();
+}
+
+bool CTextSearch::IsValid(void) const
+{
+ return m_AND.size() > 0 || m_OR.size() > 0 || m_NOT.size() > 0;
+}
+
+bool CTextSearch::Search(const CStdString &strHaystack) const
+{
+ if (strHaystack.empty() || !IsValid())
+ return false;
+
+ CStdString strSearch(strHaystack);
+ if (!m_bCaseSensitive)
+ StringUtils::ToLower(strSearch);
+
+ /* check whether any of the NOT terms matches and return false if there's a match */
+ for (unsigned int iNotPtr = 0; iNotPtr < m_NOT.size(); iNotPtr++)
+ {
+ if (strSearch.find(m_NOT.at(iNotPtr)) != std::string::npos)
+ return false;
+ }
+
+ /* check whether at least one of the OR terms matches and return false if there's no match found */
+ bool bFound(m_OR.size() == 0);
+ for (unsigned int iOrPtr = 0; iOrPtr < m_OR.size(); iOrPtr++)
+ {
+ if (strSearch.find(m_OR.at(iOrPtr)) != std::string::npos)
+ {
+ bFound = true;
+ break;
+ }
+ }
+ if (!bFound)
+ return false;
+
+ /* check whether all of the AND terms match and return false if one of them wasn't found */
+ for (unsigned int iAndPtr = 0; iAndPtr < m_AND.size(); iAndPtr++)
+ {
+ if (strSearch.find(m_AND[iAndPtr]) == std::string::npos)
+ return false;
+ }
+
+ /* all ok, return true */
+ return true;
+}
+
+void CTextSearch::GetAndCutNextTerm(CStdString &strSearchTerm, CStdString &strNextTerm)
+{
+ CStdString strFindNext(" ");
+
+ if (StringUtils::EndsWith(strSearchTerm, "\""))
+ {
+ strSearchTerm.erase(0, 1);
+ strFindNext = "\"";
+ }
+
+ size_t iNextPos = strSearchTerm.find(strFindNext);
+ if (iNextPos != std::string::npos)
+ {
+ strNextTerm = strSearchTerm.substr(0, iNextPos);
+ strSearchTerm.erase(0, iNextPos + 1);
+ }
+ else
+ {
+ strNextTerm = strSearchTerm;
+ strSearchTerm.clear();
+ }
+}
+
+void CTextSearch::ExtractSearchTerms(const CStdString &strSearchTerm, TextSearchDefault defaultSearchMode)
+{
+ CStdString strParsedSearchTerm(strSearchTerm);
+ StringUtils::Trim(strParsedSearchTerm);
+
+ if (!m_bCaseSensitive)
+ StringUtils::ToLower(strParsedSearchTerm);
+
+ bool bNextAND(defaultSearchMode == SEARCH_DEFAULT_AND);
+ bool bNextOR(defaultSearchMode == SEARCH_DEFAULT_OR);
+ bool bNextNOT(defaultSearchMode == SEARCH_DEFAULT_NOT);
+
+ while (strParsedSearchTerm.length() > 0)
+ {
+ StringUtils::TrimLeft(strParsedSearchTerm);
+
+ if (StringUtils::StartsWith(strParsedSearchTerm, "!") || StringUtils::StartsWithNoCase(strParsedSearchTerm, "not"))
+ {
+ CStdString strDummy;
+ GetAndCutNextTerm(strParsedSearchTerm, strDummy);
+ bNextNOT = true;
+ }
+ else if (StringUtils::StartsWith(strParsedSearchTerm, "+") || StringUtils::StartsWithNoCase(strParsedSearchTerm, "and"))
+ {
+ CStdString strDummy;
+ GetAndCutNextTerm(strParsedSearchTerm, strDummy);
+ bNextAND = true;
+ }
+ else if (StringUtils::StartsWith(strParsedSearchTerm, "|") || StringUtils::StartsWithNoCase(strParsedSearchTerm, "or"))
+ {
+ CStdString strDummy;
+ GetAndCutNextTerm(strParsedSearchTerm, strDummy);
+ bNextOR = true;
+ }
+ else
+ {
+ CStdString strTerm;
+ GetAndCutNextTerm(strParsedSearchTerm, strTerm);
+ if (strTerm.length() > 0)
+ {
+ if (bNextAND)
+ m_AND.push_back(strTerm);
+ else if (bNextOR)
+ m_OR.push_back(strTerm);
+ else if (bNextNOT)
+ m_NOT.push_back(strTerm);
+ }
+ else
+ {
+ break;
+ }
+
+ bNextAND = (defaultSearchMode == SEARCH_DEFAULT_AND);
+ bNextOR = (defaultSearchMode == SEARCH_DEFAULT_OR);
+ bNextNOT = (defaultSearchMode == SEARCH_DEFAULT_NOT);
+ }
+
+ StringUtils::TrimLeft(strParsedSearchTerm);
+ }
+}
diff --git a/src/utils/TextSearch.h b/src/utils/TextSearch.h
new file mode 100644
index 0000000000..034bb9fd5f
--- /dev/null
+++ b/src/utils/TextSearch.h
@@ -0,0 +1,49 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <vector>
+#include "StringUtils.h"
+
+typedef enum TextSearchDefault
+{
+ SEARCH_DEFAULT_AND = 0,
+ SEARCH_DEFAULT_OR,
+ SEARCH_DEFAULT_NOT
+} TextSearchDefault;
+
+class CTextSearch
+{
+public:
+ CTextSearch(const CStdString &strSearchTerms, bool bCaseSensitive = false, TextSearchDefault defaultSearchMode = SEARCH_DEFAULT_OR);
+ virtual ~CTextSearch(void);
+
+ bool Search(const CStdString &strHaystack) const;
+ bool IsValid(void) const;
+
+private:
+ static void GetAndCutNextTerm(CStdString &strSearchTerm, CStdString &strNextTerm);
+ void ExtractSearchTerms(const CStdString &strSearchTerm, TextSearchDefault defaultSearchMode);
+
+ bool m_bCaseSensitive;
+ std::vector<CStdString> m_AND;
+ std::vector<CStdString> m_OR;
+ std::vector<CStdString> m_NOT;
+};
diff --git a/src/utils/TimeSmoother.cpp b/src/utils/TimeSmoother.cpp
new file mode 100644
index 0000000000..29143f51a7
--- /dev/null
+++ b/src/utils/TimeSmoother.cpp
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2011-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include "TimeSmoother.h"
+#include <math.h>
+#include <limits>
+#include "utils/MathUtils.h"
+
+using namespace std;
+
+CTimeSmoother::CTimeSmoother()
+: m_diffs(num_diffs),
+ m_periods(num_periods),
+ m_period(0),
+ m_lastFrameTime(0),
+ m_prevIn(num_stamps),
+ m_prevOut(num_stamps)
+{
+}
+
+void CTimeSmoother::AddTimeStamp(unsigned int currentTime)
+{
+ double diff = m_prevIn.size() ? currentTime - m_prevIn.back() : currentTime;
+ if (diff)
+ m_diffs.push_back(diff);
+
+ vector<double> bins;
+ BinData(m_diffs, bins, 0.15, 2);
+
+ if (bins.size() && m_diffs.size() == num_diffs)
+ {
+ // have enough data to update our estimate
+ vector<unsigned int> binMultipliers;
+ GetGCDMultipliers(bins, binMultipliers, 2);
+ assert(binMultipliers.size() == bins.size());
+
+ vector<unsigned int> intRepresentation;
+ GetIntRepresentation(m_diffs, intRepresentation, bins, binMultipliers);
+ assert(intRepresentation.size() == m_diffs.size());
+
+ double period = EstimatePeriod(m_diffs, intRepresentation);
+
+ // update our mean period
+ if (fabs(period - m_period) > m_period*0.1)
+ { // more than 10 % out - kill our previous running average
+ m_periods.clear();
+ m_period = 0;
+ }
+ if (m_periods.size() < m_periods.capacity())
+ m_period = (m_period * m_periods.size() + period) / (m_periods.size() + 1);
+ else
+ m_period += (period - m_periods[0]) / m_periods.size();
+ m_periods.push_back(period);
+ }
+ double frameTime = EstimateFrameTime(currentTime);
+ m_prevIn.push_back(currentTime);
+ m_prevOut.push_back(frameTime);
+}
+
+unsigned int CTimeSmoother::GetNextFrameTime(unsigned int currentTime)
+{
+ if (m_period)
+ {
+ double frameTime = EstimateFrameTime(currentTime);
+ // ensure we jump at least 1 period ahead of the last time we were called
+ if (frameTime < m_lastFrameTime + m_period)
+ frameTime = m_lastFrameTime + m_period;
+ // Return an unsigned int in ms, so wrap into that, and round.
+ // Don't use MathUtils::round_int as that's restricted to -2^30..2^30
+ if (frameTime >= UINT_MAX)
+ frameTime = fmod(frameTime, UINT_MAX);
+ m_lastFrameTime = frameTime;
+ return (unsigned int)floor(frameTime + 0.5);
+ }
+ return currentTime;
+}
+
+void CTimeSmoother::BinData(const boost::circular_buffer<double> &data, vector<double> &bins, const double threshold, const unsigned int minbinsize)
+{
+ if (!data.size())
+ return;
+
+ bins.clear();
+ vector<unsigned int> counts;
+
+ for (boost::circular_buffer<double>::const_iterator i = data.begin(); i != data.end(); ++i)
+ {
+ bool found = false;
+ for (unsigned int j = 0; j < bins.size(); ++j)
+ {
+ if (fabs(*i - bins[j]) < threshold*bins[j])
+ {
+ found = true;
+ // update our bin mean and count
+ bins[j] = (bins[j]*counts[j] + *i)/(counts[j]+1);
+ counts[j]++;
+ break;
+ }
+ }
+ if (!found)
+ {
+ bins.push_back(*i);
+ counts.push_back(1);
+ }
+ }
+ if (minbinsize)
+ {
+ assert(counts.size() == bins.size());
+ assert(counts.size());
+ // filter out any bins that are not large enough (and any bins that aren't positive)
+ for (unsigned int j = 0; j < counts.size(); )
+ {
+ if (counts[j] < minbinsize || bins[j] < 0.05)
+ {
+ bins.erase(bins.begin() + j);
+ counts.erase(counts.begin() + j);
+ }
+ else
+ j++;
+ }
+ }
+}
+
+void CTimeSmoother::GetConvergent(double value, unsigned int &num, unsigned int &denom, const unsigned int maxnumden)
+{
+ assert(value >= 1);
+
+ unsigned int old_n = 1, old_d = 0;
+ num = 0; denom = 1;
+
+ // this while loop would typically be guaranteed to terminate as new_n, new_d are increasing non-negative
+ // integers as long as f >= 1. This in turn is guaranteed as f may never be zero as long as value > 1 and
+ // value - f < 1. Given that f = floor(value) this *should* always be true.
+ // However, as f is unsigned int and thus range restricted, we can not guarantee this, and hence
+ // break if value - f >= 1.
+
+ // In addition, just to be on the safe side we don't allow the loop to run forever ;)
+ unsigned int maxLoops = 3 * maxnumden;
+ while (maxLoops--)
+ {
+ unsigned int f = (unsigned int)floor(value);
+ if (value - f >= 1)
+ break; // value out of range of unsigned int
+ unsigned int new_n = f * num + old_n;
+ unsigned int new_d = f * denom + old_d;
+ if (min(new_n, new_d) > maxnumden)
+ break;
+ old_n = num; old_d = denom;
+ num = new_n; denom = new_d;
+ if ((double)f == value)
+ break;
+ value = 1/(value - f);
+ }
+ // ensure num, denom are positive
+ assert(num > 0 && denom > 0);
+}
+
+void CTimeSmoother::GetGCDMultipliers(const vector<double> &data, vector<unsigned int> &multipliers, const unsigned int maxminmult)
+{
+ vector<double>::const_iterator i = std::min_element(data.begin(), data.end());
+
+ multipliers.clear();
+
+ vector<unsigned int> num, denom;
+ for (vector<double>::const_iterator j = data.begin(); j != data.end(); ++j)
+ {
+ if (j != i)
+ {
+ unsigned int n, d;
+ GetConvergent(*j / *i, n, d, maxminmult);
+ num.push_back(n);
+ denom.push_back(d);
+ }
+ else
+ {
+ num.push_back(1);
+ denom.push_back(1);
+ }
+ }
+ vector<unsigned int>::const_iterator k = std::max_element(num.begin(), num.end());
+ for (unsigned int i = 0; i < num.size(); ++i)
+ multipliers.push_back(denom[i] * (*k) / num[i]);
+}
+
+void CTimeSmoother::GetIntRepresentation(const boost::circular_buffer<double> &data, vector<unsigned int> &intData, const vector<double> &bins, const vector<unsigned int> &intBins)
+{
+ intData.clear();
+ for (boost::circular_buffer<double>::const_iterator i = data.begin(); i != data.end(); ++i)
+ {
+ double min_r2 = numeric_limits<double>::max();
+ unsigned int min_j = 0;
+ for (unsigned int j = 0; j < bins.size(); ++j)
+ {
+ double d = MathUtils::round_int(*i/bins[j]);
+ double r2 = (*i - bins[j]*d)*(*i - bins[j]*d);
+ if (r2 < min_r2)
+ {
+ min_j = j;
+ min_r2 = r2;
+ }
+ }
+ intData.push_back(MathUtils::round_int(*i/bins[min_j])*intBins[min_j]);
+ }
+}
+
+double CTimeSmoother::EstimatePeriod(const boost::circular_buffer<double> &data, const vector<unsigned int> &intData)
+{
+ double sxy = 0, sxx = 0;
+ for (unsigned int i = 0; i < data.size(); ++i)
+ {
+ sxy += intData[i] * data[i];
+ sxx += intData[i] * intData[i];
+ }
+ return sxy/sxx;
+}
+
+double CTimeSmoother::EstimateFrameTime(unsigned int currentTime)
+{
+ assert(m_prevIn.size() == m_prevOut.size());
+ if (m_period)
+ {
+ vector<double> outTimes;
+ for (unsigned int i = 0; i < m_prevIn.size(); ++i)
+ outTimes.push_back(m_prevOut[i] + m_period * MathUtils::round_int((currentTime - m_prevIn[i]) / m_period));
+ sort(outTimes.begin(), outTimes.end());
+ double outTime = outTimes[(outTimes.size()-1)/2];
+ if (outTime < m_prevOut.back() + m_period)
+ outTime = m_prevOut.back() + m_period;
+ return outTime;
+ }
+ return currentTime;
+}
diff --git a/src/utils/TimeSmoother.h b/src/utils/TimeSmoother.h
new file mode 100644
index 0000000000..f7f52e265b
--- /dev/null
+++ b/src/utils/TimeSmoother.h
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2011-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#include <vector>
+#include <boost/circular_buffer.hpp>
+
+/*! \brief Class to smooth timestamps
+
+ This class is designed to smooth reasonably regular timestamps into more regular timestamps. It was designed
+ to deal with per-frame timestamps, i.e. timestamps immediately following a back/front buffer flip.
+
+ The main difficultes are:
+ 1. The timestamps are generally noisy.
+ 2. We do not know the vsync rate.
+ 3. We do not know whether or not we've missed flips, either intermittently or due to some cadence (eg 2/3/2/3
+ frame durations)
+
+ Primarily we're interested in what the vsync rate is, i.e. what is the duration of a single frame. To do this,
+ we solve the linear regression
+
+ y_i = b_0 + b_1*x_i + e_i
+
+ where y_i are the time stamps, x_i are the integer frame counts that these timestamps correspond to, b_0 is an offset,
+ b_1 is the frame duration, and e_i is an error term, assumed to be independent, identically distributed as
+ Normal(0, \sigma^2) for some fixed \sigma^2.
+
+ The main difficulty is we do not know the predictors x_i's - all we know is that they're an increasing sequence of
+ integers. Thus, to solve this problem we must estimate both the x_i's, b_0 and b_1. We can eliminate b_0 by operating
+ on the differences t_i = y_i - y_{i-1} and k_i = x_i - x_{i-1} rather than y_i and x_i directly. Further, if we assume
+ that the error in the differences is additive gaussian white noise, by the maximum likelihood principle we may estimate
+ the k_i's and b_1 by minimizing the least squares problem:
+
+ min_{b_1 \in R, k \in Z^M}||t - kb_1||_2^2
+
+ This is linearly separable, and the cost function can be concentrated to k, yielding
+
+ min_{b_1 \in R}||t - b_1 round(t/b_1)||_2^2 ....(1)
+
+ This allows the period b_1 to be estimated without knowledge of the x_i. The main problem with this is we require a
+ limit on the range for b_1, as clearly b_1 / j for any positive integer j also minimizes (1). This presents a problem
+ in the case where we have no knowledge what b_1 is. Furthermore, (1) is typically not smooth, with the concave portion
+ which contains it's minimum often being very small. Thus, in order to minimize (1) we need to evaluate it over a fairly
+ fine grid, between pre-defined limits. This is infeasible.
+
+ Instead, we attack the problem by first attempting to estimate the k_i's. We do this by binning the differences t_i
+ into common categories and then computing the greatest common divisor of those bins. Given the data is noisy, the binning
+ procedure (BinData) has a tolerance for the bin size. To minimize the influence of timestamps that are out of the ordinary
+ we then discard any bins with only one value in them, thus leaving only binned values that are common. We then compute
+ an approximate greatest common divisor by computing the continued fraction expansion of the ratio of pairs of bins, to
+ produce integer divisors that produce a common divisor. We restrict the size of these integer divisors so that our divisor
+ is not too small - typically we're wanting to pick up simple cadences such as 1,2,1,2 and 2,3,2,3 but are unlikely to need
+ to detect 5,4,5,4 etc - at that point the CPU/GPU is limiting the frame rate so much that timing issues aren't likely to be
+ the largest issue.
+
+ Once we have the GCD we can then compute the integers k_i. Given k_i we can then estimate b_1 using a standard simple
+ linear regression without intercept:
+
+ t_i = b_1*x_i + e_i
+
+ which can be solved in the standard manner.
+
+ Our procedure is thus as follows:
+ 1. We collect K differences, where K is enough to ensure we get an estimate of the period, but not too large so that the
+ computation is unnecessarily long.
+ 2. We then keep a running average of our period that is fairly long in order to stabilise the period over time.
+ 3. To allow the running average to adapt quickly to framerate changes, if the newly computed period is much different
+ from our running average, we start a new running average.
+ 4. Given our period, we then estimate our noise-free timestamps by rounding to the nearest multiple of the period
+ from past time points. We use several time points and choose the median such point as our final point. This allows
+ for deciding whether to round up or round down in cases where it may not be particularly obvious.
+ 5. Finally, we ensure the timestamps always move forward by the period.
+
+ This works well for the most part. Note that we're estimating a noise-free version of the last timestamp passed in,
+ we do not claim to be estimating the timestamp of the next frame time. Such an estimate is essentially indeterminant, as
+ the time to process the frame is non-constant.
+ */
+
+class CTimeSmoother
+{
+public:
+ CTimeSmoother();
+
+ /*! \brief Add a valid time stamp to the time smoother
+ This function will add a time stamp to the smoother and use it to update the current average frame rate
+ and estimate the current cadence. The next frame time can be retrieved using GetNextFrameTime.
+ \param currentTime the current time stamp to add to the smoother
+ \sa GetNextFrameTime
+ */
+ void AddTimeStamp(unsigned int currentTime);
+
+ /*! \brief Retreive an estimate of the next frame time, based on the current time
+ This function uses previously estimated average frame rates and the current cadence to estimate the next
+ frame time.
+ \param currentTime the current time stamp to use to estimate the next frame time
+ \return the estimated time the next frame will be displayed
+ \sa AddTimeStamp
+ */
+ unsigned int GetNextFrameTime(unsigned int currentTime);
+
+protected:
+ /*! \brief Bin data into separate clusters, determined by a given threshold and minimum bin size.
+ \param data a circular buffer of data points
+ \param bins the bins to return
+ \param threshold the threshold to determine whether a data point is close to a given bin as a proportion of bin mean
+ \param minbinsize the minimum bin size of each bin
+ */
+ static void BinData(const boost::circular_buffer<double> &data, std::vector<double> &bins, const double threshold, const unsigned int minbinsize);
+
+ /*! \brief Given a real value, find a rational convergent
+ Uses a continued fraction expansion of value to determine the numerator and denominator of a rational convergent
+ where min(num, denom) does not exceed maxnumdem
+ \param value real data value. Must be no less than 1.
+ \param num [out] the numerator
+ \param denom [out] the denominator
+ \param maxnumden the maximal value of min(num, denom)
+ */
+ static void GetConvergent(double value, unsigned int &num, unsigned int &denom, const unsigned int maxnumden);
+
+ /*! \brief Given a set of data, find integer multipliers such that data[i] \sim quotient[i] * gcd(data)
+ Uses rational convergents to data[i]/min(data) to find integer multipliers to the (approximate) greatest common divisor
+ of the data.
+ \param data a vector of data
+ \param multipliers the output multipliers
+ \param maxminmult the maximal value of multiplier[min(data)]
+ \sa GetConvergent
+ */
+ static void GetGCDMultipliers(const std::vector<double> &data, std::vector<unsigned int> &multipliers, const unsigned int maxminmult);
+
+ /*! \brief Given a set of bins and integer values associated with each bin, find the integer representation of some data
+ This allows noisy data to be approximated by a set of clean data, and to compute the integer representation of that data.
+ \param data the data on which to compute the integer representation
+ \param intData [out] the integer representation of the data
+ \param bins the bins to use for approximating the data
+ \param intBins the integer representation of the bins
+ */
+ static void GetIntRepresentation(const boost::circular_buffer<double> &data, std::vector<unsigned int> &intData, const std::vector<double> &bins, const std::vector<unsigned int> &intBins);
+
+ /*! \brief Given a set of data, and an integer representation of that data, estimate the period of the data
+ Essentially we solve a linear regression d_i = \theta*z_i, where d_i is the original data, and z_i is the integer
+ representation of that data. Note that no intercept is included, so this is appropriate for operating on difference data
+ (such as the difference between timestamps following flipping of buffers during rendering - the integers represent the vsync
+ sequence).
+ \param data noisy data to estimate the period of
+ \param intData an integral representation of the data
+ \return the period of the data
+ */
+ static double EstimatePeriod(const boost::circular_buffer<double> &data, const std::vector<unsigned int> &intData);
+
+ /*! \brief Compute the next frame time
+ \param currentTime the current time
+ \return the next frame time
+ */
+ double EstimateFrameTime(unsigned int currentTime);
+
+private:
+ static const unsigned int num_diffs = 10; ///< \brief number of differences to keep for evaluating period
+ static const unsigned int num_periods = 100; ///< \brief number of previous period estimates to use for the average period
+ static const unsigned int num_stamps = 3; ///< \brief number of previous time stamps to keep to optimize next time point
+
+ boost::circular_buffer<double> m_diffs; ///< \brief the recently received differences
+ boost::circular_buffer<double> m_periods; ///< \brief the recently evaluated periods
+ double m_period; ///< \brief the running average of m_periods
+
+ double m_lastFrameTime; ///< \brief the last frame time
+
+ boost::circular_buffer<double> m_prevIn; ///< \brief the previous timestamps coming in
+ boost::circular_buffer<double> m_prevOut; ///< \brief the previous timestamps going out
+};
diff --git a/src/utils/TimeUtils.cpp b/src/utils/TimeUtils.cpp
new file mode 100644
index 0000000000..9f2e1350a8
--- /dev/null
+++ b/src/utils/TimeUtils.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "TimeUtils.h"
+#include "XBDateTime.h"
+#include "threads/SystemClock.h"
+
+#if (defined HAVE_CONFIG_H) && (!defined TARGET_WINDOWS)
+ #include "config.h"
+#endif
+
+#if defined(TARGET_DARWIN)
+#include <mach/mach_time.h>
+#include <CoreVideo/CVHostTime.h>
+#elif defined(TARGET_WINDOWS)
+#include <windows.h>
+#else
+#include <time.h>
+#endif
+
+#include "TimeSmoother.h"
+
+int64_t CurrentHostCounter(void)
+{
+#if defined(TARGET_DARWIN)
+ return( (int64_t)CVGetCurrentHostTime() );
+#elif defined(TARGET_WINDOWS)
+ LARGE_INTEGER PerformanceCount;
+ QueryPerformanceCounter(&PerformanceCount);
+ return( (int64_t)PerformanceCount.QuadPart );
+#else
+ struct timespec now;
+#ifdef CLOCK_MONOTONIC_RAW
+ clock_gettime(CLOCK_MONOTONIC_RAW, &now);
+#else
+ clock_gettime(CLOCK_MONOTONIC, &now);
+#endif // CLOCK_MONOTONIC_RAW
+ return( ((int64_t)now.tv_sec * 1000000000L) + now.tv_nsec );
+#endif
+}
+
+int64_t CurrentHostFrequency(void)
+{
+#if defined(TARGET_DARWIN)
+ return( (int64_t)CVGetHostClockFrequency() );
+#elif defined(TARGET_WINDOWS)
+ LARGE_INTEGER Frequency;
+ QueryPerformanceFrequency(&Frequency);
+ return( (int64_t)Frequency.QuadPart );
+#else
+ return( (int64_t)1000000000L );
+#endif
+}
+
+CTimeSmoother CTimeUtils::frameTimer;
+unsigned int CTimeUtils::frameTime = 0;
+
+void CTimeUtils::UpdateFrameTime(bool flip)
+{
+ unsigned int currentTime = XbmcThreads::SystemClockMillis();
+ if (flip)
+ frameTimer.AddTimeStamp(currentTime);
+ frameTime = frameTimer.GetNextFrameTime(currentTime);
+}
+
+unsigned int CTimeUtils::GetFrameTime()
+{
+ return frameTime;
+}
+
+CDateTime CTimeUtils::GetLocalTime(time_t time)
+{
+ CDateTime result;
+
+ tm *local;
+#ifdef HAVE_LOCALTIME_R
+ tm res = {};
+ local = localtime_r(&time, &res); // Conversion to local time
+#else
+ local = localtime(&time); // Conversion to local time
+#endif
+ /*
+ * Microsoft implementation of localtime returns NULL if on or before epoch.
+ * http://msdn.microsoft.com/en-us/library/bf12f0hc(VS.80).aspx
+ */
+ if (local)
+ result = *local;
+ else
+ result = time; // Use the original time as close enough.
+
+ return result;
+}
diff --git a/src/utils/TimeUtils.h b/src/utils/TimeUtils.h
new file mode 100644
index 0000000000..f8796d5c76
--- /dev/null
+++ b/src/utils/TimeUtils.h
@@ -0,0 +1,43 @@
+#pragma once
+
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdint.h>
+#include <time.h>
+
+class CDateTime;
+class CTimeSmoother;
+
+int64_t CurrentHostCounter(void);
+int64_t CurrentHostFrequency(void);
+
+class CTimeUtils
+{
+public:
+ static void UpdateFrameTime(bool flip); ///< update the frame time. Not threadsafe
+ static unsigned int GetFrameTime(); ///< returns the frame time in MS. Not threadsafe
+ static CDateTime GetLocalTime(time_t time);
+
+private:
+ static unsigned int frameTime;
+ static CTimeSmoother frameTimer;
+};
+
diff --git a/src/utils/TuxBoxUtil.cpp b/src/utils/TuxBoxUtil.cpp
new file mode 100644
index 0000000000..70cc98e813
--- /dev/null
+++ b/src/utils/TuxBoxUtil.cpp
@@ -0,0 +1,1649 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+//
+// GeminiServer
+//
+
+#include "TuxBoxUtil.h"
+#include "URIUtils.h"
+#include "filesystem/CurlFile.h"
+#include "dialogs/GUIDialogContextMenu.h"
+#include "Application.h"
+#include "ApplicationMessenger.h"
+#include "GUIInfoManager.h"
+#include "video/VideoInfoTag.h"
+#include "guilib/GUIWindowManager.h"
+#include "dialogs/GUIDialogOK.h"
+#include "dialogs/GUIDialogYesNo.h"
+#include "filesystem/File.h"
+#include "URL.h"
+#include "settings/AdvancedSettings.h"
+#include "FileItem.h"
+#include "guilib/LocalizeStrings.h"
+#include "StringUtils.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/XMLUtils.h"
+#include "log.h"
+
+using namespace XFILE;
+using namespace std;
+
+CTuxBoxUtil g_tuxbox;
+CTuxBoxService g_tuxboxService;
+
+CTuxBoxService::CTuxBoxService() : CThread("TuxBoxService")
+{
+}
+CTuxBoxService::~CTuxBoxService()
+{
+}
+CTuxBoxUtil::CTuxBoxUtil(void)
+{
+ sCurSrvData.requested_audio_channel = 0;
+ vVideoSubChannel.mode = false;
+ sZapstream.initialized = false;
+ sZapstream.available = false;
+}
+CTuxBoxUtil::~CTuxBoxUtil(void)
+{
+}
+bool CTuxBoxService::Start()
+{
+ if(g_advancedSettings.m_iTuxBoxEpgRequestTime != 0)
+ {
+ StopThread();
+ Create(false);
+ return true;
+ }
+ else
+ return false;
+}
+void CTuxBoxService::Stop()
+{
+ CLog::Log(LOGDEBUG, "%s - Stopping CTuxBoxService thread", __FUNCTION__);
+ StopThread();
+}
+void CTuxBoxService::OnStartup()
+{
+ CLog::Log(LOGDEBUG, "%s - Starting CTuxBoxService thread", __FUNCTION__);
+ SetPriority( GetMinPriority() );
+}
+void CTuxBoxService::OnExit()
+{
+ CThread::m_bStop = true;
+}
+bool CTuxBoxService::IsRunning()
+{
+ return !CThread::m_bStop;
+}
+void CTuxBoxService::Process()
+{
+ std::string strCurrentServiceName = g_tuxbox.sCurSrvData.service_name;
+ std::string strURL;
+
+ while(!CThread::m_bStop && g_application.m_pPlayer->IsPlaying())
+ {
+ strURL = g_application.CurrentFileItem().GetPath();
+ if(!URIUtils::IsTuxBox(strURL))
+ break;
+
+ int iRequestTimer = g_advancedSettings.m_iTuxBoxEpgRequestTime *1000; //seconds
+ Sleep(iRequestTimer);
+
+ CURL url(strURL);
+ if(g_tuxbox.GetHttpXML(url,"currentservicedata"))
+ {
+ CLog::Log(LOGDEBUG, "%s - receive current service data was successful", __FUNCTION__);
+ if(!strCurrentServiceName.empty()&&
+ strCurrentServiceName != "NULL" &&
+ !g_tuxbox.sCurSrvData.service_name.empty() &&
+ g_tuxbox.sCurSrvData.service_name != "-" &&
+ !g_tuxbox.vVideoSubChannel.mode)
+ {
+ //Detect Channel Change
+ //We need to detect the channel on the TuxBox Device!
+ //On changing the channel on the device we will loose the stream and mplayer seems not able to detect it to stop
+ if (strCurrentServiceName != g_tuxbox.sCurSrvData.service_name && g_application.m_pPlayer->IsPlaying() && !g_tuxbox.sZapstream.available)
+ {
+ CLog::Log(LOGDEBUG," - ERROR: Non controlled channel change detected! Stopping current playing stream!");
+ CApplicationMessenger::Get().MediaStop();
+ break;
+ }
+ }
+ //Update infomanager from tuxbox client
+ g_infoManager.UpdateFromTuxBox();
+ }
+ else
+ CLog::Log(LOGDEBUG, "%s - Could not receive current service data", __FUNCTION__);
+ }
+}
+bool CTuxBoxUtil::CreateNewItem(const CFileItem& item, CFileItem& item_new)
+{
+ //Build new Item
+ item_new.SetLabel(item.GetLabel());
+ item_new.SetPath(item.GetPath());
+ item_new.SetArt("thumb", item.GetArt("thumb"));
+
+ if(g_tuxbox.GetZapUrl(item.GetPath(), item_new))
+ {
+ if(vVideoSubChannel.mode)
+ vVideoSubChannel.current_name = item_new.GetLabel()+" ("+vVideoSubChannel.current_name+")";
+ return true;
+ }
+ else
+ {
+ if(sBoxStatus.recording != "1") //Don't Show this Dialog, if the Box is in Recording mode! A previos YN Dialog was send to user!
+ {
+ CLog::Log(LOGDEBUG, "%s ---------------------------------------------------------", __FUNCTION__);
+ CLog::Log(LOGDEBUG, "%s - WARNING: Zaping Failed no Zap Point found!", __FUNCTION__);
+ CLog::Log(LOGDEBUG, "%s ---------------------------------------------------------", __FUNCTION__);
+ std::string strText = StringUtils::Format(g_localizeStrings.Get(21334).c_str(), item.GetLabel().c_str());
+ CGUIDialogOK::ShowAndGetInput(21331, strText, 21333, 0);
+ }
+ }
+ return false;
+}
+bool CTuxBoxUtil::ParseBouquets(TiXmlElement *root, CFileItemList &items, CURL &url, std::string strFilter, std::string strChild)
+{
+ std::string strOptions;
+ TiXmlElement *pRootElement =root;
+ TiXmlNode *pNode = NULL;
+ TiXmlNode *pIt = NULL;
+ items.m_idepth =1;
+ // Get Options
+ strOptions = url.GetOptions();
+
+ if (!pRootElement)
+ {
+ CLog::Log(LOGWARNING, "%s - No %s found", __FUNCTION__, strChild.c_str());
+ return false;
+ }
+ if (strFilter.empty())
+ {
+ pNode = pRootElement->FirstChild(strChild.c_str());
+ if (!pNode)
+ {
+ CLog::Log(LOGWARNING, "%s - No %s found", __FUNCTION__,strChild.c_str());
+ return false;
+ }
+ while(pNode)
+ {
+ pIt = pNode->FirstChild("name");
+ if (pIt)
+ {
+ std::string strItemName = pIt->FirstChild()->Value();
+
+ pIt = pNode->FirstChild("reference");
+ if (pIt)
+ {
+ std::string strItemPath = pIt->FirstChild()->Value();
+ // add. bouquets to item list!
+ CFileItemPtr pItem(new CFileItem);
+ pItem->m_bIsFolder = true;
+ pItem->SetLabel(strItemName);
+ {
+ CURL fileUrl;
+ fileUrl.SetProtocol("tuxbox");
+ fileUrl.SetUserName(url.GetUserName());
+ fileUrl.SetPassword(url.GetPassWord());
+ fileUrl.SetHostName(url.GetHostName());
+ if (url.GetPort() != 0 && url.GetPort() != 80)
+ fileUrl.SetPort(url.GetPort());
+ fileUrl.SetOptions(url.GetOptions());
+ fileUrl.SetOption("reference", strItemPath);
+ pItem->SetPath(fileUrl.Get());
+ }
+ items.Add(pItem);
+ //DEBUG Log
+ CLog::Log(LOGDEBUG, "%s - Name: %s", __FUNCTION__,strItemName.c_str());
+ CLog::Log(LOGDEBUG, "%s - Adress: %s", __FUNCTION__,pItem->GetPath().c_str());
+ }
+ }
+ pNode = pNode->NextSibling(strChild.c_str());
+ }
+ }
+ return true;
+}
+bool CTuxBoxUtil::ParseBouquetsEnigma2(TiXmlElement *root, CFileItemList &items, CURL &url, std::string& strFilter, std::string& strChild)
+{
+ TiXmlElement *pRootElement = root;
+ TiXmlNode *pNode = NULL;
+ TiXmlNode *pIt = NULL;
+ items.m_idepth = 1;
+
+ if (!pRootElement)
+ {
+ CLog::Log(LOGWARNING, "%s - No %s found", __FUNCTION__, strChild.c_str());
+ return false;
+ }
+ if (strFilter.empty())
+ {
+ pNode = pRootElement->FirstChildElement("e2bouquet");
+ if (!pNode)
+ {
+ CLog::Log(LOGWARNING, "%s - No %s found", __FUNCTION__,strChild.c_str());
+ return false;
+ }
+ while(pNode)
+ {
+ CFileItemPtr pItem(new CFileItem);
+ pIt = pNode->FirstChildElement("e2servicereference");
+ std::string strItemPath = pIt->FirstChild()->Value();
+ pIt = pNode->FirstChildElement("e2servicename");
+ std::string strItemName = pIt->FirstChild()->Value();
+ pItem->m_bIsFolder = true;
+ pItem->SetLabel(strItemName);
+ {
+ CURL fileUrl;
+ fileUrl.SetProtocol("tuxbox");
+ fileUrl.SetHostName(url.GetHostName());
+ if (url.GetPort() != 0 && url.GetPort() != 80)
+ fileUrl.SetPort(url.GetPort());
+ fileUrl.SetFileName(strItemName + "/");
+ pItem->SetPath(fileUrl.Get());
+ }
+ items.Add(pItem);
+ pNode = pNode->NextSiblingElement("e2bouquet");
+ }
+ }
+ return true;
+}
+bool CTuxBoxUtil::ParseChannels(TiXmlElement *root, CFileItemList &items, CURL &url, std::string strFilter, std::string strChild)
+{
+ TiXmlElement *pRootElement =root;
+ TiXmlNode *pNode = NULL;
+ TiXmlNode *pIt = NULL;
+ TiXmlNode *pIta = NULL;
+ items.m_idepth =2;
+
+ if (!pRootElement)
+ {
+ CLog::Log(LOGWARNING, "%s - No %ss found", __FUNCTION__,strChild.c_str());
+ return false;
+ }
+ if(!strFilter.empty())
+ {
+ pNode = pRootElement->FirstChild(strChild.c_str());
+ if (!pNode)
+ {
+ CLog::Log(LOGWARNING, "%s - No %s found", __FUNCTION__,strChild.c_str());
+ return false;
+ }
+ while(pNode)
+ {
+ pIt = pNode->FirstChild("name");
+ if (pIt)
+ {
+ std::string strItemName = pIt->FirstChild()->Value();
+
+ pIt = pNode->FirstChild("reference");
+ if (strFilter == pIt->FirstChild()->Value())
+ {
+ pIt = pNode->FirstChild("service");
+ if (!pIt)
+ {
+ CLog::Log(LOGWARNING, "%s - No service found", __FUNCTION__);
+ return false;
+ }
+ while(pIt)
+ {
+ pIta = pIt->FirstChild("name");
+ if (pIta)
+ {
+ strItemName = pIta->FirstChild()->Value();
+
+ pIta = pIt->FirstChild("reference");
+ if (pIta)
+ {
+ std::string strItemPath = pIta->FirstChild()->Value();
+ // channel listing add. to item list!
+ CFileItemPtr pbItem(new CFileItem);
+ pbItem->m_bIsFolder = false;
+ pbItem->SetLabel(strItemName);
+ pbItem->SetLabelPreformated(true);
+ {
+ CURL fileUrl;
+ fileUrl.SetProtocol("tuxbox");
+ fileUrl.SetUserName(url.GetUserName());
+ fileUrl.SetPassword(url.GetPassWord());
+ fileUrl.SetHostName(url.GetHostName());
+ if (url.GetPort() != 0 && url.GetPort() != 80)
+ fileUrl.SetPort(url.GetPort());
+ fileUrl.SetFileName("cgi-bin/zapTo");
+ fileUrl.SetOption("path", strItemPath+".ts");
+ pbItem->SetPath(fileUrl.Get());
+ }
+ pbItem->SetArt("thumb", GetPicon(strItemName)); //Set Picon Image
+
+ //DEBUG Log
+ CLog::Log(LOGDEBUG, "%s - Name: %s", __FUNCTION__,strItemName.c_str());
+ CLog::Log(LOGDEBUG, "%s - Adress: %s", __FUNCTION__,pbItem->GetPath().c_str());
+
+ //add to the list
+ items.Add(pbItem);
+ }
+ }
+ pIt = pIt->NextSibling("service");
+ }
+ }
+ }
+ pNode = pNode->NextSibling(strChild.c_str());
+ }
+ return true;
+ }
+ return false;
+}
+bool CTuxBoxUtil::ParseChannelsEnigma2(TiXmlElement *root, CFileItemList &items, CURL &url, std::string& strFilter, std::string& strChild)
+{
+ TiXmlElement *pRootElement = root;
+ TiXmlNode *pNode = NULL;
+ TiXmlNode *pIt = NULL;
+ TiXmlNode *pIta = NULL;
+ TiXmlNode *pItb = NULL;
+ items.m_idepth = 2;
+
+ if (!pRootElement)
+ {
+ CLog::Log(LOGWARNING, "%s - No %ss found", __FUNCTION__,strChild.c_str());
+ return false;
+ }
+ if(!strFilter.empty())
+ {
+ pNode = pRootElement->FirstChild(strChild.c_str());
+ if (!pNode)
+ {
+ CLog::Log(LOGWARNING, "%s - No %s found", __FUNCTION__,strChild.c_str());
+ return false;
+ }
+ while(pNode)
+ {
+ pIt = pNode->FirstChildElement("e2servicename");
+ std::string bqtName = pIt->FirstChild()->Value();
+ pIt = pNode->FirstChildElement("e2servicelist");
+ pIta = pIt->FirstChildElement("e2service");
+ while(pIta)
+ {
+ pItb = pIta->FirstChildElement("e2servicereference");
+ std::string strItemPath = pItb->FirstChild()->Value();
+ pItb = pIta->FirstChildElement("e2servicename");
+ std::string strItemName = pItb->FirstChild()->Value();
+ if(bqtName == url.GetShareName())
+ {
+ CFileItemPtr pbItem(new CFileItem);
+ pbItem->m_bIsFolder = false;
+ pbItem->SetLabel(strItemName);
+ {
+ CURL fileUrl;
+ fileUrl.SetProtocol("http");
+ fileUrl.SetHostName(url.GetHostName());
+ fileUrl.SetPort(8001);
+ fileUrl.SetFileName(strItemPath);
+ pbItem->SetPath(fileUrl.Get());
+ }
+ pbItem->SetMimeType("video/mpeg2");
+ items.Add(pbItem);
+ CLog::Log(LOGDEBUG, "%s - Name: %s", __FUNCTION__,strItemName.c_str());
+ CLog::Log(LOGDEBUG, "%s - Adress: %s", __FUNCTION__,pbItem->GetPath().c_str());
+ }
+ pIta = pIta->NextSiblingElement("e2service");
+ }
+ pNode = pNode->NextSiblingElement("e2bouquet");
+ }
+ }
+ return true;
+}
+bool CTuxBoxUtil::ZapToUrl(CURL url, const std::string &pathOption)
+{
+ // send Zap
+ //Extract the ZAP to Service String
+ //Remove the ".ts"
+ std::string strFilter = pathOption.substr(0, pathOption.size() - 3);
+ //Get the Service Name
+
+ // Create ZAP URL
+ CURL urlx;
+ urlx.SetProtocol("http");
+ urlx.SetUserName(url.GetUserName());
+ urlx.SetPassword(url.GetPassWord());
+ urlx.SetHostName(url.GetHostName());
+ if (url.GetPort() != 0 && url.GetPort() != 80)
+ urlx.SetPort(url.GetPort());
+ CURL postUrl(urlx);
+ postUrl.SetFileName("cgi-bin/zapTo");
+ postUrl.SetOption("path", strFilter);
+
+ //Check Recording State!
+ if(GetHttpXML(urlx,"boxstatus"))
+ {
+ if(sBoxStatus.recording == "1")
+ {
+ CLog::Log(LOGDEBUG, "%s ---------------------------------------------------------", __FUNCTION__);
+ CLog::Log(LOGDEBUG, "%s - WARNING: Device is Recording! Record Mode is: %s", __FUNCTION__,sBoxStatus.recording.c_str());
+ CLog::Log(LOGDEBUG, "%s ---------------------------------------------------------", __FUNCTION__);
+ CGUIDialogYesNo* dialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
+ if (dialog)
+ {
+ //Target TuxBox is in Recording mode! Are you sure to stream ?YN
+ dialog->SetHeading(21331);
+ dialog->SetLine( 0, 21332);
+ dialog->SetLine( 1, 21335);
+ dialog->SetLine( 2, "" );
+ dialog->DoModal();
+ if (!dialog->IsConfirmed())
+ {
+ //DialogYN = NO -> Return false!
+ return false;
+ }
+ }
+ }
+ }
+
+ //Send ZAP Command
+ CCurlFile http;
+ if(http.Open(postUrl))
+ {
+ //DEBUG LOG
+ CLog::Log(LOGDEBUG, "%s - Zapped to: %s", __FUNCTION__,postUrl.Get().c_str());
+
+ //Request StreamInfo
+ GetHttpXML(urlx,"streaminfo");
+
+ //Extract StreamInformations
+ int iRetry=0;
+ //PMT must be a valid value to be sure that the ZAP is OK and we can stream!
+ while(sStrmInfo.pmt == "ffffffffh" && iRetry!=10) //try 10 Times
+ {
+ CLog::Log(LOGDEBUG, "%s - Requesting STREAMINFO! TryCount: %i!", __FUNCTION__,iRetry);
+ GetHttpXML(urlx,"streaminfo");
+ iRetry=iRetry+1;
+ Sleep(200);
+ }
+
+ // PMT Not Valid? Try Time 10 reached, checking for advancedSettings m_iTuxBoxZapWaitTime
+ if(sStrmInfo.pmt == "ffffffffh" && g_advancedSettings.m_iTuxBoxZapWaitTime > 0 )
+ {
+ iRetry = 0;
+ CLog::Log(LOGDEBUG, "%s - Starting TuxBox ZapWaitTimer!", __FUNCTION__);
+ while(sStrmInfo.pmt == "ffffffffh" && iRetry!=10) //try 10 Times
+ {
+ CLog::Log(LOGDEBUG, "%s - Requesting STREAMINFO! TryCount: %i!", __FUNCTION__,iRetry);
+ GetHttpXML(urlx,"streaminfo");
+ iRetry=iRetry+1;
+ if(sStrmInfo.pmt == "ffffffffh")
+ {
+ CLog::Log(LOGERROR, "%s - STREAMINFO ERROR! Could not receive all data, TryCount: %i!", __FUNCTION__,iRetry);
+ CLog::Log(LOGERROR, "%s - PMT is: %s (not a Valid Value)! Waiting %i sec.", __FUNCTION__,sStrmInfo.pmt.c_str(), g_advancedSettings.m_iTuxBoxZapWaitTime);
+ Sleep(g_advancedSettings.m_iTuxBoxZapWaitTime*1000);
+ }
+ }
+ }
+
+ //PMT Failed! No StreamInformations availible.. closing stream
+ if (sStrmInfo.pmt == "ffffffffh")
+ {
+ CLog::Log(LOGERROR, "%s-------------------------------------------------------------", __FUNCTION__);
+ CLog::Log(LOGERROR, "%s - STREAMINFO ERROR! Could not receive all data, TryCount: %i!", __FUNCTION__,iRetry);
+ CLog::Log(LOGERROR, "%s - PMT is: %s (not a Valid Value)! There is nothing to Stream!", __FUNCTION__,sStrmInfo.pmt.c_str());
+ CLog::Log(LOGERROR, "%s - The Stream will stopped!", __FUNCTION__);
+ CLog::Log(LOGERROR, "%s-------------------------------------------------------------", __FUNCTION__);
+ return false;
+ }
+ //Currentservicedata
+ GetHttpXML(urlx,"currentservicedata");
+ //boxstatus
+ GetHttpXML(urlx,"boxstatus");
+ //boxinfo
+ GetHttpXML(urlx,"boxinfo");
+ //serviceepg
+ GetHttpXML(urlx,"serviceepg");
+ return true;
+ }
+ return false;
+}
+bool CTuxBoxUtil::GetZapUrl(const std::string& strPath, CFileItem &items )
+{
+ CURL url(strPath);
+ std::string strOptions = url.GetOptions();
+ if (strOptions.empty())
+ return false;
+
+ if (url.HasOption("path"))
+ {
+ if(ZapToUrl(url, url.GetOption("path")))
+ {
+ //Check VideoSubChannels
+ if(GetHttpXML(url,"currentservicedata")) //Update Currentservicedata
+ {
+ //Detect VideoSubChannels
+ std::string strVideoSubChannelName, strVideoSubChannelPID;
+ if(GetVideoSubChannels(strVideoSubChannelName,strVideoSubChannelPID ))
+ {
+ // new videosubchannel selected! settings options to new video zap id
+ // zap again now to new videosubchannel
+ if(ZapToUrl(url, strVideoSubChannelPID + ".ts"))
+ {
+ vVideoSubChannel.mode = true;
+ vVideoSubChannel.current_name = strVideoSubChannelName;
+ }
+ }
+ else
+ vVideoSubChannel.mode= false;
+ }
+
+ std::string strVideoStream;
+ std::string strLabel, strLabel2;
+ std::string strAudioChannelPid;
+ std::string strAPids;
+ sAudioChannel sRequestedAudioChannel;
+
+ if (!GetGUIRequestedAudioChannel(sRequestedAudioChannel))
+ {
+ if (g_advancedSettings.m_bTuxBoxSendAllAPids && sCurSrvData.audio_channels.size() > 1)
+ {
+ for (vector<sAudioChannel>::iterator sChannel = sCurSrvData.audio_channels.begin(); sChannel!=sCurSrvData.audio_channels.end(); ++sChannel)
+ {
+ if (sChannel->pid != sRequestedAudioChannel.pid && sChannel->pid.size() >= 4)
+ strAPids += "," + sChannel->pid.substr(sChannel->pid.size() - 4);
+ }
+ CLog::Log(LOGDEBUG, "%s - Sending all audio pids: %s%s", __FUNCTION__, strAudioChannelPid.c_str(), strAPids.c_str());
+
+ strVideoStream = StringUtils::Format("0,%s,%s,%s%s",
+ sStrmInfo.pmt.substr(0, 4).c_str(),
+ sStrmInfo.vpid.substr(0, 4).c_str(),
+ sStrmInfo.apid.substr(0, 4).c_str(),
+ strAPids.c_str());
+ }
+ else
+ strVideoStream = StringUtils::Format("0,%s,%s,%s",
+ sStrmInfo.pmt.substr(0, 4).c_str(),
+ sStrmInfo.vpid.substr(0, 4).c_str(),
+ sStrmInfo.apid.substr(0, 4).c_str());
+ }
+ else
+ strVideoStream = StringUtils::Format("0,%s,%s,%s",
+ sStrmInfo.pmt.substr(0, 4).c_str(),
+ sStrmInfo.vpid.substr(0, 4).c_str(),
+ strAudioChannelPid.substr(0, 4).c_str());
+
+ CURL streamURL;
+ streamURL.SetProtocol("http");
+ streamURL.SetUserName(url.GetUserName());
+ streamURL.SetPassword(url.GetPassWord());
+ streamURL.SetHostName(url.GetHostName());
+ streamURL.SetPort(g_advancedSettings.m_iTuxBoxStreamtsPort);
+ streamURL.SetFileName(strVideoStream.c_str());
+
+ if (!g_tuxbox.sZapstream.initialized)
+ g_tuxbox.InitZapstream(strPath);
+
+ // Use the Zapstream service when available.
+ if (g_tuxbox.sZapstream.available)
+ {
+ sAudioChannel sSelectedAudioChannel;
+ if (GetRequestedAudioChannel(sSelectedAudioChannel))
+ {
+ if (sSelectedAudioChannel.pid != sStrmInfo.apid)
+ {
+ if (SetAudioChannel(strPath, sSelectedAudioChannel))
+ CLog::Log(LOGDEBUG, "%s - Zapstream: Requested audio channel is %s, pid %s.", __FUNCTION__, sSelectedAudioChannel.name.c_str(), sSelectedAudioChannel.pid.c_str());
+ }
+ }
+ streamURL.SetFileName("");
+ streamURL.SetPort(g_advancedSettings.m_iTuxBoxZapstreamPort);
+ }
+
+ if (g_application.m_pPlayer->IsPlaying() && !g_tuxbox.sZapstream.available)
+ CApplicationMessenger::Get().MediaStop();
+
+ strLabel = StringUtils::Format("%s: %s %s-%s",
+ items.GetLabel().c_str(),
+ sCurSrvData.current_event_date.c_str(),
+ sCurSrvData.current_event_start.c_str(),
+ sCurSrvData.current_event_start.c_str());
+ strLabel2 = StringUtils::Format("%s", sCurSrvData.current_event_description.c_str());
+
+ // Set Event details
+ std::string strGenre, strTitle;
+ strGenre = StringUtils::Format("%s %s - (%s: %s)",
+ g_localizeStrings.Get(143).c_str(), sCurSrvData.current_event_description.c_str(),
+ g_localizeStrings.Get(209).c_str(), sCurSrvData.next_event_description.c_str());
+ strTitle = StringUtils::Format("%s", sCurSrvData.current_event_details.c_str());
+ int iDuration = atoi(sCurSrvData.current_event_duration.c_str());
+
+ items.GetVideoInfoTag()->m_genre = StringUtils::Split(strGenre, g_advancedSettings.m_videoItemSeparator); // VIDEOPLAYER_GENRE: current_event_description (Film Name)
+ items.GetVideoInfoTag()->m_strTitle = strTitle; // VIDEOPLAYER_TITLE: current_event_details (Film beschreibung)
+ items.GetVideoInfoTag()->m_duration = iDuration; //VIDEOPLAYER_DURATION: current_event_duration (laufzeit in sec.)
+
+ items.SetPath(streamURL.Get());
+ items.m_iDriveType = url.GetPort(); // Dirty Hack! But i need to hold the Port ;)
+ items.SetLabel(items.GetLabel()); // VIDEOPLAYER_DIRECTOR: service_name (Program Name)
+ items.SetLabel2(sCurSrvData.current_event_description); // current_event_description (Film Name)
+ items.m_bIsFolder = false;
+ items.SetMimeType("video/x-mpegts");
+ return true;
+ }
+ }
+ return false;
+}
+
+// Notice: Zapstream is a streamts enhancement from PLi development team.
+// If you are using a non-PLi based image you might not have Zapstream installed.
+bool CTuxBoxUtil::InitZapstream(const std::string& strPath)
+{
+ CURL url(strPath);
+ CCurlFile http;
+ int iTryConnect = 0;
+ int iTimeout = 2;
+
+ g_tuxbox.sZapstream.initialized = true;
+
+ if (!g_advancedSettings.m_bTuxBoxZapstream)
+ {
+ CLog::Log(LOGDEBUG, "%s - Zapstream is disabled in advancedsettings.xml.", __FUNCTION__);
+ return g_tuxbox.sZapstream.available = false;
+ }
+
+ url.SetProtocol("http");
+ url.SetFileName("");
+ url.SetOptions("");
+ url.SetPort(g_advancedSettings.m_iTuxBoxZapstreamPort);
+
+ while (iTryConnect < 3)
+ {
+ http.SetTimeout(iTimeout);
+
+ if (http.Open(url))
+ {
+ http.Close();
+ CHttpHeader h = http.GetHttpHeader();
+ std::string strValue = h.GetValue("server");
+
+ if (strValue.find("zapstream") != std::string::npos)
+ {
+ CLog::Log(LOGDEBUG, "%s - Zapstream is available on port %i.", __FUNCTION__, g_advancedSettings.m_iTuxBoxZapstreamPort);
+ return g_tuxbox.sZapstream.available = true;
+ }
+ }
+
+ iTryConnect++;
+ iTimeout+=5;
+ }
+
+ CLog::Log(LOGDEBUG, "%s - Zapstream is not available on port %i.", __FUNCTION__, g_advancedSettings.m_iTuxBoxZapstreamPort);
+ return false;
+}
+bool CTuxBoxUtil::SetAudioChannel( const std::string& strPath, const AUDIOCHANNEL& sAC )
+{
+ CURL url(strPath);
+ CCurlFile http;
+ int iTryConnect = 0;
+ int iTimeout = 2;
+
+ url.SetProtocol("http");
+ url.SetFileName("cgi-bin/setAudio");
+ url.SetOptions("?channel=1&language=" + sAC.pid);
+ url.SetPort(80);
+
+ g_tuxbox.sZapstream.initialized = true;
+
+ while (iTryConnect < 3)
+ {
+ http.SetTimeout(iTimeout);
+
+ if (http.Open(url))
+ {
+ http.Close();
+ return true;
+ }
+
+ iTryConnect++;
+ iTimeout+=5;
+ }
+
+ return false;
+}
+bool CTuxBoxUtil::GetHttpXML(CURL url,std::string strRequestType)
+{
+ // Check and Set URL Request Option
+ if(!strRequestType.empty())
+ {
+ if(strRequestType == "streaminfo")
+ {
+ url.SetOptions("xml/streaminfo");
+ }
+ else if(strRequestType == "currentservicedata")
+ {
+ url.SetOptions("xml/currentservicedata");
+ }
+ else if(strRequestType == "boxstatus")
+ {
+ url.SetOptions("xml/boxstatus");
+ }
+ else if(strRequestType == "boxinfo")
+ {
+ url.SetOptions("xml/boxinfo");
+ }
+ else if(strRequestType == "serviceepg")
+ {
+ url.SetOptions("xml/serviceepg");
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "%s - Request Type is not defined! You requested: %s", __FUNCTION__,strRequestType.c_str());
+ return false;
+ }
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "%s - strRequestType Request Type is Empty!", __FUNCTION__);
+ return false;
+ }
+
+ // Clean Up the URL, so we have a clean request!
+ url.SetFileName("");
+
+ //Open
+ CCurlFile http;
+ http.SetTimeout(20);
+ if(http.Open(url))
+ {
+ int size_read = 0;
+ int size_total = (int)http.GetLength();
+
+ if(size_total > 0)
+ {
+ // read response from server into string buffer
+ std::string strTmp;
+ strTmp.reserve(size_total);
+ char buffer[16384];
+ while( (size_read = http.Read( buffer, sizeof(buffer)-1) ) > 0 )
+ {
+ buffer[size_read] = 0;
+ strTmp += buffer;
+ }
+
+ // parse returned xml
+ CXBMCTinyXML doc;
+ TiXmlElement *XMLRoot=NULL;
+ StringUtils::Replace(strTmp, "></",">-</"); //FILL EMPTY ELEMENTS WITH "-"!
+ doc.Parse(strTmp, http.GetServerReportedCharset());
+ strTmp.clear();
+
+ XMLRoot = doc.RootElement();
+ std::string strRoot = XMLRoot->Value();
+ if( strRoot == "streaminfo")
+ return StreamInformations(XMLRoot);
+ if(strRoot == "currentservicedata")
+ return CurrentServiceData(XMLRoot);
+ if(strRoot == "boxstatus")
+ return BoxStatus(XMLRoot);
+ if(strRoot == "boxinfo")
+ return BoxInfo(XMLRoot);
+ if(strRoot == "serviceepg" ||
+ strRoot == "service_epg")
+ return ServiceEPG(XMLRoot);
+
+ CLog::Log(LOGERROR, "%s - Unable to parse xml", __FUNCTION__);
+ CLog::Log(LOGERROR, "%s - Request String: %s", __FUNCTION__,strRoot.c_str());
+ return false;
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "%s - http length is invalid!", __FUNCTION__);
+ return false;
+ }
+ }
+ CLog::Log(LOGERROR, "%s - Open URL Failed! Unable to get XML structure", __FUNCTION__);
+ return false;
+}
+bool CTuxBoxUtil::StreamInformations(TiXmlElement *pRootElement)
+{
+ /*
+ Sample:
+ http://192.168.0.110:31339/0,0065,01ff,0200,0201,0203,01ff
+
+ http://getIP:31339/0,pmtpid,vpid,apid,apids,apids,pcrpid;
+
+ vpid,pmtpid,pcrpid,apid --> xml/streaminfo
+ apids --> /xml/currentservicedata
+
+ apid: is the defined audio stream!
+ Normal Stereo: http://192.168.0.110:31339/0,0065,01ff,0200,0201,0203,01ff
+ Normal English: http://192.168.0.110:31339/0,0065,01ff,0201,,,01ff
+ Normal DD5.1/AC3: http://192.168.0.110:31339/0,0065,01ff,0203,,,01ff
+ */
+
+ TiXmlNode *pNode = NULL;
+ TiXmlNode *pIt = NULL;
+ if(pRootElement != NULL)
+ {
+ pNode = pRootElement->FirstChild("frontend");
+ if (pNode)
+ {
+ sStrmInfo.frontend = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Frontend: %s", __FUNCTION__, sStrmInfo.frontend.c_str());
+ }
+ pNode = pRootElement->FirstChild("service");
+ if (pNode)
+ {
+ CLog::Log(LOGDEBUG, "%s - Service", __FUNCTION__);
+ pIt = pNode->FirstChild("name");
+ if (pIt)
+ {
+ sStrmInfo.service_name = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Name: %s", __FUNCTION__, sStrmInfo.service_name.c_str());
+ }
+ pIt = pNode->FirstChild("reference");
+ if (pIt)
+ {
+ sStrmInfo.service_reference = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Reference: %s", __FUNCTION__, sStrmInfo.service_reference.c_str());
+ }
+ }
+
+ pNode = pRootElement->FirstChild("provider");
+ if(pNode && pNode->FirstChild())
+ {
+ sStrmInfo.provider= pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Provider: %s", __FUNCTION__, sStrmInfo.provider.c_str());
+ }
+ pNode = pRootElement->FirstChild("vpid");
+ if (pNode)
+ {
+ sStrmInfo.vpid = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Vpid: %s", __FUNCTION__, sStrmInfo.vpid.c_str());
+ }
+ pNode = pRootElement->FirstChild("apid");
+ if (pNode)
+ {
+ sStrmInfo.apid = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Apid: %s", __FUNCTION__, sStrmInfo.apid.c_str());
+ }
+ pNode = pRootElement->FirstChild("pcrpid");
+ if (pNode)
+ {
+ sStrmInfo.pcrpid = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - PcrPid: %s", __FUNCTION__, sStrmInfo.pcrpid.c_str());
+ }
+ pNode = pRootElement->FirstChild("tpid");
+ if (pNode)
+ {
+ sStrmInfo.tpid = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Tpid: %s", __FUNCTION__, sStrmInfo.tpid.c_str());
+ }
+ pNode = pRootElement->FirstChild("tsid");
+ if (pNode)
+ {
+ sStrmInfo.tsid = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Tsid: %s", __FUNCTION__, sStrmInfo.tsid.c_str());
+ }
+ pNode = pRootElement->FirstChild("onid");
+ if (pNode)
+ {
+ sStrmInfo.onid = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Onid: %s", __FUNCTION__, sStrmInfo.onid.c_str());
+ }
+ pNode = pRootElement->FirstChild("sid");
+ if (pNode)
+ {
+ sStrmInfo.sid = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Sid: %s", __FUNCTION__, sStrmInfo.sid.c_str());
+ }
+ pNode = pRootElement->FirstChild("pmt");
+ if (pNode)
+ {
+ sStrmInfo.pmt = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Pmt: %s", __FUNCTION__, sStrmInfo.pmt.c_str());
+ }
+ pNode = pRootElement->FirstChild("video_format");
+ if (pNode)
+ {
+ sStrmInfo.video_format = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Video Format: %s", __FUNCTION__, sStrmInfo.video_format.c_str());
+ }
+ pNode = pRootElement->FirstChild("supported_crypt_systems");
+ if (pNode)
+ {
+ sStrmInfo.supported_crypt_systems = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Supported Crypt Systems: %s", __FUNCTION__, sStrmInfo.supported_crypt_systems.c_str());
+ }
+ pNode = pRootElement->FirstChild("used_crypt_systems");
+ if (pNode)
+ {
+ sStrmInfo.used_crypt_systems = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Used Crypt Systems: %s", __FUNCTION__, sStrmInfo.used_crypt_systems.c_str());
+ }
+ pNode = pRootElement->FirstChild("satellite");
+ if (pNode)
+ {
+ sStrmInfo.satellite = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Satellite: %s", __FUNCTION__, sStrmInfo.satellite.c_str());
+ }
+ pNode = pRootElement->FirstChild("frequency");
+ if (pNode)
+ {
+ sStrmInfo.frequency = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Frequency: %s", __FUNCTION__, sStrmInfo.frequency.c_str());
+ }
+ pNode = pRootElement->FirstChild("symbol_rate");
+ if (pNode)
+ {
+ sStrmInfo.symbol_rate = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Symbol Rate: %s", __FUNCTION__, sStrmInfo.symbol_rate.c_str());
+ }
+ pNode = pRootElement->FirstChild("polarisation");
+ if (pNode)
+ {
+ sStrmInfo.polarisation = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Polarisation: %s", __FUNCTION__, sStrmInfo.polarisation.c_str());
+ }
+ pNode = pRootElement->FirstChild("inversion");
+ if (pNode)
+ {
+ sStrmInfo.inversion = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Inversion: %s", __FUNCTION__, sStrmInfo.inversion.c_str());
+ }
+ pNode = pRootElement->FirstChild("fec");
+ if (pNode)
+ {
+ sStrmInfo.fec = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Fec: %s", __FUNCTION__, sStrmInfo.fec.c_str());
+ }
+ pNode = pRootElement->FirstChild("snr");
+ if (pNode)
+ {
+ sStrmInfo.snr = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Snr: %s", __FUNCTION__, sStrmInfo.snr.c_str());
+ }
+ pNode = pRootElement->FirstChild("agc");
+ if (pNode)
+ {
+ sStrmInfo.agc = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Agc: %s", __FUNCTION__, sStrmInfo.agc.c_str());
+ }
+ pNode = pRootElement->FirstChild("ber");
+ if (pNode)
+ {
+ sStrmInfo.ber = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - ber: %s", __FUNCTION__, sStrmInfo.ber.c_str());
+ }
+ pNode = pRootElement->FirstChild("lock");
+ if (pNode)
+ {
+ sStrmInfo.lock = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Lock: %s", __FUNCTION__, sStrmInfo.lock.c_str());
+ }
+ pNode = pRootElement->FirstChild("sync");
+ if (pNode)
+ {
+ sStrmInfo.sync = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Sync: %s", __FUNCTION__, sStrmInfo.sync.c_str());
+ }
+ return true;
+ }
+ return false;
+}
+bool CTuxBoxUtil::CurrentServiceData(TiXmlElement *pRootElement)
+{
+ TiXmlNode *pNode = NULL;
+ TiXmlNode *pIt = NULL;
+ TiXmlNode *pVal = NULL;
+ if(pRootElement)
+ {
+ CLog::Log(LOGDEBUG, "%s - Current Service Data", __FUNCTION__);
+ pNode = pRootElement->FirstChild("service");
+ if (pNode)
+ {
+ CLog::Log(LOGDEBUG, "%s - Service", __FUNCTION__);
+ pIt = pNode->FirstChild("name");
+ if (pIt)
+ {
+ sCurSrvData.service_name = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Service Name: %s", __FUNCTION__, pIt->FirstChild()->Value());
+ }
+ pIt = pNode->FirstChild("reference");
+ if (pIt)
+ {
+ sCurSrvData.service_reference = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Service Reference: %s", __FUNCTION__, pIt->FirstChild()->Value());
+ }
+ }
+
+ pNode = pRootElement->FirstChild("audio_channels");
+ if (pNode)
+ {
+ CLog::Log(LOGDEBUG, "%s - Audio Channels", __FUNCTION__);
+ int i = 0;
+
+ pIt = pNode->FirstChild("channel");
+ sCurSrvData.audio_channels.clear();
+
+ while(pIt)
+ {
+ sAudioChannel newChannel;
+
+ pVal = pIt->FirstChild("pid");
+ if(pVal)
+ newChannel.pid = pVal->FirstChild()->Value();
+
+ pVal = pIt->FirstChild("selected");
+ if(pVal)
+ newChannel.selected = pVal->FirstChild()->Value();
+
+ pVal = pIt->FirstChild("name");
+ if(pVal)
+ newChannel.name = pVal->FirstChild()->Value();
+
+ CLog::Log(LOGDEBUG, "%s - Audio Channels: Channel %i -> PID: %s Selected: %s Name: %s", __FUNCTION__, i, newChannel.pid.c_str(), newChannel.selected.c_str(), newChannel.name.c_str() );
+
+ i=i+1;
+ sCurSrvData.audio_channels.push_back( newChannel );
+ pIt = pIt->NextSibling("channel");
+ }
+ }
+ pNode = pRootElement->FirstChild("audio_track");
+ if (pNode)
+ {
+ sCurSrvData.audio_track = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Audio Track: %s", __FUNCTION__, pNode->FirstChild()->Value() );
+ }
+ pNode = pRootElement->FirstChild("video_channels");
+ if (pNode)
+ {
+ CLog::Log(LOGDEBUG, "%s - Video Channels", __FUNCTION__);
+ pIt = pNode->FirstChild("service");
+ if (pIt)
+ {
+ vVideoSubChannel.name.clear();
+ vVideoSubChannel.reference.clear();
+ vVideoSubChannel.selected.clear();
+ int i = 0;
+ while(pIt)
+ {
+ pVal = pIt->FirstChild("name");
+ if(pVal)
+ {
+ vVideoSubChannel.name.push_back(pVal->FirstChild()->Value());
+ CLog::Log(LOGDEBUG, "%s - Video Sub Channel %i: Name: %s", __FUNCTION__, i,pVal->FirstChild()->Value());
+ }
+ pVal = pIt->FirstChild("reference");
+ if(pVal)
+ {
+ vVideoSubChannel.reference.push_back(pVal->FirstChild()->Value());
+ CLog::Log(LOGDEBUG, "%s - Video Sub Channel %i: Reference: %s", __FUNCTION__, i,pVal->FirstChild()->Value());
+ }
+ pVal = pIt->FirstChild("selected");
+ if(pVal)
+ {
+ vVideoSubChannel.selected.push_back(pVal->FirstChild()->Value());
+ CLog::Log(LOGDEBUG, "%s - Video Sub Channel %i: Selected: %s", __FUNCTION__, i,pVal->FirstChild()->Value());
+ }
+ pIt = pIt->NextSibling("service");
+ i++;
+ }
+ }
+ else
+ {
+ vVideoSubChannel.name.clear();
+ vVideoSubChannel.reference.clear();
+ vVideoSubChannel.selected.clear();
+ }
+ }
+ pNode = pRootElement->FirstChild("current_event");
+ if (pNode)
+ {
+ CLog::Log(LOGDEBUG, "%s - Current Event", __FUNCTION__);
+ pIt = pNode->FirstChild("date");
+ if (pIt)
+ {
+ sCurSrvData.current_event_date = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Date: %s", __FUNCTION__, pIt->FirstChild()->Value());
+ }
+ pIt = pNode->FirstChild("time");
+ if (pIt)
+ {
+ sCurSrvData.current_event_time = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Time: %s", __FUNCTION__, pIt->FirstChild()->Value());
+ }
+
+ pIt = pNode->FirstChild("start");
+ if (pIt)
+ {
+ sCurSrvData.current_event_start = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Start: %s", __FUNCTION__, pIt->FirstChild()->Value());
+ }
+
+ pIt = pNode->FirstChild("duration");
+ if (pIt)
+ {
+ sCurSrvData.current_event_duration = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Duration: %s", __FUNCTION__, pIt->FirstChild()->Value());
+ }
+
+ pIt = pNode->FirstChild("description");
+ if (pIt)
+ {
+ sCurSrvData.current_event_description = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Description: %s", __FUNCTION__, pIt->FirstChild()->Value());
+ }
+ pIt = pNode->FirstChild("details");
+ if (pIt)
+ {
+ sCurSrvData.current_event_details = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Details: %s", __FUNCTION__, pIt->FirstChild()->Value());
+ }
+ }
+ pNode = pRootElement->FirstChild("next_event");
+ if (pNode)
+ {
+ CLog::Log(LOGDEBUG, "%s - Next Event", __FUNCTION__);
+ pIt = pNode->FirstChild("date");
+ if (pIt)
+ {
+ sCurSrvData.next_event_date = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Date: %s", __FUNCTION__, pIt->FirstChild()->Value());
+ }
+ pIt = pNode->FirstChild("time");
+ if (pIt)
+ {
+ sCurSrvData.next_event_time = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Time: %s", __FUNCTION__, pIt->FirstChild()->Value());
+ }
+
+ pIt = pNode->FirstChild("start");
+ if (pIt)
+ {
+ sCurSrvData.next_event_start = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Start: %s", __FUNCTION__, pIt->FirstChild()->Value());
+ }
+
+ pIt = pNode->FirstChild("duration");
+ if (pIt)
+ {
+ sCurSrvData.next_event_duration = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Duration: %s", __FUNCTION__, pIt->FirstChild()->Value());
+ }
+
+ pIt = pNode->FirstChild("description");
+ if (pIt)
+ {
+ sCurSrvData.next_event_description = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Description: %s", __FUNCTION__, pIt->FirstChild()->Value());
+ }
+ pIt = pNode->FirstChild("details");
+ if (pIt)
+ {
+ sCurSrvData.next_event_details = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Details: %s", __FUNCTION__, pIt->FirstChild()->Value());
+ }
+ }
+ return true;
+ }
+ return false;
+
+}
+bool CTuxBoxUtil::BoxStatus(TiXmlElement *pRootElement)
+{
+ //Tuxbox Controll Commands
+ /*
+ /cgi-bin/admin?command=wakeup
+ /cgi-bin/admin?command=standby
+ /cgi-bin/admin?command=shutdown
+ /cgi-bin/admin?command=reboot
+ /cgi-bin/admin?command=restart
+ */
+
+ TiXmlNode *pNode = NULL;
+
+ if(pRootElement)
+ {
+ CLog::Log(LOGDEBUG, "%s - BoxStatus", __FUNCTION__);
+ pNode = pRootElement->FirstChild("current_time");
+ if (pNode)
+ {
+ sBoxStatus.current_time = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Current Time: %s", __FUNCTION__, pNode->FirstChild()->Value());
+ }
+ pNode = pRootElement->FirstChild("standby");
+ if (pNode)
+ {
+ sBoxStatus.standby = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Standby: %s", __FUNCTION__, pNode->FirstChild()->Value());
+ }
+ pNode = pRootElement->FirstChild("recording");
+ if (pNode)
+ {
+ sBoxStatus.recording = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Recording: %s", __FUNCTION__, pNode->FirstChild()->Value());
+ }
+ pNode = pRootElement->FirstChild("mode");
+ if (pNode)
+ {
+ sBoxStatus.mode = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Mode: %s", __FUNCTION__, pNode->FirstChild()->Value());
+ }
+ pNode = pRootElement->FirstChild("ip");
+ if (pNode)
+ {
+ if (sBoxStatus.ip != pNode->FirstChild()->Value() )
+ {
+ g_tuxbox.sZapstream.initialized = false;
+ g_tuxbox.sZapstream.available = false;
+ }
+ sBoxStatus.ip = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Ip: %s", __FUNCTION__, pNode->FirstChild()->Value());
+ }
+ return true;
+ }
+ return false;
+}
+bool CTuxBoxUtil::BoxInfo(TiXmlElement *pRootElement)
+{
+ TiXmlNode *pNode = NULL;
+ TiXmlNode *pIt = NULL;
+
+ if(pRootElement)
+ {
+ CLog::Log(LOGDEBUG, "%s - BoxInfo", __FUNCTION__);
+ pNode = pRootElement->FirstChild("image");
+ if (pNode)
+ {
+ CLog::Log(LOGDEBUG, "%s - Image", __FUNCTION__);
+ pIt = pNode->FirstChild("version");
+ if (pIt)
+ {
+ sBoxInfo.image_version = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Image Version: %s", __FUNCTION__, pIt->FirstChild()->Value());
+ }
+ pIt = pNode->FirstChild("url");
+ if (pIt)
+ {
+ sBoxInfo.image_url = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Image Url: %s", __FUNCTION__, pIt->FirstChild()->Value());
+ }
+ pIt = pNode->FirstChild("comment");
+ if (pIt)
+ {
+ sBoxInfo.image_comment = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Image Comment: %s", __FUNCTION__, pIt->FirstChild()->Value());
+ }
+ pIt = pNode->FirstChild("catalog");
+ if (pIt)
+ {
+ sBoxInfo.image_catalog = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Image Catalog: %s", __FUNCTION__, pIt->FirstChild()->Value());
+ }
+ }
+ pNode = pRootElement->FirstChild("firmware");
+ if (pNode)
+ {
+ sBoxInfo.firmware = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Firmware: %s", __FUNCTION__, pNode->FirstChild()->Value());
+ }
+ pNode = pRootElement->FirstChild("fpfirmware");
+ if (pNode)
+ {
+ sBoxInfo.fpfirmware = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - FP Firmware: %s", __FUNCTION__, pNode->FirstChild()->Value());
+ }
+ pNode = pRootElement->FirstChild("webinterface");
+ if (pNode)
+ {
+ sBoxInfo.webinterface = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Web Interface: %s", __FUNCTION__, pNode->FirstChild()->Value());
+ }
+ pNode = pRootElement->FirstChild("model");
+ if (pNode)
+ {
+ sBoxInfo.model = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Model: %s", __FUNCTION__, pNode->FirstChild()->Value());
+ }
+ pNode = pRootElement->FirstChild("manufacturer");
+ if (pNode)
+ {
+ sBoxInfo.manufacturer = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Manufacturer: %s", __FUNCTION__, pNode->FirstChild()->Value());
+ }
+ pNode = pRootElement->FirstChild("processor");
+ if (pNode)
+ {
+ sBoxInfo.processor = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Processor: %s", __FUNCTION__, pNode->FirstChild()->Value());
+ }
+ pNode = pRootElement->FirstChild("usbstick");
+ if (pNode)
+ {
+ sBoxInfo.usbstick = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - USB Stick: %s", __FUNCTION__, pNode->FirstChild()->Value());
+ }
+ pNode = pRootElement->FirstChild("disk");
+ if (pNode)
+ {
+ sBoxInfo.disk = pNode->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Disk: %s", __FUNCTION__, pNode->FirstChild()->Value());
+ }
+ return true;
+ }
+ return false;
+}
+bool CTuxBoxUtil::ServiceEPG(TiXmlElement *pRootElement)
+{
+ TiXmlNode *pNode = NULL;
+ TiXmlNode *pIt = NULL;
+
+ if(pRootElement)
+ {
+ CLog::Log(LOGDEBUG, "%s - Service EPG", __FUNCTION__);
+ pNode = pRootElement->FirstChild("service");
+ if (pNode)
+ {
+ CLog::Log(LOGDEBUG, "%s - Service", __FUNCTION__);
+ pIt = pNode->FirstChild("reference");
+ if (pIt)
+ {
+ sServiceEPG.service_reference = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Service Reference: %s", __FUNCTION__, pIt->FirstChild()->Value());
+ }
+ pIt = pNode->FirstChild("name");
+ if (pIt)
+ {
+ sServiceEPG.service_name = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Service Name: %s", __FUNCTION__, pIt->FirstChild()->Value());
+ }
+ }
+ //Todo there is more then 1 event! Create a Event List!
+ pNode = pRootElement->FirstChild("event");
+ if (pNode)
+ {
+ CLog::Log(LOGDEBUG, "%s - Event", __FUNCTION__);
+ pIt = pNode->FirstChild("date");
+ if (pIt)
+ {
+ sServiceEPG.date = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Date: %s", __FUNCTION__, pIt->FirstChild()->Value());
+ }
+ pIt = pNode->FirstChild("time");
+ if (pIt)
+ {
+ sServiceEPG.time = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Time: %s", __FUNCTION__, pIt->FirstChild()->Value());
+ }
+ pIt = pNode->FirstChild("duration");
+ if (pIt)
+ {
+ sServiceEPG.duration = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Duration: %s", __FUNCTION__, pIt->FirstChild()->Value());
+ }
+ pIt = pNode->FirstChild("descritption");
+ if (pIt)
+ {
+ sServiceEPG.descritption = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Descritption: %s", __FUNCTION__, pIt->FirstChild()->Value());
+ }
+ pIt = pNode->FirstChild("genre");
+ if (pIt)
+ {
+ sServiceEPG.genre = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Genre: %s", __FUNCTION__, pIt->FirstChild()->Value());
+ }
+ pIt = pNode->FirstChild("genrecategory");
+ if (pIt)
+ {
+ sServiceEPG.genrecategory = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Genrecategory: %s", __FUNCTION__, pIt->FirstChild()->Value());
+ }
+ pIt = pNode->FirstChild("start");
+ if (pIt)
+ {
+ sServiceEPG.start = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Start: %s", __FUNCTION__, pIt->FirstChild()->Value());
+ }
+ pIt = pNode->FirstChild("details");
+ if (pIt)
+ {
+ sServiceEPG.details = pIt->FirstChild()->Value();
+ CLog::Log(LOGDEBUG, "%s - Details: %s", __FUNCTION__, pIt->FirstChild()->Value());
+ }
+ }
+ return true;
+ }
+ return false;
+}
+//PopUp and request the AudioChannel
+//No PopUp: On 1x detected AudioChannel
+bool CTuxBoxUtil::GetGUIRequestedAudioChannel(AUDIOCHANNEL& sRequestedAC)
+{
+ sRequestedAC = sCurSrvData.audio_channels[0];
+
+ // Audio Selection is Disabled! Return false to use default values!
+ if(!g_advancedSettings.m_bTuxBoxAudioChannelSelection)
+ {
+ CLog::Log(LOGDEBUG, "%s - Audio Channel Selection is Disabled! Returning False to use the default values!", __FUNCTION__);
+ return false;
+ }
+
+ // We have only one Audio Channel return false to use default values!
+ if(sCurSrvData.audio_channels.size() == 1)
+ return false;
+
+ // popup the context menu
+ CContextButtons buttons;
+
+ // add the needed Audio buttons
+ for (unsigned int i = 0; i < sCurSrvData.audio_channels.size(); ++i)
+ buttons.Add(i, sCurSrvData.audio_channels[i].name);
+
+ int channel = CGUIDialogContextMenu::ShowAndGetChoice(buttons);
+ if (channel >= 0)
+ {
+ sRequestedAC = sCurSrvData.audio_channels[channel];
+ sCurSrvData.requested_audio_channel = channel;
+ CLog::Log(LOGDEBUG, "%s - Audio channel %s requested.", __FUNCTION__, sRequestedAC.name.c_str());
+ return true;
+ }
+ return false;
+}
+bool CTuxBoxUtil::GetRequestedAudioChannel(AUDIOCHANNEL& sRequestedAC) const
+{
+ sRequestedAC = sCurSrvData.audio_channels[sCurSrvData.requested_audio_channel];
+
+ return true;
+}
+bool CTuxBoxUtil::GetVideoSubChannels(std::string& strVideoSubChannelName, std::string& strVideoSubChannelPid)
+{
+ // no video sub channel return false!
+ if(vVideoSubChannel.name.size() <= 0 || vVideoSubChannel.reference.size() <= 0)
+ return false;
+
+ // IsPlaying, Stop it..
+ if(g_application.m_pPlayer->IsPlaying())
+ CApplicationMessenger::Get().MediaStop();
+
+ // popup the context menu
+ CContextButtons buttons;
+
+ // add the needed Audio buttons
+ for (unsigned int i = 0; i < vVideoSubChannel.name.size(); ++i)
+ buttons.Add(i, vVideoSubChannel.name[i]);
+
+ // get selected Video Sub Channel name and reference zap
+ int channel = CGUIDialogContextMenu::ShowAndGetChoice(buttons);
+ if (channel >= 0)
+ {
+ strVideoSubChannelName = vVideoSubChannel.name[channel];
+ strVideoSubChannelPid = vVideoSubChannel.reference[channel];
+ vVideoSubChannel.name.clear();
+ vVideoSubChannel.reference.clear();
+ vVideoSubChannel.selected.clear();
+ return true;
+ }
+ return false;
+}
+//Input: Service Name (Channel Namne)
+//Output: picon url (on ERROR the default icon path will be returned)
+std::string CTuxBoxUtil::GetPicon(std::string strServiceName)
+{
+ if(!g_advancedSettings.m_bTuxBoxPictureIcon)
+ {
+ CLog::Log(LOGDEBUG, "%s PictureIcon Detection is Disabled! Using default icon", __FUNCTION__);
+ return "";
+ }
+ if (strServiceName.empty())
+ {
+ CLog::Log(LOGDEBUG, "%s Service Name is Empty! Can not detect a PictureIcon. Using default icon!", __FUNCTION__);
+ return "";
+ }
+ else
+ {
+ std::string piconXML, piconPath, defaultPng;
+ piconPath = "special://xbmc/userdata/PictureIcon/Picon/";
+ defaultPng = piconPath+"tuxbox.png";
+ piconXML = "special://xbmc/userdata/PictureIcon/picon.xml";
+ CXBMCTinyXML piconDoc;
+
+ if (!CFile::Exists(piconXML))
+ return defaultPng;
+
+ if (!piconDoc.LoadFile(piconXML))
+ {
+ CLog::Log(LOGERROR, "Error loading %s, Line %d\n%s", piconXML.c_str(), piconDoc.ErrorRow(), piconDoc.ErrorDesc());
+ return defaultPng;
+ }
+
+ TiXmlElement *pRootElement = piconDoc.RootElement();
+ if (!pRootElement || strcmpi(pRootElement->Value(),"picon") != 0)
+ {
+ CLog::Log(LOGERROR, "Error loading %s, no <picon> node", piconXML.c_str());
+ return defaultPng;
+ }
+
+ TiXmlElement* pServices = pRootElement->FirstChildElement("services");
+ TiXmlElement* pService;
+ pService = pServices->FirstChildElement("service");
+ while(pService)
+ {
+ std::string strName = XMLUtils::GetAttribute(pService, "name");
+ std::string strPng = XMLUtils::GetAttribute(pService, "png");
+
+ if(strName == strServiceName)
+ {
+ strPng = piconPath + strPng;
+ StringUtils::ToLower(strPng);
+ CLog::Log(LOGDEBUG, "%s %s: Path is: %s", __FUNCTION__,strServiceName.c_str(), strPng.c_str());
+ return strPng;
+ }
+ pService = pService->NextSiblingElement("service");
+ }
+ return defaultPng;
+ }
+}
+
+// iMODE: 0 = TV, 1 = Radio, 2 = Data, 3 = Movies, 4 = Root
+// SUBMODE: 0 = n/a, 1 = All, 2 = Satellites, 2 = Providers, 4 = Bouquets
+std::string CTuxBoxUtil::GetSubMode(int iMode, std::string& strXMLRootString, std::string& strXMLChildString)
+{
+ //Todo: add a setting: "Don't Use Request mode" to advanced.xml
+
+ // MODE: 0 = TV, 1 = Radio, 2 = Data, 3 = Movies, 4 = Root
+ // SUBMODE: 0 = n/a, 1 = All, 2 = Satellites, 2 = Providers, 4 = Bouquets
+ // Default Submode
+ std::string strSubMode;
+
+ if(iMode <0 || iMode >4)
+ {
+ strSubMode = StringUtils::Format("xml/services?mode=0&submode=4");
+ strXMLRootString = StringUtils::Format("bouquets");
+ strXMLChildString = StringUtils::Format("bouquet");
+ return strSubMode;
+ }
+
+ // popup the context menu
+
+ // FIXME: Localize these
+ CContextButtons choices;
+ choices.Add(1, "All");
+ choices.Add(2, "Satellites");
+ choices.Add(3, "Providers");
+ choices.Add(4, "Bouquets");
+
+ int iSubMode = CGUIDialogContextMenu::ShowAndGetChoice(choices);
+ if (iSubMode == 1)
+ {
+ strXMLRootString = StringUtils::Format("services");
+ strXMLChildString = StringUtils::Format("service");
+ }
+ else if (iSubMode == 2)
+ {
+ strXMLRootString = StringUtils::Format("satellites");
+ strXMLChildString = StringUtils::Format("satellite");
+ }
+ else if (iSubMode == 3)
+ {
+ strXMLRootString = StringUtils::Format("providers");
+ strXMLChildString = StringUtils::Format("provider");
+ }
+ else // if (iSubMode == 4 || iSubMode < 0)
+ {
+ iSubMode = 4;
+ strXMLRootString = StringUtils::Format("bouquets");
+ strXMLChildString = StringUtils::Format("bouquet");
+ }
+ strSubMode = StringUtils::Format("xml/services?mode=%i&submode=%i",iMode,iSubMode);
+ return strSubMode;
+}
+//Input: url/path of share/item file/folder
+//Output: the detected submode root and child string
+std::string CTuxBoxUtil::DetectSubMode(std::string strSubMode, std::string& strXMLRootString, std::string& strXMLChildString)
+{
+ //strSubMode = "xml/services?mode=0&submode=1"
+ std::string strFilter;
+ size_t ipointMode = strSubMode.find("?mode=");
+ size_t ipointSubMode = strSubMode.find("&submode=");
+ if (ipointMode != std::string::npos)
+ strFilter.assign(1, strSubMode.at(ipointMode + 6));
+
+ if (ipointSubMode != std::string::npos)
+ {
+ char v = strSubMode.at(ipointSubMode + 9);
+ if(v == '1')
+ {
+ strXMLRootString = "unknowns";
+ strXMLChildString = "unknown";
+ }
+ else if(v == '2')
+ {
+ strXMLRootString = "satellites";
+ strXMLChildString = "satellite";
+ }
+ else if(v == '3')
+ {
+ strXMLRootString = "providers";
+ strXMLChildString = "provider";
+ }
+ else if(v == '4')
+ {
+ strXMLRootString = "bouquets";
+ strXMLChildString = "bouquet";
+ }
+
+ }
+ return strFilter;
+}
diff --git a/src/utils/TuxBoxUtil.h b/src/utils/TuxBoxUtil.h
new file mode 100644
index 0000000000..f6532dd8a0
--- /dev/null
+++ b/src/utils/TuxBoxUtil.h
@@ -0,0 +1,191 @@
+#pragma once
+
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string>
+#include "threads/Thread.h"
+
+class CURL;
+class TiXmlElement;
+class CFileItem;
+class CFileItemList;
+
+struct STREAMINFO
+{
+ std::string frontend;
+ std::string service_name;
+ std::string service_reference;
+ std::string provider;
+ std::string vpid;
+ std::string apid;
+ std::string pcrpid;
+ std::string tpid;
+ std::string tsid;
+ std::string onid;
+ std::string sid;
+ std::string pmt;
+ std::string video_format;
+ std::string supported_crypt_systems;
+ std::string used_crypt_systems;
+ std::string satellite;
+ std::string frequency;
+ std::string symbol_rate;
+ std::string polarisation;
+ std::string inversion;
+ std::string fec;
+ std::string snr;
+ std::string agc;
+ std::string ber;
+ std::string lock;
+ std::string sync;
+};
+struct VIDEOSUBCHANNEL
+{
+ std::vector<std::string> reference;
+ std::vector<std::string> name;
+ std::vector<std::string> selected;
+ std::string current_name;
+ bool mode;
+};
+typedef struct AUDIOCHANNEL
+{
+ std::string pid;
+ std::string selected;
+ std::string name;
+} sAudioChannel;
+struct CURRENTSERVICEDATA
+{
+ std::string service_name;
+ std::string service_reference;
+ std::vector<AUDIOCHANNEL> audio_channels;
+ int requested_audio_channel;
+ std::string audio_track;
+ std::string current_event_date;
+ std::string current_event_time;
+ std::string current_event_start;
+ std::string current_event_duration;
+ std::string current_event_description;
+ std::string current_event_details;
+ std::string next_event_date;
+ std::string next_event_time;
+ std::string next_event_start;
+ std::string next_event_duration;
+ std::string next_event_description;
+ std::string next_event_details;
+};
+struct BOXSTATUS
+{
+ std::string current_time;
+ std::string standby;
+ std::string recording;
+ std::string mode;
+ std::string ip;
+};
+struct BOXSINFO
+{
+ std::string image_version;
+ std::string image_url;
+ std::string image_comment;
+ std::string image_catalog;
+ std::string firmware;
+ std::string fpfirmware;
+ std::string webinterface;
+ std::string model;
+ std::string manufacturer;
+ std::string processor;
+ std::string usbstick;
+ std::string disk;
+};
+struct SERVICE_EPG
+{
+ std::string service_reference;
+ std::string service_name;
+ std::string image_comment;
+ std::string event;
+ std::string date;
+ std::string time;
+ std::string duration;
+ std::string descritption;
+ std::string genre;
+ std::string genrecategory;
+ std::string start;
+ std::string details;
+};
+struct ZAPSTREAM
+{
+ bool initialized;
+ bool available;
+};
+class CTuxBoxUtil
+{
+ public:
+ STREAMINFO sStrmInfo;
+ CURRENTSERVICEDATA sCurSrvData;
+ BOXSTATUS sBoxStatus;
+ BOXSINFO sBoxInfo;
+ SERVICE_EPG sServiceEPG;
+ VIDEOSUBCHANNEL vVideoSubChannel;
+ ZAPSTREAM sZapstream;
+
+ CTuxBoxUtil(void);
+ virtual ~CTuxBoxUtil(void);
+
+ bool GetZapUrl(const std::string& strPath, CFileItem &items);
+ static bool ParseBouquets(TiXmlElement *root, CFileItemList &items, CURL &url, std::string strFilter, std::string strChild);
+ static bool ParseBouquetsEnigma2(TiXmlElement *root, CFileItemList &items, CURL &url, std::string& strFilter, std::string& strChild);
+ static bool ParseChannels(TiXmlElement *root, CFileItemList &items, CURL &url, std::string strFilter, std::string strChild);
+ static bool ParseChannelsEnigma2(TiXmlElement *root, CFileItemList &items, CURL &url, std::string& strFilter, std::string& strChild);
+ bool ZapToUrl(CURL url, const std::string &pathOption);
+ bool StreamInformations(TiXmlElement *pRootElement);
+ bool CurrentServiceData(TiXmlElement *pRootElement);
+ bool BoxStatus(TiXmlElement *pRootElement);
+ bool BoxInfo(TiXmlElement *pRootElement);
+ bool ServiceEPG(TiXmlElement *pRootElement);
+ bool GetHttpXML(CURL url,std::string strRequestType);
+ bool GetGUIRequestedAudioChannel(AUDIOCHANNEL& sRequestedAC);
+ bool GetRequestedAudioChannel(AUDIOCHANNEL& sRequestedAC) const;
+ bool GetVideoSubChannels(std::string& strVideoSubChannelName, std::string& strVideoSubChannelPid);
+ bool GetVideoChannels(TiXmlElement *pRootElement);
+ bool CreateNewItem(const CFileItem& item, CFileItem& item_new);
+ static bool InitZapstream(const std::string& strPath);
+ static bool SetAudioChannel(const std::string& strPath, const AUDIOCHANNEL& sAC);
+
+ static std::string GetPicon(std::string strServiceName);
+ static std::string GetSubMode(int iMode, std::string& strXMLRootString, std::string& strXMLChildString);
+ static std::string DetectSubMode(std::string strSubMode, std::string& strXMLRootString, std::string& strXMLChildString);
+};
+extern CTuxBoxUtil g_tuxbox;
+
+class CTuxBoxService : public CThread
+{
+public:
+ CTuxBoxService();
+ ~CTuxBoxService();
+
+ bool Start();
+ void Stop();
+ bool IsRunning();
+
+ virtual void OnExit();
+ virtual void OnStartup();
+ virtual void Process();
+};
+extern CTuxBoxService g_tuxboxService;
diff --git a/src/utils/URIUtils.cpp b/src/utils/URIUtils.cpp
new file mode 100644
index 0000000000..6ab3dbb9b2
--- /dev/null
+++ b/src/utils/URIUtils.cpp
@@ -0,0 +1,1335 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "network/Network.h"
+#include "URIUtils.h"
+#include "Application.h"
+#include "FileItem.h"
+#include "filesystem/MultiPathDirectory.h"
+#include "filesystem/MythDirectory.h"
+#include "filesystem/SpecialProtocol.h"
+#include "filesystem/StackDirectory.h"
+#include "network/DNSNameCache.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/MediaSettings.h"
+#include "URL.h"
+#include "StringUtils.h"
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+using namespace std;
+using namespace XFILE;
+
+bool URIUtils::IsInPath(const CStdString &uri, const CStdString &baseURI)
+{
+ CStdString uriPath = CSpecialProtocol::TranslatePath(uri);
+ CStdString basePath = CSpecialProtocol::TranslatePath(baseURI);
+
+ return !basePath.empty() && StringUtils::StartsWith(uriPath, basePath);
+}
+
+/* returns filename extension including period of filename */
+std::string URIUtils::GetExtension(const CURL& url)
+{
+ return URIUtils::GetExtension(url.GetFileName());
+}
+
+std::string URIUtils::GetExtension(const std::string& strFileName)
+{
+ if (IsURL(strFileName))
+ {
+ CURL url(strFileName);
+ return GetExtension(url.GetFileName());
+ }
+
+ size_t period = strFileName.find_last_of("./\\");
+ if (period == string::npos || strFileName[period] != '.')
+ return CStdString();
+
+ return strFileName.substr(period);
+}
+
+bool URIUtils::HasExtension(const CStdString& strFileName)
+{
+ if (IsURL(strFileName))
+ {
+ CURL url(strFileName);
+ return HasExtension(url.GetFileName());
+ }
+
+ size_t iPeriod = strFileName.find_last_of("./\\");
+ return iPeriod != string::npos && strFileName[iPeriod] == '.';
+}
+
+bool URIUtils::HasExtension(const CURL& url, const CStdString& strExtensions)
+{
+ return HasExtension(url.GetFileName(), strExtensions);
+}
+
+bool URIUtils::HasExtension(const CStdString& strFileName, const CStdString& strExtensions)
+{
+ if (IsURL(strFileName))
+ {
+ CURL url(strFileName);
+ return HasExtension(url.GetFileName(), strExtensions);
+ }
+
+ // Search backwards so that '.' can be used as a search terminator.
+ CStdString::const_reverse_iterator itExtensions = strExtensions.rbegin();
+ while (itExtensions != strExtensions.rend())
+ {
+ // Iterate backwards over strFileName untill we hit a '.' or a mismatch
+ for (CStdString::const_reverse_iterator itFileName = strFileName.rbegin();
+ itFileName != strFileName.rend() && itExtensions != strExtensions.rend() &&
+ tolower(*itFileName) == *itExtensions;
+ ++itFileName, ++itExtensions)
+ {
+ if (*itExtensions == '.')
+ return true; // Match
+ }
+
+ // No match. Look for more extensions to try.
+ while (itExtensions != strExtensions.rend() && *itExtensions != '|')
+ ++itExtensions;
+
+ while (itExtensions != strExtensions.rend() && *itExtensions == '|')
+ ++itExtensions;
+ }
+
+ return false;
+}
+
+void URIUtils::RemoveExtension(std::string& strFileName)
+{
+ if(IsURL(strFileName))
+ {
+ CURL url(strFileName);
+ strFileName = url.GetFileName();
+ RemoveExtension(strFileName);
+ url.SetFileName(strFileName);
+ strFileName = url.Get();
+ return;
+ }
+
+ size_t period = strFileName.find_last_of("./\\");
+ if (period != string::npos && strFileName[period] == '.')
+ {
+ CStdString strExtension = strFileName.substr(period);
+ StringUtils::ToLower(strExtension);
+ strExtension += "|";
+
+ CStdString strFileMask;
+ strFileMask = g_advancedSettings.m_pictureExtensions;
+ strFileMask += "|" + g_advancedSettings.m_musicExtensions;
+ strFileMask += "|" + g_advancedSettings.m_videoExtensions;
+ strFileMask += "|" + g_advancedSettings.m_subtitlesExtensions;
+#if defined(TARGET_DARWIN)
+ strFileMask += "|.py|.xml|.milk|.xpr|.xbt|.cdg|.app|.applescript|.workflow";
+#else
+ strFileMask += "|.py|.xml|.milk|.xpr|.xbt|.cdg";
+#endif
+ strFileMask += "|";
+
+ if (strFileMask.find(strExtension) != std::string::npos)
+ strFileName.erase(period);
+ }
+}
+
+CStdString URIUtils::ReplaceExtension(const CStdString& strFile,
+ const CStdString& strNewExtension)
+{
+ if(IsURL(strFile))
+ {
+ CURL url(strFile);
+ url.SetFileName(ReplaceExtension(url.GetFileName(), strNewExtension));
+ return url.Get();
+ }
+
+ CStdString strChangedFile;
+ CStdString strExtension = GetExtension(strFile);
+ if ( strExtension.size() )
+ {
+ strChangedFile = strFile.substr(0, strFile.size() - strExtension.size()) ;
+ strChangedFile += strNewExtension;
+ }
+ else
+ {
+ strChangedFile = strFile;
+ strChangedFile += strNewExtension;
+ }
+ return strChangedFile;
+}
+
+const CStdString URIUtils::GetFileName(const CURL& url)
+{
+ return GetFileName(url.GetFileName());
+}
+
+/* returns a filename given an url */
+/* handles both / and \, and options in urls*/
+const CStdString URIUtils::GetFileName(const CStdString& strFileNameAndPath)
+{
+ if(IsURL(strFileNameAndPath))
+ {
+ CURL url(strFileNameAndPath);
+ return GetFileName(url.GetFileName());
+ }
+
+ /* find the last slash */
+ const size_t slash = strFileNameAndPath.find_last_of("/\\");
+ return strFileNameAndPath.substr(slash+1);
+}
+
+void URIUtils::Split(const std::string& strFileNameAndPath,
+ std::string& strPath, std::string& strFileName)
+{
+ //Splits a full filename in path and file.
+ //ex. smb://computer/share/directory/filename.ext -> strPath:smb://computer/share/directory/ and strFileName:filename.ext
+ //Trailing slash will be preserved
+ strFileName = "";
+ strPath = "";
+ int i = strFileNameAndPath.size() - 1;
+ while (i > 0)
+ {
+ char ch = strFileNameAndPath[i];
+ // Only break on ':' if it's a drive separator for DOS (ie d:foo)
+ if (ch == '/' || ch == '\\' || (ch == ':' && i == 1)) break;
+ else i--;
+ }
+ if (i == 0)
+ i--;
+
+ // take left including the directory separator
+ strPath = strFileNameAndPath.substr(0, i+1);
+ // everything to the right of the directory separator
+ strFileName = strFileNameAndPath.substr(i+1);
+}
+
+std::vector<std::string> URIUtils::SplitPath(const CStdString& strPath)
+{
+ CURL url(strPath);
+
+ // silly CStdString can't take a char in the constructor
+ CStdString sep(1, url.GetDirectorySeparator());
+
+ // split the filename portion of the URL up into separate dirs
+ vector<string> dirs = StringUtils::Split(url.GetFileName(), sep);
+
+ // we start with the root path
+ CStdString dir = url.GetWithoutFilename();
+
+ if (!dir.empty())
+ dirs.insert(dirs.begin(), dir);
+
+ // we don't need empty token on the end
+ if (dirs.size() > 1 && dirs.back().empty())
+ dirs.erase(dirs.end() - 1);
+
+ return dirs;
+}
+
+void URIUtils::GetCommonPath(std::string& strParent, const std::string& strPath)
+{
+ // find the common path of parent and path
+ unsigned int j = 1;
+ while (j <= min(strParent.size(), strPath.size()) && strnicmp(strParent.c_str(), strPath.c_str(), j) == 0)
+ j++;
+ strParent.erase(j - 1);
+ // they should at least share a / at the end, though for things such as path/cd1 and path/cd2 there won't be
+ if (!HasSlashAtEnd(strParent))
+ {
+ strParent = GetDirectory(strParent);
+ AddSlashAtEnd(strParent);
+ }
+}
+
+bool URIUtils::HasParentInHostname(const CURL& url)
+{
+ return url.IsProtocol("zip")
+ || url.IsProtocol("rar")
+ || url.IsProtocol("apk")
+ || url.IsProtocol("bluray")
+ || url.IsProtocol("udf");
+}
+
+bool URIUtils::HasEncodedHostname(const CURL& url)
+{
+ return HasParentInHostname(url)
+ || url.IsProtocol("musicsearch")
+ || url.IsProtocol( "image");
+}
+
+bool URIUtils::HasEncodedFilename(const CURL& url)
+{
+ const std::string prot2 = url.GetTranslatedProtocol();
+
+ // For now assume only (quasi) http internet streams use URL encoding
+ return CURL::IsProtocolEqual(prot2, "http") ||
+ CURL::IsProtocolEqual(prot2, "https");
+}
+
+std::string URIUtils::GetParentPath(const std::string& strPath)
+{
+ std::string strReturn;
+ GetParentPath(strPath, strReturn);
+ return strReturn;
+}
+
+bool URIUtils::GetParentPath(const std::string& strPath, std::string& strParent)
+{
+ strParent.clear();
+
+ CURL url(strPath);
+ std::string strFile = url.GetFileName();
+ if ( URIUtils::HasParentInHostname(url) && strFile.empty())
+ {
+ strFile = url.GetHostName();
+ return GetParentPath(strFile, strParent);
+ }
+ else if (url.IsProtocol("stack"))
+ {
+ CStackDirectory dir;
+ CFileItemList items;
+ dir.GetDirectory(url, items);
+ items[0]->m_strDVDLabel = GetDirectory(items[0]->GetPath());
+ if (IsProtocol(items[0]->m_strDVDLabel, "rar") || IsProtocol(items[0]->m_strDVDLabel, "zip"))
+ GetParentPath(items[0]->m_strDVDLabel, strParent);
+ else
+ strParent = items[0]->m_strDVDLabel;
+ for( int i=1;i<items.Size();++i)
+ {
+ items[i]->m_strDVDLabel = GetDirectory(items[i]->GetPath());
+ if (IsProtocol(items[0]->m_strDVDLabel, "rar") || IsProtocol(items[0]->m_strDVDLabel, "zip"))
+ items[i]->SetPath(GetParentPath(items[i]->m_strDVDLabel));
+ else
+ items[i]->SetPath(items[i]->m_strDVDLabel);
+
+ GetCommonPath(strParent,items[i]->GetPath());
+ }
+ return true;
+ }
+ else if (url.IsProtocol("multipath"))
+ {
+ // get the parent path of the first item
+ return GetParentPath(CMultiPathDirectory::GetFirstPath(strPath), strParent);
+ }
+ else if (url.IsProtocol("plugin"))
+ {
+ if (!url.GetOptions().empty())
+ {
+ url.SetOptions("");
+ strParent = url.Get();
+ return true;
+ }
+ if (!url.GetFileName().empty())
+ {
+ url.SetFileName("");
+ strParent = url.Get();
+ return true;
+ }
+ if (!url.GetHostName().empty())
+ {
+ url.SetHostName("");
+ strParent = url.Get();
+ return true;
+ }
+ return true; // already at root
+ }
+ else if (url.IsProtocol("special"))
+ {
+ if (HasSlashAtEnd(strFile))
+ strFile.erase(strFile.size() - 1);
+ if(strFile.rfind('/') == std::string::npos)
+ return false;
+ }
+ else if (strFile.size() == 0)
+ {
+ if (url.GetHostName().size() > 0)
+ {
+ // we have an share with only server or workgroup name
+ // set hostname to "" and return true to get back to root
+ url.SetHostName("");
+ strParent = url.Get();
+ return true;
+ }
+ return false;
+ }
+
+ if (HasSlashAtEnd(strFile) )
+ {
+ strFile.erase(strFile.size() - 1);
+ }
+
+ size_t iPos = strFile.rfind('/');
+#ifndef TARGET_POSIX
+ if (iPos == std::string::npos)
+ {
+ iPos = strFile.rfind('\\');
+ }
+#endif
+ if (iPos == std::string::npos)
+ {
+ url.SetFileName("");
+ strParent = url.Get();
+ return true;
+ }
+
+ strFile.erase(iPos);
+
+ AddSlashAtEnd(strFile);
+
+ url.SetFileName(strFile);
+ strParent = url.Get();
+ return true;
+}
+
+std::string URLEncodePath(const std::string& strPath)
+{
+ vector<string> segments = StringUtils::Split(strPath, "/");
+ for (vector<string>::iterator i = segments.begin(); i != segments.end(); ++i)
+ *i = CURL::Encode(*i);
+
+ return StringUtils::Join(segments, "/");
+}
+
+std::string URLDecodePath(const std::string& strPath)
+{
+ vector<string> segments = StringUtils::Split(strPath, "/");
+ for (vector<string>::iterator i = segments.begin(); i != segments.end(); ++i)
+ *i = CURL::Decode(*i);
+
+ return StringUtils::Join(segments, "/");
+}
+
+std::string URIUtils::ChangeBasePath(const std::string &fromPath, const std::string &fromFile, const std::string &toPath)
+{
+ std::string toFile = fromFile;
+
+ // Convert back slashes to forward slashes, if required
+ if (IsDOSPath(fromPath) && !IsDOSPath(toPath))
+ StringUtils::Replace(toFile, "\\", "/");
+
+ // Handle difference in URL encoded vs. not encoded
+ if ( HasEncodedFilename(CURL(fromPath))
+ && !HasEncodedFilename(CURL(toPath)) )
+ {
+ toFile = URLDecodePath(toFile); // Decode path
+ }
+ else if (!HasEncodedFilename(CURL(fromPath))
+ && HasEncodedFilename(CURL(toPath)) )
+ {
+ toFile = URLEncodePath(toFile); // Encode path
+ }
+
+ // Convert forward slashes to back slashes, if required
+ if (!IsDOSPath(fromPath) && IsDOSPath(toPath))
+ StringUtils::Replace(toFile, "/", "\\");
+
+ return AddFileToFolder(toPath, toFile);
+}
+
+CURL URIUtils::SubstitutePath(const CURL& url, bool reverse /* = false */)
+{
+ const CStdString pathToUrl = url.Get();
+ return CURL(SubstitutePath(pathToUrl, reverse));
+}
+
+CStdString URIUtils::SubstitutePath(const CStdString& strPath, bool reverse /* = false */)
+{
+ for (CAdvancedSettings::StringMapping::iterator i = g_advancedSettings.m_pathSubstitutions.begin();
+ i != g_advancedSettings.m_pathSubstitutions.end(); ++i)
+ {
+ CStdString fromPath;
+ CStdString toPath;
+
+ if (!reverse)
+ {
+ fromPath = i->first; // Fake path
+ toPath = i->second; // Real path
+ }
+ else
+ {
+ fromPath = i->second; // Real path
+ toPath = i->first; // Fake path
+ }
+
+ if (strncmp(strPath.c_str(), fromPath.c_str(), HasSlashAtEnd(fromPath) ? fromPath.size() - 1 : fromPath.size()) == 0)
+ {
+ if (strPath.size() > fromPath.size())
+ {
+ CStdString strSubPathAndFileName = strPath.substr(fromPath.size());
+ return ChangeBasePath(fromPath, strSubPathAndFileName, toPath); // Fix encoding + slash direction
+ }
+ else
+ {
+ return toPath;
+ }
+ }
+ }
+ return strPath;
+}
+
+bool URIUtils::IsProtocol(const std::string& url, const std::string &type)
+{
+ return StringUtils::StartsWithNoCase(url, type + "://");
+}
+
+bool URIUtils::PathStarts(const std::string& url, const char *start)
+{
+ return StringUtils::StartsWith(url, start);
+}
+
+bool URIUtils::PathEquals(const std::string& url, const std::string &start)
+{
+ return url == start;
+}
+
+bool URIUtils::IsRemote(const CStdString& strFile)
+{
+ if (IsCDDA(strFile) || IsISO9660(strFile))
+ return false;
+
+ if (IsStack(strFile))
+ return IsRemote(CStackDirectory::GetFirstStackedFile(strFile));
+
+ if (IsSpecial(strFile))
+ return IsRemote(CSpecialProtocol::TranslatePath(strFile));
+
+ if(IsMultiPath(strFile))
+ { // virtual paths need to be checked separately
+ vector<std::string> paths;
+ if (CMultiPathDirectory::GetPaths(strFile, paths))
+ {
+ for (unsigned int i = 0; i < paths.size(); i++)
+ if (IsRemote(paths[i])) return true;
+ }
+ return false;
+ }
+
+ CURL url(strFile);
+ if(HasParentInHostname(url))
+ return IsRemote(url.GetHostName());
+
+ if (!url.IsLocal())
+ return true;
+
+ return false;
+}
+
+bool URIUtils::IsOnDVD(const CStdString& strFile)
+{
+#ifdef TARGET_WINDOWS
+ if (strFile.size() >= 2 && strFile.substr(1,1) == ":")
+ return (GetDriveType(strFile.substr(0, 3).c_str()) == DRIVE_CDROM);
+#endif
+
+ if (IsProtocol(strFile, "dvd"))
+ return true;
+
+ if (IsProtocol(strFile, "udf"))
+ return true;
+
+ if (IsProtocol(strFile, "iso9660"))
+ return true;
+
+ if (IsProtocol(strFile, "cdda"))
+ return true;
+
+ return false;
+}
+
+bool URIUtils::IsOnLAN(const CStdString& strPath)
+{
+ if(IsMultiPath(strPath))
+ return IsOnLAN(CMultiPathDirectory::GetFirstPath(strPath));
+
+ if(IsStack(strPath))
+ return IsOnLAN(CStackDirectory::GetFirstStackedFile(strPath));
+
+ if(IsSpecial(strPath))
+ return IsOnLAN(CSpecialProtocol::TranslatePath(strPath));
+
+ if(IsDAAP(strPath))
+ return true;
+
+ if(IsPlugin(strPath))
+ return false;
+
+ if(IsTuxBox(strPath))
+ return true;
+
+ if(IsUPnP(strPath))
+ return true;
+
+ CURL url(strPath);
+ if (HasParentInHostname(url))
+ return IsOnLAN(url.GetHostName());
+
+ if(!IsRemote(strPath))
+ return false;
+
+ CStdString host = url.GetHostName();
+
+ return IsHostOnLAN(host);
+}
+
+static bool addr_match(uint32_t addr, const char* target, const char* submask)
+{
+ uint32_t addr2 = ntohl(inet_addr(target));
+ uint32_t mask = ntohl(inet_addr(submask));
+ return (addr & mask) == (addr2 & mask);
+}
+
+bool URIUtils::IsHostOnLAN(const CStdString& host, bool offLineCheck)
+{
+ if(host.length() == 0)
+ return false;
+
+ // assume a hostname without dot's
+ // is local (smb netbios hostnames)
+ if(host.find('.') == string::npos)
+ return true;
+
+ uint32_t address = ntohl(inet_addr(host.c_str()));
+ if(address == INADDR_NONE)
+ {
+ CStdString ip;
+ if(CDNSNameCache::Lookup(host, ip))
+ address = ntohl(inet_addr(ip.c_str()));
+ }
+
+ if(address != INADDR_NONE)
+ {
+ if (offLineCheck) // check if in private range, ref https://en.wikipedia.org/wiki/Private_network
+ {
+ if (
+ addr_match(address, "192.168.0.0", "255.255.0.0") ||
+ addr_match(address, "10.0.0.0", "255.0.0.0") ||
+ addr_match(address, "172.16.0.0", "255.240.0.0")
+ )
+ return true;
+ }
+ // check if we are on the local subnet
+ if (!g_application.getNetwork().GetFirstConnectedInterface())
+ return false;
+
+ if (g_application.getNetwork().HasInterfaceForIP(address))
+ return true;
+ }
+
+ return false;
+}
+
+bool URIUtils::IsMultiPath(const CStdString& strPath)
+{
+ return IsProtocol(strPath, "multipath");
+}
+
+bool URIUtils::IsHD(const CStdString& strFileName)
+{
+ CURL url(strFileName);
+
+ if (IsStack(strFileName))
+ return IsHD(CStackDirectory::GetFirstStackedFile(strFileName));
+
+ if (IsSpecial(strFileName))
+ return IsHD(CSpecialProtocol::TranslatePath(strFileName));
+
+ if (HasParentInHostname(url))
+ return IsHD(url.GetHostName());
+
+ return url.GetProtocol().empty() || url.IsProtocol("file");
+}
+
+bool URIUtils::IsDVD(const CStdString& strFile)
+{
+ CStdString strFileLow = strFile;
+ StringUtils::ToLower(strFileLow);
+ if (strFileLow.find("video_ts.ifo") != std::string::npos && IsOnDVD(strFile))
+ return true;
+
+#if defined(TARGET_WINDOWS)
+ if (IsProtocol(strFile, "dvd"))
+ return true;
+
+ if(strFile.size() < 2 || (strFile.substr(1) != ":\\" && strFile.substr(1) != ":"))
+ return false;
+
+ if(GetDriveType(strFile.c_str()) == DRIVE_CDROM)
+ return true;
+#else
+ if (strFileLow == "iso9660://" || strFileLow == "udf://" || strFileLow == "dvd://1" )
+ return true;
+#endif
+
+ return false;
+}
+
+bool URIUtils::IsStack(const CStdString& strFile)
+{
+ return IsProtocol(strFile, "stack");
+}
+
+bool URIUtils::IsRAR(const CStdString& strFile)
+{
+ CStdString strExtension = GetExtension(strFile);
+
+ if (strExtension.Equals(".001") && !StringUtils::EndsWithNoCase(strFile, ".ts.001"))
+ return true;
+
+ if (StringUtils::EqualsNoCase(strExtension, ".cbr"))
+ return true;
+
+ if (StringUtils::EqualsNoCase(strExtension, ".rar"))
+ return true;
+
+ return false;
+}
+
+bool URIUtils::IsInArchive(const CStdString &strFile)
+{
+ return IsInZIP(strFile) || IsInRAR(strFile) || IsInAPK(strFile);
+}
+
+bool URIUtils::IsInAPK(const CStdString& strFile)
+{
+ CURL url(strFile);
+
+ return url.IsProtocol("apk") && !url.GetFileName().empty();
+}
+
+bool URIUtils::IsInZIP(const CStdString& strFile)
+{
+ CURL url(strFile);
+
+ return url.IsProtocol("zip") && !url.GetFileName().empty();
+}
+
+bool URIUtils::IsInRAR(const CStdString& strFile)
+{
+ CURL url(strFile);
+
+ return url.IsProtocol("rar") && !url.GetFileName().empty();
+}
+
+bool URIUtils::IsAPK(const CStdString& strFile)
+{
+ return HasExtension(strFile, ".apk");
+}
+
+bool URIUtils::IsZIP(const CStdString& strFile) // also checks for comic books!
+{
+ return HasExtension(strFile, ".zip|.cbz");
+}
+
+bool URIUtils::IsArchive(const CStdString& strFile)
+{
+ return HasExtension(strFile, ".zip|.rar|.apk|.cbz|.cbr");
+}
+
+bool URIUtils::IsSpecial(const CStdString& strFile)
+{
+ CStdString strFile2(strFile);
+
+ if (IsStack(strFile))
+ strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
+
+ return IsProtocol(strFile2, "special");
+}
+
+bool URIUtils::IsPlugin(const CStdString& strFile)
+{
+ CURL url(strFile);
+ return url.IsProtocol("plugin");
+}
+
+bool URIUtils::IsScript(const CStdString& strFile)
+{
+ CURL url(strFile);
+ return url.IsProtocol("script");
+}
+
+bool URIUtils::IsAddonsPath(const CStdString& strFile)
+{
+ CURL url(strFile);
+ return url.IsProtocol("addons");
+}
+
+bool URIUtils::IsSourcesPath(const CStdString& strPath)
+{
+ CURL url(strPath);
+ return url.IsProtocol("sources");
+}
+
+bool URIUtils::IsCDDA(const CStdString& strFile)
+{
+ return IsProtocol(strFile, "cdda");
+}
+
+bool URIUtils::IsISO9660(const CStdString& strFile)
+{
+ return IsProtocol(strFile, "iso9660");
+}
+
+bool URIUtils::IsSmb(const CStdString& strFile)
+{
+ CStdString strFile2(strFile);
+
+ if (IsStack(strFile))
+ strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
+
+ return IsProtocol(strFile2, "smb");
+}
+
+bool URIUtils::IsURL(const CStdString& strFile)
+{
+ return strFile.find("://") != std::string::npos;
+}
+
+bool URIUtils::IsFTP(const CStdString& strFile)
+{
+ CStdString strFile2(strFile);
+
+ if (IsStack(strFile))
+ strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
+
+ return IsProtocol(strFile2, "ftp") ||
+ IsProtocol(strFile2, "ftps");
+}
+
+bool URIUtils::IsUDP(const CStdString& strFile)
+{
+ CStdString strFile2(strFile);
+
+ if (IsStack(strFile))
+ strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
+
+ return IsProtocol(strFile2, "udp");
+}
+
+bool URIUtils::IsTCP(const CStdString& strFile)
+{
+ CStdString strFile2(strFile);
+
+ if (IsStack(strFile))
+ strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
+
+ return IsProtocol(strFile2, "tcp");
+}
+
+bool URIUtils::IsPVRChannel(const CStdString& strFile)
+{
+ CStdString strFile2(strFile);
+
+ if (IsStack(strFile))
+ strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
+
+ return StringUtils::StartsWithNoCase(strFile2, "pvr://channels");
+}
+
+bool URIUtils::IsDAV(const CStdString& strFile)
+{
+ CStdString strFile2(strFile);
+
+ if (IsStack(strFile))
+ strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
+
+ return IsProtocol(strFile2, "dav") ||
+ IsProtocol(strFile2, "davs");
+}
+
+bool URIUtils::IsInternetStream(const std::string &path, bool bStrictCheck /* = false */)
+{
+ const CURL pathToUrl(path);
+ return IsInternetStream(pathToUrl, bStrictCheck);
+}
+
+bool URIUtils::IsInternetStream(const CURL& url, bool bStrictCheck /* = false */)
+{
+ if (url.GetProtocol().empty())
+ return false;
+
+ // there's nothing to stop internet streams from being stacked
+ if (url.IsProtocol("stack"))
+ return IsInternetStream(CStackDirectory::GetFirstStackedFile(url.Get()));
+
+ // Special case these
+ if (url.IsProtocol("ftp") || url.IsProtocol("ftps") ||
+ url.IsProtocol("dav") || url.IsProtocol("davs") ||
+ url.IsProtocol("sftp"))
+ return bStrictCheck;
+
+ std::string protocol = url.GetTranslatedProtocol();
+ if (CURL::IsProtocolEqual(protocol, "http") || CURL::IsProtocolEqual(protocol, "https") ||
+ CURL::IsProtocolEqual(protocol, "tcp") || CURL::IsProtocolEqual(protocol, "udp") ||
+ CURL::IsProtocolEqual(protocol, "rtp") || CURL::IsProtocolEqual(protocol, "sdp") ||
+ CURL::IsProtocolEqual(protocol, "mms") || CURL::IsProtocolEqual(protocol, "mmst") ||
+ CURL::IsProtocolEqual(protocol, "mmsh") || CURL::IsProtocolEqual(protocol, "rtsp") ||
+ CURL::IsProtocolEqual(protocol, "rtmp") || CURL::IsProtocolEqual(protocol, "rtmpt") ||
+ CURL::IsProtocolEqual(protocol, "rtmpe") || CURL::IsProtocolEqual(protocol, "rtmpte") ||
+ CURL::IsProtocolEqual(protocol, "rtmps"))
+ return true;
+
+ return false;
+}
+
+bool URIUtils::IsDAAP(const CStdString& strFile)
+{
+ return IsProtocol(strFile, "daap");
+}
+
+bool URIUtils::IsUPnP(const CStdString& strFile)
+{
+ return IsProtocol(strFile, "upnp");
+}
+
+bool URIUtils::IsTuxBox(const CStdString& strFile)
+{
+ return IsProtocol(strFile, "tuxbox");
+}
+
+bool URIUtils::IsMythTV(const CStdString& strFile)
+{
+ return IsProtocol(strFile, "myth");
+}
+
+bool URIUtils::IsHDHomeRun(const CStdString& strFile)
+{
+ return IsProtocol(strFile, "hdhomerun");
+}
+
+bool URIUtils::IsSlingbox(const CStdString& strFile)
+{
+ return IsProtocol(strFile, "sling");
+}
+
+bool URIUtils::IsVTP(const CStdString& strFile)
+{
+ return IsProtocol(strFile, "vtp");
+}
+
+bool URIUtils::IsHTSP(const CStdString& strFile)
+{
+ return IsProtocol(strFile, "htsp");
+}
+
+bool URIUtils::IsLiveTV(const CStdString& strFile)
+{
+ CStdString strFileWithoutSlash(strFile);
+ RemoveSlashAtEnd(strFileWithoutSlash);
+
+ if(IsTuxBox(strFile)
+ || IsVTP(strFile)
+ || IsHDHomeRun(strFile)
+ || IsSlingbox(strFile)
+ || IsHTSP(strFile)
+ || IsProtocol(strFile, "sap")
+ ||(StringUtils::EndsWithNoCase(strFileWithoutSlash, ".pvr") && !PathStarts(strFileWithoutSlash, "pvr://recordings")))
+ return true;
+
+ if (IsMythTV(strFile) && CMythDirectory::IsLiveTV(strFile))
+ return true;
+
+ return false;
+}
+
+bool URIUtils::IsPVRRecording(const CStdString& strFile)
+{
+ CStdString strFileWithoutSlash(strFile);
+ RemoveSlashAtEnd(strFileWithoutSlash);
+
+ return StringUtils::EndsWithNoCase(strFileWithoutSlash, ".pvr") &&
+ PathStarts(strFile, "pvr://recordings");
+}
+
+bool URIUtils::IsMusicDb(const CStdString& strFile)
+{
+ return IsProtocol(strFile, "musicdb");
+}
+
+bool URIUtils::IsNfs(const CStdString& strFile)
+{
+ CStdString strFile2(strFile);
+
+ if (IsStack(strFile))
+ strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
+
+ return IsProtocol(strFile2, "nfs");
+}
+
+bool URIUtils::IsAfp(const CStdString& strFile)
+{
+ CStdString strFile2(strFile);
+
+ if (IsStack(strFile))
+ strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
+
+ return IsProtocol(strFile2, "afp");
+}
+
+
+bool URIUtils::IsVideoDb(const CStdString& strFile)
+{
+ return IsProtocol(strFile, "videodb");
+}
+
+bool URIUtils::IsBluray(const CStdString& strFile)
+{
+ return IsProtocol(strFile, "bluray");
+}
+
+bool URIUtils::IsAndroidApp(const CStdString &path)
+{
+ return IsProtocol(path, "androidapp");
+}
+
+bool URIUtils::IsLibraryFolder(const CStdString& strFile)
+{
+ CURL url(strFile);
+ return url.IsProtocol("library");
+}
+
+bool URIUtils::IsLibraryContent(const std::string &strFile)
+{
+ return (IsProtocol(strFile, "library") ||
+ IsProtocol(strFile, "videodb") ||
+ IsProtocol(strFile, "musicdb") ||
+ StringUtils::EndsWith(strFile, ".xsp"));
+}
+
+bool URIUtils::IsDOSPath(const CStdString &path)
+{
+ if (path.size() > 1 && path[1] == ':' && isalpha(path[0]))
+ return true;
+
+ // windows network drives
+ if (path.size() > 1 && path[0] == '\\' && path[1] == '\\')
+ return true;
+
+ return false;
+}
+
+void URIUtils::AddSlashAtEnd(std::string& strFolder)
+{
+ if (IsURL(strFolder))
+ {
+ CURL url(strFolder);
+ std::string file = url.GetFileName();
+ if(!file.empty() && file != strFolder)
+ {
+ AddSlashAtEnd(file);
+ url.SetFileName(file);
+ strFolder = url.Get();
+ }
+ return;
+ }
+
+ if (!HasSlashAtEnd(strFolder))
+ {
+ if (IsDOSPath(strFolder))
+ strFolder += '\\';
+ else
+ strFolder += '/';
+ }
+}
+
+bool URIUtils::HasSlashAtEnd(const std::string& strFile, bool checkURL /* = false */)
+{
+ if (strFile.empty()) return false;
+ if (checkURL && IsURL(strFile))
+ {
+ CURL url(strFile);
+ CStdString file = url.GetFileName();
+ return file.empty() || HasSlashAtEnd(file, false);
+ }
+ char kar = strFile.c_str()[strFile.size() - 1];
+
+ if (kar == '/' || kar == '\\')
+ return true;
+
+ return false;
+}
+
+void URIUtils::RemoveSlashAtEnd(std::string& strFolder)
+{
+ if (IsURL(strFolder))
+ {
+ CURL url(strFolder);
+ std::string file = url.GetFileName();
+ if (!file.empty() && file != strFolder)
+ {
+ RemoveSlashAtEnd(file);
+ url.SetFileName(file);
+ strFolder = url.Get();
+ return;
+ }
+ if(url.GetHostName().empty())
+ return;
+ }
+
+ while (HasSlashAtEnd(strFolder))
+ strFolder.erase(strFolder.size()-1, 1);
+}
+
+bool URIUtils::CompareWithoutSlashAtEnd(const CStdString& strPath1, const CStdString& strPath2)
+{
+ CStdString strc1 = strPath1, strc2 = strPath2;
+ RemoveSlashAtEnd(strc1);
+ RemoveSlashAtEnd(strc2);
+ return strc1.Equals(strc2);
+}
+
+
+std::string URIUtils::FixSlashesAndDups(const std::string& path, const char slashCharacter /* = '/' */, const size_t startFrom /*= 0*/)
+{
+ const size_t len = path.length();
+ if (startFrom >= len)
+ return path;
+
+ std::string result(path, 0, startFrom);
+ result.reserve(len);
+
+ const char* const str = path.c_str();
+ size_t pos = startFrom;
+ do
+ {
+ if (str[pos] == '\\' || str[pos] == '/')
+ {
+ result.push_back(slashCharacter); // append one slash
+ pos++;
+ // skip any following slashes
+ while (str[pos] == '\\' || str[pos] == '/') // str is null-terminated, no need to check for buffer overrun
+ pos++;
+ }
+ else
+ result.push_back(str[pos++]); // append current char and advance pos to next char
+
+ } while (pos < len);
+
+ return result;
+}
+
+
+std::string URIUtils::CanonicalizePath(const std::string& path, const char slashCharacter /*= '\\'*/)
+{
+ assert(slashCharacter == '\\' || slashCharacter == '/');
+
+ if (path.empty())
+ return path;
+
+ const std::string slashStr(1, slashCharacter);
+ vector<std::string> pathVec, resultVec;
+ StringUtils::Tokenize(path, pathVec, slashStr);
+
+ for (vector<std::string>::const_iterator it = pathVec.begin(); it != pathVec.end(); ++it)
+ {
+ if (*it == ".")
+ { /* skip - do nothing */ }
+ else if (*it == ".." && !resultVec.empty() && resultVec.back() != "..")
+ resultVec.pop_back();
+ else
+ resultVec.push_back(*it);
+ }
+
+ std::string result;
+ if (path[0] == slashCharacter)
+ result.push_back(slashCharacter); // add slash at the begin
+
+ result += StringUtils::Join(resultVec, slashStr);
+
+ if (path[path.length() - 1] == slashCharacter && !result.empty() && result[result.length() - 1] != slashCharacter)
+ result.push_back(slashCharacter); // add slash at the end if result isn't empty and result isn't "/"
+
+ return result;
+}
+
+CStdString URIUtils::AddFileToFolder(const CStdString& strFolder,
+ const CStdString& strFile)
+{
+ if (IsURL(strFolder))
+ {
+ CURL url(strFolder);
+ if (url.GetFileName() != strFolder)
+ {
+ url.SetFileName(AddFileToFolder(url.GetFileName(), strFile));
+ return url.Get();
+ }
+ }
+
+ CStdString strResult = strFolder;
+ if (!strResult.empty())
+ AddSlashAtEnd(strResult);
+
+ // Remove any slash at the start of the file
+ if (strFile.size() && (strFile[0] == '/' || strFile[0] == '\\'))
+ strResult += strFile.substr(1);
+ else
+ strResult += strFile;
+
+ // correct any slash directions
+ if (!IsDOSPath(strFolder))
+ StringUtils::Replace(strResult, '\\', '/');
+ else
+ StringUtils::Replace(strResult, '/', '\\');
+
+ return strResult;
+}
+
+CStdString URIUtils::GetDirectory(const CStdString &strFilePath)
+{
+ // Will from a full filename return the directory the file resides in.
+ // Keeps the final slash at end and possible |option=foo options.
+
+ size_t iPosSlash = strFilePath.find_last_of("/\\");
+ if (iPosSlash == string::npos)
+ return ""; // No slash, so no path (ignore any options)
+
+ size_t iPosBar = strFilePath.rfind('|');
+ if (iPosBar == string::npos)
+ return strFilePath.substr(0, iPosSlash + 1); // Only path
+
+ return strFilePath.substr(0, iPosSlash + 1) + strFilePath.substr(iPosBar); // Path + options
+}
+
+CURL URIUtils::CreateArchivePath(const std::string& type,
+ const CURL& archiveUrl,
+ const std::string& pathInArchive,
+ const std::string& password)
+{
+ CURL url;
+ url.SetProtocol(type);
+ if (!password.empty())
+ url.SetUserName(password);
+ url.SetHostName(archiveUrl.Get());
+
+ /* NOTE: on posix systems, the replacement of \ with / is incorrect.
+ Ideally this would not be done. We need to check that the ZipManager
+ and RarManager code (and elsewhere) doesn't pass in non-posix paths.
+ */
+ std::string strBuffer(pathInArchive);
+ StringUtils::Replace(strBuffer, '\\', '/');
+ StringUtils::TrimLeft(strBuffer, "/");
+ url.SetFileName(strBuffer);
+
+ return url;
+}
+
+string URIUtils::GetRealPath(const string &path)
+{
+ if (path.empty())
+ return path;
+
+ CURL url(path);
+ url.SetHostName(GetRealPath(url.GetHostName()));
+ url.SetFileName(resolvePath(url.GetFileName()));
+
+ return url.Get();
+}
+
+std::string URIUtils::resolvePath(const std::string &path)
+{
+ if (path.empty())
+ return path;
+
+ size_t posSlash = path.find('/');
+ size_t posBackslash = path.find('\\');
+ string delim = posSlash < posBackslash ? "/" : "\\";
+ vector<string> parts = StringUtils::Split(path, delim);
+ vector<string> realParts;
+
+ for (vector<string>::const_iterator part = parts.begin(); part != parts.end(); ++part)
+ {
+ if (part->empty() || part->compare(".") == 0)
+ continue;
+
+ // go one level back up
+ if (part->compare("..") == 0)
+ {
+ if (!realParts.empty())
+ realParts.pop_back();
+ continue;
+ }
+
+ realParts.push_back(*part);
+ }
+
+ CStdString realPath;
+ // re-add any / or \ at the beginning
+ for (std::string::const_iterator itPath = path.begin(); itPath != path.end(); ++itPath)
+ {
+ if (*itPath != delim.at(0))
+ break;
+
+ realPath += delim;
+ }
+ // put together the path
+ realPath += StringUtils::Join(realParts, delim);
+ // re-add any / or \ at the end
+ if (path.at(path.size() - 1) == delim.at(0) && realPath.at(realPath.size() - 1) != delim.at(0))
+ realPath += delim;
+
+ return realPath;
+}
+
+bool URIUtils::UpdateUrlEncoding(std::string &strFilename)
+{
+ if (strFilename.empty())
+ return false;
+
+ CURL url(strFilename);
+ // if this is a stack:// URL we need to work with its filename
+ if (URIUtils::IsStack(strFilename))
+ {
+ vector<string> files;
+ if (!CStackDirectory::GetPaths(strFilename, files))
+ return false;
+
+ for (vector<string>::iterator file = files.begin(); file != files.end(); file++)
+ UpdateUrlEncoding(*file);
+
+ CStdString stackPath;
+ if (!CStackDirectory::ConstructStackPath(files, stackPath))
+ return false;
+
+ url.Parse(stackPath);
+ }
+ // if the protocol has an encoded hostname we need to work with its hostname
+ else if (URIUtils::HasEncodedHostname(url))
+ {
+ std::string hostname = url.GetHostName();
+ UpdateUrlEncoding(hostname);
+ url.SetHostName(hostname);
+ }
+ else
+ return false;
+
+ std::string newFilename = url.Get();
+ if (newFilename == strFilename)
+ return false;
+
+ strFilename = newFilename;
+ return true;
+}
+
+bool URIUtils::IsUsingFastSwitch(const CStdString& strFile)
+{
+ return IsUDP(strFile) || IsTCP(strFile) || IsPVRChannel(strFile);
+}
diff --git a/src/utils/URIUtils.h b/src/utils/URIUtils.h
new file mode 100644
index 0000000000..0094709d0c
--- /dev/null
+++ b/src/utils/URIUtils.h
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "StdString.h"
+
+class CURL;
+
+class URIUtils
+{
+public:
+ URIUtils(void);
+ virtual ~URIUtils(void);
+ static bool IsInPath(const CStdString &uri, const CStdString &baseURI);
+
+ static CStdString GetDirectory(const CStdString &strFilePath);
+
+ static const CStdString GetFileName(const CURL& url);
+ static const CStdString GetFileName(const CStdString& strFileNameAndPath);
+
+ static std::string GetExtension(const CURL& url);
+ static std::string GetExtension(const std::string& strFileName);
+
+ /*!
+ \brief Check if there is a file extension
+ \param strFileName Path or URL to check
+ \return \e true if strFileName have an extension.
+ \note Returns false when strFileName is empty.
+ \sa GetExtension
+ */
+ static bool HasExtension(const CStdString& strFileName);
+
+ /*!
+ \brief Check if filename have any of the listed extensions
+ \param strFileName Path or URL to check
+ \param strExtensions List of '.' prefixed lowercase extensions seperated with '|'
+ \return \e true if strFileName have any one of the extensions.
+ \note The check is case insensitive for strFileName, but requires
+ strExtensions to be lowercase. Returns false when strFileName or
+ strExtensions is empty.
+ \sa GetExtension
+ */
+ static bool HasExtension(const CStdString& strFileName, const CStdString& strExtensions);
+ static bool HasExtension(const CURL& url, const CStdString& strExtensions);
+
+ static void RemoveExtension(std::string& strFileName);
+ static CStdString ReplaceExtension(const CStdString& strFile,
+ const CStdString& strNewExtension);
+ static void Split(const std::string& strFileNameAndPath,
+ std::string& strPath, std::string& strFileName);
+ static std::vector<std::string> SplitPath(const CStdString& strPath);
+
+ static void GetCommonPath(std::string& strPath, const std::string& strPath2);
+ static std::string GetParentPath(const std::string& strPath);
+ static bool GetParentPath(const std::string& strPath, std::string& strParent);
+
+ /* \brief Change the base path of a URL: fromPath/fromFile -> toPath/toFile
+ Handles changes in path separator and filename URL encoding if necessary to derive toFile.
+ \param fromPath the base path of the original URL
+ \param fromFile the filename portion of the original URL
+ \param toPath the base path of the resulting URL
+ \return the full path.
+ */
+ static std::string ChangeBasePath(const std::string &fromPath, const std::string &fromFile, const std::string &toPath);
+
+ static CURL SubstitutePath(const CURL& url, bool reverse = false);
+ static CStdString SubstitutePath(const CStdString& strPath, bool reverse = false);
+
+ /*! \brief Check whether a URL is a given URL scheme.
+ Comparison is case-insensitve as per RFC1738
+ \param url a std::string path.
+ \param type a lower-case scheme name, e.g. "smb".
+ \return true if the url is of the given scheme, false otherwise.
+ \sa PathStarts, PathEquals
+ */
+ static bool IsProtocol(const std::string& url, const std::string& type);
+
+ /*! \brief Check whether a path starts with a given start.
+ Comparison is case-sensitive.
+ Use IsProtocol() to compare the protocol portion only.
+ \param path a std::string path.
+ \param start the string the start of the path should be compared against.
+ \return true if the path starts with the given string, false otherwise.
+ \sa IsProtocol, PathEquals
+ */
+ static bool PathStarts(const std::string& path, const char *start);
+
+ /*! \brief Check whether a path equals another path.
+ Comparison is case-sensitive.
+ \param path1 a std::string path.
+ \param path2 the second path the path should be compared against.
+ \return true if the paths are equal, false otherwise.
+ \sa IsProtocol, PathStarts
+ */
+ static bool PathEquals(const std::string& path1, const std::string &path2);
+
+ static bool IsAddonsPath(const CStdString& strFile);
+ static bool IsSourcesPath(const CStdString& strFile);
+ static bool IsCDDA(const CStdString& strFile);
+ static bool IsDAAP(const CStdString& strFile);
+ static bool IsDAV(const CStdString& strFile);
+ static bool IsDOSPath(const CStdString &path);
+ static bool IsDVD(const CStdString& strFile);
+ static bool IsFTP(const CStdString& strFile);
+ static bool IsUDP(const CStdString& strFile);
+ static bool IsTCP(const CStdString& strFile);
+ static bool IsHD(const CStdString& strFileName);
+ static bool IsHDHomeRun(const CStdString& strFile);
+ static bool IsSlingbox(const CStdString& strFile);
+ static bool IsHTSP(const CStdString& strFile);
+ static bool IsInArchive(const CStdString& strFile);
+ static bool IsInRAR(const CStdString& strFile);
+ static bool IsInternetStream(const std::string& path, bool bStrictCheck = false);
+ static bool IsInternetStream(const CURL& url, bool bStrictCheck = false);
+ static bool IsInAPK(const CStdString& strFile);
+ static bool IsInZIP(const CStdString& strFile);
+ static bool IsISO9660(const CStdString& strFile);
+ static bool IsLiveTV(const CStdString& strFile);
+ static bool IsPVRRecording(const CStdString& strFile);
+ static bool IsMultiPath(const CStdString& strPath);
+ static bool IsMusicDb(const CStdString& strFile);
+ static bool IsMythTV(const CStdString& strFile);
+ static bool IsNfs(const CStdString& strFile);
+ static bool IsAfp(const CStdString& strFile);
+ static bool IsOnDVD(const CStdString& strFile);
+ static bool IsOnLAN(const CStdString& strFile);
+ static bool IsHostOnLAN(const CStdString& hostName, bool offLineCheck = false);
+ static bool IsPlugin(const CStdString& strFile);
+ static bool IsScript(const CStdString& strFile);
+ static bool IsRAR(const CStdString& strFile);
+ static bool IsRemote(const CStdString& strFile);
+ static bool IsSmb(const CStdString& strFile);
+ static bool IsSpecial(const CStdString& strFile);
+ static bool IsStack(const CStdString& strFile);
+ static bool IsTuxBox(const CStdString& strFile);
+ static bool IsUPnP(const CStdString& strFile);
+ static bool IsURL(const CStdString& strFile);
+ static bool IsVideoDb(const CStdString& strFile);
+ static bool IsVTP(const CStdString& strFile);
+ static bool IsAPK(const CStdString& strFile);
+ static bool IsZIP(const CStdString& strFile);
+ static bool IsArchive(const CStdString& strFile);
+ static bool IsBluray(const CStdString& strFile);
+ static bool IsAndroidApp(const CStdString& strFile);
+ static bool IsLibraryFolder(const CStdString& strFile);
+ static bool IsLibraryContent(const std::string& strFile);
+ static bool IsPVRChannel(const CStdString& strFile);
+ static bool IsUsingFastSwitch(const CStdString& strFile);
+
+ static void AddSlashAtEnd(std::string& strFolder);
+ static bool HasSlashAtEnd(const std::string& strFile, bool checkURL = false);
+ static void RemoveSlashAtEnd(std::string& strFolder);
+ static bool CompareWithoutSlashAtEnd(const CStdString& strPath1, const CStdString& strPath2);
+ static std::string FixSlashesAndDups(const std::string& path, const char slashCharacter = '/', const size_t startFrom = 0);
+ /**
+ * Convert path to form without duplicated slashes and without relative directories
+ * Strip duplicated slashes
+ * Resolve and remove relative directories ("/../" and "/./")
+ * Will ignore slashes with other direction than specified
+ * Will not resolve path starting from relative directory
+ * @warning Don't use with "protocol://path"-style URLs
+ * @param path string to process
+ * @param slashCharacter character to use as directory delimiter
+ * @return transformed path
+ */
+ static std::string CanonicalizePath(const std::string& path, const char slashCharacter = '\\');
+
+ static CURL CreateArchivePath(const std::string& type,
+ const CURL& archiveUrl,
+ const std::string& pathInArchive = "",
+ const std::string& password = "");
+
+ static CStdString AddFileToFolder(const CStdString &strFolder, const CStdString &strFile);
+
+ static bool HasParentInHostname(const CURL& url);
+ static bool HasEncodedHostname(const CURL& url);
+ static bool HasEncodedFilename(const CURL& url);
+
+ /*!
+ \brief Cleans up the given path by resolving "." and ".."
+ and returns it.
+
+ This methods goes through the given path and removes any "."
+ (as it states "this directory") and resolves any ".." by
+ removing the previous directory from the path. This is done
+ for file paths and host names (in case of VFS paths).
+
+ \param path Path to be cleaned up
+ \return Actual path without any "." or ".."
+ */
+ static std::string GetRealPath(const std::string &path);
+
+ /*!
+ \brief Updates the URL encoded hostname of the given path
+
+ This method must only be used to update paths encoded with
+ the old (Eden) URL encoding implementation to the new (Frodo)
+ URL encoding implementation (which does not URL encode -_.!().
+
+ \param strFilename Path to update
+ \return True if the path has been updated/changed otherwise false
+ */
+ static bool UpdateUrlEncoding(std::string &strFilename);
+
+private:
+ static std::string resolvePath(const std::string &path);
+};
+
diff --git a/src/utils/UrlOptions.cpp b/src/utils/UrlOptions.cpp
new file mode 100644
index 0000000000..349ce1f916
--- /dev/null
+++ b/src/utils/UrlOptions.cpp
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2012-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sstream>
+
+#include "UrlOptions.h"
+#include "URL.h"
+#include "utils/StringUtils.h"
+#include "utils/log.h"
+
+using namespace std;
+
+CUrlOptions::CUrlOptions()
+ : m_strLead("")
+{ }
+
+CUrlOptions::CUrlOptions(const std::string &options, const char *strLead /* = "" */)
+ : m_strLead(strLead)
+{
+ AddOptions(options);
+}
+
+CUrlOptions::~CUrlOptions()
+{ }
+
+std::string CUrlOptions::GetOptionsString(bool withLeadingSeperator /* = false */) const
+{
+ std::string options;
+ for (UrlOptions::const_iterator opt = m_options.begin(); opt != m_options.end(); ++opt)
+ {
+ if (opt != m_options.begin())
+ options += "&";
+
+ options += CURL::Encode(opt->first);
+ if (!opt->second.empty())
+ options += "=" + CURL::Encode(opt->second.asString());
+ }
+
+ if (withLeadingSeperator && !options.empty())
+ {
+ if (m_strLead.empty())
+ options = "?" + options;
+ else
+ options = m_strLead + options;
+ }
+
+ return options;
+}
+
+void CUrlOptions::AddOption(const std::string &key, const char *value)
+{
+ if (key.empty() || value == NULL)
+ return;
+
+ return AddOption(key, string(value));
+}
+
+void CUrlOptions::AddOption(const std::string &key, const std::string &value)
+{
+ if (key.empty())
+ return;
+
+ m_options[key] = value;
+}
+
+void CUrlOptions::AddOption(const std::string &key, int value)
+{
+ if (key.empty())
+ return;
+
+ m_options[key] = value;
+}
+
+void CUrlOptions::AddOption(const std::string &key, float value)
+{
+ if (key.empty())
+ return;
+
+ m_options[key] = value;
+}
+
+void CUrlOptions::AddOption(const std::string &key, double value)
+{
+ if (key.empty())
+ return;
+
+ m_options[key] = value;
+}
+
+void CUrlOptions::AddOption(const std::string &key, bool value)
+{
+ if (key.empty())
+ return;
+
+ m_options[key] = value;
+}
+
+void CUrlOptions::AddOptions(const std::string &options)
+{
+ if (options.empty())
+ return;
+
+ string strOptions = options;
+
+ // if matching the preset leading str, remove from options.
+ if (!m_strLead.empty() && strOptions.compare(0, m_strLead.length(), m_strLead) == 0)
+ strOptions.erase(0, m_strLead.length());
+ else if (strOptions.at(0) == '?' || strOptions.at(0) == '#' || strOptions.at(0) == ';' || strOptions.at(0) == '|')
+ {
+ // remove leading ?, #, ; or | if present
+ if (!m_strLead.empty())
+ CLog::Log(LOGWARNING, "%s: original leading str %s overrided by %c", __FUNCTION__, m_strLead.c_str(), strOptions.at(0));
+ m_strLead = strOptions.at(0);
+ strOptions.erase(0, 1);
+ }
+
+ // split the options by & and process them one by one
+ vector<string> optionList = StringUtils::Split(strOptions, "&");
+ for (vector<string>::const_iterator option = optionList.begin(); option != optionList.end(); ++option)
+ {
+ if (option->empty())
+ continue;
+
+ string key, value;
+
+ size_t pos = option->find('=');
+ key = CURL::Decode(option->substr(0, pos));
+ if (pos != string::npos)
+ value = CURL::Decode(option->substr(pos + 1));
+
+ // the key cannot be empty
+ if (!key.empty())
+ AddOption(key, value);
+ }
+}
+
+void CUrlOptions::AddOptions(const CUrlOptions &options)
+{
+ m_options.insert(options.m_options.begin(), options.m_options.end());
+}
+
+void CUrlOptions::RemoveOption(const std::string &key)
+{
+ if (key.empty())
+ return;
+
+ UrlOptions::iterator option = m_options.find(key);
+ if (option != m_options.end())
+ m_options.erase(option);
+}
+
+bool CUrlOptions::HasOption(const std::string &key) const
+{
+ if (key.empty())
+ return false;
+
+ return m_options.find(key) != m_options.end();
+}
+
+bool CUrlOptions::GetOption(const std::string &key, CVariant &value) const
+{
+ if (key.empty())
+ return false;
+
+ UrlOptions::const_iterator option = m_options.find(key);
+ if (option == m_options.end())
+ return false;
+
+ value = option->second;
+ return true;
+}
diff --git a/src/utils/UrlOptions.h b/src/utils/UrlOptions.h
new file mode 100644
index 0000000000..a86cd1d214
--- /dev/null
+++ b/src/utils/UrlOptions.h
@@ -0,0 +1,57 @@
+#pragma once
+/*
+ * Copyright (C) 2012-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <map>
+#include <string>
+
+#include "utils/Variant.h"
+
+class CUrlOptions
+{
+public:
+ typedef std::map<std::string, CVariant> UrlOptions;
+
+ CUrlOptions();
+ CUrlOptions(const std::string &options, const char *strLead = "");
+ virtual ~CUrlOptions();
+
+ virtual void Clear() { m_options.clear(); m_strLead = ""; }
+
+ virtual const UrlOptions& GetOptions() const { return m_options; }
+ virtual std::string GetOptionsString(bool withLeadingSeperator = false) const;
+
+ virtual void AddOption(const std::string &key, const char *value);
+ virtual void AddOption(const std::string &key, const std::string &value);
+ virtual void AddOption(const std::string &key, int value);
+ virtual void AddOption(const std::string &key, float value);
+ virtual void AddOption(const std::string &key, double value);
+ virtual void AddOption(const std::string &key, bool value);
+ virtual void AddOptions(const std::string &options);
+ virtual void AddOptions(const CUrlOptions &options);
+ virtual void RemoveOption(const std::string &key);
+
+ virtual bool HasOption(const std::string &key) const;
+ virtual bool GetOption(const std::string &key, CVariant &value) const;
+
+protected:
+ UrlOptions m_options;
+ std::string m_strLead;
+};
diff --git a/src/utils/Utf8Utils.cpp b/src/utils/Utf8Utils.cpp
new file mode 100644
index 0000000000..d9efff510c
--- /dev/null
+++ b/src/utils/Utf8Utils.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "Utf8Utils.h"
+
+
+CUtf8Utils::utf8CheckResult CUtf8Utils::checkStrForUtf8(const std::string& str)
+{
+ const char* const strC = str.c_str();
+ const size_t len = str.length();
+ size_t pos = 0;
+ bool isPlainAscii = true;
+
+ while (pos < len)
+ {
+ const size_t chrLen = SizeOfUtf8Char(strC + pos);
+ if (chrLen == 0)
+ return hiAscii; // non valid UTF-8 sequence
+ else if (chrLen > 1)
+ isPlainAscii = false;
+
+ pos += chrLen;
+ }
+
+ if (isPlainAscii)
+ return plainAscii; // only single-byte characters (valid for US-ASCII and for UTF-8)
+
+ return utf8string; // valid UTF-8 with at least one valid UTF-8 multi-byte sequence
+}
+
+
+
+size_t CUtf8Utils::FindValidUtf8Char(const std::string& str, const size_t startPos /*= 0*/)
+{
+ const char* strC = str.c_str();
+ const size_t len = str.length();
+
+ size_t pos = startPos;
+ while (pos < len)
+ {
+ if (SizeOfUtf8Char(strC + pos))
+ return pos;
+
+ pos++;
+ }
+
+ return std::string::npos;
+}
+
+size_t CUtf8Utils::RFindValidUtf8Char(const std::string& str, const size_t startPos)
+{
+ const size_t len = str.length();
+ if (!len)
+ return std::string::npos;
+
+ const char* strC = str.c_str();
+ size_t pos = (startPos >= len) ? len - 1 : startPos;
+ while (pos < len) // pos is unsigned, after zero pos becomes large then len
+ {
+ if (SizeOfUtf8Char(strC + pos))
+ return pos;
+
+ pos--;
+ }
+
+ return std::string::npos;
+}
+
+inline size_t CUtf8Utils::SizeOfUtf8Char(const std::string& str, const size_t charStart /*= 0*/)
+{
+ if (charStart >= str.length())
+ return std::string::npos;
+
+ return SizeOfUtf8Char(str.c_str() + charStart);
+}
+
+// must be used only internally in class!
+// str must be null-terminated
+inline size_t CUtf8Utils::SizeOfUtf8Char(const char* const str)
+{
+ if (!str)
+ return 0;
+
+ const unsigned char* const strU = (const unsigned char*)str;
+ const unsigned char chr = strU[0];
+
+ /* this is an implementation of http://www.unicode.org/versions/Unicode6.2.0/ch03.pdf#G27506 */
+
+ /* U+0000 - U+007F in UTF-8 */
+ if (chr <= 0x7F)
+ return 1;
+
+ /* U+0080 - U+07FF in UTF-8 */ /* binary representation and range */
+ if (chr >= 0xC2 && chr <= 0xDF /* C2=1100 0010 - DF=1101 1111 */
+ // as str is null terminated,
+ && ((strU[1] & 0xC0) == 0x80)) /* C0=1100 0000, 80=1000 0000 - BF=1011 1111 */
+ return 2; // valid UTF-8 2 bytes sequence
+
+ /* U+0800 - U+0FFF in UTF-8 */
+ if (chr == 0xE0 /* E0=1110 0000 */
+ && (strU[1] & 0xE0) == 0xA0 /* E0=1110 0000, A0=1010 0000 - BF=1011 1111 */
+ && (strU[2] & 0xC0) == 0x80) /* C0=1100 0000, 80=1000 0000 - BF=1011 1111 */
+ return 3; // valid UTF-8 3 bytes sequence
+
+ /* U+1000 - U+CFFF in UTF-8 */
+ /* skip U+D000 - U+DFFF (handled later) */
+ /* U+E000 - U+FFFF in UTF-8 */
+ if (((chr >= 0xE1 && chr <= 0xEC) /* E1=1110 0001 - EC=1110 1100 */
+ || chr == 0xEE || chr == 0xEF) /* EE=1110 1110 - EF=1110 1111 */
+ && (strU[1] & 0xC0) == 0x80 /* C0=1100 0000, 80=1000 0000 - BF=1011 1111 */
+ && (strU[2] & 0xC0) == 0x80) /* C0=1100 0000, 80=1000 0000 - BF=1011 1111 */
+ return 3; // valid UTF-8 3 bytes sequence
+
+ /* U+D000 - U+D7FF in UTF-8 */
+ /* note: range U+D800 - U+DFFF is reserved and invalid */
+ if (chr == 0xED /* ED=1110 1101 */
+ && (strU[1] & 0xE0) == 0x80 /* E0=1110 0000, 80=1000 0000 - 9F=1001 1111 */
+ && (strU[2] & 0xC0) == 0x80) /* C0=1100 0000, 80=1000 0000 - BF=1011 1111 */
+ return 3; // valid UTF-8 3 bytes sequence
+
+ /* U+10000 - U+3FFFF in UTF-8 */
+ if (chr == 0xF0 /* F0=1111 0000 */
+ && (strU[1] & 0xE0) == 0x80 /* E0=1110 0000, 80=1000 0000 - 9F=1001 1111 */
+ && strU[2] >= 0x90 && strU[2] <= 0xBF /* 90=1001 0000 - BF=1011 1111 */
+ && (strU[3] & 0xC0) == 0x80) /* C0=1100 0000, 80=1000 0000 - BF=1011 1111 */
+ return 4; // valid UTF-8 4 bytes sequence
+
+ /* U+40000 - U+FFFFF in UTF-8 */
+ if (chr >= 0xF1 && chr <= 0xF3 /* F1=1111 0001 - F3=1111 0011 */
+ && (strU[1] & 0xC0) == 0x80 /* C0=1100 0000, 80=1000 0000 - BF=1011 1111 */
+ && (strU[2] & 0xC0) == 0x80 /* C0=1100 0000, 80=1000 0000 - BF=1011 1111 */
+ && (strU[3] & 0xC0) == 0x80) /* C0=1100 0000, 80=1000 0000 - BF=1011 1111 */
+ return 4; // valid UTF-8 4 bytes sequence
+
+ /* U+100000 - U+10FFFF in UTF-8 */
+ if (chr == 0xF4 /* F4=1111 0100 */
+ && (strU[1] & 0xF0) == 0x80 /* F0=1111 0000, 80=1000 0000 - 8F=1000 1111 */
+ && (strU[2] & 0xC0) == 0x80 /* C0=1100 0000, 80=1000 0000 - BF=1011 1111 */
+ && (strU[3] & 0xC0) == 0x80) /* C0=1100 0000, 80=1000 0000 - BF=1011 1111 */
+ return 4; // valid UTF-8 4 bytes sequence
+
+ return 0; // invalid UTF-8 char sequence
+}
diff --git a/src/utils/Utf8Utils.h b/src/utils/Utf8Utils.h
new file mode 100644
index 0000000000..a34518eb1b
--- /dev/null
+++ b/src/utils/Utf8Utils.h
@@ -0,0 +1,55 @@
+#pragma once
+
+/*
+ * Copyright (C) 2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include <string>
+
+class CUtf8Utils
+{
+public:
+ enum utf8CheckResult
+ {
+ plainAscii = -1, // only US-ASCII characters (valid for UTF-8 too)
+ hiAscii = 0, // non-UTF-8 sequence with high ASCII characters
+ // (possible single-byte national encoding like WINDOWS-1251, multi-byte encoding like UTF-32 or invalid UTF-8)
+ utf8string = 1 // valid UTF-8 sequences, but not US-ASCII only
+ };
+
+ /**
+ * Check given string for valid UTF-8 sequences
+ * @param str string to check
+ * @return result of check, "plainAscii" for empty string
+ */
+ static utf8CheckResult checkStrForUtf8(const std::string& str);
+
+ static inline bool isValidUtf8(const std::string& str)
+ {
+ return checkStrForUtf8(str) != hiAscii;
+ }
+
+ static size_t FindValidUtf8Char(const std::string& str, const size_t startPos = 0);
+ static size_t RFindValidUtf8Char(const std::string& str, const size_t startPos);
+
+ static size_t SizeOfUtf8Char(const std::string& str, const size_t charStart = 0);
+private:
+ static size_t SizeOfUtf8Char(const char* const str);
+};
diff --git a/src/utils/Variant.cpp b/src/utils/Variant.cpp
new file mode 100644
index 0000000000..bb2b493597
--- /dev/null
+++ b/src/utils/Variant.cpp
@@ -0,0 +1,794 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sstream>
+
+#include "Variant.h"
+
+#ifndef strtoll
+#ifdef TARGET_WINDOWS
+#define strtoll _strtoi64
+#define strtoull _strtoui64
+#define wcstoll _wcstoi64
+#define wcstoull _wcstoui64
+#else // TARGET_WINDOWS
+#define strtoll(str, endptr, base) (int64_t)strtod(str, endptr)
+#define strtoull(str, endptr, base) (uint64_t)strtod(str, endptr)
+#define wcstoll(str, endptr, base) (int64_t)wcstod(str, endptr)
+#define wcstoull(str, endptr, base) (uint64_t)wcstod(str, endptr)
+#endif // TARGET_WINDOWS
+#endif // strtoll
+
+using namespace std;
+
+string trimRight(const string &str)
+{
+ string tmp = str;
+ // find_last_not_of will return string::npos (which is defined as -1)
+ // or a value between 0 and size() - 1 => find_last_not_of() + 1 will
+ // always result in a valid index between 0 and size()
+ tmp.erase(tmp.find_last_not_of(" \n\r\t") + 1);
+
+ return tmp;
+}
+
+wstring trimRight(const wstring &str)
+{
+ wstring tmp = str;
+ // find_last_not_of will return string::npos (which is defined as -1)
+ // or a value between 0 and size() - 1 => find_last_not_of() + 1 will
+ // always result in a valid index between 0 and size()
+ tmp.erase(tmp.find_last_not_of(L" \n\r\t") + 1);
+
+ return tmp;
+}
+
+int64_t str2int64(const string &str, int64_t fallback /* = 0 */)
+{
+ char *end = NULL;
+ string tmp = trimRight(str);
+ int64_t result = strtoll(tmp.c_str(), &end, 0);
+ if (end == NULL || *end == '\0')
+ return result;
+
+ return fallback;
+}
+
+int64_t str2int64(const wstring &str, int64_t fallback /* = 0 */)
+{
+ wchar_t *end = NULL;
+ wstring tmp = trimRight(str);
+ int64_t result = wcstoll(tmp.c_str(), &end, 0);
+ if (end == NULL || *end == '\0')
+ return result;
+
+ return fallback;
+}
+
+uint64_t str2uint64(const string &str, uint64_t fallback /* = 0 */)
+{
+ char *end = NULL;
+ string tmp = trimRight(str);
+ uint64_t result = strtoull(tmp.c_str(), &end, 0);
+ if (end == NULL || *end == '\0')
+ return result;
+
+ return fallback;
+}
+
+uint64_t str2uint64(const wstring &str, uint64_t fallback /* = 0 */)
+{
+ wchar_t *end = NULL;
+ wstring tmp = trimRight(str);
+ uint64_t result = wcstoull(tmp.c_str(), &end, 0);
+ if (end == NULL || *end == '\0')
+ return result;
+
+ return fallback;
+}
+
+double str2double(const string &str, double fallback /* = 0.0 */)
+{
+ char *end = NULL;
+ string tmp = trimRight(str);
+ double result = strtod(tmp.c_str(), &end);
+ if (end == NULL || *end == '\0')
+ return result;
+
+ return fallback;
+}
+
+double str2double(const wstring &str, double fallback /* = 0.0 */)
+{
+ wchar_t *end = NULL;
+ wstring tmp = trimRight(str);
+ double result = wcstod(tmp.c_str(), &end);
+ if (end == NULL || *end == '\0')
+ return result;
+
+ return fallback;
+}
+
+CVariant CVariant::ConstNullVariant = CVariant::VariantTypeConstNull;
+
+CVariant::CVariant(VariantType type)
+{
+ m_type = type;
+
+ switch (type)
+ {
+ case VariantTypeInteger:
+ m_data.integer = 0;
+ break;
+ case VariantTypeUnsignedInteger:
+ m_data.unsignedinteger = 0;
+ break;
+ case VariantTypeBoolean:
+ m_data.boolean = false;
+ break;
+ case VariantTypeDouble:
+ m_data.dvalue = 0.0;
+ break;
+ case VariantTypeString:
+ m_data.string = new string();
+ break;
+ case VariantTypeWideString:
+ m_data.wstring = new wstring();
+ break;
+ case VariantTypeArray:
+ m_data.array = new VariantArray();
+ break;
+ case VariantTypeObject:
+ m_data.map = new VariantMap();
+ break;
+ default:
+ memset(&m_data, 0, sizeof(m_data));
+ break;
+ }
+}
+
+CVariant::CVariant(int integer)
+{
+ m_type = VariantTypeInteger;
+ m_data.integer = integer;
+}
+
+CVariant::CVariant(int64_t integer)
+{
+ m_type = VariantTypeInteger;
+ m_data.integer = integer;
+}
+
+CVariant::CVariant(unsigned int unsignedinteger)
+{
+ m_type = VariantTypeUnsignedInteger;
+ m_data.unsignedinteger = unsignedinteger;
+}
+
+CVariant::CVariant(uint64_t unsignedinteger)
+{
+ m_type = VariantTypeUnsignedInteger;
+ m_data.unsignedinteger = unsignedinteger;
+}
+
+CVariant::CVariant(double value)
+{
+ m_type = VariantTypeDouble;
+ m_data.dvalue = value;
+}
+
+CVariant::CVariant(float value)
+{
+ m_type = VariantTypeDouble;
+ m_data.dvalue = (double)value;
+}
+
+CVariant::CVariant(bool boolean)
+{
+ m_type = VariantTypeBoolean;
+ m_data.boolean = boolean;
+}
+
+CVariant::CVariant(const char *str)
+{
+ m_type = VariantTypeString;
+ m_data.string = new string(str);
+}
+
+CVariant::CVariant(const char *str, unsigned int length)
+{
+ m_type = VariantTypeString;
+ m_data.string = new string(str, length);
+}
+
+CVariant::CVariant(const string &str)
+{
+ m_type = VariantTypeString;
+ m_data.string = new string(str);
+}
+
+CVariant::CVariant(const wchar_t *str)
+{
+ m_type = VariantTypeWideString;
+ m_data.wstring = new wstring(str);
+}
+
+CVariant::CVariant(const wchar_t *str, unsigned int length)
+{
+ m_type = VariantTypeWideString;
+ m_data.wstring = new wstring(str, length);
+}
+
+CVariant::CVariant(const wstring &str)
+{
+ m_type = VariantTypeWideString;
+ m_data.wstring = new wstring(str);
+}
+
+CVariant::CVariant(const std::vector<std::string> &strArray)
+{
+ m_type = VariantTypeArray;
+ m_data.array = new VariantArray;
+ m_data.array->reserve(strArray.size());
+ for (unsigned int index = 0; index < strArray.size(); index++)
+ m_data.array->push_back(strArray.at(index));
+}
+
+CVariant::CVariant(const std::map<std::string, std::string> &strMap)
+{
+ m_type = VariantTypeObject;
+ m_data.map = new VariantMap;
+ for (std::map<std::string, std::string>::const_iterator it = strMap.begin(); it != strMap.end(); ++it)
+ m_data.map->insert(make_pair(it->first, CVariant(it->second)));
+}
+
+CVariant::CVariant(const std::map<std::string, CVariant> &variantMap)
+{
+ m_type = VariantTypeObject;
+ m_data.map = new VariantMap(variantMap.begin(), variantMap.end());
+}
+
+CVariant::CVariant(const CVariant &variant)
+{
+ m_type = VariantTypeNull;
+ *this = variant;
+}
+
+CVariant::~CVariant()
+{
+ cleanup();
+}
+
+void CVariant::cleanup()
+{
+ if (m_type == VariantTypeString)
+ delete m_data.string;
+ else if (m_type == VariantTypeWideString)
+ delete m_data.wstring;
+ else if (m_type == VariantTypeArray)
+ delete m_data.array;
+ else if (m_type == VariantTypeObject)
+ delete m_data.map;
+ m_type = VariantTypeNull;
+}
+
+bool CVariant::isInteger() const
+{
+ return m_type == VariantTypeInteger;
+}
+
+bool CVariant::isUnsignedInteger() const
+{
+ return m_type == VariantTypeUnsignedInteger;
+}
+
+bool CVariant::isBoolean() const
+{
+ return m_type == VariantTypeBoolean;
+}
+
+bool CVariant::isDouble() const
+{
+ return m_type == VariantTypeDouble;
+}
+
+bool CVariant::isString() const
+{
+ return m_type == VariantTypeString;
+}
+
+bool CVariant::isWideString() const
+{
+ return m_type == VariantTypeWideString;
+}
+
+bool CVariant::isArray() const
+{
+ return m_type == VariantTypeArray;
+}
+
+bool CVariant::isObject() const
+{
+ return m_type == VariantTypeObject;
+}
+
+bool CVariant::isNull() const
+{
+ return m_type == VariantTypeNull || m_type == VariantTypeConstNull;
+}
+
+CVariant::VariantType CVariant::type() const
+{
+ return m_type;
+}
+
+int64_t CVariant::asInteger(int64_t fallback) const
+{
+ switch (m_type)
+ {
+ case VariantTypeInteger:
+ return m_data.integer;
+ case VariantTypeUnsignedInteger:
+ return (int64_t)m_data.unsignedinteger;
+ case VariantTypeDouble:
+ return (int64_t)m_data.dvalue;
+ case VariantTypeString:
+ return str2int64(*m_data.string, fallback);
+ case VariantTypeWideString:
+ return str2int64(*m_data.wstring, fallback);
+ default:
+ return fallback;
+ }
+
+ return fallback;
+}
+
+uint64_t CVariant::asUnsignedInteger(uint64_t fallback) const
+{
+ switch (m_type)
+ {
+ case VariantTypeUnsignedInteger:
+ return m_data.unsignedinteger;
+ case VariantTypeInteger:
+ return (uint64_t)m_data.integer;
+ case VariantTypeDouble:
+ return (uint64_t)m_data.dvalue;
+ case VariantTypeString:
+ return str2uint64(*m_data.string, fallback);
+ case VariantTypeWideString:
+ return str2uint64(*m_data.wstring, fallback);
+ default:
+ return fallback;
+ }
+
+ return fallback;
+}
+
+double CVariant::asDouble(double fallback) const
+{
+ switch (m_type)
+ {
+ case VariantTypeDouble:
+ return m_data.dvalue;
+ case VariantTypeInteger:
+ return (double)m_data.integer;
+ case VariantTypeUnsignedInteger:
+ return (double)m_data.unsignedinteger;
+ case VariantTypeString:
+ return str2double(*m_data.string, fallback);
+ case VariantTypeWideString:
+ return str2double(*m_data.wstring, fallback);
+ default:
+ return fallback;
+ }
+
+ return fallback;
+}
+
+float CVariant::asFloat(float fallback) const
+{
+ switch (m_type)
+ {
+ case VariantTypeDouble:
+ return (float)m_data.dvalue;
+ case VariantTypeInteger:
+ return (float)m_data.integer;
+ case VariantTypeUnsignedInteger:
+ return (float)m_data.unsignedinteger;
+ case VariantTypeString:
+ return (float)str2double(*m_data.string, fallback);
+ case VariantTypeWideString:
+ return (float)str2double(*m_data.wstring, fallback);
+ default:
+ return fallback;
+ }
+
+ return fallback;
+}
+
+bool CVariant::asBoolean(bool fallback) const
+{
+ switch (m_type)
+ {
+ case VariantTypeBoolean:
+ return m_data.boolean;
+ case VariantTypeInteger:
+ return (m_data.integer != 0);
+ case VariantTypeUnsignedInteger:
+ return (m_data.unsignedinteger != 0);
+ case VariantTypeDouble:
+ return (m_data.dvalue != 0);
+ case VariantTypeString:
+ if (m_data.string->empty() || m_data.string->compare("0") == 0 || m_data.string->compare("false") == 0)
+ return false;
+ return true;
+ case VariantTypeWideString:
+ if (m_data.wstring->empty() || m_data.wstring->compare(L"0") == 0 || m_data.wstring->compare(L"false") == 0)
+ return false;
+ return true;
+ default:
+ return fallback;
+ }
+
+ return fallback;
+}
+
+std::string CVariant::asString(const std::string &fallback /* = "" */) const
+{
+ switch (m_type)
+ {
+ case VariantTypeString:
+ return *m_data.string;
+ case VariantTypeBoolean:
+ return m_data.boolean ? "true" : "false";
+ case VariantTypeInteger:
+ case VariantTypeUnsignedInteger:
+ case VariantTypeDouble:
+ {
+ std::ostringstream strStream;
+ if (m_type == VariantTypeInteger)
+ strStream << m_data.integer;
+ else if (m_type == VariantTypeUnsignedInteger)
+ strStream << m_data.unsignedinteger;
+ else
+ strStream << m_data.dvalue;
+ return strStream.str();
+ }
+ default:
+ return fallback;
+ }
+
+ return fallback;
+}
+
+std::wstring CVariant::asWideString(const std::wstring &fallback /* = L"" */) const
+{
+ switch (m_type)
+ {
+ case VariantTypeWideString:
+ return *m_data.wstring;
+ case VariantTypeBoolean:
+ return m_data.boolean ? L"true" : L"false";
+ case VariantTypeInteger:
+ case VariantTypeUnsignedInteger:
+ case VariantTypeDouble:
+ {
+ std::wostringstream strStream;
+ if (m_type == VariantTypeInteger)
+ strStream << m_data.integer;
+ else if (m_type == VariantTypeUnsignedInteger)
+ strStream << m_data.unsignedinteger;
+ else
+ strStream << m_data.dvalue;
+ return strStream.str();
+ }
+ default:
+ return fallback;
+ }
+
+ return fallback;
+}
+
+CVariant &CVariant::operator[](const std::string &key)
+{
+ if (m_type == VariantTypeNull)
+ {
+ m_type = VariantTypeObject;
+ m_data.map = new VariantMap;
+ }
+
+ if (m_type == VariantTypeObject)
+ return (*m_data.map)[key];
+ else
+ return ConstNullVariant;
+}
+
+const CVariant &CVariant::operator[](const std::string &key) const
+{
+ VariantMap::const_iterator it;
+ if (m_type == VariantTypeObject && (it = m_data.map->find(key)) != m_data.map->end())
+ return it->second;
+ else
+ return ConstNullVariant;
+}
+
+CVariant &CVariant::operator[](unsigned int position)
+{
+ if (m_type == VariantTypeArray && size() > position)
+ return m_data.array->at(position);
+ else
+ return ConstNullVariant;
+}
+
+const CVariant &CVariant::operator[](unsigned int position) const
+{
+ if (m_type == VariantTypeArray && size() > position)
+ return m_data.array->at(position);
+ else
+ return ConstNullVariant;
+}
+
+CVariant &CVariant::operator=(const CVariant &rhs)
+{
+ if (m_type == VariantTypeConstNull || this == &rhs)
+ return *this;
+
+ cleanup();
+
+ m_type = rhs.m_type;
+
+ switch (m_type)
+ {
+ case VariantTypeInteger:
+ m_data.integer = rhs.m_data.integer;
+ break;
+ case VariantTypeUnsignedInteger:
+ m_data.integer = rhs.m_data.unsignedinteger;
+ break;
+ case VariantTypeBoolean:
+ m_data.boolean = rhs.m_data.boolean;
+ break;
+ case VariantTypeDouble:
+ m_data.dvalue = rhs.m_data.dvalue;
+ break;
+ case VariantTypeString:
+ m_data.string = new string(*rhs.m_data.string);
+ break;
+ case VariantTypeWideString:
+ m_data.wstring = new wstring(*rhs.m_data.wstring);
+ break;
+ case VariantTypeArray:
+ m_data.array = new VariantArray(rhs.m_data.array->begin(), rhs.m_data.array->end());
+ break;
+ case VariantTypeObject:
+ m_data.map = new VariantMap(rhs.m_data.map->begin(), rhs.m_data.map->end());
+ break;
+ default:
+ break;
+ }
+
+ return *this;
+}
+
+bool CVariant::operator==(const CVariant &rhs) const
+{
+ if (m_type == rhs.m_type)
+ {
+ switch (m_type)
+ {
+ case VariantTypeInteger:
+ return m_data.integer == rhs.m_data.integer;
+ case VariantTypeUnsignedInteger:
+ return m_data.unsignedinteger == rhs.m_data.unsignedinteger;
+ case VariantTypeBoolean:
+ return m_data.boolean == rhs.m_data.boolean;
+ case VariantTypeDouble:
+ return m_data.dvalue == rhs.m_data.dvalue;
+ case VariantTypeString:
+ return *m_data.string == *rhs.m_data.string;
+ case VariantTypeWideString:
+ return *m_data.wstring == *rhs.m_data.wstring;
+ case VariantTypeArray:
+ return *m_data.array == *rhs.m_data.array;
+ case VariantTypeObject:
+ return *m_data.map == *rhs.m_data.map;
+ default:
+ break;
+ }
+ }
+
+ return false;
+}
+
+void CVariant::push_back(const CVariant &variant)
+{
+ if (m_type == VariantTypeNull)
+ {
+ m_type = VariantTypeArray;
+ m_data.array = new VariantArray;
+ }
+
+ if (m_type == VariantTypeArray)
+ m_data.array->push_back(variant);
+}
+
+void CVariant::append(const CVariant &variant)
+{
+ push_back(variant);
+}
+
+const char *CVariant::c_str() const
+{
+ if (m_type == VariantTypeString)
+ return m_data.string->c_str();
+ else
+ return NULL;
+}
+
+void CVariant::swap(CVariant &rhs)
+{
+ VariantType temp_type = m_type;
+ VariantUnion temp_data = m_data;
+
+ m_type = rhs.m_type;
+ m_data = rhs.m_data;
+
+ rhs.m_type = temp_type;
+ rhs.m_data = temp_data;
+}
+
+CVariant::iterator_array CVariant::begin_array()
+{
+ if (m_type == VariantTypeArray)
+ return m_data.array->begin();
+ else
+ return iterator_array();
+}
+
+CVariant::const_iterator_array CVariant::begin_array() const
+{
+ if (m_type == VariantTypeArray)
+ return m_data.array->begin();
+ else
+ return const_iterator_array();
+}
+
+CVariant::iterator_array CVariant::end_array()
+{
+ if (m_type == VariantTypeArray)
+ return m_data.array->end();
+ else
+ return iterator_array();
+}
+
+CVariant::const_iterator_array CVariant::end_array() const
+{
+ if (m_type == VariantTypeArray)
+ return m_data.array->end();
+ else
+ return const_iterator_array();
+}
+
+CVariant::iterator_map CVariant::begin_map()
+{
+ if (m_type == VariantTypeObject)
+ return m_data.map->begin();
+ else
+ return iterator_map();
+}
+
+CVariant::const_iterator_map CVariant::begin_map() const
+{
+ if (m_type == VariantTypeObject)
+ return m_data.map->begin();
+ else
+ return const_iterator_map();
+}
+
+CVariant::iterator_map CVariant::end_map()
+{
+ if (m_type == VariantTypeObject)
+ return m_data.map->end();
+ else
+ return iterator_map();
+}
+
+CVariant::const_iterator_map CVariant::end_map() const
+{
+ if (m_type == VariantTypeObject)
+ return m_data.map->end();
+ else
+ return const_iterator_map();
+}
+
+unsigned int CVariant::size() const
+{
+ if (m_type == VariantTypeObject)
+ return m_data.map->size();
+ else if (m_type == VariantTypeArray)
+ return m_data.array->size();
+ else if (m_type == VariantTypeString)
+ return m_data.string->size();
+ else if (m_type == VariantTypeWideString)
+ return m_data.wstring->size();
+ else
+ return 0;
+}
+
+bool CVariant::empty() const
+{
+ if (m_type == VariantTypeObject)
+ return m_data.map->empty();
+ else if (m_type == VariantTypeArray)
+ return m_data.array->empty();
+ else if (m_type == VariantTypeString)
+ return m_data.string->empty();
+ else if (m_type == VariantTypeWideString)
+ return m_data.wstring->empty();
+ else if (m_type == VariantTypeNull)
+ return true;
+
+ return false;
+}
+
+void CVariant::clear()
+{
+ if (m_type == VariantTypeObject)
+ m_data.map->clear();
+ else if (m_type == VariantTypeArray)
+ m_data.array->clear();
+ else if (m_type == VariantTypeString)
+ m_data.string->clear();
+ else if (m_type == VariantTypeWideString)
+ m_data.wstring->clear();
+}
+
+void CVariant::erase(const std::string &key)
+{
+ if (m_type == VariantTypeNull)
+ {
+ m_type = VariantTypeObject;
+ m_data.map = new VariantMap;
+ }
+ else if (m_type == VariantTypeObject)
+ m_data.map->erase(key);
+}
+
+void CVariant::erase(unsigned int position)
+{
+ if (m_type == VariantTypeNull)
+ {
+ m_type = VariantTypeArray;
+ m_data.array = new VariantArray;
+ }
+
+ if (m_type == VariantTypeArray && position < size())
+ m_data.array->erase(m_data.array->begin() + position);
+}
+
+bool CVariant::isMember(const std::string &key) const
+{
+ if (m_type == VariantTypeObject)
+ return m_data.map->find(key) != m_data.map->end();
+
+ return false;
+}
diff --git a/src/utils/Variant.h b/src/utils/Variant.h
new file mode 100644
index 0000000000..2be0d7e606
--- /dev/null
+++ b/src/utils/Variant.h
@@ -0,0 +1,154 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <map>
+#include <vector>
+#include <string>
+#include <stdint.h>
+#include <wchar.h>
+
+int64_t str2int64(const std::string &str, int64_t fallback = 0);
+int64_t str2int64(const std::wstring &str, int64_t fallback = 0);
+uint64_t str2uint64(const std::string &str, uint64_t fallback = 0);
+uint64_t str2uint64(const std::wstring &str, uint64_t fallback = 0);
+double str2double(const std::string &str, double fallback = 0.0);
+double str2double(const std::wstring &str, double fallback = 0.0);
+
+class CVariant
+{
+public:
+ enum VariantType
+ {
+ VariantTypeInteger,
+ VariantTypeUnsignedInteger,
+ VariantTypeBoolean,
+ VariantTypeString,
+ VariantTypeWideString,
+ VariantTypeDouble,
+ VariantTypeArray,
+ VariantTypeObject,
+ VariantTypeNull,
+ VariantTypeConstNull
+ };
+
+ CVariant(VariantType type = VariantTypeNull);
+ CVariant(int integer);
+ CVariant(int64_t integer);
+ CVariant(unsigned int unsignedinteger);
+ CVariant(uint64_t unsignedinteger);
+ CVariant(double value);
+ CVariant(float value);
+ CVariant(bool boolean);
+ CVariant(const char *str);
+ CVariant(const char *str, unsigned int length);
+ CVariant(const std::string &str);
+ CVariant(const wchar_t *str);
+ CVariant(const wchar_t *str, unsigned int length);
+ CVariant(const std::wstring &str);
+ CVariant(const std::vector<std::string> &strArray);
+ CVariant(const std::map<std::string, std::string> &strMap);
+ CVariant(const std::map<std::string, CVariant> &variantMap);
+ CVariant(const CVariant &variant);
+ ~CVariant();
+
+ bool isInteger() const;
+ bool isUnsignedInteger() const;
+ bool isBoolean() const;
+ bool isString() const;
+ bool isWideString() const;
+ bool isDouble() const;
+ bool isArray() const;
+ bool isObject() const;
+ bool isNull() const;
+
+ VariantType type() const;
+
+ int64_t asInteger(int64_t fallback = 0) const;
+ uint64_t asUnsignedInteger(uint64_t fallback = 0u) const;
+ bool asBoolean(bool fallback = false) const;
+ std::string asString(const std::string &fallback = "") const;
+ std::wstring asWideString(const std::wstring &fallback = L"") const;
+ double asDouble(double fallback = 0.0) const;
+ float asFloat(float fallback = 0.0f) const;
+
+ CVariant &operator[](const std::string &key);
+ const CVariant &operator[](const std::string &key) const;
+ CVariant &operator[](unsigned int position);
+ const CVariant &operator[](unsigned int position) const;
+
+ CVariant &operator=(const CVariant &rhs);
+ bool operator==(const CVariant &rhs) const;
+ bool operator!=(const CVariant &rhs) const { return !(*this == rhs); }
+
+ void push_back(const CVariant &variant);
+ void append(const CVariant &variant);
+
+ const char *c_str() const;
+
+ void swap(CVariant &rhs);
+
+private:
+ typedef std::vector<CVariant> VariantArray;
+ typedef std::map<std::string, CVariant> VariantMap;
+
+public:
+ typedef VariantArray::iterator iterator_array;
+ typedef VariantArray::const_iterator const_iterator_array;
+
+ typedef VariantMap::iterator iterator_map;
+ typedef VariantMap::const_iterator const_iterator_map;
+
+ iterator_array begin_array();
+ const_iterator_array begin_array() const;
+ iterator_array end_array();
+ const_iterator_array end_array() const;
+
+ iterator_map begin_map();
+ const_iterator_map begin_map() const;
+ iterator_map end_map();
+ const_iterator_map end_map() const;
+
+ unsigned int size() const;
+ bool empty() const;
+ void clear();
+ void erase(const std::string &key);
+ void erase(unsigned int position);
+
+ bool isMember(const std::string &key) const;
+
+ static CVariant ConstNullVariant;
+
+private:
+ void cleanup();
+ union VariantUnion
+ {
+ int64_t integer;
+ uint64_t unsignedinteger;
+ bool boolean;
+ double dvalue;
+ std::string *string;
+ std::wstring *wstring;
+ VariantArray *array;
+ VariantMap *map;
+ };
+
+ VariantType m_type;
+ VariantUnion m_data;
+};
diff --git a/src/utils/Vector.cpp b/src/utils/Vector.cpp
new file mode 100644
index 0000000000..ba6d046bb6
--- /dev/null
+++ b/src/utils/Vector.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2012-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <math.h>
+
+#include "Vector.h"
+
+CVector::CVector()
+{
+ reset();
+}
+
+CVector::CVector(float xCoord, float yCoord)
+ : x(xCoord),
+ y(yCoord)
+{ }
+
+void CVector::reset()
+{
+ x = 0.0f;
+ y = 0.0f;
+}
+
+const CVector CVector::operator+(const CVector &other) const
+{
+ return CVector(x + other.x, y + other.y);
+}
+
+const CVector CVector::operator-(const CVector &other) const
+{
+ return CVector(x - other.x, y - other.y);
+}
+
+CVector& CVector::operator+=(const CVector &other)
+{
+ x += other.x;
+ y += other.y;
+
+ return *this;
+}
+
+CVector& CVector::operator-=(const CVector &other)
+{
+ x -= other.x;
+ y -= other.y;
+
+ return *this;
+}
+
+float CVector::scalar(const CVector &other) const
+{
+ return x * other.x + y * other.y;
+}
+
+float CVector::length() const
+{
+ return sqrt(pow(x, 2) + pow(y, 2));
+}
diff --git a/src/utils/Vector.h b/src/utils/Vector.h
new file mode 100644
index 0000000000..3c541ca0d7
--- /dev/null
+++ b/src/utils/Vector.h
@@ -0,0 +1,41 @@
+#pragma once
+/*
+ * Copyright (C) 2012-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+class CVector
+{
+public:
+ CVector();
+ CVector(float xCoord, float yCoord);
+ virtual ~CVector() { }
+
+ virtual void reset();
+
+ const CVector operator+(const CVector &other) const;
+ const CVector operator-(const CVector &other) const;
+ CVector& operator+=(const CVector &other);
+ CVector& operator-=(const CVector &other);
+
+ float scalar(const CVector &other) const;
+ float length() const;
+
+ float x;
+ float y;
+}; \ No newline at end of file
diff --git a/src/utils/Weather.cpp b/src/utils/Weather.cpp
new file mode 100644
index 0000000000..a85cb15538
--- /dev/null
+++ b/src/utils/Weather.cpp
@@ -0,0 +1,501 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if (defined HAVE_CONFIG_H) && (!defined TARGET_WINDOWS)
+ #include "config.h"
+#endif
+#include "Weather.h"
+#include "filesystem/ZipManager.h"
+#include "XMLUtils.h"
+#include "utils/POUtils.h"
+#include "Temperature.h"
+#include "network/Network.h"
+#include "Application.h"
+#include "settings/lib/Setting.h"
+#include "settings/Settings.h"
+#include "guilib/GUIWindowManager.h"
+#include "GUIUserMessages.h"
+#include "XBDateTime.h"
+#include "LangInfo.h"
+#include "guilib/WindowIDs.h"
+#include "guilib/LocalizeStrings.h"
+#include "filesystem/Directory.h"
+#include "StringUtils.h"
+#include "URIUtils.h"
+#include "log.h"
+#include "addons/AddonManager.h"
+#include "interfaces/generic/ScriptInvocationManager.h"
+#include "CharsetConverter.h"
+#include "addons/GUIDialogAddonSettings.h"
+
+using namespace std;
+using namespace ADDON;
+using namespace XFILE;
+
+#define LOCALIZED_TOKEN_FIRSTID 370
+#define LOCALIZED_TOKEN_LASTID 395
+#define LOCALIZED_TOKEN_FIRSTID2 1350
+#define LOCALIZED_TOKEN_LASTID2 1449
+#define LOCALIZED_TOKEN_FIRSTID3 11
+#define LOCALIZED_TOKEN_LASTID3 17
+#define LOCALIZED_TOKEN_FIRSTID4 71
+#define LOCALIZED_TOKEN_LASTID4 97
+
+/*
+FIXME'S
+>strings are not centered
+*/
+
+#define WEATHER_BASE_PATH "special://temp/weather/"
+#define WEATHER_ICON_PATH "special://temp/weather/"
+#define WEATHER_SOURCE_FILE "special://xbmc/media/weather.zip"
+
+bool CWeatherJob::m_imagesOkay = false;
+
+CWeatherJob::CWeatherJob(int location)
+{
+ m_location = location;
+}
+
+bool CWeatherJob::DoWork()
+{
+ // wait for the network
+ if (!g_application.getNetwork().IsAvailable(true))
+ return false;
+
+ AddonPtr addon;
+ if (!ADDON::CAddonMgr::Get().GetAddon(CSettings::Get().GetString("weather.addon"), addon, ADDON_SCRIPT_WEATHER))
+ return false;
+
+ // initialize our sys.argv variables
+ std::vector<std::string> argv;
+ argv.push_back(addon->LibPath());
+
+ std::string strSetting = StringUtils::Format("%i", m_location);
+ argv.push_back(strSetting);
+
+ // Download our weather
+ CLog::Log(LOGINFO, "WEATHER: Downloading weather");
+ // call our script, passing the areacode
+ int scriptId = -1;
+ if ((scriptId = CScriptInvocationManager::Get().Execute(argv[0], addon, argv)) >= 0)
+ {
+ while (true)
+ {
+ if (!CScriptInvocationManager::Get().IsRunning(scriptId))
+ break;
+ Sleep(100);
+ }
+ if (!m_imagesOkay)
+ {
+ CDirectory::Create(WEATHER_BASE_PATH);
+ g_ZipManager.ExtractArchive(WEATHER_SOURCE_FILE, WEATHER_BASE_PATH);
+ m_imagesOkay = true;
+ }
+
+ SetFromProperties();
+
+ // and send a message that we're done
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_WEATHER_FETCHED);
+ g_windowManager.SendThreadMessage(msg);
+ }
+ else
+ CLog::Log(LOGERROR, "WEATHER: Weather download failed!");
+
+ return true;
+}
+
+const CWeatherInfo &CWeatherJob::GetInfo() const
+{
+ return m_info;
+}
+
+void CWeatherJob::LocalizeOverviewToken(std::string &token)
+{
+ // This routine is case-insensitive.
+ std::string strLocStr;
+ if (!token.empty())
+ {
+ ilocalizedTokens i;
+ i = m_localizedTokens.find(token);
+ if (i != m_localizedTokens.end())
+ {
+ strLocStr = g_localizeStrings.Get(i->second);
+ }
+ }
+ if (strLocStr == "")
+ strLocStr = token; //if not found, let fallback
+ token = strLocStr;
+}
+
+void CWeatherJob::LocalizeOverview(std::string &str)
+{
+ vector<string> words = StringUtils::Split(str, " ");
+ for (vector<string>::iterator i = words.begin(); i != words.end(); ++i)
+ LocalizeOverviewToken(*i);
+ str = StringUtils::Join(words, " ");
+}
+
+// input param must be kmh
+int CWeatherJob::ConvertSpeed(int curSpeed)
+{
+ switch (g_langInfo.GetSpeedUnit())
+ {
+ case CLangInfo::SPEED_UNIT_KMH:
+ break;
+ case CLangInfo::SPEED_UNIT_MPS:
+ curSpeed=(int)(curSpeed * (1000.0 / 3600.0) + 0.5);
+ break;
+ case CLangInfo::SPEED_UNIT_MPH:
+ curSpeed=(int)(curSpeed / (8.0 / 5.0));
+ break;
+ case CLangInfo::SPEED_UNIT_MPMIN:
+ curSpeed=(int)(curSpeed * (1000.0 / 3600.0) + 0.5*60);
+ break;
+ case CLangInfo::SPEED_UNIT_FTH:
+ curSpeed=(int)(curSpeed * 3280.8398888889f);
+ break;
+ case CLangInfo::SPEED_UNIT_FTMIN:
+ curSpeed=(int)(curSpeed * 54.6805555556f);
+ break;
+ case CLangInfo::SPEED_UNIT_FTS:
+ curSpeed=(int)(curSpeed * 0.911344f);
+ break;
+ case CLangInfo::SPEED_UNIT_KTS:
+ curSpeed=(int)(curSpeed * 0.5399568f);
+ break;
+ case CLangInfo::SPEED_UNIT_INCHPS:
+ curSpeed=(int)(curSpeed * 10.9361388889f);
+ break;
+ case CLangInfo::SPEED_UNIT_YARDPS:
+ curSpeed=(int)(curSpeed * 0.3037814722f);
+ break;
+ case CLangInfo::SPEED_UNIT_FPF:
+ curSpeed=(int)(curSpeed * 1670.25f);
+ break;
+ case CLangInfo::SPEED_UNIT_BEAUFORT:
+ {
+ float knot=(float)curSpeed * 0.5399568f; // to kts first
+ if(knot<=1.0) curSpeed=0;
+ if(knot>1.0 && knot<3.5) curSpeed=1;
+ if(knot>=3.5 && knot<6.5) curSpeed=2;
+ if(knot>=6.5 && knot<10.5) curSpeed=3;
+ if(knot>=10.5 && knot<16.5) curSpeed=4;
+ if(knot>=16.5 && knot<21.5) curSpeed=5;
+ if(knot>=21.5 && knot<27.5) curSpeed=6;
+ if(knot>=27.5 && knot<33.5) curSpeed=7;
+ if(knot>=33.5 && knot<40.5) curSpeed=8;
+ if(knot>=40.5 && knot<47.5) curSpeed=9;
+ if(knot>=47.5 && knot<55.5) curSpeed=10;
+ if(knot>=55.5 && knot<63.5) curSpeed=11;
+ if(knot>=63.5 && knot<74.5) curSpeed=12;
+ if(knot>=74.5 && knot<80.5) curSpeed=13;
+ if(knot>=80.5 && knot<89.5) curSpeed=14;
+ if(knot>=89.5) curSpeed=15;
+ }
+ break;
+ default:
+ assert(false);
+ }
+
+ return curSpeed;
+}
+
+void CWeatherJob::FormatTemperature(std::string &text, int temp)
+{
+ CTemperature temperature = CTemperature::CreateFromCelsius(temp);
+ text = StringUtils::Format("%.0f", temperature.ToLocale());
+}
+
+void CWeatherJob::LoadLocalizedToken()
+{
+ // We load the english strings in to get our tokens
+
+ // Try the strings PO file first
+ CPODocument PODoc;
+ if (PODoc.LoadFile("special://xbmc/language/English/strings.po"))
+ {
+ int counter = 0;
+
+ while (PODoc.GetNextEntry())
+ {
+ if (PODoc.GetEntryType() != ID_FOUND)
+ continue;
+
+ uint32_t id = PODoc.GetEntryID();
+ PODoc.ParseEntry(ISSOURCELANG);
+
+ if (id > LOCALIZED_TOKEN_LASTID2) break;
+ if ((LOCALIZED_TOKEN_FIRSTID <= id && id <= LOCALIZED_TOKEN_LASTID) ||
+ (LOCALIZED_TOKEN_FIRSTID2 <= id && id <= LOCALIZED_TOKEN_LASTID2) ||
+ (LOCALIZED_TOKEN_FIRSTID3 <= id && id <= LOCALIZED_TOKEN_LASTID3) ||
+ (LOCALIZED_TOKEN_FIRSTID4 <= id && id <= LOCALIZED_TOKEN_LASTID4))
+ {
+ if (!PODoc.GetMsgid().empty())
+ {
+ m_localizedTokens.insert(make_pair(PODoc.GetMsgid(), id));
+ counter++;
+ }
+ }
+ }
+
+ CLog::Log(LOGDEBUG, "POParser: loaded %i weather tokens", counter);
+ return;
+ }
+
+ CLog::Log(LOGDEBUG,
+ "Weather: no PO string file available, to load English tokens, "
+ "fallback to strings.xml file");
+
+ // We load the tokens from the strings.xml file
+ std::string strLanguagePath = "special://xbmc/language/English/strings.xml";
+
+ CXBMCTinyXML xmlDoc;
+ if (!xmlDoc.LoadFile(strLanguagePath) || !xmlDoc.RootElement())
+ {
+ CLog::Log(LOGERROR, "Weather: unable to load %s: %s at line %d", strLanguagePath.c_str(), xmlDoc.ErrorDesc(), xmlDoc.ErrorRow());
+ return;
+ }
+
+ TiXmlElement* pRootElement = xmlDoc.RootElement();
+ if (pRootElement->ValueStr() != "strings")
+ return;
+
+ const TiXmlElement *pChild = pRootElement->FirstChildElement();
+ while (pChild)
+ {
+ std::string strValue = pChild->ValueStr();
+ if (strValue == "string")
+ { // Load new style language file with id as attribute
+ const char* attrId = pChild->Attribute("id");
+ if (attrId && !pChild->NoChildren())
+ {
+ int id = atoi(attrId);
+ if ((LOCALIZED_TOKEN_FIRSTID <= id && id <= LOCALIZED_TOKEN_LASTID) ||
+ (LOCALIZED_TOKEN_FIRSTID2 <= id && id <= LOCALIZED_TOKEN_LASTID2) ||
+ (LOCALIZED_TOKEN_FIRSTID3 <= id && id <= LOCALIZED_TOKEN_LASTID3) ||
+ (LOCALIZED_TOKEN_FIRSTID4 <= id && id <= LOCALIZED_TOKEN_LASTID4))
+ {
+ std::string utf8Label(pChild->FirstChild()->ValueStr());
+ if (!utf8Label.empty())
+ m_localizedTokens.insert(make_pair(utf8Label, id));
+ }
+ }
+ }
+ pChild = pChild->NextSiblingElement();
+ }
+}
+
+static std::string ConstructPath(std::string in) // copy intended
+{
+ if (in.find("/") != std::string::npos || in.find("\\") != std::string::npos)
+ return in;
+ if (in.empty() || in == "N/A")
+ in = "na.png";
+
+ return URIUtils::AddFileToFolder(WEATHER_ICON_PATH,in);
+}
+
+void CWeatherJob::SetFromProperties()
+{
+ // Load in our tokens if necessary
+ if (m_localizedTokens.empty())
+ LoadLocalizedToken();
+
+ CGUIWindow* window = g_windowManager.GetWindow(WINDOW_WEATHER);
+ if (window)
+ {
+ CDateTime time = CDateTime::GetCurrentDateTime();
+ m_info.lastUpdateTime = time.GetAsLocalizedDateTime(false, false);
+ m_info.currentConditions = window->GetProperty("Current.Condition").asString();
+ m_info.currentIcon = ConstructPath(window->GetProperty("Current.OutlookIcon").asString());
+ LocalizeOverview(m_info.currentConditions);
+ FormatTemperature(m_info.currentTemperature,
+ strtol(window->GetProperty("Current.Temperature").asString().c_str(),0,10));
+ FormatTemperature(m_info.currentFeelsLike,
+ strtol(window->GetProperty("Current.FeelsLike").asString().c_str(),0,10));
+ m_info.currentUVIndex = window->GetProperty("Current.UVIndex").asString();
+ LocalizeOverview(m_info.currentUVIndex);
+ int speed = ConvertSpeed(strtol(window->GetProperty("Current.Wind").asString().c_str(),0,10));
+ std::string direction = window->GetProperty("Current.WindDirection").asString();
+ if (direction == "CALM")
+ m_info.currentWind = g_localizeStrings.Get(1410);
+ else
+ {
+ LocalizeOverviewToken(direction);
+ m_info.currentWind = StringUtils::Format(g_localizeStrings.Get(434).c_str(),
+ direction.c_str(), speed, g_langInfo.GetSpeedUnitString().c_str());
+ }
+ std::string windspeed = StringUtils::Format("%i %s",speed,g_langInfo.GetSpeedUnitString().c_str());
+ window->SetProperty("Current.WindSpeed",windspeed);
+ FormatTemperature(m_info.currentDewPoint,
+ strtol(window->GetProperty("Current.DewPoint").asString().c_str(),0,10));
+ if (window->GetProperty("Current.Humidity").asString().empty())
+ m_info.currentHumidity.clear();
+ else
+ m_info.currentHumidity = StringUtils::Format("%s%%", window->GetProperty("Current.Humidity").asString().c_str());
+ m_info.location = window->GetProperty("Current.Location").asString();
+ for (int i=0;i<NUM_DAYS;++i)
+ {
+ std::string strDay = StringUtils::Format("Day%i.Title",i);
+ m_info.forecast[i].m_day = window->GetProperty(strDay).asString();
+ LocalizeOverviewToken(m_info.forecast[i].m_day);
+ strDay = StringUtils::Format("Day%i.HighTemp",i);
+ FormatTemperature(m_info.forecast[i].m_high,
+ strtol(window->GetProperty(strDay).asString().c_str(),0,10));
+ strDay = StringUtils::Format("Day%i.LowTemp",i);
+ FormatTemperature(m_info.forecast[i].m_low,
+ strtol(window->GetProperty(strDay).asString().c_str(),0,10));
+ strDay = StringUtils::Format("Day%i.OutlookIcon",i);
+ m_info.forecast[i].m_icon = ConstructPath(window->GetProperty(strDay).asString());
+ strDay = StringUtils::Format("Day%i.Outlook",i);
+ m_info.forecast[i].m_overview = window->GetProperty(strDay).asString();
+ LocalizeOverview(m_info.forecast[i].m_overview);
+ }
+ }
+}
+
+CWeather::CWeather(void) : CInfoLoader(30 * 60 * 1000) // 30 minutes
+{
+ Reset();
+}
+
+CWeather::~CWeather(void)
+{
+}
+
+std::string CWeather::BusyInfo(int info) const
+{
+ if (info == WEATHER_IMAGE_CURRENT_ICON)
+ return URIUtils::AddFileToFolder(WEATHER_ICON_PATH,"na.png");
+
+ return CInfoLoader::BusyInfo(info);
+}
+
+std::string CWeather::TranslateInfo(int info) const
+{
+ if (info == WEATHER_LABEL_CURRENT_COND) return m_info.currentConditions;
+ else if (info == WEATHER_IMAGE_CURRENT_ICON) return m_info.currentIcon;
+ else if (info == WEATHER_LABEL_CURRENT_TEMP) return m_info.currentTemperature;
+ else if (info == WEATHER_LABEL_CURRENT_FEEL) return m_info.currentFeelsLike;
+ else if (info == WEATHER_LABEL_CURRENT_UVID) return m_info.currentUVIndex;
+ else if (info == WEATHER_LABEL_CURRENT_WIND) return m_info.currentWind;
+ else if (info == WEATHER_LABEL_CURRENT_DEWP) return m_info.currentDewPoint;
+ else if (info == WEATHER_LABEL_CURRENT_HUMI) return m_info.currentHumidity;
+ else if (info == WEATHER_LABEL_LOCATION) return m_info.location;
+ return "";
+}
+
+/*!
+ \brief Retrieve the city name for the specified location from the settings
+ \param iLocation the location index (can be in the range [1..MAXLOCATION])
+ \return the city name (without the accompanying region area code)
+ */
+std::string CWeather::GetLocation(int iLocation)
+{
+ CGUIWindow* window = g_windowManager.GetWindow(WINDOW_WEATHER);
+ if (window)
+ {
+ std::string setting = StringUtils::Format("Location%i", iLocation);
+ return window->GetProperty(setting).asString();
+ }
+ return "";
+}
+
+void CWeather::Reset()
+{
+ m_info.Reset();
+}
+
+bool CWeather::IsFetched()
+{
+ // call GetInfo() to make sure that we actually start up
+ GetInfo(0);
+ return !m_info.lastUpdateTime.empty();
+}
+
+const day_forecast &CWeather::GetForecast(int day) const
+{
+ return m_info.forecast[day];
+}
+
+/*!
+ \brief Saves the specified location index to the settings. Call Refresh()
+ afterwards to update weather info for the new location.
+ \param iLocation the new location index (can be in the range [1..MAXLOCATION])
+ */
+void CWeather::SetArea(int iLocation)
+{
+ CSettings::Get().SetInt("weather.currentlocation", iLocation);
+ CSettings::Get().Save();
+}
+
+/*!
+ \brief Retrieves the current location index from the settings
+ \return the active location index (will be in the range [1..MAXLOCATION])
+ */
+int CWeather::GetArea() const
+{
+ return CSettings::Get().GetInt("weather.currentlocation");
+}
+
+CJob *CWeather::GetJob() const
+{
+ return new CWeatherJob(GetArea());
+}
+
+void CWeather::OnJobComplete(unsigned int jobID, bool success, CJob *job)
+{
+ m_info = ((CWeatherJob *)job)->GetInfo();
+ CInfoLoader::OnJobComplete(jobID, success, job);
+}
+
+void CWeather::OnSettingChanged(const CSetting *setting)
+{
+ if (setting == NULL)
+ return;
+
+ const std::string settingId = setting->GetId();
+ if (settingId == "weather.addon")
+ {
+ // clear "WeatherProviderLogo" property that some weather addons set
+ CGUIWindow* window = g_windowManager.GetWindow(WINDOW_WEATHER);
+ window->SetProperty("WeatherProviderLogo", "");
+ Refresh();
+ }
+}
+
+void CWeather::OnSettingAction(const CSetting *setting)
+{
+ if (setting == NULL)
+ return;
+
+ const std::string settingId = setting->GetId();
+ if (settingId == "weather.addonsettings")
+ {
+ AddonPtr addon;
+ if (CAddonMgr::Get().GetAddon(CSettings::Get().GetString("weather.addon"), addon, ADDON_SCRIPT_WEATHER) && addon != NULL)
+ { // TODO: maybe have ShowAndGetInput return a bool if settings changed, then only reset weather if true.
+ CGUIDialogAddonSettings::ShowAndGetInput(addon);
+ Refresh();
+ }
+ }
+}
+
diff --git a/src/utils/Weather.h b/src/utils/Weather.h
new file mode 100644
index 0000000000..1ed5eba90a
--- /dev/null
+++ b/src/utils/Weather.h
@@ -0,0 +1,173 @@
+#pragma once
+
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "InfoLoader.h"
+#include "settings/lib/ISettingCallback.h"
+#include "utils/GlobalsHandling.h"
+
+#include <map>
+#include <string>
+
+class TiXmlElement;
+
+#define WEATHER_LABEL_LOCATION 10
+#define WEATHER_IMAGE_CURRENT_ICON 21
+#define WEATHER_LABEL_CURRENT_COND 22
+#define WEATHER_LABEL_CURRENT_TEMP 23
+#define WEATHER_LABEL_CURRENT_FEEL 24
+#define WEATHER_LABEL_CURRENT_UVID 25
+#define WEATHER_LABEL_CURRENT_WIND 26
+#define WEATHER_LABEL_CURRENT_DEWP 27
+#define WEATHER_LABEL_CURRENT_HUMI 28
+
+struct day_forecast
+{
+ std::string m_icon;
+ std::string m_overview;
+ std::string m_day;
+ std::string m_high;
+ std::string m_low;
+};
+
+#define NUM_DAYS 7
+
+class CWeatherInfo
+{
+public:
+ day_forecast forecast[NUM_DAYS];
+
+ void Reset()
+ {
+ lastUpdateTime.clear();
+ currentIcon.clear();
+ currentConditions.clear();
+ currentTemperature.clear();
+ currentFeelsLike.clear();
+ currentWind.clear();
+ currentHumidity.clear();
+ currentUVIndex.clear();
+ currentDewPoint.clear();
+
+ for (int i = 0; i < NUM_DAYS; i++)
+ {
+ forecast[i].m_icon.clear();
+ forecast[i].m_overview.clear();
+ forecast[i].m_day.clear();
+ forecast[i].m_high.clear();
+ forecast[i].m_low.clear();
+ }
+ };
+
+ std::string lastUpdateTime;
+ std::string location;
+ std::string currentIcon;
+ std::string currentConditions;
+ std::string currentTemperature;
+ std::string currentFeelsLike;
+ std::string currentUVIndex;
+ std::string currentWind;
+ std::string currentDewPoint;
+ std::string currentHumidity;
+ std::string busyString;
+ std::string naIcon;
+};
+
+class CWeatherJob : public CJob
+{
+public:
+ CWeatherJob(int location);
+
+ virtual bool DoWork();
+
+ const CWeatherInfo &GetInfo() const;
+private:
+ void LocalizeOverview(std::string &str);
+ void LocalizeOverviewToken(std::string &str);
+ void LoadLocalizedToken();
+ static int ConvertSpeed(int speed);
+
+ void SetFromProperties();
+
+ /*! \brief Formats a celcius temperature into a string based on the users locale
+ \param text the string to format
+ \param temp the temperature (in degrees celcius).
+ */
+ static void FormatTemperature(std::string &text, int temp);
+
+ struct ci_less : std::binary_function<std::string, std::string, bool>
+ {
+ // case-independent (ci) compare_less binary function
+ struct nocase_compare : public std::binary_function<unsigned char,unsigned char,bool>
+ {
+ bool operator() (const unsigned char& c1, const unsigned char& c2) const {
+ return tolower (c1) < tolower (c2);
+ }
+ };
+ bool operator() (const std::string & s1, const std::string & s2) const {
+ return std::lexicographical_compare
+ (s1.begin (), s1.end (),
+ s2.begin (), s2.end (),
+ nocase_compare ());
+ }
+ };
+
+ std::map<std::string, int, ci_less> m_localizedTokens;
+ typedef std::map<std::string, int, ci_less>::const_iterator ilocalizedTokens;
+
+ CWeatherInfo m_info;
+ int m_location;
+
+ static bool m_imagesOkay;
+};
+
+class CWeather : public CInfoLoader,
+ public ISettingCallback
+{
+public:
+ CWeather(void);
+ virtual ~CWeather(void);
+ static bool GetSearchResults(const std::string &strSearch, std::string &strResult);
+
+ std::string GetLocation(int iLocation);
+ const std::string &GetLastUpdateTime() const { return m_info.lastUpdateTime; };
+ const day_forecast &GetForecast(int day) const;
+ bool IsFetched();
+ void Reset();
+
+ void SetArea(int iLocation);
+ int GetArea() const;
+protected:
+ virtual CJob *GetJob() const;
+ virtual std::string TranslateInfo(int info) const;
+ virtual std::string BusyInfo(int info) const;
+ virtual void OnJobComplete(unsigned int jobID, bool success, CJob *job);
+
+ virtual void OnSettingChanged(const CSetting *setting);
+ virtual void OnSettingAction(const CSetting *setting);
+
+private:
+
+ CWeatherInfo m_info;
+};
+
+XBMC_GLOBAL_REF(CWeather, g_weatherManager);
+#define g_weatherManager XBMC_GLOBAL_USE(CWeather)
diff --git a/src/utils/WindowsShortcut.cpp b/src/utils/WindowsShortcut.cpp
new file mode 100644
index 0000000000..1ba63250fb
--- /dev/null
+++ b/src/utils/WindowsShortcut.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+// WindowsShortcut.cpp: implementation of the CWindowsShortcut class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#include "sc.h"
+#include "WindowsShortcut.h"
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#define new DEBUG_NEW
+#endif
+
+
+#define FLAG_SHELLITEMIDLIST 1
+#define FLAG_FILEORDIRECTORY 2
+#define FLAG_DESCRIPTION 4
+#define FLAG_RELATIVEPATH 8
+#define FLAG_WORKINGDIRECTORY 0x10
+#define FLAG_ARGUMENTS 0x20
+#define FLAG_ICON 0x40
+
+#define VOLUME_LOCAL 1
+#define VOLUME_NETWORK 2
+
+using namespace std;
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+CWindowsShortcut::CWindowsShortcut()
+{
+}
+
+CWindowsShortcut::~CWindowsShortcut()
+{
+}
+
+bool CWindowsShortcut::GetShortcut(const string& strFileName, string& strFileOrDir)
+{
+ strFileOrDir = "";
+ if (!IsShortcut(strFileName) ) return false;
+
+
+ CFile file;
+ if (!file.Open(strFileName.c_str(), CFile::typeBinary | CFile::modeRead)) return false;
+ byte byHeader[2048];
+ int iBytesRead = file.Read(byHeader, 2048);
+ file.Close();
+
+ DWORD dwFlags = *((DWORD*)(&byHeader[0x14]));
+
+ int iPos = 0x4c;
+ if (dwFlags & FLAG_SHELLITEMIDLIST)
+ {
+ WORD sLength = *((WORD*)(&byHeader[iPos]));
+ iPos += sLength;
+ iPos += 2;
+ }
+
+ // skip File Location Info
+ DWORD dwLen = 0x1c;
+ if (dwFlags & FLAG_SHELLITEMIDLIST)
+ {
+ dwLen = *((DWORD*)(&byHeader[iPos]));
+ }
+
+
+ DWORD dwVolumeFlags = *((DWORD*)(&byHeader[iPos + 0x8]));
+ DWORD dwOffsetLocalVolumeInfo = *((DWORD*)(&byHeader[iPos + 0xc]));
+ DWORD dwOffsetBasePathName = *((DWORD*)(&byHeader[iPos + 0x10]));
+ DWORD dwOffsetNetworkVolumeInfo = *((DWORD*)(&byHeader[iPos + 0x14]));
+ DWORD dwOffsetRemainingPathName = *((DWORD*)(&byHeader[iPos + 0x18]));
+
+
+ if ((dwVolumeFlags & VOLUME_NETWORK) == 0) return false;
+
+ strFileOrDir = "smb:";
+ // share name
+ iPos += dwOffsetNetworkVolumeInfo + 0x14;
+ while (iPos < iBytesRead && byHeader[iPos] != 0)
+ {
+ if (byHeader[iPos] == '\\') byHeader[iPos] = '/';
+ strFileOrDir += (char)byHeader[iPos];
+ iPos++;
+ }
+ iPos++;
+ // file/folder name
+ strFileOrDir += '/';
+ while (iPos < iBytesRead && byHeader[iPos] != 0)
+ {
+ if (byHeader[iPos] == '\\') byHeader[iPos] = '/';
+ strFileOrDir += (char)byHeader[iPos];
+ iPos++;
+ }
+ return true;
+}
+
+bool CWindowsShortcut::IsShortcut(const string& strFileName)
+{
+ CFile file;
+ if (!file.Open(strFileName.c_str(), CFile::typeBinary | CFile::modeRead)) return false;
+ byte byHeader[0x80];
+ int iBytesRead = file.Read(byHeader, 0x80);
+ file.Close();
+ if (iBytesRead < 0x4c)
+ {
+ return false;
+ }
+ //long integer that is always set to 4Ch
+ if (byHeader[0] != 0x4c) return false;
+ if (byHeader[1] != 0x0 ) return false;
+ if (byHeader[2] != 0x0 ) return false;
+ if (byHeader[3] != 0x0 ) return false;
+
+ //globally unique identifier GUID of the shell links
+ if (byHeader[0x04] != 0x01) return false;
+ if (byHeader[0x05] != 0x14) return false;
+ if (byHeader[0x06] != 0x02) return false;
+ if (byHeader[0x07] != 0x00) return false;
+ if (byHeader[0x08] != 0x00) return false;
+ if (byHeader[0x09] != 0x00) return false;
+ if (byHeader[0x0a] != 0x00) return false;
+ if (byHeader[0x0b] != 0x00) return false;
+ if (byHeader[0x0c] != 0xc0) return false;
+ if (byHeader[0x0d] != 0x00) return false;
+ if (byHeader[0x0e] != 0x00) return false;
+ if (byHeader[0x0f] != 0x00) return false;
+ if (byHeader[0x10] != 0x00) return false;
+ if (byHeader[0x11] != 0x00) return false;
+ if (byHeader[0x12] != 0x00) return false;
+ if (byHeader[0x13] != 0x46) return false;
+
+ // 2dwords, always 0
+ if (byHeader[0x44] != 0x0 ) return false;
+ if (byHeader[0x45] != 0x0 ) return false;
+ if (byHeader[0x46] != 0x0 ) return false;
+ if (byHeader[0x47] != 0x0 ) return false;
+ if (byHeader[0x48] != 0x0 ) return false;
+ if (byHeader[0x49] != 0x0 ) return false;
+ if (byHeader[0x4a] != 0x0 ) return false;
+ if (byHeader[0x4b] != 0x0 ) return false;
+
+ return true;
+}
diff --git a/src/utils/WindowsShortcut.h b/src/utils/WindowsShortcut.h
new file mode 100644
index 0000000000..a79e95f3a4
--- /dev/null
+++ b/src/utils/WindowsShortcut.h
@@ -0,0 +1,43 @@
+// WindowsShortcut.h: interface for the CWindowsShortcut class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#if !defined(AFX_WINDOWSSHORTCUT_H__A905CF83_3C3D_44FF_B3EF_778D70676D2C__INCLUDED_)
+#define AFX_WINDOWSSHORTCUT_H__A905CF83_3C3D_44FF_B3EF_778D70676D2C__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string>
+
+class CWindowsShortcut
+{
+public:
+ CWindowsShortcut();
+ virtual ~CWindowsShortcut();
+ static bool IsShortcut(const string& strFileName);
+ static bool GetShortcut(const string& strFileName, string& strFileOrDir);
+};
+
+#endif // !defined(AFX_WINDOWSSHORTCUT_H__A905CF83_3C3D_44FF_B3EF_778D70676D2C__INCLUDED_)
diff --git a/src/utils/XBMCTinyXML.cpp b/src/utils/XBMCTinyXML.cpp
new file mode 100644
index 0000000000..e7db9f132d
--- /dev/null
+++ b/src/utils/XBMCTinyXML.cpp
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "XBMCTinyXML.h"
+#include "filesystem/File.h"
+#include "utils/StringUtils.h"
+#include "utils/CharsetConverter.h"
+#include "utils/CharsetDetection.h"
+#include "utils/Utf8Utils.h"
+#include "LangInfo.h"
+#include "RegExp.h"
+#include "utils/log.h"
+
+#define MAX_ENTITY_LENGTH 8 // size of largest entity "&#xNNNN;"
+#define BUFFER_SIZE 4096
+
+CXBMCTinyXML::CXBMCTinyXML()
+: TiXmlDocument()
+{
+}
+
+CXBMCTinyXML::CXBMCTinyXML(const char *documentName)
+: TiXmlDocument(documentName)
+{
+}
+
+CXBMCTinyXML::CXBMCTinyXML(const std::string& documentName)
+: TiXmlDocument(documentName)
+{
+}
+
+CXBMCTinyXML::CXBMCTinyXML(const std::string& documentName, const std::string& documentCharset)
+: TiXmlDocument(documentName), m_SuggestedCharset(documentCharset)
+{
+ StringUtils::ToUpper(m_SuggestedCharset);
+}
+
+bool CXBMCTinyXML::LoadFile(TiXmlEncoding encoding)
+{
+ return LoadFile(value, encoding);
+}
+
+bool CXBMCTinyXML::LoadFile(const char *_filename, TiXmlEncoding encoding)
+{
+ return LoadFile(std::string(_filename), encoding);
+}
+
+bool CXBMCTinyXML::LoadFile(const std::string& _filename, TiXmlEncoding encoding)
+{
+ value = _filename.c_str();
+
+ XFILE::CFile file;
+ XFILE::auto_buffer buffer;
+
+ if (file.LoadFile(value, buffer) <= 0)
+ {
+ SetError(TIXML_ERROR_OPENING_FILE, NULL, NULL, TIXML_ENCODING_UNKNOWN);
+ return false;
+ }
+
+ // Delete the existing data:
+ Clear();
+ location.Clear();
+
+ std::string data(buffer.get(), buffer.length());
+ buffer.clear(); // free memory early
+
+ if (encoding == TIXML_ENCODING_UNKNOWN)
+ Parse(data, file.GetContentCharset());
+ else
+ Parse(data, encoding);
+
+ if (Error())
+ return false;
+ return true;
+}
+
+bool CXBMCTinyXML::LoadFile(const std::string& _filename, const std::string& documentCharset)
+{
+ m_SuggestedCharset = documentCharset;
+ StringUtils::ToUpper(m_SuggestedCharset);
+ return LoadFile(_filename, TIXML_ENCODING_UNKNOWN);
+}
+
+bool CXBMCTinyXML::LoadFile(FILE *f, TiXmlEncoding encoding)
+{
+ std::string data;
+ char buf[BUFFER_SIZE];
+ memset(buf, 0, BUFFER_SIZE);
+ int result;
+ while ((result = fread(buf, 1, BUFFER_SIZE, f)) > 0)
+ data.append(buf, result);
+ return Parse(data, encoding);
+}
+
+bool CXBMCTinyXML::SaveFile(const char *_filename) const
+{
+ return SaveFile(std::string(_filename));
+}
+
+bool CXBMCTinyXML::SaveFile(const std::string& filename) const
+{
+ XFILE::CFile file;
+ if (file.OpenForWrite(filename, true))
+ {
+ TiXmlPrinter printer;
+ Accept(&printer);
+ return file.Write(printer.CStr(), printer.Size()) == printer.Size();
+ }
+ return false;
+}
+
+bool CXBMCTinyXML::Parse(const char *_data, TiXmlEncoding encoding)
+{
+ return Parse(std::string(_data), encoding);
+}
+
+bool CXBMCTinyXML::Parse(const std::string& data, const std::string& dataCharset)
+{
+ m_SuggestedCharset = dataCharset;
+ StringUtils::ToUpper(m_SuggestedCharset);
+ return Parse(data, TIXML_ENCODING_UNKNOWN);
+}
+
+bool CXBMCTinyXML::Parse(const std::string& data, TiXmlEncoding encoding /*= TIXML_DEFAULT_ENCODING */)
+{
+ m_UsedCharset.clear();
+ if (encoding != TIXML_ENCODING_UNKNOWN)
+ { // encoding != TIXML_ENCODING_UNKNOWN means "do not use m_SuggestedCharset and charset detection"
+ m_SuggestedCharset.clear();
+ if (encoding == TIXML_ENCODING_UTF8)
+ m_UsedCharset = "UTF-8";
+
+ return InternalParse(data, encoding);
+ }
+
+ if (!m_SuggestedCharset.empty() && TryParse(data, m_SuggestedCharset))
+ return true;
+
+ std::string detectedCharset;
+ if (CCharsetDetection::DetectXmlEncoding(data, detectedCharset) && TryParse(data, detectedCharset))
+ {
+ if (!m_SuggestedCharset.empty())
+ CLog::Log(LOGWARNING, "%s: \"%s\" charset was used instead of suggested charset \"%s\" for %s", __FUNCTION__, m_UsedCharset.c_str(), m_SuggestedCharset.c_str(),
+ (value.empty() ? "XML data" : ("file \"" + value + "\"").c_str()));
+
+ return true;
+ }
+
+ // check for valid UTF-8
+ if (m_SuggestedCharset != "UTF-8" && detectedCharset != "UTF-8" && CUtf8Utils::isValidUtf8(data) &&
+ TryParse(data, "UTF-8"))
+ {
+ if (!m_SuggestedCharset.empty())
+ CLog::Log(LOGWARNING, "%s: \"%s\" charset was used instead of suggested charset \"%s\" for %s", __FUNCTION__, m_UsedCharset.c_str(), m_SuggestedCharset.c_str(),
+ (value.empty() ? "XML data" : ("file \"" + value + "\"").c_str()));
+ else if (!detectedCharset.empty())
+ CLog::Log(LOGWARNING, "%s: \"%s\" charset was used instead of detected charset \"%s\" for %s", __FUNCTION__, m_UsedCharset.c_str(), detectedCharset.c_str(),
+ (value.empty() ? "XML data" : ("file \"" + value + "\"").c_str()));
+ return true;
+ }
+
+ // fallback: try user GUI charset
+ if (TryParse(data, g_langInfo.GetGuiCharSet()))
+ {
+ if (!m_SuggestedCharset.empty())
+ CLog::Log(LOGWARNING, "%s: \"%s\" charset was used instead of suggested charset \"%s\" for %s", __FUNCTION__, m_UsedCharset.c_str(), m_SuggestedCharset.c_str(),
+ (value.empty() ? "XML data" : ("file \"" + value + "\"").c_str()));
+ else if (!detectedCharset.empty())
+ CLog::Log(LOGWARNING, "%s: \"%s\" charset was used instead of detected charset \"%s\" for %s", __FUNCTION__, m_UsedCharset.c_str(), detectedCharset.c_str(),
+ (value.empty() ? "XML data" : ("file \"" + value + "\"").c_str()));
+ return true;
+ }
+
+ // can't detect correct data charset, try to process data as is
+ if (InternalParse(data, TIXML_ENCODING_UNKNOWN))
+ {
+ if (!m_SuggestedCharset.empty())
+ CLog::Log(LOGWARNING, "%s: Processed %s as unknown encoding instead of suggested \"%s\"", __FUNCTION__,
+ (value.empty() ? "XML data" : ("file \"" + value + "\"").c_str()), m_SuggestedCharset.c_str());
+ else if (!detectedCharset.empty())
+ CLog::Log(LOGWARNING, "%s: Processed %s as unknown encoding instead of detected \"%s\"", __FUNCTION__,
+ (value.empty() ? "XML data" : ("file \"" + value + "\"").c_str()), detectedCharset.c_str());
+ return true;
+ }
+
+ return false;
+}
+
+bool CXBMCTinyXML::TryParse(const std::string& data, const std::string& tryDataCharset)
+{
+ if (tryDataCharset == "UTF-8")
+ InternalParse(data, TIXML_ENCODING_UTF8); // process data without conversion
+ else if (!tryDataCharset.empty())
+ {
+ std::string converted;
+ /* some wrong conversions can leave US-ASCII XML header and structure untouched but break non-English data
+ * so conversion must fail on wrong character and then other encodings will be tried */
+ if (!g_charsetConverter.ToUtf8(tryDataCharset, data, converted, true) || converted.empty())
+ return false; // can't convert data
+
+ InternalParse(converted, TIXML_ENCODING_UTF8);
+ }
+ else
+ InternalParse(data, TIXML_ENCODING_LEGACY);
+
+ // 'Error()' contains result of last run of 'TiXmlDocument::Parse()'
+ if (Error())
+ {
+ Clear();
+ location.Clear();
+
+ return false;
+ }
+
+ m_UsedCharset = tryDataCharset;
+ return true;
+}
+
+bool CXBMCTinyXML::InternalParse(const std::string& rawdata, TiXmlEncoding encoding /*= TIXML_DEFAULT_ENCODING */)
+{
+ // Preprocess string, replacing '&' with '&amp; for invalid XML entities
+ size_t pos = rawdata.find('&');
+ if (pos == std::string::npos)
+ return (TiXmlDocument::Parse(rawdata.c_str(), NULL, encoding) != NULL); // nothing to fix, process data directly
+
+ std::string data(rawdata);
+ CRegExp re(false, CRegExp::asciiOnly, "^&(amp|lt|gt|quot|apos|#x[a-fA-F0-9]{1,4}|#[0-9]{1,5});.*");
+ do
+ {
+ if (re.RegFind(data, pos, MAX_ENTITY_LENGTH) < 0)
+ data.insert(pos + 1, "amp;");
+ pos = data.find('&', pos + 1);
+ } while (pos != std::string::npos);
+
+ return (TiXmlDocument::Parse(data.c_str(), NULL, encoding) != NULL);
+}
+
+bool CXBMCTinyXML::Test()
+{
+ // scraper results with unescaped &
+ CXBMCTinyXML doc;
+ std::string data("<details><url function=\"ParseTMDBRating\" "
+ "cache=\"tmdb-en-12244.json\">"
+ "http://api.themoviedb.org/3/movie/12244"
+ "?api_key=57983e31fb435df4df77afb854740ea9"
+ "&language=en&#x3f;&#x003F;&#0063;</url></details>");
+ doc.Parse(data.c_str());
+ TiXmlNode *root = doc.RootElement();
+ if (root && root->ValueStr() == "details")
+ {
+ TiXmlElement *url = root->FirstChildElement("url");
+ if (url && url->FirstChild())
+ {
+ return (url->FirstChild()->ValueStr() == "http://api.themoviedb.org/3/movie/12244?api_key=57983e31fb435df4df77afb854740ea9&language=en???");
+ }
+ }
+ return false;
+}
diff --git a/src/utils/XBMCTinyXML.h b/src/utils/XBMCTinyXML.h
new file mode 100644
index 0000000000..0e875ebab5
--- /dev/null
+++ b/src/utils/XBMCTinyXML.h
@@ -0,0 +1,77 @@
+#pragma once
+
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if (defined HAVE_CONFIG_H) && (!defined TARGET_WINDOWS)
+ #include "config.h"
+#endif
+#ifdef TARGET_WINDOWS
+#define TIXML_USE_STL
+#pragma comment(lib, "tinyxmlSTL.lib")
+#else
+//compile fix for TinyXml < 2.6.0
+#define DOCUMENT TINYXML_DOCUMENT
+#define ELEMENT TINYXML_ELEMENT
+#define COMMENT TINYXML_COMMENT
+#define UNKNOWN TINYXML_UNKNOWN
+#define TEXT TINYXML_TEXT
+#define DECLARATION TINYXML_DECLARATION
+#define TYPECOUNT TINYXML_TYPECOUNT
+#endif
+
+#include <tinyxml.h>
+#include <string>
+
+#undef DOCUMENT
+#undef ELEMENT
+#undef COMMENT
+#undef UNKNOWN
+//#undef TEXT
+#undef DECLARATION
+#undef TYPECOUNT
+
+class CXBMCTinyXML : public TiXmlDocument
+{
+public:
+ CXBMCTinyXML();
+ CXBMCTinyXML(const char*);
+ CXBMCTinyXML(const std::string& documentName);
+ CXBMCTinyXML(const std::string& documentName, const std::string& documentCharset);
+ bool LoadFile(TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING);
+ bool LoadFile(const char*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING);
+ bool LoadFile(const std::string& _filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING);
+ bool LoadFile(const std::string& _filename, const std::string& documentCharset);
+ bool LoadFile(FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING);
+ bool SaveFile(const char*) const;
+ bool SaveFile(const std::string& filename) const;
+ bool Parse(const char*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING);
+ bool Parse(const std::string& data, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING);
+ bool Parse(const std::string& data, const std::string& dataCharset);
+ inline std::string GetSuggestedCharset(void) const { return m_SuggestedCharset; }
+ inline std::string GetUsedCharset(void) const { return m_UsedCharset; }
+ static bool Test();
+protected:
+ bool TryParse(const std::string& data, const std::string& tryDataCharset);
+ bool InternalParse(const std::string& rawdata, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING);
+
+ std::string m_SuggestedCharset;
+ std::string m_UsedCharset;
+};
diff --git a/src/utils/XMLUtils.cpp b/src/utils/XMLUtils.cpp
new file mode 100644
index 0000000000..dc234da225
--- /dev/null
+++ b/src/utils/XMLUtils.cpp
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "XMLUtils.h"
+#include "URL.h"
+#include "StringUtils.h"
+#ifdef TARGET_WINDOWS
+#include "PlatformDefs.h" //for strcasecmp
+#endif
+
+bool XMLUtils::GetHex(const TiXmlNode* pRootNode, const char* strTag, uint32_t& hexValue)
+{
+ const TiXmlNode* pNode = pRootNode->FirstChild(strTag );
+ if (!pNode || !pNode->FirstChild()) return false;
+ return sscanf(pNode->FirstChild()->Value(), "%x", (uint32_t*)&hexValue) == 1;
+}
+
+
+bool XMLUtils::GetUInt(const TiXmlNode* pRootNode, const char* strTag, uint32_t& uintValue)
+{
+ const TiXmlNode* pNode = pRootNode->FirstChild(strTag );
+ if (!pNode || !pNode->FirstChild()) return false;
+ uintValue = atol(pNode->FirstChild()->Value());
+ return true;
+}
+
+bool XMLUtils::GetUInt(const TiXmlNode* pRootNode, const char* strTag, uint32_t &value, const uint32_t min, const uint32_t max)
+{
+ if (GetUInt(pRootNode, strTag, value))
+ {
+ if (value < min) value = min;
+ if (value > max) value = max;
+ return true;
+ }
+ return false;
+}
+
+bool XMLUtils::GetLong(const TiXmlNode* pRootNode, const char* strTag, long& lLongValue)
+{
+ const TiXmlNode* pNode = pRootNode->FirstChild(strTag );
+ if (!pNode || !pNode->FirstChild()) return false;
+ lLongValue = atol(pNode->FirstChild()->Value());
+ return true;
+}
+
+bool XMLUtils::GetInt(const TiXmlNode* pRootNode, const char* strTag, int& iIntValue)
+{
+ const TiXmlNode* pNode = pRootNode->FirstChild(strTag );
+ if (!pNode || !pNode->FirstChild()) return false;
+ iIntValue = atoi(pNode->FirstChild()->Value());
+ return true;
+}
+
+bool XMLUtils::GetInt(const TiXmlNode* pRootNode, const char* strTag, int &value, const int min, const int max)
+{
+ if (GetInt(pRootNode, strTag, value))
+ {
+ if (value < min) value = min;
+ if (value > max) value = max;
+ return true;
+ }
+ return false;
+}
+
+bool XMLUtils::GetDouble(const TiXmlNode *root, const char *tag, double &value)
+{
+ const TiXmlNode* node = root->FirstChild(tag);
+ if (!node || !node->FirstChild()) return false;
+ value = atof(node->FirstChild()->Value());
+ return true;
+}
+
+bool XMLUtils::GetFloat(const TiXmlNode* pRootNode, const char* strTag, float& value)
+{
+ const TiXmlNode* pNode = pRootNode->FirstChild(strTag );
+ if (!pNode || !pNode->FirstChild()) return false;
+ value = (float)atof(pNode->FirstChild()->Value());
+ return true;
+}
+
+bool XMLUtils::GetFloat(const TiXmlNode* pRootElement, const char *tagName, float& fValue, const float fMin, const float fMax)
+{
+ if (GetFloat(pRootElement, tagName, fValue))
+ { // check range
+ if (fValue < fMin) fValue = fMin;
+ if (fValue > fMax) fValue = fMax;
+ return true;
+ }
+ return false;
+}
+
+bool XMLUtils::GetBoolean(const TiXmlNode* pRootNode, const char* strTag, bool& bBoolValue)
+{
+ const TiXmlNode* pNode = pRootNode->FirstChild(strTag );
+ if (!pNode || !pNode->FirstChild()) return false;
+ std::string strEnabled = pNode->FirstChild()->ValueStr();
+ StringUtils::ToLower(strEnabled);
+ if (strEnabled == "off" || strEnabled == "no" || strEnabled == "disabled" || strEnabled == "false" || strEnabled == "0" )
+ bBoolValue = false;
+ else
+ {
+ bBoolValue = true;
+ if (strEnabled != "on" && strEnabled != "yes" && strEnabled != "enabled" && strEnabled != "true")
+ return false; // invalid bool switch - it's probably some other string.
+ }
+ return true;
+}
+
+bool XMLUtils::GetString(const TiXmlNode* pRootNode, const char* strTag, std::string& strStringValue)
+{
+ const TiXmlElement* pElement = pRootNode->FirstChildElement(strTag );
+ if (!pElement) return false;
+ const char* encoded = pElement->Attribute("urlencoded");
+ const TiXmlNode* pNode = pElement->FirstChild();
+ if (pNode != NULL)
+ {
+ strStringValue = pNode->ValueStr();
+ if (encoded && strcasecmp(encoded,"yes") == 0)
+ strStringValue = CURL::Decode(strStringValue);
+ return true;
+ }
+ strStringValue.clear();
+ return true;
+}
+
+bool XMLUtils::HasChild(const TiXmlNode* pRootNode, const char* strTag)
+{
+ const TiXmlElement* pElement = pRootNode->FirstChildElement(strTag);
+ if (!pElement) return false;
+ const TiXmlNode* pNode = pElement->FirstChild();
+ return (pNode != NULL);
+}
+
+bool XMLUtils::GetAdditiveString(const TiXmlNode* pRootNode, const char* strTag,
+ const std::string& strSeparator, std::string& strStringValue,
+ bool clear)
+{
+ std::string strTemp;
+ const TiXmlElement* node = pRootNode->FirstChildElement(strTag);
+ bool bResult=false;
+ if (node && node->FirstChild() && clear)
+ strStringValue.clear();
+ while (node)
+ {
+ if (node->FirstChild())
+ {
+ bResult = true;
+ strTemp = node->FirstChild()->Value();
+ const char* clear=node->Attribute("clear");
+ if (strStringValue.empty() || (clear && strcasecmp(clear,"true")==0))
+ strStringValue = strTemp;
+ else
+ strStringValue += strSeparator+strTemp;
+ }
+ node = node->NextSiblingElement(strTag);
+ }
+
+ return bResult;
+}
+
+/*!
+ Parses the XML for multiple tags of the given name.
+ Does not clear the array to support chaining.
+*/
+bool XMLUtils::GetStringArray(const TiXmlNode* pRootNode, const char* strTag, std::vector<std::string>& arrayValue, bool clear /* = false */, const std::string& separator /* = "" */)
+{
+ std::string strTemp;
+ const TiXmlElement* node = pRootNode->FirstChildElement(strTag);
+ bool bResult=false;
+ if (node && node->FirstChild() && clear)
+ arrayValue.clear();
+ while (node)
+ {
+ if (node->FirstChild())
+ {
+ bResult = true;
+ strTemp = node->FirstChild()->ValueStr();
+
+ const char* clearAttr = node->Attribute("clear");
+ if (clearAttr && strcasecmp(clearAttr, "true") == 0)
+ arrayValue.clear();
+
+ if (strTemp.empty())
+ continue;
+
+ if (separator.empty())
+ arrayValue.push_back(strTemp);
+ else
+ {
+ std::vector<std::string> tempArray = StringUtils::Split(strTemp, separator);
+ arrayValue.insert(arrayValue.end(), tempArray.begin(), tempArray.end());
+ }
+ }
+ node = node->NextSiblingElement(strTag);
+ }
+
+ return bResult;
+}
+
+bool XMLUtils::GetPath(const TiXmlNode* pRootNode, const char* strTag, std::string& strStringValue)
+{
+ const TiXmlElement* pElement = pRootNode->FirstChildElement(strTag);
+ if (!pElement) return false;
+
+ const char* encoded = pElement->Attribute("urlencoded");
+ const TiXmlNode* pNode = pElement->FirstChild();
+ if (pNode != NULL)
+ {
+ strStringValue = pNode->Value();
+ if (encoded && strcasecmp(encoded,"yes") == 0)
+ strStringValue = CURL::Decode(strStringValue);
+ return true;
+ }
+ strStringValue.clear();
+ return false;
+}
+
+bool XMLUtils::GetDate(const TiXmlNode* pRootNode, const char* strTag, CDateTime& date)
+{
+ std::string strDate;
+ if (GetString(pRootNode, strTag, strDate) && !strDate.empty())
+ {
+ date.SetFromDBDate(strDate);
+ return true;
+ }
+
+ return false;
+}
+
+bool XMLUtils::GetDateTime(const TiXmlNode* pRootNode, const char* strTag, CDateTime& dateTime)
+{
+ std::string strDateTime;
+ if (GetString(pRootNode, strTag, strDateTime) && !strDateTime.empty())
+ {
+ dateTime.SetFromDBDateTime(strDateTime);
+ return true;
+ }
+
+ return false;
+}
+
+std::string XMLUtils::GetAttribute(const TiXmlElement *element, const char *tag)
+{
+ if (element)
+ {
+ const char *attribute = element->Attribute(tag);
+ if (attribute)
+ return attribute;
+ }
+ return "";
+}
+
+void XMLUtils::SetAdditiveString(TiXmlNode* pRootNode, const char *strTag, const std::string& strSeparator, const std::string& strValue)
+{
+ std::vector<std::string> list = StringUtils::Split(strValue, strSeparator);
+ for (std::vector<std::string>::const_iterator i = list.begin(); i != list.end(); ++i)
+ SetString(pRootNode, strTag, *i);
+}
+
+void XMLUtils::SetStringArray(TiXmlNode* pRootNode, const char *strTag, const std::vector<std::string>& arrayValue)
+{
+ for (unsigned int i = 0; i < arrayValue.size(); i++)
+ SetString(pRootNode, strTag, arrayValue.at(i));
+}
+
+void XMLUtils::SetString(TiXmlNode* pRootNode, const char *strTag, const std::string& strValue)
+{
+ TiXmlElement newElement(strTag);
+ TiXmlNode *pNewNode = pRootNode->InsertEndChild(newElement);
+ if (pNewNode)
+ {
+ TiXmlText value(strValue);
+ pNewNode->InsertEndChild(value);
+ }
+}
+
+void XMLUtils::SetInt(TiXmlNode* pRootNode, const char *strTag, int value)
+{
+ std::string strValue = StringUtils::Format("%i", value);
+ SetString(pRootNode, strTag, strValue);
+}
+
+void XMLUtils::SetLong(TiXmlNode* pRootNode, const char *strTag, long value)
+{
+ std::string strValue = StringUtils::Format("%ld", value);
+ SetString(pRootNode, strTag, strValue);
+}
+
+void XMLUtils::SetFloat(TiXmlNode* pRootNode, const char *strTag, float value)
+{
+ std::string strValue = StringUtils::Format("%f", value);
+ SetString(pRootNode, strTag, strValue);
+}
+
+void XMLUtils::SetBoolean(TiXmlNode* pRootNode, const char *strTag, bool value)
+{
+ SetString(pRootNode, strTag, value ? "true" : "false");
+}
+
+void XMLUtils::SetHex(TiXmlNode* pRootNode, const char *strTag, uint32_t value)
+{
+ std::string strValue = StringUtils::Format("%x", value);
+ SetString(pRootNode, strTag, strValue);
+}
+
+void XMLUtils::SetPath(TiXmlNode* pRootNode, const char *strTag, const std::string& strValue)
+{
+ TiXmlElement newElement(strTag);
+ newElement.SetAttribute("pathversion", path_version);
+ TiXmlNode *pNewNode = pRootNode->InsertEndChild(newElement);
+ if (pNewNode)
+ {
+ TiXmlText value(strValue);
+ pNewNode->InsertEndChild(value);
+ }
+}
+
+void XMLUtils::SetDate(TiXmlNode* pRootNode, const char *strTag, const CDateTime& date)
+{
+ SetString(pRootNode, strTag, date.IsValid() ? date.GetAsDBDate() : "");
+}
+
+void XMLUtils::SetDateTime(TiXmlNode* pRootNode, const char *strTag, const CDateTime& dateTime)
+{
+ SetString(pRootNode, strTag, dateTime.IsValid() ? dateTime.GetAsDBDateTime() : "");
+}
diff --git a/src/utils/XMLUtils.h b/src/utils/XMLUtils.h
new file mode 100644
index 0000000000..70e6446e46
--- /dev/null
+++ b/src/utils/XMLUtils.h
@@ -0,0 +1,86 @@
+#pragma once
+
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string>
+#include <stdint.h>
+#include <vector>
+#include "utils/XBMCTinyXML.h"
+
+class CDateTime;
+
+class XMLUtils
+{
+public:
+ static bool HasChild(const TiXmlNode* pRootNode, const char* strTag);
+
+ static bool GetHex(const TiXmlNode* pRootNode, const char* strTag, uint32_t& dwHexValue);
+ static bool GetUInt(const TiXmlNode* pRootNode, const char* strTag, uint32_t& dwUIntValue);
+ static bool GetLong(const TiXmlNode* pRootNode, const char* strTag, long& lLongValue);
+ static bool GetFloat(const TiXmlNode* pRootNode, const char* strTag, float& value);
+ static bool GetDouble(const TiXmlNode* pRootNode, const char* strTag, double &value);
+ static bool GetInt(const TiXmlNode* pRootNode, const char* strTag, int& iIntValue);
+ static bool GetBoolean(const TiXmlNode* pRootNode, const char* strTag, bool& bBoolValue);
+ static bool GetString(const TiXmlNode* pRootNode, const char* strTag, std::string& strStringValue);
+ /*! \brief Get multiple tags, concatenating the values together.
+ Transforms
+ <tag>value1</tag>
+ <tag clear="true">value2</tag>
+ ...
+ <tag>valuen</tag>
+ into value2<sep>...<sep>valuen, appending it to the value string. Note that <value1> is overwritten by the clear="true" tag.
+
+ \param rootNode the parent containing the <tag>'s.
+ \param tag the <tag> in question.
+ \param separator the separator to use when concatenating values.
+ \param value [out] the resulting string. Remains untouched if no <tag> is available, else is appended (or cleared based on the clear parameter).
+ \param clear if true, clears the string prior to adding tags, if tags are available. Defaults to false.
+ */
+ static bool GetAdditiveString(const TiXmlNode* rootNode, const char* tag, const std::string& separator, std::string& value, bool clear = false);
+ static bool GetStringArray(const TiXmlNode* rootNode, const char* tag, std::vector<std::string>& arrayValue, bool clear = false, const std::string& separator = "");
+ static bool GetPath(const TiXmlNode* pRootNode, const char* strTag, std::string& strStringValue);
+ static bool GetFloat(const TiXmlNode* pRootNode, const char* strTag, float& value, const float min, const float max);
+ static bool GetUInt(const TiXmlNode* pRootNode, const char* strTag, uint32_t& dwUIntValue, const uint32_t min, const uint32_t max);
+ static bool GetInt(const TiXmlNode* pRootNode, const char* strTag, int& iIntValue, const int min, const int max);
+ static bool GetDate(const TiXmlNode* pRootNode, const char* strTag, CDateTime& date);
+ static bool GetDateTime(const TiXmlNode* pRootNode, const char* strTag, CDateTime& dateTime);
+ /*! \brief Fetch a std::string copy of an attribute, if it exists. Cannot distinguish between empty and non-existent attributes.
+ \param element the element to query.
+ \param tag the name of the attribute.
+ \return the attribute, if it exists, else an empty string
+ */
+ static std::string GetAttribute(const TiXmlElement *element, const char *tag);
+
+ static void SetString(TiXmlNode* pRootNode, const char *strTag, const std::string& strValue);
+ static void SetAdditiveString(TiXmlNode* pRootNode, const char *strTag, const std::string& strSeparator, const std::string& strValue);
+ static void SetStringArray(TiXmlNode* pRootNode, const char *strTag, const std::vector<std::string>& arrayValue);
+ static void SetInt(TiXmlNode* pRootNode, const char *strTag, int value);
+ static void SetFloat(TiXmlNode* pRootNode, const char *strTag, float value);
+ static void SetBoolean(TiXmlNode* pRootNode, const char *strTag, bool value);
+ static void SetHex(TiXmlNode* pRootNode, const char *strTag, uint32_t value);
+ static void SetPath(TiXmlNode* pRootNode, const char *strTag, const std::string& strValue);
+ static void SetLong(TiXmlNode* pRootNode, const char *strTag, long iValue);
+ static void SetDate(TiXmlNode* pRootNode, const char *strTag, const CDateTime& date);
+ static void SetDateTime(TiXmlNode* pRootNode, const char *strTag, const CDateTime& dateTime);
+
+ static const int path_version = 1;
+};
+
diff --git a/src/utils/XSLTUtils.cpp b/src/utils/XSLTUtils.cpp
new file mode 100644
index 0000000000..61b0e9d3dd
--- /dev/null
+++ b/src/utils/XSLTUtils.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://www.xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "XSLTUtils.h"
+#include "log.h"
+#include <libxslt/xslt.h>
+#include <libxslt/transform.h>
+
+#ifdef TARGET_WINDOWS
+#pragma comment(lib, "libxslt.lib")
+#pragma comment(lib, "libxml2.lib")
+#else
+#include <iostream>
+#endif
+
+#define TMP_BUF_SIZE 512
+void err(void *ctx, const char *msg, ...) {
+ char string[TMP_BUF_SIZE];
+ va_list arg_ptr;
+ va_start(arg_ptr, msg);
+ vsnprintf(string, TMP_BUF_SIZE, msg, arg_ptr);
+ va_end(arg_ptr);
+ CLog::Log(LOGDEBUG, "XSLT: %s", string);
+ return;
+}
+
+XSLTUtils::XSLTUtils() :
+m_xmlInput(NULL), m_xmlStylesheet(NULL), m_xsltStylesheet(NULL)
+{
+ // initialize libxslt
+ xmlSubstituteEntitiesDefault(1);
+ xmlLoadExtDtdDefaultValue = 0;
+ xsltSetGenericErrorFunc(NULL, err);
+}
+
+XSLTUtils::~XSLTUtils()
+{
+ if (m_xmlInput)
+ xmlFreeDoc(m_xmlInput);
+ if (m_xmlOutput)
+ xmlFreeDoc(m_xmlOutput);
+ if (m_xsltStylesheet)
+ xsltFreeStylesheet(m_xsltStylesheet);
+}
+
+bool XSLTUtils::XSLTTransform(std::string& output)
+{
+ const char *params[16+1];
+ params[0] = NULL;
+ m_xmlOutput = xsltApplyStylesheet(m_xsltStylesheet, m_xmlInput, params);
+ if (!m_xmlOutput)
+ {
+ CLog::Log(LOGDEBUG, "XSLT: xslt transformation failed");
+ return false;
+ }
+
+ xmlChar *xmlResultBuffer = NULL;
+ int xmlResultLength = 0;
+ int res = xsltSaveResultToString(&xmlResultBuffer, &xmlResultLength, m_xmlOutput, m_xsltStylesheet);
+ if (res == -1)
+ {
+ xmlFree(xmlResultBuffer);
+ return false;
+ }
+
+ output.append((const char *)xmlResultBuffer, xmlResultLength);
+ xmlFree(xmlResultBuffer);
+
+ return true;
+}
+
+bool XSLTUtils::SetInput(const std::string& input)
+{
+ m_xmlInput = xmlParseMemory(input.c_str(), input.size());
+ if (!m_xmlInput)
+ return false;
+ return true;
+}
+
+bool XSLTUtils::SetStylesheet(const std::string& stylesheet)
+{
+ if (m_xsltStylesheet) {
+ xsltFreeStylesheet(m_xsltStylesheet);
+ m_xsltStylesheet = NULL;
+ }
+
+ m_xmlStylesheet = xmlParseMemory(stylesheet.c_str(), stylesheet.size());
+ if (!m_xmlStylesheet)
+ {
+ CLog::Log(LOGDEBUG, "could not xmlParseMemory stylesheetdoc");
+ return false;
+ }
+
+ m_xsltStylesheet = xsltParseStylesheetDoc(m_xmlStylesheet);
+ if (!m_xsltStylesheet) {
+ CLog::Log(LOGDEBUG, "could not parse stylesheetdoc");
+ xmlFree(m_xmlStylesheet);
+ m_xmlStylesheet = NULL;
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/utils/XSLTUtils.h b/src/utils/XSLTUtils.h
new file mode 100644
index 0000000000..18269fb953
--- /dev/null
+++ b/src/utils/XSLTUtils.h
@@ -0,0 +1,62 @@
+#pragma once
+
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://www.xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string>
+#include <libxslt/xslt.h>
+#include <libxslt/xsltutils.h>
+
+class XSLTUtils
+{
+public:
+ XSLTUtils();
+ ~XSLTUtils();
+
+ /*! \brief Set the input XML for an XSLT transform from a string.
+ This sets up the XSLT transformer with some input XML from a string in memory.
+ The input XML should be well formed.
+ \param input the XML document to be transformed.
+ */
+ bool SetInput(const std::string& input);
+
+ /*! \brief Set the stylesheet (XSL) for an XSLT transform from a string.
+ This sets up the XSLT transformer with some stylesheet XML from a string in memory.
+ The input XSL should be well formed.
+ \param input the XSL document to be transformed.
+ */
+ bool SetStylesheet(const std::string& stylesheet);
+
+ /*! \brief Perform an XSLT transform on an inbound XML document.
+ This will apply an XSLT transformation on an input XML document,
+ giving an output XML document, using the specified XSLT document
+ as the transformer.
+ \param input the parent containing the <tag>'s.
+ \param filename the <tag> in question.
+ */
+ bool XSLTTransform(std::string& output);
+
+
+private:
+ xmlDocPtr m_xmlInput;
+ xmlDocPtr m_xmlOutput;
+ xmlDocPtr m_xmlStylesheet;
+ xsltStylesheetPtr m_xsltStylesheet;
+};
diff --git a/src/utils/auto_buffer.cpp b/src/utils/auto_buffer.cpp
new file mode 100644
index 0000000000..c54ff73b64
--- /dev/null
+++ b/src/utils/auto_buffer.cpp
@@ -0,0 +1,95 @@
+/*
+* Copyright (C) 2013-2014 Team XBMC
+* http://xbmc.org
+*
+* This Program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2, or (at your option)
+* any later version.
+*
+* This Program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with XBMC; see the file COPYING. If not, see
+* <http://www.gnu.org/licenses/>.
+*
+*/
+
+#include "auto_buffer.h"
+#include <new> // for std::bad_alloc
+#include <stdlib.h> // for malloc(), realloc() and free()
+
+using namespace XUTILS;
+
+auto_buffer::auto_buffer(size_t size) : p(0), s(0)
+{
+ if (!size)
+ return;
+
+ p = malloc(size); // "malloc()" instead of "new" allow to use "realloc()"
+ if (!p)
+ throw std::bad_alloc();
+ s = size;
+}
+
+auto_buffer::~auto_buffer()
+{
+ free(p);
+}
+
+auto_buffer& auto_buffer::allocate(size_t size)
+{
+ clear();
+ if (size)
+ {
+ p = malloc(size);
+ if (!p)
+ throw std::bad_alloc();
+ s = size;
+ }
+ return *this;
+}
+
+auto_buffer& auto_buffer::resize(size_t newSize)
+{
+ if (!newSize)
+ return clear();
+
+ void* newPtr = realloc(p, newSize);
+ if (!newPtr)
+ throw std::bad_alloc();
+ p = newPtr;
+ s = newSize;
+ return *this;
+}
+
+auto_buffer& auto_buffer::clear(void)
+{
+ free(p);
+ p = 0;
+ s = 0;
+ return *this;
+}
+
+auto_buffer& auto_buffer::attach(void* pointer, size_t size)
+{
+ clear();
+ if ((pointer && size) || (!pointer && !size))
+ {
+ p = pointer;
+ s = size;
+ }
+ return *this;
+}
+
+void* auto_buffer::detach(void)
+{
+ void* returnPtr = p;
+ p = 0;
+ s = 0;
+ return returnPtr;
+}
+
diff --git a/src/utils/auto_buffer.h b/src/utils/auto_buffer.h
new file mode 100644
index 0000000000..43c9be86ec
--- /dev/null
+++ b/src/utils/auto_buffer.h
@@ -0,0 +1,105 @@
+#pragma once
+/*
+* Copyright (C) 2013-2014 Team XBMC
+* http://xbmc.org
+*
+* This Program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2, or (at your option)
+* any later version.
+*
+* This Program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with XBMC; see the file COPYING. If not, see
+* <http://www.gnu.org/licenses/>.
+*
+*/
+
+#include <stddef.h> // for size_t
+
+namespace XUTILS
+{
+
+ class auto_buffer
+ {
+ public:
+ /**
+ * Create buffer with zero size
+ */
+ auto_buffer(void) : p(0), s(0)
+ {}
+ /**
+ * Create buffer with specified size
+ * @param size of created buffer
+ */
+ explicit auto_buffer(size_t size);
+ ~auto_buffer();
+
+ /**
+ * Allocate specified size for buffer, discarding current buffer content
+ * @param size of buffer to allocate
+ * @return reference to itself
+ */
+ auto_buffer& allocate(size_t size);
+ /**
+ * Resize current buffer to new size. Buffer will be extended or truncated at the end.
+ * @param newSize of buffer
+ * @return reference to itself
+ */
+ auto_buffer& resize(size_t newSize);
+ /**
+ * Reset buffer to zero size
+ * @return reference to itself
+ */
+ auto_buffer& clear(void);
+
+ /**
+ * Get pointer to buffer content
+ * @return pointer to buffer content or NULL if buffer is zero size
+ */
+ inline char* get(void) { return static_cast<char*>(p); }
+ /**
+ * Get constant pointer to buffer content
+ * @return constant pointer to buffer content
+ */
+ inline const char* get(void) const { return static_cast<char*>(p); }
+ /**
+ * Get size of the buffer
+ * @return size of the buffer
+ */
+ inline size_t size(void) const { return s; }
+ /**
+ * Get size of the buffer
+ * @return size of the buffer
+ */
+ inline size_t length(void) const { return s; }
+
+ /**
+ * Attach malloc'ed pointer to the buffer, discarding current buffer content
+ * Pointer must be acquired by malloc() or realloc().
+ * Pointer will be automatically freed on destroy of the buffer.
+ * @param pointer to attach
+ * @param size of new memory region pointed by pointer
+ * @return reference to itself
+ */
+ auto_buffer& attach(void* pointer, size_t size);
+ /**
+ * Detach current buffer content from the buffer, reset buffer to zero size
+ * Caller is responsible to free memory by calling free() for returned pointer
+ * when pointer in not needed anymore
+ * @return detached from buffer pointer to content
+ */
+ void* detach(void);
+
+ private:
+ auto_buffer(const auto_buffer& other); // disallow copy constructor
+ auto_buffer& operator=(const auto_buffer& other); // disallow assignment
+
+ void* p;
+ size_t s;
+ };
+}
diff --git a/src/utils/fastmemcpy-arm.S b/src/utils/fastmemcpy-arm.S
new file mode 100644
index 0000000000..6cb8b0cfc6
--- /dev/null
+++ b/src/utils/fastmemcpy-arm.S
@@ -0,0 +1,528 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Copyright (C) 2011-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#if defined(__arm__) && !defined(TARGET_ANDROID) && !defined(TARGET_DARWIN_IOS)
+#if defined(__ARM_NEON__)
+
+ .text
+#ifndef __APPLE__
+ .fpu neon
+ .global fast_memcpy
+ .type fast_memcpy, %function
+#else
+ .globl _fast_memcpy
+#endif
+ .align 4
+
+/* a prefetch distance of 4 cache-lines works best experimentally */
+#define CACHE_LINE_SIZE 64
+#define PREFETCH_DISTANCE (CACHE_LINE_SIZE*4)
+
+#ifndef __APPLE__
+ .fnstart
+ .save {r0, lr}
+fast_memcpy:
+#else
+_fast_memcpy:
+#endif
+ stmfd sp!, {r0, lr}
+
+ /* start preloading as early as possible */
+ pld [r1, #(CACHE_LINE_SIZE*0)]
+ pld [r1, #(CACHE_LINE_SIZE*1)]
+
+ /* do we have at least 16-bytes to copy (needed for alignment below) */
+ cmp r2, #16
+ blo 5f
+
+ /* align destination to half cache-line for the write-buffer */
+ rsb r3, r0, #0
+ ands r3, r3, #0xF
+ beq 0f
+
+ /* copy up to 15-bytes (count in r3) */
+ sub r2, r2, r3
+ movs ip, r3, lsl #31
+ ldrmib lr, [r1], #1
+ strmib lr, [r0], #1
+ ldrcsb ip, [r1], #1
+ ldrcsb lr, [r1], #1
+ strcsb ip, [r0], #1
+ strcsb lr, [r0], #1
+ movs ip, r3, lsl #29
+ bge 1f
+ // copies 4 bytes, destination 32-bits aligned
+ vld4.8 {d0[0], d1[0], d2[0], d3[0]}, [r1]!
+ vst4.8 {d0[0], d1[0], d2[0], d3[0]}, [r0, :32]!
+1: bcc 2f
+ // copies 8 bytes, destination 64-bits aligned
+ vld1.8 {d0}, [r1]!
+ vst1.8 {d0}, [r0, :64]!
+2:
+
+0: /* preload immediately the next cache line, which we may need */
+ pld [r1, #(CACHE_LINE_SIZE*0)]
+ pld [r1, #(CACHE_LINE_SIZE*1)]
+
+ /* make sure we have at least 64 bytes to copy */
+ subs r2, r2, #64
+ blo 2f
+
+ /* preload all the cache lines we need.
+ * NOTE: the number of pld below depends on PREFETCH_DISTANCE,
+ * ideally would would increase the distance in the main loop to
+ * avoid the goofy code below. In practice this doesn't seem to make
+ * a big difference.
+ */
+ pld [r1, #(CACHE_LINE_SIZE*2)]
+ pld [r1, #(CACHE_LINE_SIZE*3)]
+ pld [r1, #(PREFETCH_DISTANCE)]
+
+1: /* The main loop copies 64 bytes at a time */
+ vld1.8 {d0 - d3}, [r1]!
+ vld1.8 {d4 - d7}, [r1]!
+ pld [r1, #(PREFETCH_DISTANCE)]
+ subs r2, r2, #64
+ vst1.8 {d0 - d3}, [r0, :128]!
+ vst1.8 {d4 - d7}, [r0, :128]!
+ bhs 1b
+
+2: /* fix-up the remaining count and make sure we have >= 32 bytes left */
+ add r2, r2, #64
+ subs r2, r2, #32
+ blo 4f
+
+3: /* 32 bytes at a time. These cache lines were already preloaded */
+ vld1.8 {d0 - d3}, [r1]!
+ subs r2, r2, #32
+ vst1.8 {d0 - d3}, [r0, :128]!
+ bhs 3b
+
+4: /* less than 32 left */
+ add r2, r2, #32
+ tst r2, #0x10
+ beq 5f
+ // copies 16 bytes, 128-bits aligned
+ vld1.8 {d0, d1}, [r1]!
+ vst1.8 {d0, d1}, [r0, :128]!
+
+5: /* copy up to 15-bytes (count in r2) */
+ movs ip, r2, lsl #29
+ bcc 1f
+ vld1.8 {d0}, [r1]!
+ vst1.8 {d0}, [r0]!
+1: bge 2f
+ vld4.8 {d0[0], d1[0], d2[0], d3[0]}, [r1]!
+ vst4.8 {d0[0], d1[0], d2[0], d3[0]}, [r0]!
+2: movs ip, r2, lsl #31
+ ldrmib r3, [r1], #1
+ ldrcsb ip, [r1], #1
+ ldrcsb lr, [r1], #1
+ strmib r3, [r0], #1
+ strcsb ip, [r0], #1
+ strcsb lr, [r0], #1
+
+ ldmfd sp!, {r0, lr}
+ bx lr
+#ifndef __APPLE__
+ .fnend
+#endif
+
+#else /* __ARM_ARCH__ < 7 */
+
+
+ .text
+
+#ifndef __APPLE__
+ .global fast_memcpy
+ .type fast_memcpy, %function
+#else
+ .globl _fast_memcpy
+#endif
+ .align 4
+
+ /*
+ * Optimized memcpy() for ARM.
+ *
+ * note that memcpy() always returns the destination pointer,
+ * so we have to preserve R0.
+ */
+
+#ifndef __APPLE__
+fast_memcpy:
+#else
+_fast_memcpy:
+#endif
+ /* The stack must always be 64-bits aligned to be compliant with the
+ * ARM ABI. Since we have to save R0, we might as well save R4
+ * which we can use for better pipelining of the reads below
+ */
+#ifndef __APPLE__
+ .fnstart
+ .save {r0, r4, lr}
+#endif
+ stmfd sp!, {r0, r4, lr}
+ /* Making room for r5-r11 which will be spilled later */
+ .pad #28
+ sub sp, sp, #28
+
+ // preload the destination because we'll align it to a cache line
+ // with small writes. Also start the source "pump".
+ //PLD (r0, #0)
+ //PLD (r1, #0)
+ //PLD (r1, #32)
+
+ /* it simplifies things to take care of len<4 early */
+ cmp r2, #4
+ blo copy_last_3_and_return
+
+ /* compute the offset to align the source
+ * offset = (4-(src&3))&3 = -src & 3
+ */
+ rsb r3, r1, #0
+ ands r3, r3, #3
+ beq src_aligned
+
+ /* align source to 32 bits. We need to insert 2 instructions between
+ * a ldr[b|h] and str[b|h] because byte and half-word instructions
+ * stall 2 cycles.
+ */
+ movs r12, r3, lsl #31
+ sub r2, r2, r3 /* we know that r3 <= r2 because r2 >= 4 */
+ ldrmib r3, [r1], #1
+ ldrcsb r4, [r1], #1
+ ldrcsb r12,[r1], #1
+ strmib r3, [r0], #1
+ strcsb r4, [r0], #1
+ strcsb r12,[r0], #1
+
+src_aligned:
+
+ /* see if src and dst are aligned together (congruent) */
+ eor r12, r0, r1
+ tst r12, #3
+ bne non_congruent
+
+ /* Use post-incriment mode for stm to spill r5-r11 to reserved stack
+ * frame. Don't update sp.
+ */
+ stmea sp, {r5-r11}
+
+ /* align the destination to a cache-line */
+ rsb r3, r0, #0
+ ands r3, r3, #0x1C
+ beq congruent_aligned32
+ cmp r3, r2
+ andhi r3, r2, #0x1C
+
+ /* conditionnaly copies 0 to 7 words (length in r3) */
+ movs r12, r3, lsl #28
+ ldmcsia r1!, {r4, r5, r6, r7} /* 16 bytes */
+ ldmmiia r1!, {r8, r9} /* 8 bytes */
+ stmcsia r0!, {r4, r5, r6, r7}
+ stmmiia r0!, {r8, r9}
+ tst r3, #0x4
+ ldrne r10,[r1], #4 /* 4 bytes */
+ strne r10,[r0], #4
+ sub r2, r2, r3
+
+congruent_aligned32:
+ /*
+ * here source is aligned to 32 bytes.
+ */
+
+cached_aligned32:
+ subs r2, r2, #32
+ blo less_than_32_left
+
+ /*
+ * We preload a cache-line up to 64 bytes ahead. On the 926, this will
+ * stall only until the requested world is fetched, but the linefill
+ * continues in the the background.
+ * While the linefill is going, we write our previous cache-line
+ * into the write-buffer (which should have some free space).
+ * When the linefill is done, the writebuffer will
+ * start dumping its content into memory
+ *
+ * While all this is going, we then load a full cache line into
+ * 8 registers, this cache line should be in the cache by now
+ * (or partly in the cache).
+ *
+ * This code should work well regardless of the source/dest alignment.
+ *
+ */
+
+ // Align the preload register to a cache-line because the cpu does
+ // "critical word first" (the first word requested is loaded first).
+ bic r12, r1, #0x1F
+ add r12, r12, #64
+
+1: ldmia r1!, { r4-r11 }
+ //PLD (r12, #64)
+ subs r2, r2, #32
+
+ // NOTE: if r12 is more than 64 ahead of r1, the following ldrhi
+ // for ARM9 preload will not be safely guarded by the preceding subs.
+ // When it is safely guarded the only possibility to have SIGSEGV here
+ // is because the caller overstates the length.
+ ldrhi r3, [r12], #32 /* cheap ARM9 preload */
+ stmia r0!, { r4-r11 }
+ bhs 1b
+
+ add r2, r2, #32
+
+
+
+
+less_than_32_left:
+ /*
+ * less than 32 bytes left at this point (length in r2)
+ */
+
+ /* skip all this if there is nothing to do, which should
+ * be a common case (if not executed the code below takes
+ * about 16 cycles)
+ */
+ tst r2, #0x1F
+ beq 1f
+
+ /* conditionnaly copies 0 to 31 bytes */
+ movs r12, r2, lsl #28
+ ldmcsia r1!, {r4, r5, r6, r7} /* 16 bytes */
+ ldmmiia r1!, {r8, r9} /* 8 bytes */
+ stmcsia r0!, {r4, r5, r6, r7}
+ stmmiia r0!, {r8, r9}
+ movs r12, r2, lsl #30
+ ldrcs r3, [r1], #4 /* 4 bytes */
+ ldrmih r4, [r1], #2 /* 2 bytes */
+ strcs r3, [r0], #4
+ strmih r4, [r0], #2
+ tst r2, #0x1
+ ldrneb r3, [r1] /* last byte */
+ strneb r3, [r0]
+
+ /* we're done! restore everything and return */
+1: ldmfd sp!, {r5-r11}
+ ldmfd sp!, {r0, r4, lr}
+ bx lr
+
+ /********************************************************************/
+
+non_congruent:
+ /*
+ * here source is aligned to 4 bytes
+ * but destination is not.
+ *
+ * in the code below r2 is the number of bytes read
+ * (the number of bytes written is always smaller, because we have
+ * partial words in the shift queue)
+ */
+ cmp r2, #4
+ blo copy_last_3_and_return
+
+ /* Use post-incriment mode for stm to spill r5-r11 to reserved stack
+ * frame. Don't update sp.
+ */
+ stmea sp, {r5-r11}
+
+ /* compute shifts needed to align src to dest */
+ rsb r5, r0, #0
+ and r5, r5, #3 /* r5 = # bytes in partial words */
+ mov r12, r5, lsl #3 /* r12 = right */
+ rsb lr, r12, #32 /* lr = left */
+
+ /* read the first word */
+ ldr r3, [r1], #4
+ sub r2, r2, #4
+
+ /* write a partial word (0 to 3 bytes), such that destination
+ * becomes aligned to 32 bits (r5 = nb of words to copy for alignment)
+ */
+ movs r5, r5, lsl #31
+ strmib r3, [r0], #1
+ movmi r3, r3, lsr #8
+ strcsb r3, [r0], #1
+ movcs r3, r3, lsr #8
+ strcsb r3, [r0], #1
+ movcs r3, r3, lsr #8
+
+ cmp r2, #4
+ blo partial_word_tail
+
+ /* Align destination to 32 bytes (cache line boundary) */
+1: tst r0, #0x1c
+ beq 2f
+ ldr r5, [r1], #4
+ sub r2, r2, #4
+ orr r4, r3, r5, lsl lr
+ mov r3, r5, lsr r12
+ str r4, [r0], #4
+ cmp r2, #4
+ bhs 1b
+ blo partial_word_tail
+
+ /* copy 32 bytes at a time */
+2: subs r2, r2, #32
+ blo less_than_thirtytwo
+
+ /* Use immediate mode for the shifts, because there is an extra cycle
+ * for register shifts, which could account for up to 50% of
+ * performance hit.
+ */
+
+ cmp r12, #24
+ beq loop24
+ cmp r12, #8
+ beq loop8
+
+loop16:
+ ldr r12, [r1], #4
+1: mov r4, r12
+ ldmia r1!, { r5,r6,r7, r8,r9,r10,r11}
+ //PLD (r1, #64)
+ subs r2, r2, #32
+ ldrhs r12, [r1], #4
+ orr r3, r3, r4, lsl #16
+ mov r4, r4, lsr #16
+ orr r4, r4, r5, lsl #16
+ mov r5, r5, lsr #16
+ orr r5, r5, r6, lsl #16
+ mov r6, r6, lsr #16
+ orr r6, r6, r7, lsl #16
+ mov r7, r7, lsr #16
+ orr r7, r7, r8, lsl #16
+ mov r8, r8, lsr #16
+ orr r8, r8, r9, lsl #16
+ mov r9, r9, lsr #16
+ orr r9, r9, r10, lsl #16
+ mov r10, r10, lsr #16
+ orr r10, r10, r11, lsl #16
+ stmia r0!, {r3,r4,r5,r6, r7,r8,r9,r10}
+ mov r3, r11, lsr #16
+ bhs 1b
+ b less_than_thirtytwo
+
+loop8:
+ ldr r12, [r1], #4
+1: mov r4, r12
+ ldmia r1!, { r5,r6,r7, r8,r9,r10,r11}
+ //PLD (r1, #64)
+ subs r2, r2, #32
+ ldrhs r12, [r1], #4
+ orr r3, r3, r4, lsl #24
+ mov r4, r4, lsr #8
+ orr r4, r4, r5, lsl #24
+ mov r5, r5, lsr #8
+ orr r5, r5, r6, lsl #24
+ mov r6, r6, lsr #8
+ orr r6, r6, r7, lsl #24
+ mov r7, r7, lsr #8
+ orr r7, r7, r8, lsl #24
+ mov r8, r8, lsr #8
+ orr r8, r8, r9, lsl #24
+ mov r9, r9, lsr #8
+ orr r9, r9, r10, lsl #24
+ mov r10, r10, lsr #8
+ orr r10, r10, r11, lsl #24
+ stmia r0!, {r3,r4,r5,r6, r7,r8,r9,r10}
+ mov r3, r11, lsr #8
+ bhs 1b
+ b less_than_thirtytwo
+
+loop24:
+ ldr r12, [r1], #4
+1: mov r4, r12
+ ldmia r1!, { r5,r6,r7, r8,r9,r10,r11}
+ //PLD (r1, #64)
+ subs r2, r2, #32
+ ldrhs r12, [r1], #4
+ orr r3, r3, r4, lsl #8
+ mov r4, r4, lsr #24
+ orr r4, r4, r5, lsl #8
+ mov r5, r5, lsr #24
+ orr r5, r5, r6, lsl #8
+ mov r6, r6, lsr #24
+ orr r6, r6, r7, lsl #8
+ mov r7, r7, lsr #24
+ orr r7, r7, r8, lsl #8
+ mov r8, r8, lsr #24
+ orr r8, r8, r9, lsl #8
+ mov r9, r9, lsr #24
+ orr r9, r9, r10, lsl #8
+ mov r10, r10, lsr #24
+ orr r10, r10, r11, lsl #8
+ stmia r0!, {r3,r4,r5,r6, r7,r8,r9,r10}
+ mov r3, r11, lsr #24
+ bhs 1b
+
+
+less_than_thirtytwo:
+ /* copy the last 0 to 31 bytes of the source */
+ rsb r12, lr, #32 /* we corrupted r12, recompute it */
+ add r2, r2, #32
+ cmp r2, #4
+ blo partial_word_tail
+
+1: ldr r5, [r1], #4
+ sub r2, r2, #4
+ orr r4, r3, r5, lsl lr
+ mov r3, r5, lsr r12
+ str r4, [r0], #4
+ cmp r2, #4
+ bhs 1b
+
+partial_word_tail:
+ /* we have a partial word in the input buffer */
+ movs r5, lr, lsl #(31-3)
+ strmib r3, [r0], #1
+ movmi r3, r3, lsr #8
+ strcsb r3, [r0], #1
+ movcs r3, r3, lsr #8
+ strcsb r3, [r0], #1
+
+ /* Refill spilled registers from the stack. Don't update sp. */
+ ldmfd sp, {r5-r11}
+
+copy_last_3_and_return:
+ movs r2, r2, lsl #31 /* copy remaining 0, 1, 2 or 3 bytes */
+ ldrmib r2, [r1], #1
+ ldrcsb r3, [r1], #1
+ ldrcsb r12,[r1]
+ strmib r2, [r0], #1
+ strcsb r3, [r0], #1
+ strcsb r12,[r0]
+
+ /* we're done! restore sp and spilled registers and return */
+ add sp, sp, #28
+ ldmfd sp!, {r0, r4, lr}
+ bx lr
+#ifndef __APPLE__
+ .fnend
+#endif
+
+#endif /* __ARM_ARCH__ < 7 */
+#endif
+
+#if defined(__linux__) && defined(__ELF__)
+/* we don't need an executable stack */
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/src/utils/fastmemcpy.c b/src/utils/fastmemcpy.c
new file mode 100644
index 0000000000..d2a1d49d54
--- /dev/null
+++ b/src/utils/fastmemcpy.c
@@ -0,0 +1,396 @@
+/*
+ * fastmemcpy.h : fast memcpy routines
+ *****************************************************************************
+ * $Id: fastmemcpy.h 13905 2006-01-12 23:10:04Z dionoea $
+ *
+ * Authors: various Linux kernel hackers
+ * various MPlayer hackers
+ * Nick Kurshev <nickols_k@mail.ru>
+ *
+ * Copyright (C) 2011-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#if !defined(TARGET_WINDOWS) && !defined(__ppc__) && !defined(__powerpc__) && !defined(__arm__)
+#define HAVE_MMX2
+#define HAVE_SSE
+
+/*
+ aclib - advanced C library ;)
+ This file contains functions which improve and expand standard C-library
+*/
+#include <stddef.h>
+
+#define BLOCK_SIZE 4096
+#define CONFUSION_FACTOR 0
+/*Feel free to fine-tune the above 2, it might be possible to get some speedup with them :)*/
+
+/*#define STATISTICS*/
+
+#ifndef HAVE_SSE2
+/*
+ P3 processor has only one SSE decoder so can execute only 1 sse insn per
+ cpu clock, but it has 3 mmx decoders (include load/store unit)
+ and executes 3 mmx insns per cpu clock.
+ P4 processor has some chances, but after reading:
+ http://www.emulators.com/pentium4.htm
+ I have doubts. Anyway SSE2 version of this code can be written better.
+*/
+#undef HAVE_SSE
+#endif
+
+
+/*
+ This part of code was taken by me from Linux-2.4.3 and slightly modified
+for MMX, MMX2, SSE instruction set. I have done it since linux uses page aligned
+blocks but mplayer uses weakly ordered data and original sources can not
+speedup them. Only using PREFETCHNTA and MOVNTQ together have effect!
+
+>From IA-32 Intel Architecture Software Developer's Manual Volume 1,
+
+Order Number 245470:
+"10.4.6. Cacheability Control, Prefetch, and Memory Ordering Instructions"
+
+Data referenced by a program can be temporal (data will be used again) or
+non-temporal (data will be referenced once and not reused in the immediate
+future). To make efficient use of the processor's caches, it is generally
+desirable to cache temporal data and not cache non-temporal data. Overloading
+the processor's caches with non-temporal data is sometimes referred to as
+"polluting the caches".
+The non-temporal data is written to memory with Write-Combining semantics.
+
+The PREFETCHh instructions permits a program to load data into the processor
+at a suggested cache level, so that it is closer to the processors load and
+store unit when it is needed. If the data is already present in a level of
+the cache hierarchy that is closer to the processor, the PREFETCHh instruction
+will not result in any data movement.
+But we should you PREFETCHNTA: Non-temporal data fetch data into location
+close to the processor, minimizing cache pollution.
+
+The MOVNTQ (store quadword using non-temporal hint) instruction stores
+packed integer data from an MMX register to memory, using a non-temporal hint.
+The MOVNTPS (store packed single-precision floating-point values using
+non-temporal hint) instruction stores packed floating-point data from an
+XMM register to memory, using a non-temporal hint.
+
+The SFENCE (Store Fence) instruction controls write ordering by creating a
+fence for memory store operations. This instruction guarantees that the results
+of every store instruction that precedes the store fence in program order is
+globally visible before any store instruction that follows the fence. The
+SFENCE instruction provides an efficient way of ensuring ordering between
+procedures that produce weakly-ordered data and procedures that consume that
+data.
+
+If you have questions please contact with me: Nick Kurshev: nickols_k@mail.ru.
+*/
+
+/* 3dnow memcpy support from kernel 2.4.2 */
+/* by Pontscho/fresh!mindworkz */
+
+#if defined( HAVE_MMX2 ) || defined( HAVE_3DNOW ) || defined( HAVE_MMX )
+
+#undef HAVE_MMX1
+#if defined(HAVE_MMX) && !defined(HAVE_MMX2) && !defined(HAVE_3DNOW) && !defined(HAVE_SSE)
+/* means: mmx v.1. Note: Since we added alignment of destinition it speedups
+ of memory copying on PentMMX, Celeron-1 and P2 upto 12% versus
+ standard (non MMX-optimized) version.
+ Note: on K6-2+ it speedups memory copying upto 25% and
+ on K7 and P3 about 500% (5 times). */
+#define HAVE_MMX1
+#endif
+
+
+#undef HAVE_K6_2PLUS
+#if !defined( HAVE_MMX2) && defined( HAVE_3DNOW)
+#define HAVE_K6_2PLUS
+#endif
+
+/* for small memory blocks (<256 bytes) this version is faster */
+#define small_memcpy(to,from,n)\
+{\
+register unsigned long int dummy;\
+__asm__ __volatile__(\
+ "rep; movsb"\
+ :"=&D"(to), "=&S"(from), "=&c"(dummy)\
+/* It's most portable way to notify compiler */\
+/* that edi, esi and ecx are clobbered in asm block. */\
+/* Thanks to A'rpi for hint!!! */\
+ :"0" (to), "1" (from),"2" (n)\
+ : "memory");\
+}
+
+#ifdef HAVE_SSE
+#define MMREG_SIZE 16
+#else
+#define MMREG_SIZE 64 /*8*/
+#endif
+
+/* Small defines (for readability only) ;) */
+#ifdef HAVE_K6_2PLUS
+#define PREFETCH "prefetch"
+/* On K6 femms is faster of emms. On K7 femms is directly mapped on emms. */
+#define EMMS "femms"
+#else
+#define PREFETCH "prefetchnta"
+#define EMMS "emms"
+#endif
+
+#ifdef HAVE_MMX2
+#define MOVNTQ "movntq"
+#else
+#define MOVNTQ "movq"
+#endif
+
+#ifdef HAVE_MMX1
+#define MIN_LEN 0x800 /* 2K blocks */
+#else
+#define MIN_LEN 0x40 /* 64-byte blocks */
+#endif
+
+void * fast_memcpy(void * to, const void * from, size_t len)
+{
+ void *retval;
+ size_t i;
+ retval = to;
+#ifdef STATISTICS
+ {
+ static int freq[33];
+ static int t=0;
+ int i;
+ for(i=0; len>(1<<i); i++);
+ freq[i]++;
+ t++;
+ if(1024*1024*1024 % t == 0)
+ for(i=0; i<32; i++)
+ printf("freq < %8d %4d\n", 1<<i, freq[i]);
+ }
+#endif
+#ifndef HAVE_MMX1
+ /* PREFETCH has effect even for MOVSB instruction ;) */
+ __asm__ __volatile__ (
+ PREFETCH" (%0)\n"
+ PREFETCH" 64(%0)\n"
+ PREFETCH" 128(%0)\n"
+ PREFETCH" 192(%0)\n"
+ PREFETCH" 256(%0)\n"
+ : : "r" (from) );
+#endif
+ if(len >= MIN_LEN)
+ {
+ register unsigned long int delta;
+ /* Align destinition to MMREG_SIZE -boundary */
+ delta = ((unsigned long int)to)&(MMREG_SIZE-1);
+ if(delta)
+ {
+ delta=MMREG_SIZE-delta;
+ len -= delta;
+ small_memcpy(to, from, delta);
+ }
+ i = len >> 6; /* len/64 */
+ len&=63;
+ /*
+ This algorithm is top effective when the code consequently
+ reads and writes blocks which have size of cache line.
+ Size of cache line is processor-dependent.
+ It will, however, be a minimum of 32 bytes on any processors.
+ It would be better to have a number of instructions which
+ perform reading and writing to be multiple to a number of
+ processor's decoders, but it's not always possible.
+ */
+#ifdef HAVE_SSE /* Only P3 (may be Cyrix3) */
+ if(((unsigned long)from) & 15)
+ /* if SRC is misaligned */
+ for(; i>0; i--)
+ {
+ __asm__ __volatile__ (
+ PREFETCH" 320(%0)\n"
+ "movups (%0), %%xmm0\n"
+ "movups 16(%0), %%xmm1\n"
+ "movups 32(%0), %%xmm2\n"
+ "movups 48(%0), %%xmm3\n"
+ "movntps %%xmm0, (%1)\n"
+ "movntps %%xmm1, 16(%1)\n"
+ "movntps %%xmm2, 32(%1)\n"
+ "movntps %%xmm3, 48(%1)\n"
+ :: "r" (from), "r" (to) : "memory");
+ ((const unsigned char *)from)+=64;
+ ((unsigned char *)to)+=64;
+ }
+ else
+ /*
+ Only if SRC is aligned on 16-byte boundary.
+ It allows to use movaps instead of movups, which required data
+ to be aligned or a general-protection exception (#GP) is generated.
+ */
+ for(; i>0; i--)
+ {
+ __asm__ __volatile__ (
+ PREFETCH" 320(%0)\n"
+ "movaps (%0), %%xmm0\n"
+ "movaps 16(%0), %%xmm1\n"
+ "movaps 32(%0), %%xmm2\n"
+ "movaps 48(%0), %%xmm3\n"
+ "movntps %%xmm0, (%1)\n"
+ "movntps %%xmm1, 16(%1)\n"
+ "movntps %%xmm2, 32(%1)\n"
+ "movntps %%xmm3, 48(%1)\n"
+ :: "r" (from), "r" (to) : "memory");
+ ((const unsigned char *)from)+=64;
+ ((unsigned char *)to)+=64;
+ }
+#else
+ /* Align destination at BLOCK_SIZE boundary */
+ for(; ((ptrdiff_t)to & (BLOCK_SIZE-1)) && i>0; i--)
+ {
+ __asm__ __volatile__ (
+#ifndef HAVE_MMX1
+ PREFETCH" 320(%0)\n"
+#endif
+ "movq (%0), %%mm0\n"
+ "movq 8(%0), %%mm1\n"
+ "movq 16(%0), %%mm2\n"
+ "movq 24(%0), %%mm3\n"
+ "movq 32(%0), %%mm4\n"
+ "movq 40(%0), %%mm5\n"
+ "movq 48(%0), %%mm6\n"
+ "movq 56(%0), %%mm7\n"
+ MOVNTQ" %%mm0, (%1)\n"
+ MOVNTQ" %%mm1, 8(%1)\n"
+ MOVNTQ" %%mm2, 16(%1)\n"
+ MOVNTQ" %%mm3, 24(%1)\n"
+ MOVNTQ" %%mm4, 32(%1)\n"
+ MOVNTQ" %%mm5, 40(%1)\n"
+ MOVNTQ" %%mm6, 48(%1)\n"
+ MOVNTQ" %%mm7, 56(%1)\n"
+ :: "r" (from), "r" (to) : "memory");
+ from = (const void *) (((const unsigned char *)from)+64);
+ to = (void *) (((unsigned char *)to)+64);
+ }
+
+/* printf(" %p %p\n", (ptrdiff_t)from&1023, (ptrdiff_t)to&1023); */
+ /* Pure Assembly cuz gcc is a bit unpredictable ;) */
+# if 0
+ if(i>=BLOCK_SIZE/64)
+ asm volatile(
+ "xorl %%eax, %%eax \n\t"
+ ".balign 16 \n\t"
+ "1: \n\t"
+ "movl (%0, %%eax), %%ebx \n\t"
+ "movl 32(%0, %%eax), %%ebx \n\t"
+ "movl 64(%0, %%eax), %%ebx \n\t"
+ "movl 96(%0, %%eax), %%ebx \n\t"
+ "addl $128, %%eax \n\t"
+ "cmpl %3, %%eax \n\t"
+ " jb 1b \n\t"
+
+ "xorl %%eax, %%eax \n\t"
+
+ ".balign 16 \n\t"
+ "2: \n\t"
+ "movq (%0, %%eax), %%mm0\n"
+ "movq 8(%0, %%eax), %%mm1\n"
+ "movq 16(%0, %%eax), %%mm2\n"
+ "movq 24(%0, %%eax), %%mm3\n"
+ "movq 32(%0, %%eax), %%mm4\n"
+ "movq 40(%0, %%eax), %%mm5\n"
+ "movq 48(%0, %%eax), %%mm6\n"
+ "movq 56(%0, %%eax), %%mm7\n"
+ MOVNTQ" %%mm0, (%1, %%eax)\n"
+ MOVNTQ" %%mm1, 8(%1, %%eax)\n"
+ MOVNTQ" %%mm2, 16(%1, %%eax)\n"
+ MOVNTQ" %%mm3, 24(%1, %%eax)\n"
+ MOVNTQ" %%mm4, 32(%1, %%eax)\n"
+ MOVNTQ" %%mm5, 40(%1, %%eax)\n"
+ MOVNTQ" %%mm6, 48(%1, %%eax)\n"
+ MOVNTQ" %%mm7, 56(%1, %%eax)\n"
+ "addl $64, %%eax \n\t"
+ "cmpl %3, %%eax \n\t"
+ "jb 2b \n\t"
+
+#if CONFUSION_FACTOR > 0
+ /* a few percent speedup on out of order executing CPUs */
+ "movl %5, %%eax \n\t"
+ "2: \n\t"
+ "movl (%0), %%ebx \n\t"
+ "movl (%0), %%ebx \n\t"
+ "movl (%0), %%ebx \n\t"
+ "movl (%0), %%ebx \n\t"
+ "decl %%eax \n\t"
+ " jnz 2b \n\t"
+#endif
+
+ "xorl %%eax, %%eax \n\t"
+ "addl %3, %0 \n\t"
+ "addl %3, %1 \n\t"
+ "subl %4, %2 \n\t"
+ "cmpl %4, %2 \n\t"
+ " jae 1b \n\t"
+ : "+r" (from), "+r" (to), "+r" (i)
+ : "r" (BLOCK_SIZE), "i" (BLOCK_SIZE/64), "i" (CONFUSION_FACTOR)
+ : "%eax", "%ebx"
+ );
+#endif
+
+ for(; i>0; i--)
+ {
+ __asm__ __volatile__ (
+#ifndef HAVE_MMX1
+ PREFETCH" 320(%0)\n"
+#endif
+ "movq (%0), %%mm0\n"
+ "movq 8(%0), %%mm1\n"
+ "movq 16(%0), %%mm2\n"
+ "movq 24(%0), %%mm3\n"
+ "movq 32(%0), %%mm4\n"
+ "movq 40(%0), %%mm5\n"
+ "movq 48(%0), %%mm6\n"
+ "movq 56(%0), %%mm7\n"
+ MOVNTQ" %%mm0, (%1)\n"
+ MOVNTQ" %%mm1, 8(%1)\n"
+ MOVNTQ" %%mm2, 16(%1)\n"
+ MOVNTQ" %%mm3, 24(%1)\n"
+ MOVNTQ" %%mm4, 32(%1)\n"
+ MOVNTQ" %%mm5, 40(%1)\n"
+ MOVNTQ" %%mm6, 48(%1)\n"
+ MOVNTQ" %%mm7, 56(%1)\n"
+ :: "r" (from), "r" (to) : "memory");
+ from = (const void *) (((const unsigned char *)from)+64);
+ to = (void *) (((unsigned char *)to)+64);
+ }
+
+#endif /* Have SSE */
+#ifdef HAVE_MMX2
+ /* since movntq is weakly-ordered, a "sfence"
+ * is needed to become ordered again. */
+ __asm__ __volatile__ ("sfence":::"memory");
+#endif
+#ifndef HAVE_SSE
+ /* enables to use FPU */
+ __asm__ __volatile__ (EMMS:::"memory");
+#endif
+ }
+ /*
+ * Now do the tail of the block
+ */
+ if(len) small_memcpy(to, from, len);
+ return retval;
+}
+
+
+#endif /* #if defined( HAVE_MMX2 ) || defined( HAVE_3DNOW ) || defined( HAVE_MMX ) */
+
+#endif
diff --git a/src/utils/fastmemcpy.h b/src/utils/fastmemcpy.h
new file mode 100644
index 0000000000..ce78d491ce
--- /dev/null
+++ b/src/utils/fastmemcpy.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if !defined(TARGET_WINDOWS) && !defined(__ppc__) && !defined(__powerpc__) && !defined(TARGET_ANDROID) && !defined(TARGET_DARWIN_IOS)
+void * fast_memcpy(void * to, const void * from, size_t len);
+//#define fast_memcpy memcpy
+#else
+#define fast_memcpy memcpy
+#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/utils/fft.cpp b/src/utils/fft.cpp
new file mode 100644
index 0000000000..8f0ddab696
--- /dev/null
+++ b/src/utils/fft.cpp
@@ -0,0 +1,176 @@
+/*
+ * COPYRIGHT
+ *
+ * XAnalyser, frequence spectrum analyser for X Window
+ * Copyright (C) 1998 Arvin Schnell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Contact addresses:
+ * arvin@informatik.uni-bremen.de
+ * Arvin Schnell, Am Heidberg 8, 28865 Lilienthal, Germany
+ *
+ */
+
+
+#include <math.h>
+#include <algorithm>
+#include "fft.h"
+
+#ifndef M_PI
+#define M_PI 3.1415926535897932384626433832795
+#endif
+
+#ifndef M_SQRT2
+#define M_SQRT2 1.4142135623730950488016887242097
+#endif
+
+// WARNING:
+// Whenever you call that method directly, make sure
+// that you pass in ptr - 1. Also remember this method
+// has a complex result. You most likely want to have a
+// realfft only instead
+void fft( float data[], int nn, int isign )
+{
+ int n = nn << 1;
+ int i, j, m;
+
+ /* bit reversal section */
+
+ j = 1;
+ for ( i = 1; i < n; i += 2 )
+ {
+ if ( j > i )
+ {
+ std::swap( data[j], data[i] );
+ std::swap( data[j + 1], data[i + 1] );
+ }
+ m = nn;
+ while ( m >= 2 && j > m )
+ {
+ j -= m;
+ m >>= 1;
+ }
+ j += m;
+ }
+
+ /* Daniel-Lanczos section */
+
+ long double theta, wr, wpr, wpi, wi, wtemp;
+ float tempr, tempi;
+ int mmax = 2;
+ while (n > mmax)
+ {
+ int istep = mmax << 1;
+ theta = isign * ( 2.0 * M_PI / mmax );
+ wtemp = sin(0.5 * theta);
+ wpr = -2.0 * wtemp * wtemp;
+ wpi = sin( theta );
+ wr = 1.0;
+ wi = 0.0;
+ for ( m = 1; m < mmax; m += 2 )
+ {
+ for ( i = m; i <= n; i += istep )
+ {
+ j = i + mmax;
+ if (j >= n || i >= n)
+ break;
+ tempr = (float) (wr * data[j] - wi * data[j + 1]);
+ tempi = (float) (wr * data[j + 1] + wi * data[j]);
+ data[j] = data[i] - tempr;
+ data[j + 1] = data[i + 1] - tempi;
+ data[i] += tempr;
+ data[i + 1] += tempi;
+ }
+ wr = (wtemp = wr) * wpr - wi * wpi + wr;
+ wi = wi * wpr + wtemp * wpi + wi;
+ }
+ mmax = istep;
+ }
+}
+
+// By JM - packed 2 channel real fft - returns the amplitudes of the fft array
+// data[] is a 2n size array, with interleaved channels, and the fft is returned in data[]
+// interleaving is preserved.
+void twochannelrfft(float data[], int n)
+{
+ float rep, rem, aip, aim;
+ int nn = n + n;
+ int nn1 = nn + 1;
+ // data is already packed - do the transform
+ fft( data - 1, n , + 1 );
+
+ // now repack the array as needed
+ data[0] = data[0] * data[0]; // only need the amplitude squared
+ data[1] = data[1] * data[1];
+ data[n] = data[n] * data[n];
+ data[n + 1] = data[n + 1] * data[n + 1];
+ // don't need the last component - this is the constant component?
+
+ for (int j = 2; j < n; j += 2)
+ {
+ rep = (float)(0.5 * (data[j] + data[nn - j]));
+ rem = (float)(0.5 * (data[j] - data[nn - j]));
+ aip = (float)(0.5 * (data[j + 1] + data[nn1 - j]));
+ aim = (float)(0.5 * (data[j + 1] - data[nn1 - j]));
+ /* this works out the complex FT
+ fft1[j]=rep;
+ fft1[j+1]=aim;
+ fft1[nn-j]=rep;
+ fft1[nn1-j]=-aim;
+ fft2[j]=aip;
+ fft2[j+1]=-rem;
+ fft2[nn-j]=aip;
+ fft2[nn1-j]=rem; */
+ // we just need the amplitudes
+ data[j] = (float)(2 * (sqr(rep) + sqr(aim))); // was sqrt'd
+ data[j + 1] = (float)(2 * (sqr(rem) + sqr(aip)));
+ }
+}
+
+void twochanwithwindow(float data[], int n)
+{
+ float rep, rem, aip, aim;
+ int nn = n + n;
+ int nn1 = nn + 1;
+ // window the data
+ float wn;
+ for (int i = 0; i < nn; i += 2)
+ {
+ wn = (float)(0.5 * (1 - cos(M_PI * i / n)));
+ data[i] *= wn;
+ data[i + 1] *= wn;
+ }
+ // data is already packed - do the transform
+ fft( data - 1, n , + 1 );
+
+ // now repack the array as needed
+ data[0] = data[0] * data[0]; // only need the amplitude squared
+ data[1] = data[1] * data[1];
+ data[n] = data[n] * data[n];
+ data[n + 1] = data[n + 1] * data[n + 1];
+ // don't need the last component - this is the constant component?
+
+ for (int j = 2; j < n; j += 2)
+ {
+ rep = data[j] + data[nn - j];
+ rem = data[j] - data[nn - j];
+ aip = data[j + 1] + data[nn1 - j];
+ aim = data[j + 1] - data[nn1 - j];
+ data[j] = (float)(0.5 * (sqr(rep) + sqr(aim)));
+ data[j + 1] = (float)(0.5 * (sqr(rem) + sqr(aip)));
+ }
+}
+
diff --git a/src/utils/fft.h b/src/utils/fft.h
new file mode 100644
index 0000000000..8a78aa77f8
--- /dev/null
+++ b/src/utils/fft.h
@@ -0,0 +1,52 @@
+#ifndef fft_hh
+#define fft_hh
+/*
+ * COPYRIGHT
+ *
+ * XAnalyser, frequence spectrum analyser for X Window
+ * Copyright (C) 1998 Arvin Schnell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Contact addresses:
+ * arvin@informatik.uni-bremen.de
+ * Arvin Schnell, Am Heidberg 8, 28865 Lilienthal, Germany
+ *
+ */
+static __inline long double sqr( long double arg )
+{
+ return arg * arg;
+}
+
+
+static __inline void swap( float &a, float &b )
+{
+ float t = a; a = b; b = t;
+}
+
+// (complex) fast fourier transformation
+// Based on four1() in Numerical Recipes in C, Page 507-508.
+
+// The input in data[1..2*nn] is replaced by its fft or inverse fft, depending
+// only on isign (+1 for fft, -1 for inverse fft). The number of complex numbers
+// n must be a power of 2 (which is not checked).
+
+void fft( float data[], int nn, int isign );
+
+void twochannelrfft(float data[], int n);
+void twochanwithwindow(float data[], int n); // test
+
+
+#endif
diff --git a/src/utils/fstrcmp.c b/src/utils/fstrcmp.c
new file mode 100644
index 0000000000..3b0cc233c1
--- /dev/null
+++ b/src/utils/fstrcmp.c
@@ -0,0 +1,114 @@
+/*
+ * Functions to make fuzzy comparisons between strings.
+ *
+ * Derived from PHP 5 similar_text() function
+ *
+ * The basic algorithm is described in:
+ * Oliver [1993] and the complexity is O(N**3) with N == length of longest string
+
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2010 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Rasmus Lerdorf <rasmus@php.net> |
+ | Stig Sther Bakken <ssb@php.net> |
+ | Zeev Suraski <zeev@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <string.h>
+
+static int similar_text(const char *str1, const char *str2, int len1, int len2)
+{
+ int sum;
+ int pos1 = 0, pos2 = 0;
+ int max = 0;
+
+ char *p, *q;
+ char *end1 = (char *)str1 + len1;
+ char *end2 = (char *)str2 + len2;
+ int l;
+
+ for (p = (char *)str1; p < end1; p++)
+ {
+ for (q = (char *)str2; q < end2; q++)
+ {
+ for (l = 0; (p + l < end1) && (q + l < end2) && (p[l] == q[l]); l++)
+ ;
+ if (l > max)
+ {
+ max = l;
+ pos1 = p - str1;
+ pos2 = q - str2;
+ }
+ }
+ }
+ if ((sum = max))
+ {
+ if (pos1 && pos2)
+ sum += similar_text(str1, str2, pos1, pos2);
+
+ if ((pos1 + max < len1) && (pos2 + max < len2))
+ sum += similar_text(str1 + pos1 + max, str2 + pos2 + max,
+ len1 - pos1 - max, len2 - pos2 - max);
+ }
+
+ return sum;
+}
+
+/* NAME
+ fstrcmp - fuzzy string compare
+
+ SYNOPSIS
+ double fstrcmp(const char *, const char *, double);
+
+ DESCRIPTION
+ The fstrcmp function may be used to compare two string for
+ similarity. It is very useful in reducing "cascade" or
+ "secondary" errors in compilers or other situations where
+ symbol tables occur.
+
+ RETURNS
+ double; 0 if the strings are entirly dissimilar, 1 if the
+ strings are identical, and a number in between if they are
+ similar. */
+
+double
+fstrcmp (const char *string1, const char *string2, double minimum)
+{
+ int len1, len2, score;
+
+ len1 = (int)strlen(string1);
+ len2 = (int)strlen(string2);
+
+ /* short-circuit obvious comparisons */
+ if (len1 == 0 && len2 == 0)
+ return 1.0;
+ if (len1 == 0 || len2 == 0)
+ return 0.0;
+
+ score = similar_text(string1, string2, len1, len2);
+ /* The result is
+ ((number of chars in common) / (average length of the strings)).
+ This is admittedly biased towards finding that the strings are
+ similar, however it does produce meaningful results. */
+ return ((double)score * 2.0 / (len1 + len2));
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/src/utils/fstrcmp.h b/src/utils/fstrcmp.h
new file mode 100644
index 0000000000..d848085fd7
--- /dev/null
+++ b/src/utils/fstrcmp.h
@@ -0,0 +1,36 @@
+#ifndef _FSTRCMP_H
+#define _FSTRCMP_H
+
+ /* GNU gettext - internationalization aids
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ This file was written by Peter Miller <pmiller@agso.gov.au>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with XBMC; see the file COPYING. If not, see
+<http://www.gnu.org/licenses/>.
+*/
+#define PARAMS(proto) proto
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+double fstrcmp (const char *__s1, const char *__s2, double __minimum);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif
diff --git a/src/utils/log.cpp b/src/utils/log.cpp
new file mode 100644
index 0000000000..3443f1293d
--- /dev/null
+++ b/src/utils/log.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2005-2014 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "log.h"
+#include "system.h"
+#include "threads/SingleLock.h"
+#include "threads/Thread.h"
+#include "utils/StringUtils.h"
+#include "CompileInfo.h"
+
+static const char* const levelNames[] =
+{"DEBUG", "INFO", "NOTICE", "WARNING", "ERROR", "SEVERE", "FATAL", "NONE"};
+
+// add 1 to level number to get index of name
+static const char* const logLevelNames[] =
+{ "LOG_LEVEL_NONE" /*-1*/, "LOG_LEVEL_NORMAL" /*0*/, "LOG_LEVEL_DEBUG" /*1*/, "LOG_LEVEL_DEBUG_FREEMEM" /*2*/ };
+
+// s_globals is used as static global with CLog global variables
+#define s_globals XBMC_GLOBAL_USE(CLog).m_globalInstance
+
+CLog::CLog()
+{}
+
+CLog::~CLog()
+{}
+
+void CLog::Close()
+{
+ CSingleLock waitLock(s_globals.critSec);
+ s_globals.m_platform.CloseLogFile();
+ s_globals.m_repeatLine.clear();
+}
+
+void CLog::Log(int loglevel, const char *format, ...)
+{
+ if (IsLogLevelLogged(loglevel))
+ {
+ va_list va;
+ va_start(va, format);
+ LogString(loglevel, StringUtils::FormatV(format, va));
+ va_end(va);
+ }
+}
+
+void CLog::LogFunction(int loglevel, const char* functionName, const char* format, ...)
+{
+ if (IsLogLevelLogged(loglevel))
+ {
+ std::string fNameStr;
+ if (functionName && functionName[0])
+ fNameStr.assign(functionName).append(": ");
+ va_list va;
+ va_start(va, format);
+ LogString(loglevel, fNameStr + StringUtils::FormatV(format, va));
+ va_end(va);
+ }
+}
+
+void CLog::LogString(int logLevel, const std::string& logString)
+{
+ CSingleLock waitLock(s_globals.critSec);
+ std::string strData(logString);
+ StringUtils::TrimRight(strData);
+ if (!strData.empty())
+ {
+ if (s_globals.m_repeatLogLevel == logLevel && s_globals.m_repeatLine == strData)
+ {
+ s_globals.m_repeatCount++;
+ return;
+ }
+ else if (s_globals.m_repeatCount)
+ {
+ std::string strData2 = StringUtils::Format("Previous line repeats %d times.",
+ s_globals.m_repeatCount);
+ PrintDebugString(strData2);
+ WriteLogString(s_globals.m_repeatLogLevel, strData2);
+ s_globals.m_repeatCount = 0;
+ }
+
+ s_globals.m_repeatLine = strData;
+ s_globals.m_repeatLogLevel = logLevel;
+
+ PrintDebugString(strData);
+
+ WriteLogString(logLevel, strData);
+ }
+}
+
+bool CLog::Init(const std::string& path)
+{
+ CSingleLock waitLock(s_globals.critSec);
+
+ // the log folder location is initialized in the CAdvancedSettings
+ // constructor and changed in CApplication::Create()
+
+ std::string appName = CCompileInfo::GetAppName();
+ StringUtils::ToLower(appName);
+ return s_globals.m_platform.OpenLogFile(path + appName + ".log", path + appName + ".old.log");
+}
+
+void CLog::MemDump(char *pData, int length)
+{
+ Log(LOGDEBUG, "MEM_DUMP: Dumping from %p", pData);
+ for (int i = 0; i < length; i+=16)
+ {
+ std::string strLine = StringUtils::Format("MEM_DUMP: %04x ", i);
+ char *alpha = pData;
+ for (int k=0; k < 4 && i + 4*k < length; k++)
+ {
+ for (int j=0; j < 4 && i + 4*k + j < length; j++)
+ {
+ std::string strFormat = StringUtils::Format(" %02x", (unsigned char)*pData++);
+ strLine += strFormat;
+ }
+ strLine += " ";
+ }
+ // pad with spaces
+ while (strLine.size() < 13*4 + 16)
+ strLine += " ";
+ for (int j=0; j < 16 && i + j < length; j++)
+ {
+ if (*alpha > 31)
+ strLine += *alpha;
+ else
+ strLine += '.';
+ alpha++;
+ }
+ Log(LOGDEBUG, "%s", strLine.c_str());
+ }
+}
+
+void CLog::SetLogLevel(int level)
+{
+ CSingleLock waitLock(s_globals.critSec);
+ if (level >= LOG_LEVEL_NONE && level <= LOG_LEVEL_MAX)
+ {
+ s_globals.m_logLevel = level;
+ CLog::Log(LOGNOTICE, "Log level changed to \"%s\"", logLevelNames[s_globals.m_logLevel + 1]);
+ }
+ else
+ CLog::Log(LOGERROR, "%s: Invalid log level requested: %d", __FUNCTION__, level);
+}
+
+int CLog::GetLogLevel()
+{
+ return s_globals.m_logLevel;
+}
+
+void CLog::SetExtraLogLevels(int level)
+{
+ CSingleLock waitLock(s_globals.critSec);
+ s_globals.m_extraLogLevels = level;
+}
+
+bool CLog::IsLogLevelLogged(int loglevel)
+{
+ const int extras = (loglevel & ~LOGMASK);
+ if (extras != 0 && (s_globals.m_extraLogLevels & extras) == 0)
+ return false;
+
+#if defined(_DEBUG) || defined(PROFILE)
+ return true;
+#else
+ if (s_globals.m_logLevel >= LOG_LEVEL_DEBUG)
+ return true;
+ if (s_globals.m_logLevel <= LOG_LEVEL_NONE)
+ return false;
+
+ // "m_logLevel" is "LOG_LEVEL_NORMAL"
+ return (loglevel & LOGMASK) >= LOGNOTICE;
+#endif
+}
+
+
+void CLog::PrintDebugString(const std::string& line)
+{
+#if defined(_DEBUG) || defined(PROFILE)
+ s_globals.m_platform.PrintDebugString(line);
+#endif // defined(_DEBUG) || defined(PROFILE)
+}
+
+bool CLog::WriteLogString(int logLevel, const std::string& logString)
+{
+ static const char* prefixFormat = "%02.2d:%02.2d:%02.2d T:%" PRIu64" %7s: ";
+
+ std::string strData(logString);
+ /* fixup newline alignment, number of spaces should equal prefix length */
+ StringUtils::Replace(strData, "\n", "\n ");
+
+ int hour, minute, second;
+ s_globals.m_platform.GetCurrentLocalTime(hour, minute, second);
+
+ strData = StringUtils::Format(prefixFormat,
+ hour,
+ minute,
+ second,
+ (uint64_t)CThread::GetCurrentThreadId(),
+ levelNames[logLevel]) + strData;
+
+ return s_globals.m_platform.WriteStringToLog(strData);
+}
diff --git a/src/utils/log.h b/src/utils/log.h
new file mode 100644
index 0000000000..0e9d51b9ac
--- /dev/null
+++ b/src/utils/log.h
@@ -0,0 +1,86 @@
+#pragma once
+
+/*
+ * Copyright (C) 2005-2014 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string>
+
+#if defined(TARGET_POSIX)
+#include "posix/PosixInterfaceForCLog.h"
+typedef class CPosixInterfaceForCLog PlatformInterfaceForCLog;
+#elif defined(TARGET_WINDOWS)
+#include "win32/Win32InterfaceForCLog.h"
+typedef class CWin32InterfaceForCLog PlatformInterfaceForCLog;
+#endif
+
+#include "commons/ilog.h"
+#include "threads/CriticalSection.h"
+#include "utils/GlobalsHandling.h"
+
+#include "utils/params_check_macros.h"
+
+class CLog
+{
+public:
+ CLog();
+ ~CLog(void);
+ static void Close();
+ static void Log(int loglevel, PRINTF_FORMAT_STRING const char *format, ...) PARAM2_PRINTF_FORMAT;
+ static void LogFunction(int loglevel, IN_OPT_STRING const char* functionName, PRINTF_FORMAT_STRING const char* format, ...) PARAM3_PRINTF_FORMAT;
+#define LogF(loglevel,format,...) LogFunction((loglevel),__FUNCTION__,(format),##__VA_ARGS__)
+ static void MemDump(char *pData, int length);
+ static bool Init(const std::string& path);
+ static void PrintDebugString(const std::string& line); // universal interface for printing debug strings
+ static void SetLogLevel(int level);
+ static int GetLogLevel();
+ static void SetExtraLogLevels(int level);
+ static bool IsLogLevelLogged(int loglevel);
+
+protected:
+ class CLogGlobals
+ {
+ public:
+ CLogGlobals(void) : m_repeatCount(0), m_repeatLogLevel(-1), m_logLevel(LOG_LEVEL_DEBUG), m_extraLogLevels(0) {}
+ ~CLogGlobals() {}
+ PlatformInterfaceForCLog m_platform;
+ int m_repeatCount;
+ int m_repeatLogLevel;
+ std::string m_repeatLine;
+ int m_logLevel;
+ int m_extraLogLevels;
+ CCriticalSection critSec;
+ };
+ class CLogGlobals m_globalInstance; // used as static global variable
+ static void LogString(int logLevel, const std::string& logString);
+ static bool WriteLogString(int logLevel, const std::string& logString);
+};
+
+
+namespace XbmcUtils
+{
+ class LogImplementation : public XbmcCommons::ILogger
+ {
+ public:
+ virtual ~LogImplementation() {}
+ inline virtual void log(int logLevel, IN_STRING const char* message) { CLog::Log(logLevel, "%s", message); }
+ };
+}
+
+XBMC_GLOBAL_REF(CLog, g_log);
diff --git a/src/utils/md5.cpp b/src/utils/md5.cpp
new file mode 100644
index 0000000000..ce9ff6b703
--- /dev/null
+++ b/src/utils/md5.cpp
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2009-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "md5.h"
+#include "utils/StringUtils.h"
+
+typedef unsigned char md5byte;
+
+static void MD5Init(struct MD5Context *context);
+static void MD5Update(struct MD5Context *context, md5byte const *buf, unsigned len);
+static void MD5Final(unsigned char digest[16], struct MD5Context *context);
+static void MD5Transform(uint32_t buf[4], uint32_t const in[16]);
+
+
+XBMC::XBMC_MD5::XBMC_MD5(void)
+{
+ MD5Init(&m_ctx);
+}
+
+XBMC::XBMC_MD5::~XBMC_MD5(void)
+{}
+
+void XBMC::XBMC_MD5::append(const void *inBuf, size_t inLen)
+{
+ MD5Update(&m_ctx, (md5byte*)inBuf, inLen);
+}
+
+void XBMC::XBMC_MD5::append(const std::string& str)
+{
+ append((unsigned char*) str.c_str(), (unsigned int) str.length());
+}
+
+void XBMC::XBMC_MD5::getDigest(unsigned char digest[16])
+{
+ MD5Final(digest, &m_ctx);
+}
+
+std::string XBMC::XBMC_MD5::getDigest()
+{
+ unsigned char szBuf[16] = {'\0'};
+ getDigest(szBuf);
+ return StringUtils::Format("%02X%02X%02X%02X%02X%02X%02X%02X"\
+ "%02X%02X%02X%02X%02X%02X%02X%02X",
+ szBuf[0], szBuf[1], szBuf[2],
+ szBuf[3], szBuf[4], szBuf[5], szBuf[6], szBuf[7], szBuf[8],
+ szBuf[9], szBuf[10], szBuf[11], szBuf[12], szBuf[13], szBuf[14],
+ szBuf[15]);
+}
+
+std::string XBMC::XBMC_MD5::GetMD5(const std::string &text)
+{
+ if (text.empty())
+ return "";
+ XBMC_MD5 state;
+ state.append(text);
+ return state.getDigest();
+}
+
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ *
+ * Changed so as no longer to depend on Colin Plumb's `usual.h' header
+ * definitions; now uses stuff from dpkg's config.h.
+ * - Ian Jackson <ian@chiark.greenend.org.uk>.
+ * Still in the public domain.
+ */
+
+#include "md5.h"
+
+#include <sys/types.h> /* for stupid systems */
+#include <string.h> /* for memcpy() */
+#if defined(HAVE_CONFIG_H) && !defined(TARGET_WINDOWS)
+#include "../config.h"
+#endif
+
+#ifdef WORDS_BIGENDIAN
+void
+byteSwap(uint32_t *buf, unsigned words)
+{
+ md5byte *p = (md5byte *)buf;
+
+ do {
+ *buf++ = (uint32_t)((unsigned)p[3] << 8 | p[2]) << 16 |
+ ((unsigned)p[1] << 8 | p[0]);
+ p += 4;
+ } while (--words);
+}
+#else
+#define byteSwap(buf,words)
+#endif
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+static void
+MD5Init(struct MD5Context *ctx)
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bytes[0] = 0;
+ ctx->bytes[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+static void
+MD5Update(struct MD5Context *ctx, md5byte const *buf, unsigned len)
+{
+ uint32_t t;
+
+ /* Update byte count */
+
+ t = ctx->bytes[0];
+ if ((ctx->bytes[0] = t + len) < t)
+ ctx->bytes[1]++; /* Carry from low to high */
+
+ t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */
+ if (t > len) {
+ memcpy((md5byte *)ctx->in + 64 - t, buf, len);
+ return;
+ }
+ /* First chunk is an odd size */
+ memcpy((md5byte *)ctx->in + 64 - t, buf, t);
+ byteSwap(ctx->in, 16);
+ MD5Transform(ctx->buf, ctx->in);
+ buf += t;
+ len -= t;
+
+ /* Process data in 64-byte chunks */
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ byteSwap(ctx->in, 16);
+ MD5Transform(ctx->buf, ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+ memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+static void
+MD5Final(md5byte digest[16], struct MD5Context *ctx)
+{
+ int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */
+ md5byte *p = (md5byte *)ctx->in + count;
+
+ /* Set the first char of padding to 0x80. There is always room. */
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 56 bytes (-8..55) */
+ count = 56 - 1 - count;
+
+ if (count < 0) { /* Padding forces an extra block */
+ memset(p, 0, count + 8);
+ byteSwap(ctx->in, 16);
+ MD5Transform(ctx->buf, ctx->in);
+ p = (md5byte *)ctx->in;
+ count = 56;
+ }
+ memset(p, 0, count);
+ byteSwap(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ctx->in[14] = ctx->bytes[0] << 3;
+ ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29;
+ MD5Transform(ctx->buf, ctx->in);
+
+ byteSwap(ctx->buf, 4);
+ memcpy(digest, ctx->buf, 16);
+ memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f,w,x,y,z,in,s) \
+ (w += f(x,y,z) + in, w = (w<<s | w>>(32-s)) + x)
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void
+MD5Transform(uint32_t buf[4], uint32_t const in[16])
+{
+ register uint32_t a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+#endif
diff --git a/src/utils/md5.h b/src/utils/md5.h
new file mode 100644
index 0000000000..d0c336a4cd
--- /dev/null
+++ b/src/utils/md5.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2009-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _MD5_H_
+#define _MD5_H_
+
+#include <string>
+#include <stdint.h>
+
+struct MD5Context {
+ uint32_t buf[4];
+ uint32_t bytes[2];
+ uint32_t in[16];
+};
+
+namespace XBMC
+{
+ class XBMC_MD5
+ {
+ public:
+ XBMC_MD5(void);
+ ~XBMC_MD5(void);
+ void append(const void *inBuf, size_t inLen);
+ void append(const std::string& str);
+ void getDigest(unsigned char digest[16]);
+ std::string getDigest();
+
+ /*! \brief Get the MD5 digest of the given text
+ \param text text to compute the MD5 for
+ \return MD5 digest
+ */
+ static std::string GetMD5(const std::string &text);
+private:
+ MD5Context m_ctx;
+ };
+}
+
+#endif
diff --git a/src/utils/params_check_macros.h b/src/utils/params_check_macros.h
new file mode 100644
index 0000000000..e145fa214c
--- /dev/null
+++ b/src/utils/params_check_macros.h
@@ -0,0 +1,68 @@
+#pragma once
+/*
+* Copyright (C) 2014 Team XBMC
+* http://xbmc.org
+*
+* This Program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2, or (at your option)
+* any later version.
+*
+* This Program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with XBMC; see the file COPYING. If not, see
+* <http://www.gnu.org/licenses/>.
+*
+*/
+
+// macros for gcc, clang & others
+#ifndef PARAM1_PRINTF_FORMAT
+#ifdef __GNUC__
+// for use in functions that take printf format string as first parameter and additional printf parameters as second parameter
+// for example: int myprintf(const char* format, ...) PARAM1_PRINTF_FORMAT;
+#define PARAM1_PRINTF_FORMAT __attribute__((format(printf,1,2)))
+
+// for use in functions that take printf format string as second parameter and additional printf parameters as third parameter
+// for example: bool log_string(int logLevel, const char* format, ...) PARAM2_PRINTF_FORMAT;
+// note: all non-static class member functions take pointer to class object as hidden first parameter
+// another example: class A { int myprintf(const char* format, ...) PARAM2_PRINTF_FORMAT; };
+#define PARAM2_PRINTF_FORMAT __attribute__((format(printf,2,3)))
+
+// for use in functions that take printf format string as third parameter and additional printf parameters as fourth parameter
+// note: all non-static class member functions take pointer to class object as hidden first parameter
+// for example: class A { bool log_string(int logLevel, const char* format, ...) PARAM3_PRINTF_FORMAT; };
+#define PARAM3_PRINTF_FORMAT __attribute__((format(printf,3,4)))
+#else // ! __GNUC__
+#define PARAM1_PRINTF_FORMAT
+#define PARAM2_PRINTF_FORMAT
+#define PARAM3_PRINTF_FORMAT
+#endif // ! __GNUC__
+#endif // PARAM1_PRINTF_FORMAT
+
+// macros for VC
+// VC check parameters only when "Code Analysis" is called
+#ifndef PRINTF_FORMAT_STRING
+#ifdef _MSC_VER
+#include <sal.h>
+
+// for use in any function that take printf format string and parameters
+// for example: bool log_string(int logLevel, PRINTF_FORMAT_STRING const char* format, ...);
+#define PRINTF_FORMAT_STRING _In_z_ _Printf_format_string_
+
+// specify that parameter must be zero-terminated string
+// for example: void SetName(IN_STRING const char* newName);
+#define IN_STRING _In_z_
+
+// specify that parameter must be zero-terminated string or NULL
+// for example: bool SetAdditionalName(IN_OPT_STRING const char* addName);
+#define IN_OPT_STRING _In_opt_z_
+#else // ! _MSC_VER
+#define PRINTF_FORMAT_STRING
+#define IN_STRING
+#define IN_OPT_STRING
+#endif // ! _MSC_VER
+#endif // PRINTF_FORMAT_STRING
diff --git a/src/utils/posix/PosixInterfaceForCLog.cpp b/src/utils/posix/PosixInterfaceForCLog.cpp
new file mode 100644
index 0000000000..3f80594f06
--- /dev/null
+++ b/src/utils/posix/PosixInterfaceForCLog.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2014 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "PosixInterfaceForCLog.h"
+#include <stdio.h>
+#include <time.h>
+
+#if defined(TARGET_DARWIN)
+#include "DarwinUtils.h"
+#elif defined(TARGET_ANDROID)
+#include "android/activity/XBMCApp.h"
+#endif // TARGET_ANDROID
+
+struct FILEWRAP : public FILE
+{};
+
+
+CPosixInterfaceForCLog::CPosixInterfaceForCLog() :
+ m_file(NULL)
+{ }
+
+CPosixInterfaceForCLog::~CPosixInterfaceForCLog()
+{
+ if (m_file)
+ fclose(m_file);
+}
+
+bool CPosixInterfaceForCLog::OpenLogFile(const std::string &logFilename, const std::string &backupOldLogToFilename)
+{
+ if (m_file)
+ return false; // file was already opened
+
+ (void)remove(backupOldLogToFilename.c_str()); // if it's failed, try to continue
+ (void)rename(logFilename.c_str(), backupOldLogToFilename.c_str()); // if it's failed, try to continue
+
+ m_file = (FILEWRAP*)fopen(logFilename.c_str(), "wb");
+ if (!m_file)
+ return false; // error, can't open log file
+
+ static const unsigned char BOM[3] = { 0xEF, 0xBB, 0xBF };
+ (void)fwrite(BOM, sizeof(BOM), 1, m_file); // write BOM, ignore possible errors
+
+ return true;
+}
+
+void CPosixInterfaceForCLog::CloseLogFile()
+{
+ if (m_file)
+ {
+ fclose(m_file);
+ m_file = NULL;
+ }
+}
+
+bool CPosixInterfaceForCLog::WriteStringToLog(const std::string &logString)
+{
+ if (!m_file)
+ return false;
+
+ const bool ret = (fwrite(logString.data(), logString.size(), 1, m_file) == 1) &&
+ (fwrite("\n", 1, 1, m_file) == 1);
+ (void)fflush(m_file);
+
+ return ret;
+}
+
+void CPosixInterfaceForCLog::PrintDebugString(const std::string &debugString)
+{
+#ifdef _DEBUG
+#if defined(TARGET_DARWIN)
+ CDarwinUtils::PrintDebugString(debugString);
+#elif defined(TARGET_ANDROID)
+ //print to adb
+ CXBMCApp::android_printf("Debug Print: %s", debugString.c_str());
+#endif // TARGET_ANDROID
+#endif // _DEBUG
+}
+
+void CPosixInterfaceForCLog::GetCurrentLocalTime(int &hour, int &minute, int &second)
+{
+ time_t curTime;
+ struct tm localTime;
+ if (time(&curTime) != -1 && localtime_r(&curTime, &localTime) != NULL)
+ {
+ hour = localTime.tm_hour;
+ minute = localTime.tm_min;
+ second = localTime.tm_sec;
+ }
+ else
+ hour = minute = second = 0;
+}
diff --git a/src/utils/posix/PosixInterfaceForCLog.h b/src/utils/posix/PosixInterfaceForCLog.h
new file mode 100644
index 0000000000..bb534427c1
--- /dev/null
+++ b/src/utils/posix/PosixInterfaceForCLog.h
@@ -0,0 +1,38 @@
+#pragma once
+/*
+ * Copyright (C) 2014 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string>
+
+struct FILEWRAP; // forward declaration, wrapper for FILE
+
+class CPosixInterfaceForCLog
+{
+public:
+ CPosixInterfaceForCLog();
+ ~CPosixInterfaceForCLog();
+ bool OpenLogFile(const std::string& logFilename, const std::string& backupOldLogToFilename);
+ void CloseLogFile(void);
+ bool WriteStringToLog(const std::string& logString);
+ void PrintDebugString(const std::string& debugString);
+ static void GetCurrentLocalTime(int& hour, int& minute, int& second);
+private:
+ FILEWRAP* m_file;
+};
diff --git a/src/utils/test/CXBMCTinyXML-test.xml b/src/utils/test/CXBMCTinyXML-test.xml
new file mode 100644
index 0000000000..9444dc8ace
--- /dev/null
+++ b/src/utils/test/CXBMCTinyXML-test.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<details>
+ <url function="ParseTMDBRating" cache="tmdb-en-12244.json">
+ http://api.themoviedb.org/3/movie/12244?api_key=57983e31fb435df4df77afb854740ea9&language=en&#x3f;&#x003F;&#0063;
+ </url>
+</details>
diff --git a/src/utils/test/Makefile b/src/utils/test/Makefile
new file mode 100644
index 0000000000..2c38acd74f
--- /dev/null
+++ b/src/utils/test/Makefile
@@ -0,0 +1,60 @@
+SRCS= \
+ TestAlarmClock.cpp \
+ TestAliasShortcutUtils.cpp \
+ TestArchive.cpp \
+ TestAsyncFileCopy.cpp \
+ TestBase64.cpp \
+ TestBitstreamStats.cpp \
+ TestCharsetConverter.cpp \
+ TestCPUInfo.cpp \
+ TestCrc32.cpp \
+ TestCryptThreading.cpp \
+ TestDatabaseUtils.cpp \
+ TestEndianSwap.cpp \
+ Testfastmemcpy.cpp \
+ Testfft.cpp \
+ TestFileOperationJob.cpp \
+ TestFileUtils.cpp \
+ Testfstrcmp.cpp \
+ TestGlobalsHandling.cpp \
+ TestHTMLTable.cpp \
+ TestHTMLUtil.cpp \
+ TestHttpHeader.cpp \
+ TestHttpParser.cpp \
+ TestHttpResponse.cpp \
+ TestJobManager.cpp \
+ TestJSONVariantParser.cpp \
+ TestJSONVariantWriter.cpp \
+ TestLabelFormatter.cpp \
+ TestLangCodeExpander.cpp \
+ Testlog.cpp \
+ TestMathUtils.cpp \
+ Testmd5.cpp \
+ TestMime.cpp \
+ TestPerformanceSample.cpp \
+ TestPOUtils.cpp \
+ TestRegExp.cpp \
+ TestRingBuffer.cpp \
+ TestScraperParser.cpp \
+ TestScraperUrl.cpp \
+ TestSortUtils.cpp \
+ TestStdString.cpp \
+ TestStopwatch.cpp \
+ TestStreamDetails.cpp \
+ TestStreamUtils.cpp \
+ TestStringUtils.cpp \
+ TestSystemInfo.cpp \
+ TestTimeSmoother.cpp \
+ TestTimeUtils.cpp \
+ TestURIUtils.cpp \
+ TestUrlOptions.cpp \
+ TestVariant.cpp \
+ TestXBMCTinyXML.cpp \
+ TestXMLUtils.cpp
+
+LIB=utilsTest.a
+
+INCLUDES += -I../../../lib/gtest/include
+
+include ../../../Makefile.include
+-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
diff --git a/src/utils/test/TestAlarmClock.cpp b/src/utils/test/TestAlarmClock.cpp
new file mode 100644
index 0000000000..5cd13799d2
--- /dev/null
+++ b/src/utils/test/TestAlarmClock.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/AlarmClock.h"
+
+#include "gtest/gtest.h"
+
+TEST(TestAlarmClock, General)
+{
+ CAlarmClock a;
+ EXPECT_FALSE(a.IsRunning());
+ EXPECT_FALSE(a.HasAlarm("test"));
+ a.Start("test", 100.f, "test");
+ EXPECT_TRUE(a.IsRunning());
+ EXPECT_TRUE(a.HasAlarm("test"));
+ EXPECT_FALSE(a.HasAlarm("test2"));
+ EXPECT_NE(0.f, a.GetRemaining("test"));
+ EXPECT_EQ(0.f, a.GetRemaining("test2"));
+ a.Stop("test");
+}
diff --git a/src/utils/test/TestAliasShortcutUtils.cpp b/src/utils/test/TestAliasShortcutUtils.cpp
new file mode 100644
index 0000000000..15dd76d234
--- /dev/null
+++ b/src/utils/test/TestAliasShortcutUtils.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/AliasShortcutUtils.h"
+
+#include "gtest/gtest.h"
+
+TEST(TestAliasShortcutUtils, IsAliasShortcut)
+{
+ std::string a;
+#if defined(TARGET_DARWIN_OSX)
+ /* TODO: Write test case for OSX */
+#else
+ EXPECT_FALSE(IsAliasShortcut(a));
+#endif
+}
+
+TEST(TestAliasShortcutUtils, TranslateAliasShortcut)
+{
+ std::string a;
+ TranslateAliasShortcut(a);
+#if defined(TARGET_DARWIN_OSX)
+ /* TODO: Write test case for OSX */
+#else
+ EXPECT_STREQ("", a.c_str());
+#endif
+}
diff --git a/src/utils/test/TestArchive.cpp b/src/utils/test/TestArchive.cpp
new file mode 100644
index 0000000000..9d082dcd86
--- /dev/null
+++ b/src/utils/test/TestArchive.cpp
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/Archive.h"
+#include "utils/Variant.h"
+#include "filesystem/File.h"
+#include "utils/StdString.h"
+
+#include "test/TestUtils.h"
+
+#include "gtest/gtest.h"
+
+class TestArchive : public testing::Test
+{
+protected:
+ TestArchive()
+ {
+ file = XBMC_CREATETEMPFILE(".ar");
+ }
+ ~TestArchive()
+ {
+ EXPECT_TRUE(XBMC_DELETETEMPFILE(file));
+ }
+ XFILE::CFile *file;
+};
+
+TEST_F(TestArchive, IsStoring)
+{
+ ASSERT_TRUE(file);
+ CArchive arstore(file, CArchive::store);
+ EXPECT_TRUE(arstore.IsStoring());
+ EXPECT_FALSE(arstore.IsLoading());
+ arstore.Close();
+}
+
+TEST_F(TestArchive, IsLoading)
+{
+ ASSERT_TRUE(file);
+ CArchive arload(file, CArchive::load);
+ EXPECT_TRUE(arload.IsLoading());
+ EXPECT_FALSE(arload.IsStoring());
+ arload.Close();
+}
+
+TEST_F(TestArchive, FloatArchive)
+{
+ ASSERT_TRUE(file);
+ float float_ref = 1, float_var = 0;
+
+ CArchive arstore(file, CArchive::store);
+ arstore << float_ref;
+ arstore.Close();
+
+ ASSERT_TRUE((file->Seek(0, SEEK_SET) == 0));
+ CArchive arload(file, CArchive::load);
+ arload >> float_var;
+ arload.Close();
+
+ EXPECT_EQ(float_ref, float_var);
+}
+
+TEST_F(TestArchive, DoubleArchive)
+{
+ ASSERT_TRUE(file);
+ double double_ref = 2, double_var = 0;
+
+ CArchive arstore(file, CArchive::store);
+ arstore << double_ref;
+ arstore.Close();
+
+ ASSERT_TRUE((file->Seek(0, SEEK_SET) == 0));
+ CArchive arload(file, CArchive::load);
+ arload >> double_var;
+ arload.Close();
+
+ EXPECT_EQ(double_ref, double_var);
+}
+
+TEST_F(TestArchive, IntegerArchive)
+{
+ ASSERT_TRUE(file);
+ int int_ref = 3, int_var = 0;
+
+ CArchive arstore(file, CArchive::store);
+ arstore << int_ref;
+ arstore.Close();
+
+ ASSERT_TRUE((file->Seek(0, SEEK_SET) == 0));
+ CArchive arload(file, CArchive::load);
+ arload >> int_var;
+ arload.Close();
+
+ EXPECT_EQ(int_ref, int_var);
+}
+
+TEST_F(TestArchive, UnsignedIntegerArchive)
+{
+ ASSERT_TRUE(file);
+ unsigned int unsigned_int_ref = 4, unsigned_int_var = 0;
+
+ CArchive arstore(file, CArchive::store);
+ arstore << unsigned_int_ref;
+ arstore.Close();
+
+ ASSERT_TRUE((file->Seek(0, SEEK_SET) == 0));
+ CArchive arload(file, CArchive::load);
+ arload >> unsigned_int_var;
+ arload.Close();
+
+ EXPECT_EQ(unsigned_int_ref, unsigned_int_var);
+}
+
+TEST_F(TestArchive, Int64tArchive)
+{
+ ASSERT_TRUE(file);
+ int64_t int64_t_ref = 5, int64_t_var = 0;
+
+ CArchive arstore(file, CArchive::store);
+ arstore << int64_t_ref;
+ arstore.Close();
+
+ ASSERT_TRUE((file->Seek(0, SEEK_SET) == 0));
+ CArchive arload(file, CArchive::load);
+ arload >> int64_t_var;
+ arload.Close();
+
+ EXPECT_EQ(int64_t_ref, int64_t_var);
+}
+
+TEST_F(TestArchive, UInt64tArchive)
+{
+ ASSERT_TRUE(file);
+ uint64_t uint64_t_ref = 6, uint64_t_var = 0;
+
+ CArchive arstore(file, CArchive::store);
+ arstore << uint64_t_ref;
+ arstore.Close();
+
+ ASSERT_TRUE((file->Seek(0, SEEK_SET) == 0));
+ CArchive arload(file, CArchive::load);
+ arload >> uint64_t_var;
+ arload.Close();
+
+ EXPECT_EQ(uint64_t_ref, uint64_t_var);
+}
+
+TEST_F(TestArchive, BoolArchive)
+{
+ ASSERT_TRUE(file);
+ bool bool_ref = true, bool_var = false;
+
+ CArchive arstore(file, CArchive::store);
+ arstore << bool_ref;
+ arstore.Close();
+
+ ASSERT_TRUE((file->Seek(0, SEEK_SET) == 0));
+ CArchive arload(file, CArchive::load);
+ arload >> bool_var;
+ arload.Close();
+
+ EXPECT_EQ(bool_ref, bool_var);
+}
+
+TEST_F(TestArchive, CharArchive)
+{
+ ASSERT_TRUE(file);
+ char char_ref = 'A', char_var = '\0';
+
+ CArchive arstore(file, CArchive::store);
+ arstore << char_ref;
+ arstore.Close();
+
+ ASSERT_TRUE((file->Seek(0, SEEK_SET) == 0));
+ CArchive arload(file, CArchive::load);
+ arload >> char_var;
+ arload.Close();
+
+ EXPECT_EQ(char_ref, char_var);
+}
+
+TEST_F(TestArchive, CStdStringArchive)
+{
+ ASSERT_TRUE(file);
+ CStdStringW CStdStringW_ref = L"test CStdStringW", CStdStringW_var = L"";
+
+ CArchive arstore(file, CArchive::store);
+ arstore << CStdStringW_ref;
+ arstore.Close();
+
+ ASSERT_TRUE((file->Seek(0, SEEK_SET) == 0));
+ CArchive arload(file, CArchive::load);
+ arload >> CStdStringW_var;
+ arload.Close();
+
+ EXPECT_STREQ(CStdStringW_ref.c_str(), CStdStringW_var.c_str());
+}
+
+TEST_F(TestArchive, CStdStringWArchive)
+{
+ ASSERT_TRUE(file);
+ CStdString CStdString_ref = "test CStdString", CStdString_var = "";
+
+ CArchive arstore(file, CArchive::store);
+ arstore << CStdString_ref;
+ arstore.Close();
+
+ ASSERT_TRUE((file->Seek(0, SEEK_SET) == 0));
+ CArchive arload(file, CArchive::load);
+ arload >> CStdString_var;
+ arload.Close();
+
+ EXPECT_STREQ(CStdString_ref.c_str(), CStdString_var.c_str());
+}
+
+TEST_F(TestArchive, SYSTEMTIMEArchive)
+{
+ ASSERT_TRUE(file);
+ SYSTEMTIME SYSTEMTIME_ref = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ SYSTEMTIME SYSTEMTIME_var = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ CArchive arstore(file, CArchive::store);
+ arstore << SYSTEMTIME_ref;
+ arstore.Close();
+
+ ASSERT_TRUE((file->Seek(0, SEEK_SET) == 0));
+ CArchive arload(file, CArchive::load);
+ arload >> SYSTEMTIME_var;
+ arload.Close();
+
+ EXPECT_TRUE(!memcmp(&SYSTEMTIME_ref, &SYSTEMTIME_var, sizeof(SYSTEMTIME)));
+}
+
+TEST_F(TestArchive, CVariantArchive)
+{
+ ASSERT_TRUE(file);
+ CVariant CVariant_ref((int)1), CVariant_var;
+
+ CArchive arstore(file, CArchive::store);
+ arstore << CVariant_ref;
+ arstore.Close();
+
+ ASSERT_TRUE((file->Seek(0, SEEK_SET) == 0));
+ CArchive arload(file, CArchive::load);
+ arload >> CVariant_var;
+ arload.Close();
+
+ EXPECT_TRUE(CVariant_var.isInteger());
+ EXPECT_EQ(1, CVariant_var.asInteger());
+}
+
+TEST_F(TestArchive, CVariantArchiveString)
+{
+ ASSERT_TRUE(file);
+ CVariant CVariant_ref("teststring"), CVariant_var;
+
+ CArchive arstore(file, CArchive::store);
+ arstore << CVariant_ref;
+ arstore.Close();
+
+ ASSERT_TRUE((file->Seek(0, SEEK_SET) == 0));
+ CArchive arload(file, CArchive::load);
+ arload >> CVariant_var;
+ arload.Close();
+
+ EXPECT_TRUE(CVariant_var.isString());
+ EXPECT_STREQ("teststring", CVariant_var.asString().c_str());
+}
+
+TEST_F(TestArchive, StringVectorArchive)
+{
+ ASSERT_TRUE(file);
+ std::vector<std::string> strArray_ref, strArray_var;
+ strArray_ref.push_back("test strArray_ref 0");
+ strArray_ref.push_back("test strArray_ref 1");
+ strArray_ref.push_back("test strArray_ref 2");
+ strArray_ref.push_back("test strArray_ref 3");
+
+ CArchive arstore(file, CArchive::store);
+ arstore << strArray_ref;
+ arstore.Close();
+
+ ASSERT_TRUE((file->Seek(0, SEEK_SET) == 0));
+ CArchive arload(file, CArchive::load);
+ arload >> strArray_var;
+ arload.Close();
+
+ EXPECT_STREQ("test strArray_ref 0", strArray_var.at(0).c_str());
+ EXPECT_STREQ("test strArray_ref 1", strArray_var.at(1).c_str());
+ EXPECT_STREQ("test strArray_ref 2", strArray_var.at(2).c_str());
+ EXPECT_STREQ("test strArray_ref 3", strArray_var.at(3).c_str());
+}
+
+TEST_F(TestArchive, IntegerVectorArchive)
+{
+ ASSERT_TRUE(file);
+ std::vector<int> iArray_ref, iArray_var;
+ iArray_ref.push_back(0);
+ iArray_ref.push_back(1);
+ iArray_ref.push_back(2);
+ iArray_ref.push_back(3);
+
+ CArchive arstore(file, CArchive::store);
+ arstore << iArray_ref;
+ arstore.Close();
+
+ ASSERT_TRUE((file->Seek(0, SEEK_SET) == 0));
+ CArchive arload(file, CArchive::load);
+ arload >> iArray_var;
+ arload.Close();
+
+ EXPECT_EQ(0, iArray_var.at(0));
+ EXPECT_EQ(1, iArray_var.at(1));
+ EXPECT_EQ(2, iArray_var.at(2));
+ EXPECT_EQ(3, iArray_var.at(3));
+}
+
+TEST_F(TestArchive, MultiTypeArchive)
+{
+ ASSERT_TRUE(file);
+ float float_ref = 1, float_var = 0;
+ double double_ref = 2, double_var = 0;
+ int int_ref = 3, int_var = 0;
+ unsigned int unsigned_int_ref = 4, unsigned_int_var = 0;
+ int64_t int64_t_ref = 5, int64_t_var = 0;
+ uint64_t uint64_t_ref = 6, uint64_t_var = 0;
+ bool bool_ref = true, bool_var = false;
+ char char_ref = 'A', char_var = '\0';
+ CStdString CStdString_ref = "test CStdString", CStdString_var = "";
+ CStdStringW CStdStringW_ref = L"test CStdStringW", CStdStringW_var = L"";
+ SYSTEMTIME SYSTEMTIME_ref = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ SYSTEMTIME SYSTEMTIME_var = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ CVariant CVariant_ref((int)1), CVariant_var;
+ std::vector<std::string> strArray_ref, strArray_var;
+ strArray_ref.push_back("test strArray_ref 0");
+ strArray_ref.push_back("test strArray_ref 1");
+ strArray_ref.push_back("test strArray_ref 2");
+ strArray_ref.push_back("test strArray_ref 3");
+ std::vector<int> iArray_ref, iArray_var;
+ iArray_ref.push_back(0);
+ iArray_ref.push_back(1);
+ iArray_ref.push_back(2);
+ iArray_ref.push_back(3);
+
+ CArchive arstore(file, CArchive::store);
+ EXPECT_TRUE(arstore.IsStoring());
+ EXPECT_FALSE(arstore.IsLoading());
+ arstore << float_ref;
+ arstore << double_ref;
+ arstore << int_ref;
+ arstore << unsigned_int_ref;
+ arstore << int64_t_ref;
+ arstore << uint64_t_ref;
+ arstore << bool_ref;
+ arstore << char_ref;
+ arstore << CStdString_ref;
+ arstore << CStdStringW_ref;
+ arstore << SYSTEMTIME_ref;
+ arstore << CVariant_ref;
+ arstore << strArray_ref;
+ arstore << iArray_ref;
+ arstore.Close();
+
+ ASSERT_TRUE((file->Seek(0, SEEK_SET) == 0));
+ CArchive arload(file, CArchive::load);
+ EXPECT_TRUE(arload.IsLoading());
+ EXPECT_FALSE(arload.IsStoring());
+ arload >> float_var;
+ arload >> double_var;
+ arload >> int_var;
+ arload >> unsigned_int_var;
+ arload >> int64_t_var;
+ arload >> uint64_t_var;
+ arload >> bool_var;
+ arload >> char_var;
+ arload >> CStdString_var;
+ arload >> CStdStringW_var;
+ arload >> SYSTEMTIME_var;
+ arload >> CVariant_var;
+ arload >> strArray_var;
+ arload >> iArray_var;
+ arload.Close();
+
+ EXPECT_EQ(float_ref, float_var);
+ EXPECT_EQ(double_ref, double_var);
+ EXPECT_EQ(int_ref, int_var);
+ EXPECT_EQ(unsigned_int_ref, unsigned_int_var);
+ EXPECT_EQ(int64_t_ref, int64_t_var);
+ EXPECT_EQ(uint64_t_ref, uint64_t_var);
+ EXPECT_EQ(bool_ref, bool_var);
+ EXPECT_EQ(char_ref, char_var);
+ EXPECT_STREQ(CStdString_ref.c_str(), CStdString_var.c_str());
+ EXPECT_STREQ(CStdStringW_ref.c_str(), CStdStringW_var.c_str());
+ EXPECT_TRUE(!memcmp(&SYSTEMTIME_ref, &SYSTEMTIME_var, sizeof(SYSTEMTIME)));
+ EXPECT_TRUE(CVariant_var.isInteger());
+ EXPECT_STREQ("test strArray_ref 0", strArray_var.at(0).c_str());
+ EXPECT_STREQ("test strArray_ref 1", strArray_var.at(1).c_str());
+ EXPECT_STREQ("test strArray_ref 2", strArray_var.at(2).c_str());
+ EXPECT_STREQ("test strArray_ref 3", strArray_var.at(3).c_str());
+ EXPECT_EQ(0, iArray_var.at(0));
+ EXPECT_EQ(1, iArray_var.at(1));
+ EXPECT_EQ(2, iArray_var.at(2));
+ EXPECT_EQ(3, iArray_var.at(3));
+}
diff --git a/src/utils/test/TestAsyncFileCopy.cpp b/src/utils/test/TestAsyncFileCopy.cpp
new file mode 100644
index 0000000000..62636d88d3
--- /dev/null
+++ b/src/utils/test/TestAsyncFileCopy.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/AsyncFileCopy.h"
+#include "filesystem/File.h"
+
+#include "test/TestUtils.h"
+
+#include "gtest/gtest.h"
+
+static const char refdata[] = "\x01\x02\x03\x04\x05\x06\x07\x08"
+ "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
+ "\x11\x12\x13\x14\x15\x16\x17\x18"
+ "\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
+ "\x21\x22\x23\x24\x25\x26\x27\x28"
+ "\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30";
+
+TEST(TestAsyncFileCopy, General)
+{
+ CAsyncFileCopy c;
+ XFILE::CFile *f1, *f2;
+ char vardata[sizeof(refdata)];
+
+ ASSERT_TRUE((f1 = XBMC_CREATETEMPFILE("")));
+ ASSERT_TRUE((f2 = XBMC_CREATETEMPFILE(".copy")));
+
+ EXPECT_EQ((int)sizeof(refdata), f1->Write(refdata, sizeof(refdata)));
+ f1->Close();
+ f2->Close();
+ EXPECT_TRUE(c.Copy(XBMC_TEMPFILEPATH(f1), XBMC_TEMPFILEPATH(f2), ""));
+ EXPECT_TRUE(f2->Open(XBMC_TEMPFILEPATH(f2)));
+ EXPECT_EQ(sizeof(refdata), f2->Read(vardata, sizeof(refdata)));
+ f2->Close();
+ EXPECT_TRUE(!memcmp(vardata, refdata, sizeof(refdata)));
+
+ EXPECT_TRUE(XBMC_DELETETEMPFILE(f1));
+ EXPECT_TRUE(XBMC_DELETETEMPFILE(f2));
+}
diff --git a/src/utils/test/TestBase64.cpp b/src/utils/test/TestBase64.cpp
new file mode 100644
index 0000000000..0ae643159b
--- /dev/null
+++ b/src/utils/test/TestBase64.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/Base64.h"
+
+#include "gtest/gtest.h"
+
+static const char refdata[] = "\x01\x02\x03\x04\x05\x06\x07\x08"
+ "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
+ "\x11\x12\x13\x14\x15\x16\x17\x18"
+ "\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
+ "\x21\x22\x23\x24\x25\x26\x27\x28"
+ "\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30";
+
+static const char refbase64data[] = "AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcY"
+ "GRobHB0eHyAhIiMkJSYnKCkqKywtLi8w";
+
+TEST(TestBase64, Encode_1)
+{
+ std::string a;
+ Base64::Encode(refdata, sizeof(refdata) - 1, a);
+ EXPECT_STREQ(refbase64data, a.c_str());
+}
+
+TEST(TestBase64, Encode_2)
+{
+ std::string a;
+ a = Base64::Encode(refdata, sizeof(refdata) - 1);
+ EXPECT_STREQ(refbase64data, a.c_str());
+}
+
+TEST(TestBase64, Encode_3)
+{
+ std::string a;
+ Base64::Encode(refdata, a);
+ EXPECT_STREQ(refbase64data, a.c_str());
+}
+
+TEST(TestBase64, Encode_4)
+{
+ std::string a;
+ a = Base64::Encode(refdata);
+ EXPECT_STREQ(refbase64data, a.c_str());
+}
+
+TEST(TestBase64, Decode_1)
+{
+ std::string a;
+ Base64::Decode(refbase64data, sizeof(refbase64data) - 1, a);
+ EXPECT_STREQ(refdata, a.c_str());
+}
+
+TEST(TestBase64, Decode_2)
+{
+ std::string a;
+ a = Base64::Decode(refbase64data, sizeof(refbase64data) - 1);
+ EXPECT_STREQ(refdata, a.c_str());
+}
+
+TEST(TestBase64, Decode_3)
+{
+ std::string a;
+ Base64::Decode(refbase64data, a);
+ EXPECT_STREQ(refdata, a.c_str());
+}
+
+TEST(TestBase64, Decode_4)
+{
+ std::string a;
+ a = Base64::Decode(refbase64data);
+ EXPECT_STREQ(refdata, a.c_str());
+}
diff --git a/src/utils/test/TestBitstreamStats.cpp b/src/utils/test/TestBitstreamStats.cpp
new file mode 100644
index 0000000000..1e8d90c89c
--- /dev/null
+++ b/src/utils/test/TestBitstreamStats.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "threads/Thread.h"
+#include "utils/BitstreamStats.h"
+
+#include "gtest/gtest.h"
+
+#define BITS (256 * 8)
+#define BYTES (256)
+
+class CTestBitstreamStatsThread : public CThread
+{
+public:
+ CTestBitstreamStatsThread() :
+ CThread("TestBitstreamStats"){}
+
+};
+
+TEST(TestBitstreamStats, General)
+{
+ int i;
+ BitstreamStats a;
+ CTestBitstreamStatsThread t;
+
+ i = 0;
+ a.Start();
+ EXPECT_EQ(0.0, a.GetBitrate());
+ EXPECT_EQ(0.0, a.GetMaxBitrate());
+ EXPECT_EQ(-1.0, a.GetMinBitrate());
+ while (i <= BITS)
+ {
+ a.AddSampleBits(1);
+ i++;
+ t.Sleep(1);
+ }
+ a.CalculateBitrate();
+ EXPECT_GT(a.GetBitrate(), 0.0);
+ EXPECT_GT(a.GetMaxBitrate(), 0.0);
+ EXPECT_GT(a.GetMinBitrate(), 0.0);
+
+ i = 0;
+ while (i <= BYTES)
+ {
+ a.AddSampleBytes(1);
+ t.Sleep(2);
+ i++;
+ }
+ a.CalculateBitrate();
+ EXPECT_GT(a.GetBitrate(), 0.0);
+ EXPECT_GT(a.GetMaxBitrate(), 0.0);
+ EXPECT_LE(a.GetMinBitrate(), a.GetMaxBitrate());
+}
diff --git a/src/utils/test/TestCPUInfo.cpp b/src/utils/test/TestCPUInfo.cpp
new file mode 100644
index 0000000000..b43289933b
--- /dev/null
+++ b/src/utils/test/TestCPUInfo.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/CPUInfo.h"
+#include "Temperature.h"
+#include "settings/AdvancedSettings.h"
+
+#ifdef TARGET_POSIX
+#include "../linux/XTimeUtils.h"
+#endif
+
+#include "gtest/gtest.h"
+
+TEST(TestCPUInfo, getUsedPercentage)
+{
+ EXPECT_GE(g_cpuInfo.getUsedPercentage(), 0);
+}
+
+TEST(TestCPUInfo, getCPUCount)
+{
+ EXPECT_GT(g_cpuInfo.getCPUCount(), 0);
+}
+
+TEST(TestCPUInfo, getCPUFrequency)
+{
+ EXPECT_GE(g_cpuInfo.getCPUFrequency(), 0.f);
+}
+
+namespace
+{
+class TemporarySetting
+{
+public:
+
+ TemporarySetting(std::string &setting, const char *newValue) :
+ m_Setting(setting),
+ m_OldValue(setting)
+ {
+ m_Setting = newValue;
+ }
+
+ ~TemporarySetting()
+ {
+ m_Setting = m_OldValue;
+ }
+
+private:
+
+ std::string &m_Setting;
+ std::string m_OldValue;
+};
+}
+
+TEST(TestCPUInfo, getTemperature)
+{
+ TemporarySetting command(g_advancedSettings.m_cpuTempCmd, "echo '50 c'");
+ CTemperature t;
+ EXPECT_TRUE(g_cpuInfo.getTemperature(t));
+ EXPECT_TRUE(t.IsValid());
+}
+
+TEST(TestCPUInfo, getCPUModel)
+{
+ std::string s = g_cpuInfo.getCPUModel();
+ EXPECT_STRNE("", s.c_str());
+}
+
+TEST(TestCPUInfo, getCPUBogoMips)
+{
+ std::string s = g_cpuInfo.getCPUBogoMips();
+ EXPECT_STRNE("", s.c_str());
+}
+
+TEST(TestCPUInfo, getCPUHardware)
+{
+ std::string s = g_cpuInfo.getCPUHardware();
+ EXPECT_STRNE("", s.c_str());
+}
+
+TEST(TestCPUInfo, getCPURevision)
+{
+ std::string s = g_cpuInfo.getCPURevision();
+ EXPECT_STRNE("", s.c_str());
+}
+
+TEST(TestCPUInfo, getCPUSerial)
+{
+ std::string s = g_cpuInfo.getCPUSerial();
+ EXPECT_STRNE("", s.c_str());
+}
+
+TEST(TestCPUInfo, CoreInfo)
+{
+ ASSERT_TRUE(g_cpuInfo.HasCoreId(0));
+ const CoreInfo c = g_cpuInfo.GetCoreInfo(0);
+ EXPECT_FALSE(c.m_strModel.empty());
+}
+
+TEST(TestCPUInfo, GetCoresUsageString)
+{
+ EXPECT_STRNE("", g_cpuInfo.GetCoresUsageString().c_str());
+}
+
+TEST(TestCPUInfo, GetCPUFeatures)
+{
+ unsigned int a = g_cpuInfo.GetCPUFeatures();
+ (void)a;
+}
+
+TEST(TestCPUInfo, getUsedPercentage_output)
+{
+ CCPUInfo c;
+ Sleep(1); /* TODO: Support option from main that sets this parameter */
+ int r = c.getUsedPercentage();
+ std::cout << "Percentage: " << testing::PrintToString(r) << std::endl;
+}
diff --git a/src/utils/test/TestCharsetConverter.cpp b/src/utils/test/TestCharsetConverter.cpp
new file mode 100644
index 0000000000..aceb0fbee7
--- /dev/null
+++ b/src/utils/test/TestCharsetConverter.cpp
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "settings/Settings.h"
+#include "utils/CharsetConverter.h"
+#include "utils/StdString.h"
+#include "utils/Utf8Utils.h"
+#include "system.h"
+
+#include "gtest/gtest.h"
+
+static const uint16_t refutf16LE1[] = { 0xff54, 0xff45, 0xff53, 0xff54,
+ 0xff3f, 0xff55, 0xff54, 0xff46,
+ 0xff11, 0xff16, 0xff2c, 0xff25,
+ 0xff54, 0xff4f, 0xff57, 0x0 };
+
+static const uint16_t refutf16LE2[] = { 0xff54, 0xff45, 0xff53, 0xff54,
+ 0xff3f, 0xff55, 0xff54, 0xff46,
+ 0xff18, 0xff34, 0xff4f, 0xff1a,
+ 0xff3f, 0xff43, 0xff48, 0xff41,
+ 0xff52, 0xff53, 0xff45, 0xff54,
+ 0xff3f, 0xff35, 0xff34, 0xff26,
+ 0xff0d, 0xff11, 0xff16, 0xff2c,
+ 0xff25, 0xff0c, 0xff3f, 0xff23,
+ 0xff33, 0xff54, 0xff44, 0xff33,
+ 0xff54, 0xff52, 0xff49, 0xff4e,
+ 0xff47, 0xff11, 0xff16, 0x0 };
+
+static const char refutf16LE3[] = "T\377E\377S\377T\377?\377S\377T\377"
+ "R\377I\377N\377G\377#\377H\377A\377"
+ "R\377S\377E\377T\377\064\377O\377\065"
+ "\377T\377F\377\030\377";
+
+static const uint16_t refutf16LE4[] = { 0xff54, 0xff45, 0xff53, 0xff54,
+ 0xff3f, 0xff55, 0xff54, 0xff46,
+ 0xff11, 0xff16, 0xff2c, 0xff25,
+ 0xff54, 0xff4f, 0xff35, 0xff34,
+ 0xff26, 0xff18, 0x0 };
+
+static const uint32_t refutf32LE1[] = { 0xff54, 0xff45, 0xff53, 0xff54,
+ 0xff3f, 0xff55, 0xff54, 0xff46,
+ 0xff18, 0xff34, 0xff4f, 0xff1a,
+ 0xff3f, 0xff43, 0xff48, 0xff41,
+ 0xff52, 0xff53, 0xff45, 0xff54,
+ 0xff3f, 0xff35, 0xff34, 0xff26,
+ 0xff0d, 0xff13, 0xff12, 0xff2c,
+ 0xff25, 0xff0c, 0xff3f, 0xff23,
+ 0xff33, 0xff54, 0xff44, 0xff33,
+ 0xff54, 0xff52, 0xff49, 0xff4e,
+ 0xff47, 0xff13, 0xff12, 0xff3f,
+#ifdef TARGET_DARWIN
+ 0x0 };
+#else
+ 0x1f42d, 0x1f42e, 0x0 };
+#endif
+
+static const uint16_t refutf16BE[] = { 0x54ff, 0x45ff, 0x53ff, 0x54ff,
+ 0x3fff, 0x55ff, 0x54ff, 0x46ff,
+ 0x11ff, 0x16ff, 0x22ff, 0x25ff,
+ 0x54ff, 0x4fff, 0x35ff, 0x34ff,
+ 0x26ff, 0x18ff, 0x0};
+
+static const uint16_t refucs2[] = { 0xff54, 0xff45, 0xff53, 0xff54,
+ 0xff3f, 0xff55, 0xff43, 0xff53,
+ 0xff12, 0xff54, 0xff4f, 0xff35,
+ 0xff34, 0xff26, 0xff18, 0x0 };
+
+class TestCharsetConverter : public testing::Test
+{
+protected:
+ TestCharsetConverter()
+ {
+ /* Add default settings for locale.
+ * Settings here are taken from CGUISettings::Initialize()
+ */
+ /* TODO
+ CSettingsCategory *loc = CSettings::Get().AddCategory(7, "locale", 14090);
+ CSettings::Get().AddString(loc, "locale.language",248,"english",
+ SPIN_CONTROL_TEXT);
+ CSettings::Get().AddString(loc, "locale.country", 20026, "USA",
+ SPIN_CONTROL_TEXT);
+ CSettings::Get().AddString(loc, "locale.charset", 14091, "DEFAULT",
+ SPIN_CONTROL_TEXT); // charset is set by the
+ // language file
+
+ // Add default settings for subtitles
+ CSettingsCategory *sub = CSettings::Get().AddCategory(5, "subtitles", 287);
+ CSettings::Get().AddString(sub, "subtitles.charset", 735, "DEFAULT",
+ SPIN_CONTROL_TEXT);
+ */
+
+ g_charsetConverter.reset();
+ g_charsetConverter.clear();
+ }
+
+ ~TestCharsetConverter()
+ {
+ CSettings::Get().Unload();
+ }
+
+ CStdStringA refstra1, refstra2, varstra1;
+ CStdStringW refstrw1, varstrw1;
+ CStdString16 refstr16_1, varstr16_1;
+ CStdString32 refstr32_1, varstr32_1;
+ CStdString refstr1;
+};
+
+TEST_F(TestCharsetConverter, utf8ToW)
+{
+ refstra1 = "test utf8ToW";
+ refstrw1 = L"test utf8ToW";
+ varstrw1.clear();
+ g_charsetConverter.utf8ToW(refstra1, varstrw1, true, false, NULL);
+ EXPECT_STREQ(refstrw1.c_str(), varstrw1.c_str());
+}
+
+TEST_F(TestCharsetConverter, utf16LEtoW)
+{
+ refstrw1 = L"test_utf16LEtow";
+ /* TODO: Should be able to use '=' operator instead of assign() */
+ refstr16_1.assign(refutf16LE1);
+ varstrw1.clear();
+ g_charsetConverter.utf16LEtoW(refstr16_1, varstrw1);
+ EXPECT_STREQ(refstrw1.c_str(), varstrw1.c_str());
+}
+
+TEST_F(TestCharsetConverter, subtitleCharsetToUtf8)
+{
+ refstra1 = "test subtitleCharsetToW";
+ varstra1.clear();
+ g_charsetConverter.subtitleCharsetToUtf8(refstra1, varstra1);
+
+ /* Assign refstra1 to refstrw1 so that we can compare */
+ EXPECT_STREQ(refstra1.c_str(), varstra1.c_str());
+}
+
+TEST_F(TestCharsetConverter, utf8ToStringCharset_1)
+{
+ refstra1 = "test utf8ToStringCharset";
+ varstra1.clear();
+ g_charsetConverter.utf8ToStringCharset(refstra1, varstra1);
+ EXPECT_STREQ(refstra1.c_str(), varstra1.c_str());
+}
+
+TEST_F(TestCharsetConverter, utf8ToStringCharset_2)
+{
+ refstra1 = "test utf8ToStringCharset";
+ varstra1 = "test utf8ToStringCharset";
+ g_charsetConverter.utf8ToStringCharset(varstra1);
+ EXPECT_STREQ(refstra1.c_str(), varstra1.c_str());
+}
+
+TEST_F(TestCharsetConverter, utf8ToSystem)
+{
+ refstra1 = "test utf8ToSystem";
+ varstra1 = "test utf8ToSystem";
+ g_charsetConverter.utf8ToSystem(varstra1);
+ EXPECT_STREQ(refstra1.c_str(), varstra1.c_str());
+}
+
+TEST_F(TestCharsetConverter, utf8To_ASCII)
+{
+ refstra1 = "test utf8To: charset ASCII, CStdStringA";
+ varstra1.clear();
+ g_charsetConverter.utf8To("ASCII", refstra1, varstra1);
+ EXPECT_STREQ(refstra1.c_str(), varstra1.c_str());
+}
+
+TEST_F(TestCharsetConverter, utf8To_UTF16LE)
+{
+ refstra1 = "test_utf8To:_charset_UTF-16LE,_"
+ "CStdString16";
+ refstr16_1.assign(refutf16LE2);
+ varstr16_1.clear();
+ g_charsetConverter.utf8To("UTF-16LE", refstra1, varstr16_1);
+ EXPECT_TRUE(!memcmp(refstr16_1.c_str(), varstr16_1.c_str(),
+ refstr16_1.length() * sizeof(uint16_t)));
+}
+
+TEST_F(TestCharsetConverter, utf8To_UTF32LE)
+{
+ refstra1 = "test_utf8To:_charset_UTF-32LE,_"
+#ifdef TARGET_DARWIN
+/* OSX has it's own 'special' utf-8 charset which we use (see UTF8_SOURCE in CharsetConverter.cpp)
+ which is basically NFD (decomposed) utf-8. The trouble is, it fails on the COW FACE and MOUSE FACE
+ characters for some reason (possibly anything over 0x100000, or maybe there's a decomposed form of these
+ that I couldn't find???) If UTF8_SOURCE is switched to UTF-8 then this test would pass as-is, but then
+ some filenames stored in utf8-mac wouldn't display correctly in the UI. */
+ "CStdString32_";
+#else
+ "CStdString32_🐭🐮";
+#endif
+ refstr32_1.assign(refutf32LE1);
+ varstr32_1.clear();
+ g_charsetConverter.utf8To("UTF-32LE", refstra1, varstr32_1);
+ EXPECT_TRUE(!memcmp(refstr32_1.c_str(), varstr32_1.c_str(),
+ sizeof(refutf32LE1)));
+}
+
+TEST_F(TestCharsetConverter, stringCharsetToUtf8)
+{
+ refstra1 = "test_stringCharsetToUtf8";
+ varstra1.clear();
+ g_charsetConverter.ToUtf8("UTF-16LE", refutf16LE3, varstra1);
+ EXPECT_STREQ(refstra1.c_str(), varstra1.c_str());
+}
+
+TEST_F(TestCharsetConverter, isValidUtf8_1)
+{
+ varstra1.clear();
+ g_charsetConverter.ToUtf8("UTF-16LE", refutf16LE3, varstra1);
+ EXPECT_TRUE(CUtf8Utils::isValidUtf8(varstra1.c_str()));
+}
+
+TEST_F(TestCharsetConverter, isValidUtf8_2)
+{
+ refstr1 = refutf16LE3;
+ EXPECT_FALSE(CUtf8Utils::isValidUtf8(refstr1));
+}
+
+TEST_F(TestCharsetConverter, isValidUtf8_3)
+{
+ varstra1.clear();
+ g_charsetConverter.ToUtf8("UTF-16LE", refutf16LE3, varstra1);
+ EXPECT_TRUE(CUtf8Utils::isValidUtf8(varstra1.c_str()));
+}
+
+TEST_F(TestCharsetConverter, isValidUtf8_4)
+{
+ EXPECT_FALSE(CUtf8Utils::isValidUtf8(refutf16LE3));
+}
+
+/* TODO: Resolve correct input/output for this function */
+// TEST_F(TestCharsetConverter, ucs2CharsetToStringCharset)
+// {
+// void ucs2CharsetToStringCharset(const CStdStringW& strSource,
+// CStdStringA& strDest, bool swap = false);
+// }
+
+TEST_F(TestCharsetConverter, wToUTF8)
+{
+ refstrw1 = L"test_wToUTF8";
+ refstra1 = "test_wToUTF8";
+ varstra1.clear();
+ g_charsetConverter.wToUTF8(refstrw1, varstra1);
+ EXPECT_STREQ(refstra1.c_str(), varstra1.c_str());
+}
+
+TEST_F(TestCharsetConverter, utf16BEtoUTF8)
+{
+ refstr16_1.assign(refutf16BE);
+ refstra1 = "test_utf16BEtoUTF8";
+ varstra1.clear();
+ g_charsetConverter.utf16BEtoUTF8(refstr16_1, varstra1);
+ EXPECT_STREQ(refstra1.c_str(), varstra1.c_str());
+}
+
+TEST_F(TestCharsetConverter, utf16LEtoUTF8)
+{
+ refstr16_1.assign(refutf16LE4);
+ refstra1 = "test_utf16LEtoUTF8";
+ varstra1.clear();
+ g_charsetConverter.utf16LEtoUTF8(refstr16_1, varstra1);
+ EXPECT_STREQ(refstra1.c_str(), varstra1.c_str());
+}
+
+TEST_F(TestCharsetConverter, ucs2ToUTF8)
+{
+ refstr16_1.assign(refucs2);
+ refstra1 = "test_ucs2toUTF8";
+ varstra1.clear();
+ g_charsetConverter.ucs2ToUTF8(refstr16_1, varstra1);
+ EXPECT_STREQ(refstra1.c_str(), varstra1.c_str());
+}
+
+TEST_F(TestCharsetConverter, utf8logicalToVisualBiDi)
+{
+ refstra1 = "test_utf8logicalToVisualBiDi";
+ refstra2 = "test_utf8logicalToVisualBiDi";
+ varstra1.clear();
+ g_charsetConverter.utf8logicalToVisualBiDi(refstra1, varstra1);
+ EXPECT_STREQ(refstra2.c_str(), varstra1.c_str());
+}
+
+/* TODO: Resolve correct input/output for this function */
+// TEST_F(TestCharsetConverter, utf32ToStringCharset)
+// {
+// void utf32ToStringCharset(const unsigned long* strSource, CStdStringA& strDest);
+// }
+
+TEST_F(TestCharsetConverter, getCharsetLabels)
+{
+ std::vector<CStdString> reflabels;
+ reflabels.push_back("Western Europe (ISO)");
+ reflabels.push_back("Central Europe (ISO)");
+ reflabels.push_back("South Europe (ISO)");
+ reflabels.push_back("Baltic (ISO)");
+ reflabels.push_back("Cyrillic (ISO)");
+ reflabels.push_back("Arabic (ISO)");
+ reflabels.push_back("Greek (ISO)");
+ reflabels.push_back("Hebrew (ISO)");
+ reflabels.push_back("Turkish (ISO)");
+ reflabels.push_back("Central Europe (Windows)");
+ reflabels.push_back("Cyrillic (Windows)");
+ reflabels.push_back("Western Europe (Windows)");
+ reflabels.push_back("Greek (Windows)");
+ reflabels.push_back("Turkish (Windows)");
+ reflabels.push_back("Hebrew (Windows)");
+ reflabels.push_back("Arabic (Windows)");
+ reflabels.push_back("Baltic (Windows)");
+ reflabels.push_back("Vietnamesse (Windows)");
+ reflabels.push_back("Thai (Windows)");
+ reflabels.push_back("Chinese Traditional (Big5)");
+ reflabels.push_back("Chinese Simplified (GBK)");
+ reflabels.push_back("Japanese (Shift-JIS)");
+ reflabels.push_back("Korean");
+ reflabels.push_back("Hong Kong (Big5-HKSCS)");
+
+ std::vector<std::string> varlabels = g_charsetConverter.getCharsetLabels();
+ ASSERT_EQ(reflabels.size(), varlabels.size());
+
+ std::vector<std::string>::iterator it;
+ for (it = varlabels.begin(); it < varlabels.end(); ++it)
+ {
+ EXPECT_STREQ((reflabels.at(it - varlabels.begin())).c_str(), (*it).c_str());
+ }
+}
+
+TEST_F(TestCharsetConverter, getCharsetLabelByName)
+{
+ CStdString varstr =
+ g_charsetConverter.getCharsetLabelByName("ISO-8859-1");
+ EXPECT_STREQ("Western Europe (ISO)", varstr.c_str());
+ varstr.clear();
+ varstr = g_charsetConverter.getCharsetLabelByName("Bogus");
+ EXPECT_STREQ("", varstr.c_str());
+}
+
+TEST_F(TestCharsetConverter, getCharsetNameByLabel)
+{
+ CStdString varstr =
+ g_charsetConverter.getCharsetNameByLabel("Western Europe (ISO)");
+ EXPECT_STREQ("ISO-8859-1", varstr.c_str());
+ varstr.clear();
+ varstr = g_charsetConverter.getCharsetNameByLabel("Bogus");
+ EXPECT_STREQ("", varstr.c_str());
+}
+
+TEST_F(TestCharsetConverter, unknownToUTF8_1)
+{
+ refstra1 = "test_unknownToUTF8";
+ varstra1 = "test_unknownToUTF8";
+ g_charsetConverter.unknownToUTF8(varstra1);
+ EXPECT_STREQ(refstra1.c_str(), varstra1.c_str());
+}
+
+TEST_F(TestCharsetConverter, unknownToUTF8_2)
+{
+ refstra1 = "test_unknownToUTF8";
+ varstra1.clear();
+ g_charsetConverter.unknownToUTF8(refstra1, varstra1);
+ EXPECT_STREQ(refstra1.c_str(), varstra1.c_str());
+}
+
+TEST_F(TestCharsetConverter, toW)
+{
+ refstra1 = "test_toW:_charset_UTF-16LE";
+ refstrw1 = L"\xBDEF\xEF94\x85BD\xBDEF\xEF93\x94BD\xBCEF\xEFBF"
+ L"\x94BD\xBDEF\xEF8F\xB7BC\xBCEF\xEF9A\xBFBC\xBDEF"
+ L"\xEF83\x88BD\xBDEF\xEF81\x92BD\xBDEF\xEF93\x85BD"
+ L"\xBDEF\xEF94\xBFBC\xBCEF\xEFB5\xB4BC\xBCEF\xEFA6"
+ L"\x8DBC\xBCEF\xEF91\x96BC\xBCEF\xEFAC\xA5BC";
+ varstrw1.clear();
+ g_charsetConverter.toW(refstra1, varstrw1, "UTF-16LE");
+ EXPECT_STREQ(refstrw1.c_str(), varstrw1.c_str());
+}
+
+TEST_F(TestCharsetConverter, fromW)
+{
+ refstrw1 = L"\xBDEF\xEF94\x85BD\xBDEF\xEF93\x94BD\xBCEF\xEFBF"
+ L"\x86BD\xBDEF\xEF92\x8FBD\xBDEF\xEF8D\xB7BC\xBCEF"
+ L"\xEF9A\xBFBC\xBDEF\xEF83\x88BD\xBDEF\xEF81\x92BD"
+ L"\xBDEF\xEF93\x85BD\xBDEF\xEF94\xBFBC\xBCEF\xEFB5"
+ L"\xB4BC\xBCEF\xEFA6\x8DBC\xBCEF\xEF91\x96BC\xBCEF"
+ L"\xEFAC\xA5BC";
+ refstra1 = "test_fromW:_charset_UTF-16LE";
+ varstra1.clear();
+ g_charsetConverter.fromW(refstrw1, varstra1, "UTF-16LE");
+ EXPECT_STREQ(refstra1.c_str(), varstra1.c_str());
+}
diff --git a/src/utils/test/TestCrc32.cpp b/src/utils/test/TestCrc32.cpp
new file mode 100644
index 0000000000..4396a94771
--- /dev/null
+++ b/src/utils/test/TestCrc32.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/Crc32.h"
+
+#include "gtest/gtest.h"
+
+static const char refdata[] = "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "01234567890!@#$%^&*()";
+
+TEST(TestCrc32, Compute_1)
+{
+ Crc32 a;
+ uint32_t varcrc;
+ a.Compute(refdata, sizeof(refdata) - 1);
+ varcrc = a;
+ EXPECT_EQ(0xa4eb60e3, varcrc);
+}
+
+TEST(TestCrc32, Compute_2)
+{
+ Crc32 a;
+ uint32_t varcrc;
+ std::string s = refdata;
+ a.Compute(s);
+ varcrc = a;
+ EXPECT_EQ(0xa4eb60e3, varcrc);
+}
+
+TEST(TestCrc32, ComputeFromLowerCase)
+{
+ Crc32 a;
+ uint32_t varcrc;
+ std::string s = refdata;
+ a.ComputeFromLowerCase(s);
+ varcrc = a;
+ EXPECT_EQ((uint32_t)0x7f045b3e, varcrc);
+}
+
+TEST(TestCrc32, Reset)
+{
+ Crc32 a;
+ uint32_t varcrc;
+ std::string s = refdata;
+ a.ComputeFromLowerCase(s);
+ a.Reset();
+ varcrc = a;
+ EXPECT_EQ(0xffffffff, varcrc);
+}
diff --git a/src/utils/test/TestCryptThreading.cpp b/src/utils/test/TestCryptThreading.cpp
new file mode 100644
index 0000000000..75f784d8cc
--- /dev/null
+++ b/src/utils/test/TestCryptThreading.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/CryptThreading.h"
+
+#include "gtest/gtest.h"
+
+TEST(TestCryptThreadingInitializer, General)
+{
+ std::cout << "g_cryptThreadingInitializer address: " <<
+ testing::PrintToString(&g_cryptThreadingInitializer) << "\n";
+}
diff --git a/src/utils/test/TestDatabaseUtils.cpp b/src/utils/test/TestDatabaseUtils.cpp
new file mode 100644
index 0000000000..5d846772f3
--- /dev/null
+++ b/src/utils/test/TestDatabaseUtils.cpp
@@ -0,0 +1,1312 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/DatabaseUtils.h"
+#include "video/VideoDatabase.h"
+#include "music/MusicDatabase.h"
+#include "dbwrappers/qry_dat.h"
+#include "utils/Variant.h"
+#include "utils/StringUtils.h"
+
+#include "gtest/gtest.h"
+
+class TestDatabaseUtilsHelper
+{
+public:
+ TestDatabaseUtilsHelper()
+ {
+ album_idAlbum = CMusicDatabase::album_idAlbum;
+ album_strAlbum = CMusicDatabase::album_strAlbum;
+ album_strArtists = CMusicDatabase::album_strArtists;
+ album_strGenres = CMusicDatabase::album_strGenres;
+ album_iYear = CMusicDatabase::album_iYear;
+ album_strMoods = CMusicDatabase::album_strMoods;
+ album_strStyles = CMusicDatabase::album_strStyles;
+ album_strThemes = CMusicDatabase::album_strThemes;
+ album_strReview = CMusicDatabase::album_strReview;
+ album_strLabel = CMusicDatabase::album_strLabel;
+ album_strType = CMusicDatabase::album_strType;
+ album_iRating = CMusicDatabase::album_iRating;
+
+ song_idSong = CMusicDatabase::song_idSong;
+ song_strTitle = CMusicDatabase::song_strTitle;
+ song_iTrack = CMusicDatabase::song_iTrack;
+ song_iDuration = CMusicDatabase::song_iDuration;
+ song_iYear = CMusicDatabase::song_iYear;
+ song_strFileName = CMusicDatabase::song_strFileName;
+ song_iTimesPlayed = CMusicDatabase::song_iTimesPlayed;
+ song_iStartOffset = CMusicDatabase::song_iStartOffset;
+ song_iEndOffset = CMusicDatabase::song_iEndOffset;
+ song_lastplayed = CMusicDatabase::song_lastplayed;
+ song_rating = CMusicDatabase::song_rating;
+ song_comment = CMusicDatabase::song_comment;
+ song_strAlbum = CMusicDatabase::song_strAlbum;
+ song_strPath = CMusicDatabase::song_strPath;
+ song_strGenres = CMusicDatabase::song_strGenres;
+ song_strArtists = CMusicDatabase::song_strArtists;
+ }
+
+ int album_idAlbum;
+ int album_strAlbum;
+ int album_strArtists;
+ int album_strGenres;
+ int album_iYear;
+ int album_strMoods;
+ int album_strStyles;
+ int album_strThemes;
+ int album_strReview;
+ int album_strLabel;
+ int album_strType;
+ int album_iRating;
+
+ int song_idSong;
+ int song_strTitle;
+ int song_iTrack;
+ int song_iDuration;
+ int song_iYear;
+ int song_strFileName;
+ int song_iTimesPlayed;
+ int song_iStartOffset;
+ int song_iEndOffset;
+ int song_lastplayed;
+ int song_rating;
+ int song_comment;
+ int song_strAlbum;
+ int song_strPath;
+ int song_strGenres;
+ int song_strArtists;
+};
+
+TEST(TestDatabaseUtils, GetField_None)
+{
+ std::string refstr, varstr;
+
+ refstr = "";
+ varstr = DatabaseUtils::GetField(FieldNone, MediaTypeNone,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ varstr = DatabaseUtils::GetField(FieldNone, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestDatabaseUtils, GetField_MediaTypeAlbum)
+{
+ std::string refstr, varstr;
+
+ refstr = "albumview.idAlbum";
+ varstr = DatabaseUtils::GetField(FieldId, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.strAlbum";
+ varstr = DatabaseUtils::GetField(FieldAlbum, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.strArtists";
+ varstr = DatabaseUtils::GetField(FieldArtist, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.strArtists";
+ varstr = DatabaseUtils::GetField(FieldAlbumArtist, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.strGenre";
+ varstr = DatabaseUtils::GetField(FieldGenre, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.iYear";
+ varstr = DatabaseUtils::GetField(FieldYear, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.strMoods";
+ varstr = DatabaseUtils::GetField(FieldMoods, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.strStyles";
+ varstr = DatabaseUtils::GetField(FieldStyles, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.strThemes";
+ varstr = DatabaseUtils::GetField(FieldThemes, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.strReview";
+ varstr = DatabaseUtils::GetField(FieldReview, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.strLabel";
+ varstr = DatabaseUtils::GetField(FieldMusicLabel, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.strType";
+ varstr = DatabaseUtils::GetField(FieldAlbumType, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.iRating";
+ varstr = DatabaseUtils::GetField(FieldRating, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.idalbum";
+ varstr = DatabaseUtils::GetField(FieldDateAdded, MediaTypeAlbum,
+ DatabaseQueryPartOrderBy);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "";
+ varstr = DatabaseUtils::GetField(FieldDateAdded, MediaTypeAlbum,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "albumview.strAlbum";
+ varstr = DatabaseUtils::GetField(FieldAlbum, MediaTypeAlbum,
+ DatabaseQueryPartWhere);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ varstr = DatabaseUtils::GetField(FieldAlbum, MediaTypeAlbum,
+ DatabaseQueryPartOrderBy);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestDatabaseUtils, GetField_MediaTypeSong)
+{
+ std::string refstr, varstr;
+
+ refstr = "songview.idSong";
+ varstr = DatabaseUtils::GetField(FieldId, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.strTitle";
+ varstr = DatabaseUtils::GetField(FieldTitle, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.iTrack";
+ varstr = DatabaseUtils::GetField(FieldTrackNumber, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.iDuration";
+ varstr = DatabaseUtils::GetField(FieldTime, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.iYear";
+ varstr = DatabaseUtils::GetField(FieldYear, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.strFilename";
+ varstr = DatabaseUtils::GetField(FieldFilename, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.iTimesPlayed";
+ varstr = DatabaseUtils::GetField(FieldPlaycount, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.iStartOffset";
+ varstr = DatabaseUtils::GetField(FieldStartOffset, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.iEndOffset";
+ varstr = DatabaseUtils::GetField(FieldEndOffset, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.lastPlayed";
+ varstr = DatabaseUtils::GetField(FieldLastPlayed, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.rating";
+ varstr = DatabaseUtils::GetField(FieldRating, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.comment";
+ varstr = DatabaseUtils::GetField(FieldComment, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.strAlbum";
+ varstr = DatabaseUtils::GetField(FieldAlbum, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.strPath";
+ varstr = DatabaseUtils::GetField(FieldPath, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.strArtists";
+ varstr = DatabaseUtils::GetField(FieldArtist, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.strArtists";
+ varstr = DatabaseUtils::GetField(FieldAlbumArtist, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.strGenre";
+ varstr = DatabaseUtils::GetField(FieldGenre, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.idSong";
+ varstr = DatabaseUtils::GetField(FieldDateAdded, MediaTypeSong,
+ DatabaseQueryPartOrderBy);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "";
+ varstr = DatabaseUtils::GetField(FieldDateAdded, MediaTypeSong,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "songview.strPath";
+ varstr = DatabaseUtils::GetField(FieldPath, MediaTypeSong,
+ DatabaseQueryPartWhere);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ varstr = DatabaseUtils::GetField(FieldPath, MediaTypeSong,
+ DatabaseQueryPartOrderBy);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestDatabaseUtils, GetField_MediaTypeMusicVideo)
+{
+ CStdString refstr, varstr;
+
+ refstr = "musicvideoview.idMVideo";
+ varstr = DatabaseUtils::GetField(FieldId, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("musicvideoview.c%02d",VIDEODB_ID_MUSICVIDEO_TITLE);
+ varstr = DatabaseUtils::GetField(FieldTitle, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("musicvideoview.c%02d",VIDEODB_ID_MUSICVIDEO_RUNTIME);
+ varstr = DatabaseUtils::GetField(FieldTime, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("musicvideoview.c%02d",VIDEODB_ID_MUSICVIDEO_DIRECTOR);
+ varstr = DatabaseUtils::GetField(FieldDirector, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("musicvideoview.c%02d",VIDEODB_ID_MUSICVIDEO_STUDIOS);
+ varstr = DatabaseUtils::GetField(FieldStudio, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("musicvideoview.c%02d",VIDEODB_ID_MUSICVIDEO_YEAR);
+ varstr = DatabaseUtils::GetField(FieldYear, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("musicvideoview.c%02d",VIDEODB_ID_MUSICVIDEO_PLOT);
+ varstr = DatabaseUtils::GetField(FieldPlot, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("musicvideoview.c%02d",VIDEODB_ID_MUSICVIDEO_ALBUM);
+ varstr = DatabaseUtils::GetField(FieldAlbum, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("musicvideoview.c%02d",VIDEODB_ID_MUSICVIDEO_ARTIST);
+ varstr = DatabaseUtils::GetField(FieldArtist, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("musicvideoview.c%02d",VIDEODB_ID_MUSICVIDEO_GENRE);
+ varstr = DatabaseUtils::GetField(FieldGenre, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("musicvideoview.c%02d",VIDEODB_ID_MUSICVIDEO_TRACK);
+ varstr = DatabaseUtils::GetField(FieldTrackNumber, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "musicvideoview.strFilename";
+ varstr = DatabaseUtils::GetField(FieldFilename, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "musicvideoview.strPath";
+ varstr = DatabaseUtils::GetField(FieldPath, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "musicvideoview.playCount";
+ varstr = DatabaseUtils::GetField(FieldPlaycount, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "musicvideoview.lastPlayed";
+ varstr = DatabaseUtils::GetField(FieldLastPlayed, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "musicvideoview.dateAdded";
+ varstr = DatabaseUtils::GetField(FieldDateAdded, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "";
+ varstr = DatabaseUtils::GetField(FieldVideoResolution, MediaTypeMusicVideo,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "musicvideoview.strPath";
+ varstr = DatabaseUtils::GetField(FieldPath, MediaTypeMusicVideo,
+ DatabaseQueryPartWhere);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "musicvideoview.strPath";
+ varstr = DatabaseUtils::GetField(FieldPath, MediaTypeMusicVideo,
+ DatabaseQueryPartOrderBy);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestDatabaseUtils, GetField_MediaTypeMovie)
+{
+ CStdString refstr, varstr;
+
+ refstr = "movieview.idMovie";
+ varstr = DatabaseUtils::GetField(FieldId, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movieview.c%02d", VIDEODB_ID_TITLE);
+ varstr = DatabaseUtils::GetField(FieldTitle, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("CASE WHEN length(movieview.c%02d) > 0 THEN movieview.c%02d "
+ "ELSE movieview.c%02d END", VIDEODB_ID_SORTTITLE,
+ VIDEODB_ID_SORTTITLE, VIDEODB_ID_TITLE, VIDEODB_ID_TITLE);
+ varstr = DatabaseUtils::GetField(FieldTitle, MediaTypeMovie,
+ DatabaseQueryPartOrderBy);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movieview.c%02d", VIDEODB_ID_PLOT);
+ varstr = DatabaseUtils::GetField(FieldPlot, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movieview.c%02d", VIDEODB_ID_PLOTOUTLINE);
+ varstr = DatabaseUtils::GetField(FieldPlotOutline, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movieview.c%02d", VIDEODB_ID_TAGLINE);
+ varstr = DatabaseUtils::GetField(FieldTagline, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movieview.c%02d", VIDEODB_ID_VOTES);
+ varstr = DatabaseUtils::GetField(FieldVotes, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movieview.c%02d", VIDEODB_ID_RATING);
+ varstr = DatabaseUtils::GetField(FieldRating, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("CAST(movieview.c%02d as DECIMAL(5,3))", VIDEODB_ID_RATING);
+ varstr = DatabaseUtils::GetField(FieldRating, MediaTypeMovie,
+ DatabaseQueryPartOrderBy);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movieview.c%02d", VIDEODB_ID_CREDITS);
+ varstr = DatabaseUtils::GetField(FieldWriter, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movieview.c%02d", VIDEODB_ID_YEAR);
+ varstr = DatabaseUtils::GetField(FieldYear, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movieview.c%02d", VIDEODB_ID_SORTTITLE);
+ varstr = DatabaseUtils::GetField(FieldSortTitle, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movieview.c%02d", VIDEODB_ID_RUNTIME);
+ varstr = DatabaseUtils::GetField(FieldTime, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movieview.c%02d", VIDEODB_ID_MPAA);
+ varstr = DatabaseUtils::GetField(FieldMPAA, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movieview.c%02d", VIDEODB_ID_TOP250);
+ varstr = DatabaseUtils::GetField(FieldTop250, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movieview.c%02d", VIDEODB_ID_GENRE);
+ varstr = DatabaseUtils::GetField(FieldGenre, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movieview.c%02d", VIDEODB_ID_DIRECTOR);
+ varstr = DatabaseUtils::GetField(FieldDirector, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movieview.c%02d", VIDEODB_ID_STUDIOS);
+ varstr = DatabaseUtils::GetField(FieldStudio, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movieview.c%02d", VIDEODB_ID_TRAILER);
+ varstr = DatabaseUtils::GetField(FieldTrailer, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("movieview.c%02d", VIDEODB_ID_COUNTRY);
+ varstr = DatabaseUtils::GetField(FieldCountry, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "movieview.strFilename";
+ varstr = DatabaseUtils::GetField(FieldFilename, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "movieview.strPath";
+ varstr = DatabaseUtils::GetField(FieldPath, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "movieview.playCount";
+ varstr = DatabaseUtils::GetField(FieldPlaycount, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "movieview.lastPlayed";
+ varstr = DatabaseUtils::GetField(FieldLastPlayed, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "movieview.dateAdded";
+ varstr = DatabaseUtils::GetField(FieldDateAdded, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "";
+ varstr = DatabaseUtils::GetField(FieldRandom, MediaTypeMovie,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestDatabaseUtils, GetField_MediaTypeTvShow)
+{
+ CStdString refstr, varstr;
+
+ refstr = "tvshowview.idShow";
+ varstr = DatabaseUtils::GetField(FieldId, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("CASE WHEN length(tvshowview.c%02d) > 0 THEN tvshowview.c%02d "
+ "ELSE tvshowview.c%02d END", VIDEODB_ID_TV_SORTTITLE,
+ VIDEODB_ID_TV_SORTTITLE, VIDEODB_ID_TV_TITLE);
+ varstr = DatabaseUtils::GetField(FieldTitle, MediaTypeTvShow,
+ DatabaseQueryPartOrderBy);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("tvshowview.c%02d", VIDEODB_ID_TV_TITLE);
+ varstr = DatabaseUtils::GetField(FieldTitle, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("tvshowview.c%02d", VIDEODB_ID_TV_PLOT);
+ varstr = DatabaseUtils::GetField(FieldPlot, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("tvshowview.c%02d", VIDEODB_ID_TV_STATUS);
+ varstr = DatabaseUtils::GetField(FieldTvShowStatus, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("tvshowview.c%02d", VIDEODB_ID_TV_VOTES);
+ varstr = DatabaseUtils::GetField(FieldVotes, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("tvshowview.c%02d", VIDEODB_ID_TV_RATING);
+ varstr = DatabaseUtils::GetField(FieldRating, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("tvshowview.c%02d", VIDEODB_ID_TV_PREMIERED);
+ varstr = DatabaseUtils::GetField(FieldYear, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("tvshowview.c%02d", VIDEODB_ID_TV_GENRE);
+ varstr = DatabaseUtils::GetField(FieldGenre, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("tvshowview.c%02d", VIDEODB_ID_TV_MPAA);
+ varstr = DatabaseUtils::GetField(FieldMPAA, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("tvshowview.c%02d", VIDEODB_ID_TV_STUDIOS);
+ varstr = DatabaseUtils::GetField(FieldStudio, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("tvshowview.c%02d", VIDEODB_ID_TV_SORTTITLE);
+ varstr = DatabaseUtils::GetField(FieldSortTitle, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "tvshowview.strPath";
+ varstr = DatabaseUtils::GetField(FieldPath, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "tvshowview.dateAdded";
+ varstr = DatabaseUtils::GetField(FieldDateAdded, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "tvshowview.totalSeasons";
+ varstr = DatabaseUtils::GetField(FieldSeason, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "tvshowview.totalCount";
+ varstr = DatabaseUtils::GetField(FieldNumberOfEpisodes, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "tvshowview.watchedcount";
+ varstr = DatabaseUtils::GetField(FieldNumberOfWatchedEpisodes,
+ MediaTypeTvShow, DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "";
+ varstr = DatabaseUtils::GetField(FieldRandom, MediaTypeTvShow,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestDatabaseUtils, GetField_MediaTypeEpisode)
+{
+ CStdString refstr, varstr;
+
+ refstr = "episodeview.idEpisode";
+ varstr = DatabaseUtils::GetField(FieldId, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("episodeview.c%02d", VIDEODB_ID_EPISODE_TITLE);
+ varstr = DatabaseUtils::GetField(FieldTitle, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("episodeview.c%02d", VIDEODB_ID_EPISODE_PLOT);
+ varstr = DatabaseUtils::GetField(FieldPlot, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("episodeview.c%02d", VIDEODB_ID_EPISODE_VOTES);
+ varstr = DatabaseUtils::GetField(FieldVotes, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("episodeview.c%02d", VIDEODB_ID_EPISODE_RATING);
+ varstr = DatabaseUtils::GetField(FieldRating, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("episodeview.c%02d", VIDEODB_ID_EPISODE_CREDITS);
+ varstr = DatabaseUtils::GetField(FieldWriter, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("episodeview.c%02d", VIDEODB_ID_EPISODE_AIRED);
+ varstr = DatabaseUtils::GetField(FieldAirDate, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("episodeview.c%02d", VIDEODB_ID_EPISODE_RUNTIME);
+ varstr = DatabaseUtils::GetField(FieldTime, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("episodeview.c%02d", VIDEODB_ID_EPISODE_DIRECTOR);
+ varstr = DatabaseUtils::GetField(FieldDirector, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("episodeview.c%02d", VIDEODB_ID_EPISODE_SEASON);
+ varstr = DatabaseUtils::GetField(FieldSeason, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = StringUtils::Format("episodeview.c%02d", VIDEODB_ID_EPISODE_EPISODE);
+ varstr = DatabaseUtils::GetField(FieldEpisodeNumber, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "episodeview.strFilename";
+ varstr = DatabaseUtils::GetField(FieldFilename, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "episodeview.strPath";
+ varstr = DatabaseUtils::GetField(FieldPath, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "episodeview.playCount";
+ varstr = DatabaseUtils::GetField(FieldPlaycount, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "episodeview.lastPlayed";
+ varstr = DatabaseUtils::GetField(FieldLastPlayed, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "episodeview.dateAdded";
+ varstr = DatabaseUtils::GetField(FieldDateAdded, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "episodeview.strTitle";
+ varstr = DatabaseUtils::GetField(FieldTvShowTitle, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "episodeview.premiered";
+ varstr = DatabaseUtils::GetField(FieldYear, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "episodeview.mpaa";
+ varstr = DatabaseUtils::GetField(FieldMPAA, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "episodeview.strStudio";
+ varstr = DatabaseUtils::GetField(FieldStudio, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "";
+ varstr = DatabaseUtils::GetField(FieldRandom, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestDatabaseUtils, GetField_FieldRandom)
+{
+ std::string refstr, varstr;
+
+ refstr = "";
+ varstr = DatabaseUtils::GetField(FieldRandom, MediaTypeEpisode,
+ DatabaseQueryPartSelect);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "";
+ varstr = DatabaseUtils::GetField(FieldRandom, MediaTypeEpisode,
+ DatabaseQueryPartWhere);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "RANDOM()";
+ varstr = DatabaseUtils::GetField(FieldRandom, MediaTypeEpisode,
+ DatabaseQueryPartOrderBy);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestDatabaseUtils, GetFieldIndex_None)
+{
+ int refindex, varindex;
+
+ refindex = -1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldRandom, MediaTypeNone);
+ EXPECT_EQ(refindex, varindex);
+
+ varindex = DatabaseUtils::GetFieldIndex(FieldNone, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+}
+
+/* TODO: Should enums in CMusicDatabase be made public instead? */
+TEST(TestDatabaseUtils, GetFieldIndex_MediaTypeAlbum)
+{
+ int refindex, varindex;
+ TestDatabaseUtilsHelper a;
+
+ refindex = a.album_idAlbum;
+ varindex = DatabaseUtils::GetFieldIndex(FieldId, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.album_strAlbum;
+ varindex = DatabaseUtils::GetFieldIndex(FieldAlbum, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.album_strArtists;
+ varindex = DatabaseUtils::GetFieldIndex(FieldArtist, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.album_strArtists;
+ varindex = DatabaseUtils::GetFieldIndex(FieldAlbumArtist, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.album_strGenres;
+ varindex = DatabaseUtils::GetFieldIndex(FieldGenre, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.album_iYear;
+ varindex = DatabaseUtils::GetFieldIndex(FieldYear, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.album_strMoods;
+ varindex = DatabaseUtils::GetFieldIndex(FieldMoods, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.album_strStyles;
+ varindex = DatabaseUtils::GetFieldIndex(FieldStyles, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.album_strThemes;
+ varindex = DatabaseUtils::GetFieldIndex(FieldThemes, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.album_strReview;
+ varindex = DatabaseUtils::GetFieldIndex(FieldReview, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.album_strLabel;
+ varindex = DatabaseUtils::GetFieldIndex(FieldMusicLabel, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.album_strType;
+ varindex = DatabaseUtils::GetFieldIndex(FieldAlbumType, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.album_iRating;
+ varindex = DatabaseUtils::GetFieldIndex(FieldRating, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = -1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldRandom, MediaTypeAlbum);
+ EXPECT_EQ(refindex, varindex);
+}
+
+TEST(TestDatabaseUtils, GetFieldIndex_MediaTypeSong)
+{
+ int refindex, varindex;
+ TestDatabaseUtilsHelper a;
+
+ refindex = a.song_idSong;
+ varindex = DatabaseUtils::GetFieldIndex(FieldId, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_strTitle;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTitle, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_iTrack;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTrackNumber, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_iDuration;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTime, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_iYear;
+ varindex = DatabaseUtils::GetFieldIndex(FieldYear, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_strFileName;
+ varindex = DatabaseUtils::GetFieldIndex(FieldFilename, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_iTimesPlayed;
+ varindex = DatabaseUtils::GetFieldIndex(FieldPlaycount, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_iStartOffset;
+ varindex = DatabaseUtils::GetFieldIndex(FieldStartOffset, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_iEndOffset;
+ varindex = DatabaseUtils::GetFieldIndex(FieldEndOffset, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_lastplayed;
+ varindex = DatabaseUtils::GetFieldIndex(FieldLastPlayed, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_rating;
+ varindex = DatabaseUtils::GetFieldIndex(FieldRating, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_comment;
+ varindex = DatabaseUtils::GetFieldIndex(FieldComment, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_strAlbum;
+ varindex = DatabaseUtils::GetFieldIndex(FieldAlbum, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_strPath;
+ varindex = DatabaseUtils::GetFieldIndex(FieldPath, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_strArtists;
+ varindex = DatabaseUtils::GetFieldIndex(FieldArtist, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = a.song_strGenres;
+ varindex = DatabaseUtils::GetFieldIndex(FieldGenre, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = -1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldRandom, MediaTypeSong);
+ EXPECT_EQ(refindex, varindex);
+}
+
+TEST(TestDatabaseUtils, GetFieldIndex_MediaTypeMusicVideo)
+{
+ int refindex, varindex;
+
+ refindex = 0;
+ varindex = DatabaseUtils::GetFieldIndex(FieldId, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_MUSICVIDEO_TITLE + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTitle, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_MUSICVIDEO_RUNTIME + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTime, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_MUSICVIDEO_DIRECTOR + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldDirector, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_MUSICVIDEO_STUDIOS + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldStudio, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_MUSICVIDEO_YEAR + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldYear, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_MUSICVIDEO_PLOT + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldPlot, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_MUSICVIDEO_ALBUM + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldAlbum, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_MUSICVIDEO_ARTIST + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldArtist, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_MUSICVIDEO_GENRE + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldGenre, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_MUSICVIDEO_TRACK + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTrackNumber, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_MUSICVIDEO_FILE;
+ varindex = DatabaseUtils::GetFieldIndex(FieldFilename, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_MUSICVIDEO_PATH;
+ varindex = DatabaseUtils::GetFieldIndex(FieldPath, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_MUSICVIDEO_PLAYCOUNT;
+ varindex = DatabaseUtils::GetFieldIndex(FieldPlaycount, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_MUSICVIDEO_LASTPLAYED;
+ varindex = DatabaseUtils::GetFieldIndex(FieldLastPlayed, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_MUSICVIDEO_DATEADDED;
+ varindex = DatabaseUtils::GetFieldIndex(FieldDateAdded, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = -1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldRandom, MediaTypeMusicVideo);
+ EXPECT_EQ(refindex, varindex);
+}
+
+TEST(TestDatabaseUtils, GetFieldIndex_MediaTypeMovie)
+{
+ int refindex, varindex;
+
+ refindex = 0;
+ varindex = DatabaseUtils::GetFieldIndex(FieldId, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_TITLE + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTitle, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_SORTTITLE + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldSortTitle, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_PLOT + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldPlot, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_PLOTOUTLINE + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldPlotOutline, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_TAGLINE + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTagline, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_VOTES + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldVotes, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_RATING + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldRating, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_CREDITS + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldWriter, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_YEAR + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldYear, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_RUNTIME + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTime, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_MPAA + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldMPAA, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_TOP250 + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTop250, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_GENRE + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldGenre, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_DIRECTOR + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldDirector, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_STUDIOS + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldStudio, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_TRAILER + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTrailer, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_COUNTRY + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldCountry, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_MOVIE_FILE + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldFilename, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_MOVIE_PATH;
+ varindex = DatabaseUtils::GetFieldIndex(FieldPath, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_MOVIE_PLAYCOUNT;
+ varindex = DatabaseUtils::GetFieldIndex(FieldPlaycount, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_MOVIE_LASTPLAYED;
+ varindex = DatabaseUtils::GetFieldIndex(FieldLastPlayed, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_MOVIE_DATEADDED;
+ varindex = DatabaseUtils::GetFieldIndex(FieldDateAdded, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = -1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldRandom, MediaTypeMovie);
+ EXPECT_EQ(refindex, varindex);
+}
+
+TEST(TestDatabaseUtils, GetFieldIndex_MediaTypeTvShow)
+{
+ int refindex, varindex;
+
+ refindex = 0;
+ varindex = DatabaseUtils::GetFieldIndex(FieldId, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_TV_TITLE + 1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTitle, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_TV_SORTTITLE + 1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldSortTitle, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_TV_PLOT + 1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldPlot, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_TV_STATUS + 1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTvShowStatus, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_TV_VOTES + 1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldVotes, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_TV_RATING + 1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldRating, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_TV_PREMIERED + 1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldYear, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_TV_GENRE + 1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldGenre, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_TV_MPAA + 1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldMPAA, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_TV_STUDIOS + 1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldStudio, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_TVSHOW_PATH;
+ varindex = DatabaseUtils::GetFieldIndex(FieldPath, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_TVSHOW_DATEADDED;
+ varindex = DatabaseUtils::GetFieldIndex(FieldDateAdded, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_TVSHOW_NUM_EPISODES;
+ varindex = DatabaseUtils::GetFieldIndex(FieldNumberOfEpisodes, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_TVSHOW_NUM_WATCHED;
+ varindex = DatabaseUtils::GetFieldIndex(FieldNumberOfWatchedEpisodes, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_TVSHOW_NUM_SEASONS;
+ varindex = DatabaseUtils::GetFieldIndex(FieldSeason, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = -1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldRandom, MediaTypeTvShow);
+ EXPECT_EQ(refindex, varindex);
+}
+
+TEST(TestDatabaseUtils, GetFieldIndex_MediaTypeEpisode)
+{
+ int refindex, varindex;
+
+ refindex = 0;
+ varindex = DatabaseUtils::GetFieldIndex(FieldId, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_EPISODE_TITLE + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTitle, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_EPISODE_PLOT + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldPlot, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_EPISODE_VOTES + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldVotes, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_EPISODE_RATING + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldRating, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_EPISODE_CREDITS + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldWriter, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_EPISODE_AIRED + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldAirDate, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_EPISODE_RUNTIME + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTime, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_EPISODE_DIRECTOR + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldDirector, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_EPISODE_SEASON + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldSeason, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_ID_EPISODE_EPISODE + 2;
+ varindex = DatabaseUtils::GetFieldIndex(FieldEpisodeNumber, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_EPISODE_FILE;
+ varindex = DatabaseUtils::GetFieldIndex(FieldFilename, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_EPISODE_PATH;
+ varindex = DatabaseUtils::GetFieldIndex(FieldPath, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_EPISODE_PLAYCOUNT;
+ varindex = DatabaseUtils::GetFieldIndex(FieldPlaycount, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_EPISODE_LASTPLAYED;
+ varindex = DatabaseUtils::GetFieldIndex(FieldLastPlayed, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_EPISODE_DATEADDED;
+ varindex = DatabaseUtils::GetFieldIndex(FieldDateAdded, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_EPISODE_TVSHOW_NAME;
+ varindex = DatabaseUtils::GetFieldIndex(FieldTvShowTitle, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_EPISODE_TVSHOW_STUDIO;
+ varindex = DatabaseUtils::GetFieldIndex(FieldStudio, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_EPISODE_TVSHOW_AIRED;
+ varindex = DatabaseUtils::GetFieldIndex(FieldYear, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = VIDEODB_DETAILS_EPISODE_TVSHOW_MPAA;
+ varindex = DatabaseUtils::GetFieldIndex(FieldMPAA, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+
+ refindex = -1;
+ varindex = DatabaseUtils::GetFieldIndex(FieldRandom, MediaTypeEpisode);
+ EXPECT_EQ(refindex, varindex);
+}
+
+TEST(TestDatabaseUtils, GetSelectFields)
+{
+ Fields fields;
+ FieldList fieldlist;
+
+ EXPECT_FALSE(DatabaseUtils::GetSelectFields(fields, MediaTypeAlbum,
+ fieldlist));
+
+ fields.insert(FieldId);
+ fields.insert(FieldGenre);
+ fields.insert(FieldAlbum);
+ fields.insert(FieldArtist);
+ fields.insert(FieldTitle);
+ EXPECT_FALSE(DatabaseUtils::GetSelectFields(fields, MediaTypeNone,
+ fieldlist));
+ EXPECT_TRUE(DatabaseUtils::GetSelectFields(fields, MediaTypeAlbum,
+ fieldlist));
+ EXPECT_FALSE(fieldlist.empty());
+}
+
+TEST(TestDatabaseUtils, GetFieldValue)
+{
+ CVariant v_null, v_string;
+ dbiplus::field_value f_null, f_string("test");
+
+ f_null.set_isNull();
+ EXPECT_TRUE(DatabaseUtils::GetFieldValue(f_null, v_null));
+ EXPECT_TRUE(v_null.isNull());
+
+ EXPECT_TRUE(DatabaseUtils::GetFieldValue(f_string, v_string));
+ EXPECT_FALSE(v_string.isNull());
+ EXPECT_TRUE(v_string.isString());
+}
+
+/* TODO: Need some way to test this function */
+// TEST(TestDatabaseUtils, GetDatabaseResults)
+// {
+// static bool GetDatabaseResults(MediaType mediaType, const FieldList &fields,
+// const std::auto_ptr<dbiplus::Dataset> &dataset,
+// DatabaseResults &results);
+// }
+
+TEST(TestDatabaseUtils, BuildLimitClause)
+{
+ std::string a = DatabaseUtils::BuildLimitClause(100);
+ EXPECT_STREQ(" LIMIT 100", a.c_str());
+}
+
+// class DatabaseUtils
+// {
+// public:
+//
+//
+// static std::string BuildLimitClause(int end, int start = 0);
+// };
diff --git a/src/utils/test/TestEndianSwap.cpp b/src/utils/test/TestEndianSwap.cpp
new file mode 100644
index 0000000000..bbb16441cb
--- /dev/null
+++ b/src/utils/test/TestEndianSwap.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/EndianSwap.h"
+
+#include "gtest/gtest.h"
+
+TEST(TestEndianSwap, Endian_Swap16)
+{
+ uint16_t ref, var;
+ ref = 0x00FF;
+ var = Endian_Swap16(0xFF00);
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestEndianSwap, Endian_Swap32)
+{
+ uint32_t ref, var;
+ ref = 0x00FF00FF;
+ var = Endian_Swap32(0xFF00FF00);
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestEndianSwap, Endian_Swap64)
+{
+ uint64_t ref, var;
+ ref = UINT64_C(0x00FF00FF00FF00FF);
+ var = Endian_Swap64(UINT64_C(0xFF00FF00FF00FF00));
+ EXPECT_EQ(ref, var);
+}
+
+#ifndef WORDS_BIGENDIAN
+TEST(TestEndianSwap, Endian_SwapLE16)
+{
+ uint16_t ref, var;
+ ref = 0x00FF;
+ var = Endian_SwapLE16(0x00FF);
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestEndianSwap, Endian_SwapLE32)
+{
+ uint32_t ref, var;
+ ref = 0x00FF00FF;
+ var = Endian_SwapLE32(0x00FF00FF);
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestEndianSwap, Endian_SwapLE64)
+{
+ uint64_t ref, var;
+ ref = UINT64_C(0x00FF00FF00FF00FF);
+ var = Endian_SwapLE64(UINT64_C(0x00FF00FF00FF00FF));
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestEndianSwap, Endian_SwapBE16)
+{
+ uint16_t ref, var;
+ ref = 0x00FF;
+ var = Endian_SwapBE16(0xFF00);
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestEndianSwap, Endian_SwapBE32)
+{
+ uint32_t ref, var;
+ ref = 0x00FF00FF;
+ var = Endian_SwapBE32(0xFF00FF00);
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestEndianSwap, Endian_SwapBE64)
+{
+ uint64_t ref, var;
+ ref = UINT64_C(0x00FF00FF00FF00FF);
+ var = Endian_SwapBE64(UINT64_C(0xFF00FF00FF00FF00));
+ EXPECT_EQ(ref, var);
+}
+#else
+TEST(TestEndianSwap, Endian_SwapLE16)
+{
+ uint16_t ref, var;
+ ref = 0x00FF;
+ var = Endian_SwapLE16(0xFF00);
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestEndianSwap, Endian_SwapLE32)
+{
+ uint32_t ref, var;
+ ref = 0x00FF00FF;
+ var = Endian_SwapLE32(0xFF00FF00);
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestEndianSwap, Endian_SwapLE64)
+{
+ uint64_t ref, var;
+ ref = UINT64_C(0x00FF00FF00FF00FF);
+ var = Endian_SwapLE64(UINT64_C(0xFF00FF00FF00FF00));
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestEndianSwap, Endian_SwapBE16)
+{
+ uint16_t ref, var;
+ ref = 0x00FF;
+ var = Endian_SwapBE16(0x00FF);
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestEndianSwap, Endian_SwapBE32)
+{
+ uint32_t ref, var;
+ ref = 0x00FF00FF;
+ var = Endian_SwapBE32(0x00FF00FF);
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestEndianSwap, Endian_SwapBE64)
+{
+ uint64_t ref, var;
+ ref = UINT64_C(0x00FF00FF00FF00FF);
+ var = Endian_SwapBE64(UINT64_C(0x00FF00FF00FF00FF));
+ EXPECT_EQ(ref, var);
+}
+#endif
diff --git a/src/utils/test/TestFileOperationJob.cpp b/src/utils/test/TestFileOperationJob.cpp
new file mode 100644
index 0000000000..47a12e02e9
--- /dev/null
+++ b/src/utils/test/TestFileOperationJob.cpp
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/FileOperationJob.h"
+#include "filesystem/File.h"
+#include "filesystem/Directory.h"
+#include "utils/URIUtils.h"
+
+#include "test/TestUtils.h"
+
+#include "gtest/gtest.h"
+
+TEST(TestFileOperationJob, ActionCopy)
+{
+ XFILE::CFile *tmpfile;
+ CStdString tmpfilepath, destfile;
+ CFileItemList items;
+ CFileOperationJob job;
+
+ ASSERT_TRUE((tmpfile = XBMC_CREATETEMPFILE("")));
+ tmpfilepath = XBMC_TEMPFILEPATH(tmpfile);
+ tmpfile->Close();
+
+ CFileItemPtr item(new CFileItem(tmpfilepath));
+ item->SetPath(tmpfilepath);
+ item->m_bIsFolder = false;
+ item->Select(true);
+ items.Add(item);
+
+ CStdString destpath = URIUtils::GetDirectory(tmpfilepath);
+ destpath = URIUtils::AddFileToFolder(destpath, "copy");
+ destfile = URIUtils::AddFileToFolder(destpath, URIUtils::GetFileName(tmpfilepath));
+ ASSERT_FALSE(XFILE::CFile::Exists(destfile));
+
+ job.SetFileOperation(CFileOperationJob::ActionCopy, items, destpath);
+ EXPECT_EQ(CFileOperationJob::ActionCopy, job.GetAction());
+
+ EXPECT_TRUE(job.DoWork());
+ EXPECT_TRUE(XFILE::CFile::Exists(tmpfilepath));
+ EXPECT_TRUE(XFILE::CFile::Exists(destfile));
+
+ EXPECT_TRUE(XBMC_DELETETEMPFILE(tmpfile));
+ EXPECT_TRUE(XFILE::CFile::Delete(destfile));
+ EXPECT_TRUE(XFILE::CDirectory::Remove(destpath));
+}
+
+TEST(TestFileOperationJob, ActionMove)
+{
+ XFILE::CFile *tmpfile;
+ CStdString tmpfilepath, destfile;
+ CFileItemList items;
+ CFileOperationJob job;
+
+ ASSERT_TRUE((tmpfile = XBMC_CREATETEMPFILE("")));
+ tmpfilepath = XBMC_TEMPFILEPATH(tmpfile);
+ tmpfile->Close();
+
+ CFileItemPtr item(new CFileItem(tmpfilepath));
+ item->SetPath(tmpfilepath);
+ item->m_bIsFolder = false;
+ item->Select(true);
+ items.Add(item);
+
+ CStdString destpath = URIUtils::GetDirectory(tmpfilepath);
+ destpath = URIUtils::AddFileToFolder(destpath, "move");
+ destfile = URIUtils::AddFileToFolder(destpath, URIUtils::GetFileName(tmpfilepath));
+ ASSERT_FALSE(XFILE::CFile::Exists(destfile));
+ ASSERT_TRUE(XFILE::CDirectory::Create(destpath));
+
+ job.SetFileOperation(CFileOperationJob::ActionMove, items, destpath);
+ EXPECT_EQ(CFileOperationJob::ActionMove, job.GetAction());
+
+ EXPECT_TRUE(job.DoWork());
+ EXPECT_FALSE(XFILE::CFile::Exists(tmpfilepath));
+ EXPECT_TRUE(XFILE::CFile::Exists(destfile));
+
+ EXPECT_TRUE(XFILE::CFile::Delete(destfile));
+ EXPECT_TRUE(XFILE::CDirectory::Remove(destpath));
+}
+
+TEST(TestFileOperationJob, ActionDelete)
+{
+ XFILE::CFile *tmpfile;
+ CStdString tmpfilepath, destfile;
+ CFileItemList items;
+ CFileOperationJob job;
+
+ ASSERT_TRUE((tmpfile = XBMC_CREATETEMPFILE("")));
+ tmpfilepath = XBMC_TEMPFILEPATH(tmpfile);
+ tmpfile->Close();
+
+ CFileItemPtr item(new CFileItem(tmpfilepath));
+ item->SetPath(tmpfilepath);
+ item->m_bIsFolder = false;
+ item->Select(true);
+ items.Add(item);
+
+ CStdString destpath = URIUtils::GetDirectory(tmpfilepath);
+ destpath = URIUtils::AddFileToFolder(destpath, "delete");
+ destfile = URIUtils::AddFileToFolder(destpath, URIUtils::GetFileName(tmpfilepath));
+ ASSERT_FALSE(XFILE::CFile::Exists(destfile));
+
+ job.SetFileOperation(CFileOperationJob::ActionCopy, items, destpath);
+ EXPECT_EQ(CFileOperationJob::ActionCopy, job.GetAction());
+
+ EXPECT_TRUE(job.DoWork());
+ EXPECT_TRUE(XFILE::CFile::Exists(tmpfilepath));
+ EXPECT_TRUE(XFILE::CFile::Exists(destfile));
+
+ job.SetFileOperation(CFileOperationJob::ActionDelete, items, "");
+ EXPECT_EQ(CFileOperationJob::ActionDelete, job.GetAction());
+
+ EXPECT_TRUE(job.DoWork());
+ EXPECT_FALSE(XFILE::CFile::Exists(tmpfilepath));
+
+ items.Clear();
+ CFileItemPtr item2(new CFileItem(destfile));
+ item2->SetPath(destfile);
+ item2->m_bIsFolder = false;
+ item2->Select(true);
+ items.Add(item2);
+
+ job.SetFileOperation(CFileOperationJob::ActionDelete, items, "");
+ EXPECT_EQ(CFileOperationJob::ActionDelete, job.GetAction());
+
+ EXPECT_TRUE(job.DoWork());
+ EXPECT_FALSE(XFILE::CFile::Exists(destfile));
+ EXPECT_TRUE(XFILE::CDirectory::Remove(destpath));
+}
+
+TEST(TestFileOperationJob, ActionReplace)
+{
+ XFILE::CFile *tmpfile;
+ CStdString tmpfilepath, destfile;
+ CFileItemList items;
+ CFileOperationJob job;
+
+ ASSERT_TRUE((tmpfile = XBMC_CREATETEMPFILE("")));
+ tmpfilepath = XBMC_TEMPFILEPATH(tmpfile);
+ tmpfile->Close();
+
+ CFileItemPtr item(new CFileItem(tmpfilepath));
+ item->SetPath(tmpfilepath);
+ item->m_bIsFolder = false;
+ item->Select(true);
+ items.Add(item);
+
+ CStdString destpath = URIUtils::GetDirectory(tmpfilepath);
+ destpath = URIUtils::AddFileToFolder(destpath, "replace");
+ destfile = URIUtils::AddFileToFolder(destpath, URIUtils::GetFileName(tmpfilepath));
+ ASSERT_FALSE(XFILE::CFile::Exists(destfile));
+
+ job.SetFileOperation(CFileOperationJob::ActionCopy, items, destpath);
+ EXPECT_EQ(CFileOperationJob::ActionCopy, job.GetAction());
+
+ EXPECT_TRUE(job.DoWork());
+ EXPECT_TRUE(XFILE::CFile::Exists(tmpfilepath));
+ EXPECT_TRUE(XFILE::CFile::Exists(destfile));
+
+ job.SetFileOperation(CFileOperationJob::ActionReplace, items, destpath);
+ EXPECT_EQ(CFileOperationJob::ActionReplace, job.GetAction());
+
+ EXPECT_TRUE(job.DoWork());
+ EXPECT_TRUE(XFILE::CFile::Exists(destfile));
+
+ EXPECT_TRUE(XBMC_DELETETEMPFILE(tmpfile));
+ EXPECT_TRUE(XFILE::CFile::Delete(destfile));
+ EXPECT_TRUE(XFILE::CDirectory::Remove(destpath));
+}
+
+TEST(TestFileOperationJob, ActionCreateFolder)
+{
+ XFILE::CFile *tmpfile;
+ CStdString tmpfilepath, destpath;
+ CFileItemList items;
+ CFileOperationJob job;
+
+ ASSERT_TRUE((tmpfile = XBMC_CREATETEMPFILE("")));
+ tmpfilepath = XBMC_TEMPFILEPATH(tmpfile);
+
+ CStdString tmpfiledirectory =
+ CXBMCTestUtils::Instance().TempFileDirectory(tmpfile);
+
+ tmpfile->Close();
+
+ destpath = tmpfilepath;
+ destpath += ".createfolder";
+ ASSERT_FALSE(XFILE::CFile::Exists(destpath));
+
+ CFileItemPtr item(new CFileItem(destpath));
+ item->SetPath(destpath);
+ item->m_bIsFolder = true;
+ item->Select(true);
+ items.Add(item);
+
+ job.SetFileOperation(CFileOperationJob::ActionCreateFolder, items, tmpfiledirectory);
+ EXPECT_EQ(CFileOperationJob::ActionCreateFolder, job.GetAction());
+
+ EXPECT_TRUE(job.DoWork());
+ EXPECT_TRUE(XFILE::CDirectory::Exists(destpath));
+
+ EXPECT_TRUE(XBMC_DELETETEMPFILE(tmpfile));
+ EXPECT_TRUE(XFILE::CDirectory::Remove(destpath));
+}
+
+// This test will fail until ActionDeleteFolder has a proper implementation
+TEST(TestFileOperationJob, ActionDeleteFolder)
+{
+ XFILE::CFile *tmpfile;
+ CStdString tmpfilepath, destpath;
+ CFileItemList items;
+ CFileOperationJob job;
+
+ ASSERT_TRUE((tmpfile = XBMC_CREATETEMPFILE("")));
+ tmpfilepath = XBMC_TEMPFILEPATH(tmpfile);
+
+ CStdString tmpfiledirectory =
+ CXBMCTestUtils::Instance().TempFileDirectory(tmpfile);
+
+ tmpfile->Close();
+
+ destpath = tmpfilepath;
+ destpath += ".deletefolder";
+ ASSERT_FALSE(XFILE::CFile::Exists(destpath));
+
+ CFileItemPtr item(new CFileItem(destpath));
+ item->SetPath(destpath);
+ item->m_bIsFolder = true;
+ item->Select(true);
+ items.Add(item);
+
+ job.SetFileOperation(CFileOperationJob::ActionCreateFolder, items, tmpfiledirectory);
+ EXPECT_EQ(CFileOperationJob::ActionCreateFolder, job.GetAction());
+
+ EXPECT_TRUE(job.DoWork());
+ EXPECT_TRUE(XFILE::CDirectory::Exists(destpath));
+
+ job.SetFileOperation(CFileOperationJob::ActionDeleteFolder, items, tmpfiledirectory);
+ EXPECT_EQ(CFileOperationJob::ActionDeleteFolder, job.GetAction());
+
+ EXPECT_TRUE(job.DoWork());
+ EXPECT_FALSE(XFILE::CDirectory::Exists(destpath));
+
+ EXPECT_TRUE(XBMC_DELETETEMPFILE(tmpfile));
+}
+
+TEST(TestFileOperationJob, GetFunctions)
+{
+ XFILE::CFile *tmpfile;
+ CStdString tmpfilepath, destfile;
+ CFileItemList items;
+ CFileOperationJob job;
+
+ ASSERT_TRUE((tmpfile = XBMC_CREATETEMPFILE("")));
+ tmpfilepath = XBMC_TEMPFILEPATH(tmpfile);
+ tmpfile->Close();
+
+ CFileItemPtr item(new CFileItem(tmpfilepath));
+ item->SetPath(tmpfilepath);
+ item->m_bIsFolder = false;
+ item->Select(true);
+ items.Add(item);
+
+ CStdString destpath = URIUtils::GetDirectory(tmpfilepath);
+ destpath = URIUtils::AddFileToFolder(destpath, "getfunctions");
+ destfile = URIUtils::AddFileToFolder(destpath, URIUtils::GetFileName(tmpfilepath));
+ ASSERT_FALSE(XFILE::CFile::Exists(destfile));
+
+ job.SetFileOperation(CFileOperationJob::ActionCopy, items, destpath);
+ EXPECT_EQ(CFileOperationJob::ActionCopy, job.GetAction());
+
+ EXPECT_TRUE(job.DoWork());
+ EXPECT_TRUE(XFILE::CFile::Exists(tmpfilepath));
+ EXPECT_TRUE(XFILE::CFile::Exists(destfile));
+
+ std::cout << "GetAverageSpeed(): " << job.GetAverageSpeed() << std::endl;
+ std::cout << "GetCurrentOperation(): " << job.GetCurrentOperation() << std::endl;
+ std::cout << "GetCurrentFile(): " << job.GetCurrentFile() << std::endl;
+ EXPECT_FALSE(job.GetItems().IsEmpty());
+
+ EXPECT_TRUE(XBMC_DELETETEMPFILE(tmpfile));
+ EXPECT_TRUE(XFILE::CFile::Delete(destfile));
+ EXPECT_TRUE(XFILE::CDirectory::Remove(destpath));
+}
diff --git a/src/utils/test/TestFileUtils.cpp b/src/utils/test/TestFileUtils.cpp
new file mode 100644
index 0000000000..45f9682892
--- /dev/null
+++ b/src/utils/test/TestFileUtils.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/FileUtils.h"
+#include "filesystem/File.h"
+
+#include "test/TestUtils.h"
+
+#include "gtest/gtest.h"
+
+TEST(TestFileUtils, DeleteItem_CFileItemPtr)
+{
+ XFILE::CFile *tmpfile;
+ CStdString tmpfilepath;
+
+ ASSERT_TRUE((tmpfile = XBMC_CREATETEMPFILE("")));
+ tmpfilepath = XBMC_TEMPFILEPATH(tmpfile);
+
+ CFileItemPtr item(new CFileItem(tmpfilepath));
+ item->SetPath(tmpfilepath);
+ item->m_bIsFolder = false;
+ item->Select(true);
+
+ EXPECT_TRUE(CFileUtils::DeleteItem(item));
+}
+
+TEST(TestFileUtils, DeleteItem_CStdString)
+{
+ XFILE::CFile *tmpfile;
+
+ ASSERT_TRUE((tmpfile = XBMC_CREATETEMPFILE("")));
+ EXPECT_TRUE(CFileUtils::DeleteItem(XBMC_TEMPFILEPATH(tmpfile)));
+}
+
+/* Executing RenameFile() requires input from the user */
+// static bool RenameFile(const CStdString &strFile);
diff --git a/src/utils/test/TestGlobalsHandling.cpp b/src/utils/test/TestGlobalsHandling.cpp
new file mode 100644
index 0000000000..fc00763fd2
--- /dev/null
+++ b/src/utils/test/TestGlobalsHandling.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/test/TestGlobalsHandlingPattern1.h"
+
+#include "gtest/gtest.h"
+
+using namespace xbmcutil;
+using namespace test;
+
+bool TestGlobalPattern1::ctorCalled = false;
+bool TestGlobalPattern1::dtorCalled = false;
+
+TEST(TestGlobal, Pattern1)
+{
+ EXPECT_TRUE(TestGlobalPattern1::ctorCalled);
+ {
+ boost::shared_ptr<TestGlobalPattern1> ptr = g_testGlobalPattern1Ref;
+ }
+}
diff --git a/src/utils/test/TestGlobalsHandlingPattern1.h b/src/utils/test/TestGlobalsHandlingPattern1.h
new file mode 100644
index 0000000000..55482dcf9a
--- /dev/null
+++ b/src/utils/test/TestGlobalsHandlingPattern1.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#include "utils/GlobalsHandling.h"
+
+#include <iostream>
+
+namespace xbmcutil
+{
+ namespace test
+ {
+ class TestGlobalPattern1
+ {
+ public:
+ static bool ctorCalled;
+ static bool dtorCalled;
+
+ int somethingToAccess;
+
+ TestGlobalPattern1() : somethingToAccess(0) { ctorCalled = true; }
+ ~TestGlobalPattern1()
+ {
+ std::cout << "Clean shutdown of TestGlobalPattern1" << std::endl << std::flush;
+ dtorCalled = true;
+ }
+
+ void beHappy() { if (somethingToAccess) throw somethingToAccess; }
+ };
+ }
+}
+
+XBMC_GLOBAL_REF(xbmcutil::test::TestGlobalPattern1,g_testGlobalPattern1);
+#define g_testGlobalPattern1 XBMC_GLOBAL_USE(xbmcutil::test::TestGlobalPattern1)
diff --git a/src/utils/test/TestHTMLTable.cpp b/src/utils/test/TestHTMLTable.cpp
new file mode 100644
index 0000000000..a13ff2b68f
--- /dev/null
+++ b/src/utils/test/TestHTMLTable.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/HTMLTable.h"
+
+#include "gtest/gtest.h"
+
+// class CHTMLRow
+// {
+// public:
+// CHTMLRow(void);
+// virtual ~CHTMLRow(void);
+// int GetColumns() const;
+// const std::string& GetColumValue(int iColumn) const;
+// void Parse(const std::string& strTableRow);
+//
+// protected:
+// std::vector<std::string> m_vecColums;
+// };
+//
+// class CHTMLTable
+// {
+// public:
+// CHTMLTable(void);
+// virtual ~CHTMLTable(void);
+// void Parse(const std::string& strHTML);
+// int GetRows() const;
+// const CHTMLRow& GetRow(int iRow) const;
+// protected:
+// std::vector<CHTMLRow> m_vecRows;
+// };
+
+TEST(TestHTMLTable, General)
+{
+ HTML::CHTMLTable table;
+ HTML::CHTMLRow row1, row2;
+ std::string str;
+ str = "<table>\n"
+ " <tr>\n"
+ " <td>r1c1</td>\n"
+ " <td>r1c2</td>\n"
+ " </tr>\n"
+ " <tr>\n"
+ " <td>r2c1</td>\n"
+ " <td>r2c2</td>\n"
+ " </tr>\n"
+ " <tr>\n"
+ " <td>r3c1</td>\n"
+ " <td>r3c2</td>\n"
+ " </tr>\n"
+ "</table>\n";
+ table.Parse(str);
+ EXPECT_EQ(3, table.GetRows());
+
+ row1 = table.GetRow(0);
+ EXPECT_EQ(2, row1.GetColumns());
+ EXPECT_STREQ("r1c1", row1.GetColumValue(0).c_str());
+
+ str = "<tr>\n"
+ " <td>new row1 column1</td>\n"
+ " <td>new row1 column2</td>\n"
+ " <td>new row1 column3</td>\n"
+ "</tr>\n";
+ row2.Parse(str);
+ EXPECT_EQ(3, row2.GetColumns());
+ EXPECT_STREQ("new row1 column2", row2.GetColumValue(1).c_str());
+}
diff --git a/src/utils/test/TestHTMLUtil.cpp b/src/utils/test/TestHTMLUtil.cpp
new file mode 100644
index 0000000000..a7032ae2a4
--- /dev/null
+++ b/src/utils/test/TestHTMLUtil.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/HTMLUtil.h"
+
+#include "gtest/gtest.h"
+
+TEST(TestHTMLUtil, FindTag)
+{
+ HTML::CHTMLUtil util;
+ std::string str, found;
+ str = "<!DOCTYPE html>\n"
+ "<html>\n"
+ " <head class=\"someclass\">\n"
+ " <body>\n"
+ " <p>blah blah blah</p>\n"
+ " </body>\n"
+ " </head>\n"
+ "</html>\n";
+ EXPECT_EQ(54, util.FindTag(str, "<body", found));
+ EXPECT_STREQ("<body>", found.c_str());
+
+ found.clear();
+ EXPECT_EQ(-1, util.FindTag(str, "<span", found));
+ EXPECT_STREQ("", found.c_str());
+}
+
+TEST(TestHTMLUtil, FindClosingTag)
+{
+ HTML::CHTMLUtil util;
+ std::string str, found;
+ str = "<!DOCTYPE html>\n"
+ "<html>\n"
+ " <head class=\"someclass\">\n"
+ " <body>\n"
+ " <p>blah blah blah</p>\n"
+ " </body>\n"
+ " </head>\n"
+ "</html>\n";
+ /* Need to set position past '<body>' tag */
+ EXPECT_EQ(93, util.FindClosingTag(str, "body", found, 61));
+ EXPECT_STREQ("</body>", found.c_str());
+}
+
+TEST(TestHTMLUtil, getValueOfTag)
+{
+ HTML::CHTMLUtil util;
+ std::string str, value;
+ str = "<p>blah blah blah</p>";
+ util.getValueOfTag(str, value);
+ EXPECT_STREQ("blah blah blah", value.c_str());
+}
+
+TEST(TestHTMLUtil, getAttributeOfTag)
+{
+ HTML::CHTMLUtil util;
+ std::string str, tag, value;
+ str = "<head class=\"someclass\"></head>\n";
+ util.getAttributeOfTag(str, "class", value);
+ EXPECT_STREQ("\"someclass", value.c_str());
+}
+
+TEST(TestHTMLUtil, RemoveTags)
+{
+ std::string str;
+ str = "<!DOCTYPE html>\n"
+ "<html>\n"
+ " <head class=\"someclass\">\n"
+ " <body>\n"
+ " <p>blah blah blah</p>\n"
+ " </body>\n"
+ " </head>\n"
+ "</html>\n";
+ HTML::CHTMLUtil::RemoveTags(str);
+ EXPECT_STREQ("\n\n \n \n blah blah blah\n \n \n\n",
+ str.c_str());
+}
+
+TEST(TestHTMLUtil, ConvertHTMLToW)
+{
+ std::wstring inw, refstrw, varstrw;
+ inw = L"&aring;&amp;&euro;";
+ refstrw = L"å&€";
+ HTML::CHTMLUtil::ConvertHTMLToW(inw, varstrw);
+ EXPECT_STREQ(refstrw.c_str(), varstrw.c_str());
+}
diff --git a/src/utils/test/TestHttpHeader.cpp b/src/utils/test/TestHttpHeader.cpp
new file mode 100644
index 0000000000..170282685f
--- /dev/null
+++ b/src/utils/test/TestHttpHeader.cpp
@@ -0,0 +1,515 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string.h>
+#include "utils/HttpHeader.h"
+#include "gtest/gtest.h"
+
+#define CHECK_CNT_TYPE_NAME "Content-Type"
+#define CHECK_CONTENT_TYPE_HTML "text/html"
+#define CHECK_CONTENT_TYPE_HTML_CHRS "text/html; charset=WINDOWS-1251"
+#define CHECK_CONTENT_TYPE_XML_CHRS "text/xml; charset=uTf-8"
+#define CHECK_CONTENT_TYPE_TEXT "text/plain"
+#define CHECK_DATE_NAME "Date"
+#define CHECK_DATE_VALUE1 "Thu, 09 Jan 2014 17:58:30 GMT"
+#define CHECK_DATE_VALUE2 "Thu, 09 Jan 2014 20:21:20 GMT"
+#define CHECK_DATE_VALUE3 "Thu, 09 Jan 2014 20:25:02 GMT"
+#define CHECK_PROT_LINE_200 "HTTP/1.1 200 OK"
+#define CHECK_PROT_LINE_301 "HTTP/1.1 301 Moved Permanently"
+
+#define CHECK_HEADER_SMPL CHECK_PROT_LINE_200 "\r\n" \
+ CHECK_CNT_TYPE_NAME ": " CHECK_CONTENT_TYPE_HTML "\r\n" \
+ "\r\n"
+
+#define CHECK_HEADER_L1 CHECK_PROT_LINE_200 "\r\n" \
+ "Server: nginx/1.4.4\r\n" \
+ CHECK_DATE_NAME ": " CHECK_DATE_VALUE1 "\r\n" \
+ CHECK_CNT_TYPE_NAME ": " CHECK_CONTENT_TYPE_HTML_CHRS "\r\n" \
+ "Transfer-Encoding: chunked\r\n" \
+ "Connection: close\r\n" \
+ "Set-Cookie: PHPSESSID=90857d437518db8f0944ca012761048a; path=/; domain=example.com\r\n" \
+ "Expires: Thu, 19 Nov 1981 08:52:00 GMT\r\n" \
+ "Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0\r\n" \
+ "Pragma: no-cache\r\n" \
+ "Set-Cookie: user_country=ot; expires=Thu, 09-Jan-2014 18:58:30 GMT; path=/; domain=.example.com\r\n" \
+ "\r\n"
+
+#define CHECK_HEADER_R CHECK_PROT_LINE_301 "\r\n" \
+ "Server: nginx/1.4.4\r\n" \
+ CHECK_DATE_NAME ": " CHECK_DATE_VALUE2 "\r\n" \
+ CHECK_CNT_TYPE_NAME ": " CHECK_CONTENT_TYPE_HTML "\r\n" \
+ "Content-Length: 150\r\n" \
+ "Connection: close\r\n" \
+ "Location: http://www.Example.Com\r\n" \
+ "\r\n"
+
+#define CHECK_HEADER_L2 CHECK_PROT_LINE_200 "\r\n" \
+ CHECK_DATE_NAME ": " CHECK_DATE_VALUE3 "\r\n" \
+ "Server: Apache/2.4.7 (Unix) mod_wsgi/3.4 Python/2.7.5 OpenSSL/1.0.1e\r\n" \
+ "Last-Modified: Thu, 09 Jan 2014 20:10:28 GMT\r\n" \
+ "ETag: \"9a97-4ef8f335ebd10\"\r\n" \
+ "Accept-Ranges: bytes\r\n" \
+ "Content-Length: 33355\r\n" \
+ "Vary: Accept-Encoding\r\n" \
+ "Cache-Control: max-age=3600\r\n" \
+ "Expires: Thu, 09 Jan 2014 21:25:02 GMT\r\n" \
+ "Connection: close\r\n" \
+ CHECK_CNT_TYPE_NAME ": " CHECK_CONTENT_TYPE_XML_CHRS "\r\n" \
+ "\r\n"
+
+// local helper function: replace substrings
+std::string strReplc(const std::string& str, const std::string& from, const std::string& to)
+{
+ std::string result;
+ size_t prevPos = 0;
+ size_t pos;
+ const size_t len = str.length();
+
+ do
+ {
+ pos = str.find(from, prevPos);
+ result.append(str, prevPos, pos - prevPos);
+ if (pos >= len)
+ break;
+ result.append(to);
+ prevPos = pos + from.length();
+ } while (true);
+
+ return result;
+}
+
+TEST(TestHttpHeader, General)
+{
+ /* check freshly created object */
+ CHttpHeader testHdr;
+ EXPECT_TRUE(testHdr.GetHeader().empty()) << "Newly created object is not empty";
+ EXPECT_TRUE(testHdr.GetProtoLine().empty()) << "Newly created object has non-empty protocol line";
+ EXPECT_TRUE(testHdr.GetMimeType().empty()) << "Newly created object has non-empty MIME-type";
+ EXPECT_TRUE(testHdr.GetCharset().empty()) << "Newly created object has non-empty charset";
+ EXPECT_TRUE(testHdr.GetValue("foo").empty()) << "Newly created object has some parameter";
+ EXPECT_TRUE(testHdr.GetValues("bar").empty()) << "Newly created object has some parameters";
+ EXPECT_FALSE(testHdr.IsHeaderDone()) << "Newly created object has \"parsing finished\" state";
+
+ /* check general functions in simple case */
+ testHdr.Parse(CHECK_HEADER_SMPL);
+ EXPECT_FALSE(testHdr.GetHeader().empty()) << "Parsed header is empty";
+ EXPECT_FALSE(testHdr.GetProtoLine().empty()) << "Parsed header has empty protocol line";
+ EXPECT_FALSE(testHdr.GetMimeType().empty()) << "Parsed header has empty MIME-type";
+ EXPECT_FALSE(testHdr.GetValue(CHECK_CNT_TYPE_NAME).empty()) << "Parsed header has empty \"" CHECK_CNT_TYPE_NAME "\" parameter";
+ EXPECT_FALSE(testHdr.GetValues(CHECK_CNT_TYPE_NAME).empty()) << "Parsed header has no \"" CHECK_CNT_TYPE_NAME "\" parameters";
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Parsed header has \"parsing not finished\" state";
+
+ /* check clearing of object */
+ testHdr.Clear();
+ EXPECT_TRUE(testHdr.GetHeader().empty()) << "Cleared object is not empty";
+ EXPECT_TRUE(testHdr.GetProtoLine().empty()) << "Cleared object has non-empty protocol line";
+ EXPECT_TRUE(testHdr.GetMimeType().empty()) << "Cleared object has non-empty MIME-type";
+ EXPECT_TRUE(testHdr.GetCharset().empty()) << "Cleared object has non-empty charset";
+ EXPECT_TRUE(testHdr.GetValue(CHECK_CNT_TYPE_NAME).empty()) << "Cleared object has some parameter";
+ EXPECT_TRUE(testHdr.GetValues(CHECK_CNT_TYPE_NAME).empty()) << "Cleared object has some parameters";
+ EXPECT_FALSE(testHdr.IsHeaderDone()) << "Cleared object has \"parsing finished\" state";
+
+ /* check general functions after object clearing */
+ testHdr.Parse(CHECK_HEADER_R);
+ EXPECT_FALSE(testHdr.GetHeader().empty()) << "Parsed header is empty";
+ EXPECT_FALSE(testHdr.GetProtoLine().empty()) << "Parsed header has empty protocol line";
+ EXPECT_FALSE(testHdr.GetMimeType().empty()) << "Parsed header has empty MIME-type";
+ EXPECT_FALSE(testHdr.GetValue(CHECK_CNT_TYPE_NAME).empty()) << "Parsed header has empty \"" CHECK_CNT_TYPE_NAME "\" parameter";
+ EXPECT_FALSE(testHdr.GetValues(CHECK_CNT_TYPE_NAME).empty()) << "Parsed header has no \"" CHECK_CNT_TYPE_NAME "\" parameters";
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Parsed header has \"parsing not finished\" state";
+}
+
+TEST(TestHttpHeader, Parse)
+{
+ CHttpHeader testHdr;
+
+ /* check parsing line-by-line */
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n");
+ EXPECT_FALSE(testHdr.IsHeaderDone()) << "Not completed header has \"parsing finished\" state";
+ testHdr.Parse(CHECK_CNT_TYPE_NAME ": " CHECK_CONTENT_TYPE_HTML "\r\n");
+ EXPECT_FALSE(testHdr.IsHeaderDone()) << "Not completed header has \"parsing finished\" state";
+ testHdr.Parse("\r\n");
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ(CHECK_PROT_LINE_200, testHdr.GetProtoLine().c_str()) << "Wrong protocol line";
+ EXPECT_STREQ(CHECK_CONTENT_TYPE_HTML, testHdr.GetValue(CHECK_CNT_TYPE_NAME).c_str()) << "Wrong value of parameter \"" CHECK_CNT_TYPE_NAME "\"";
+
+ /* check autoclearing when new header is parsed */
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n");
+ EXPECT_FALSE(testHdr.IsHeaderDone()) << "Not completed header has \"parsing finished\" state";
+ EXPECT_TRUE(testHdr.GetValues(CHECK_CNT_TYPE_NAME).empty()) << "Cleared header has some parameters";
+ testHdr.Clear();
+ EXPECT_TRUE(testHdr.GetHeader().empty()) << "Cleared object is not empty";
+
+ /* general check parsing */
+ testHdr.Parse(CHECK_HEADER_SMPL);
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STRCASEEQ(CHECK_HEADER_SMPL, testHdr.GetHeader().c_str()) << "Parsed header mismatch the original header";
+ testHdr.Parse(CHECK_HEADER_L1);
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STRCASEEQ(CHECK_HEADER_L1, testHdr.GetHeader().c_str()) << "Parsed header mismatch the original header";
+ EXPECT_STREQ("Thu, 09 Jan 2014 17:58:30 GMT", testHdr.GetValue("Date").c_str()); // case-sensitive match of value
+ testHdr.Parse(CHECK_HEADER_L2);
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STRCASEEQ(CHECK_HEADER_L2, testHdr.GetHeader().c_str()) << "Parsed header mismatch the original header";
+ EXPECT_STREQ("Thu, 09 Jan 2014 20:10:28 GMT", testHdr.GetValue("Last-Modified").c_str()); // case-sensitive match of value
+ testHdr.Parse(CHECK_HEADER_R);
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STRCASEEQ(CHECK_HEADER_R, testHdr.GetHeader().c_str()) << "Parsed header mismatch the original header";
+ EXPECT_STREQ("http://www.Example.Com", testHdr.GetValue("Location").c_str()); // case-sensitive match of value
+
+ /* check support for '\n' line endings */
+ testHdr.Parse(strReplc(CHECK_HEADER_SMPL, "\r\n", "\n"));
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STRCASEEQ(CHECK_HEADER_SMPL, testHdr.GetHeader().c_str()) << "Parsed header mismatch the original header";
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ testHdr.Parse(strReplc(CHECK_HEADER_L1, "\r\n", "\n"));
+ EXPECT_STRCASEEQ(CHECK_HEADER_L1, testHdr.GetHeader().c_str()) << "Parsed header mismatch the original header";
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ testHdr.Parse(strReplc(CHECK_HEADER_L2, "\r\n", "\n"));
+ EXPECT_STRCASEEQ(CHECK_HEADER_L2, testHdr.GetHeader().c_str()) << "Parsed header mismatch the original header";
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ testHdr.Parse(CHECK_PROT_LINE_200 "\n" CHECK_CNT_TYPE_NAME ": " CHECK_CONTENT_TYPE_HTML "\r\n"); // mixed "\n" and "\r\n"
+ testHdr.Parse("\n");
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STRCASEEQ(CHECK_PROT_LINE_200 "\r\n" CHECK_CNT_TYPE_NAME ": " CHECK_CONTENT_TYPE_HTML "\r\n\r\n", testHdr.GetHeader().c_str()) << "Parsed header mismatch the original header";
+ EXPECT_STREQ(CHECK_CONTENT_TYPE_HTML, testHdr.GetValue(CHECK_CNT_TYPE_NAME).c_str()) << "Wrong value of parameter \"" CHECK_CNT_TYPE_NAME "\"";
+
+ /* check trimming of whitespaces for parameter name and value */
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n" CHECK_CNT_TYPE_NAME ": " CHECK_CONTENT_TYPE_HTML "\r\n\r\n");
+ EXPECT_STREQ(CHECK_CONTENT_TYPE_HTML, testHdr.GetValue(CHECK_CNT_TYPE_NAME).c_str()) << "Wrong value of parameter \"" CHECK_CNT_TYPE_NAME "\"";
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n" CHECK_CNT_TYPE_NAME ": " CHECK_CONTENT_TYPE_HTML " \r\n\r\n");
+ EXPECT_STREQ(CHECK_CONTENT_TYPE_HTML, testHdr.GetValue(CHECK_CNT_TYPE_NAME).c_str()) << "Wrong value of parameter \"" CHECK_CNT_TYPE_NAME "\"";
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n" CHECK_CNT_TYPE_NAME ":" CHECK_CONTENT_TYPE_HTML " \r\n\r\n");
+ EXPECT_STREQ(CHECK_CONTENT_TYPE_HTML, testHdr.GetValue(CHECK_CNT_TYPE_NAME).c_str()) << "Wrong value of parameter \"" CHECK_CNT_TYPE_NAME "\"";
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n" CHECK_CNT_TYPE_NAME ":\t" CHECK_CONTENT_TYPE_HTML " \t \r\n\r\n");
+ EXPECT_STREQ(CHECK_CONTENT_TYPE_HTML, testHdr.GetValue(CHECK_CNT_TYPE_NAME).c_str()) << "Wrong value of parameter \"" CHECK_CNT_TYPE_NAME "\"";
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n" CHECK_CNT_TYPE_NAME ":\t " CHECK_CONTENT_TYPE_HTML " \t \r\n\r\n");
+ EXPECT_STREQ(CHECK_CONTENT_TYPE_HTML, testHdr.GetValue(CHECK_CNT_TYPE_NAME).c_str()) << "Wrong value of parameter \"" CHECK_CNT_TYPE_NAME "\"";
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n" CHECK_CNT_TYPE_NAME "\t:" CHECK_CONTENT_TYPE_HTML " \t \r\n\r\n");
+ EXPECT_STREQ(CHECK_CONTENT_TYPE_HTML, testHdr.GetValue(CHECK_CNT_TYPE_NAME).c_str()) << "Wrong value of parameter \"" CHECK_CNT_TYPE_NAME "\"";
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n" CHECK_CNT_TYPE_NAME " \t : " CHECK_CONTENT_TYPE_HTML " \t \r\n\r\n");
+ EXPECT_STREQ(CHECK_CONTENT_TYPE_HTML, testHdr.GetValue(CHECK_CNT_TYPE_NAME).c_str()) << "Wrong value of parameter \"" CHECK_CNT_TYPE_NAME "\"";
+}
+
+TEST(TestHttpHeader, Parse_Multiline)
+{
+ CHttpHeader testHdr;
+
+ /* Check multiline parameter parsing line-by-line */
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n");
+ testHdr.Parse(CHECK_DATE_NAME ": " CHECK_DATE_VALUE3 "\r\n");
+ testHdr.Parse("X-Comment: This\r\n"); // between singleline parameters
+ testHdr.Parse(" is\r\n");
+ testHdr.Parse(" multi\r\n");
+ testHdr.Parse(" line\r\n");
+ testHdr.Parse(" value\r\n");
+ testHdr.Parse(CHECK_CNT_TYPE_NAME ": " CHECK_CONTENT_TYPE_TEXT "\r\n");
+ testHdr.Parse("\r\n");
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ("This is multi line value", testHdr.GetValue("X-Comment").c_str()) << "Wrong multiline value";
+
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n");
+ testHdr.Parse("X-Comment: This\r\n"); // first parameter
+ testHdr.Parse(" is\r\n");
+ testHdr.Parse(" multi\r\n");
+ testHdr.Parse(" line\r\n");
+ testHdr.Parse(" value\r\n");
+ testHdr.Parse(CHECK_CNT_TYPE_NAME ": " CHECK_CONTENT_TYPE_TEXT "\r\n");
+ testHdr.Parse("\r\n");
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ("This is multi line value", testHdr.GetValue("X-Comment").c_str()) << "Wrong multiline value";
+
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n");
+ testHdr.Parse(CHECK_DATE_NAME ": " CHECK_DATE_VALUE3 "\r\n");
+ testHdr.Parse("X-Comment: This\r\n"); // last parameter
+ testHdr.Parse(" is\r\n");
+ testHdr.Parse(" multi\r\n");
+ testHdr.Parse(" line\r\n");
+ testHdr.Parse(" value\r\n");
+ testHdr.Parse("\r\n");
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ("This is multi line value", testHdr.GetValue("X-Comment").c_str()) << "Wrong multiline value";
+
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n");
+ testHdr.Parse("X-Comment: This\r\n"); // the only parameter
+ testHdr.Parse(" is\r\n");
+ testHdr.Parse(" multi\r\n");
+ testHdr.Parse(" line\r\n");
+ testHdr.Parse(" value\r\n");
+ testHdr.Parse("\r\n");
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ("This is multi line value", testHdr.GetValue("X-Comment").c_str()) << "Wrong multiline value";
+
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n");
+ testHdr.Parse("X-Comment: This\n"); // the only parameter with mixed ending style
+ testHdr.Parse(" is\r\n");
+ testHdr.Parse(" multi\n");
+ testHdr.Parse(" line\r\n");
+ testHdr.Parse(" value\n");
+ testHdr.Parse("\r\n");
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ("This is multi line value", testHdr.GetValue("X-Comment").c_str()) << "Wrong multiline value";
+
+ /* Check multiline parameter parsing as one line */
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n" CHECK_DATE_NAME ": " CHECK_DATE_VALUE3 "\r\nX-Comment: This\r\n is\r\n multi\r\n line\r\n value\r\n" \
+ CHECK_CNT_TYPE_NAME ": " CHECK_CONTENT_TYPE_TEXT "\r\n\r\n"); // between singleline parameters
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ("This is multi line value", testHdr.GetValue("X-Comment").c_str()) << "Wrong multiline value";
+
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\nX-Comment: This\r\n is\r\n multi\r\n line\r\n value\r\n" \
+ CHECK_CNT_TYPE_NAME ": " CHECK_CONTENT_TYPE_TEXT "\r\n\r\n"); // first parameter
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ("This is multi line value", testHdr.GetValue("X-Comment").c_str()) << "Wrong multiline value";
+
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n" CHECK_DATE_NAME ": " CHECK_DATE_VALUE3 "\r\nX-Comment: This\r\n is\r\n multi\r\n line\r\n value\r\n\r\n"); // last parameter
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ("This is multi line value", testHdr.GetValue("X-Comment").c_str()) << "Wrong multiline value";
+
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\nX-Comment: This\r\n is\r\n multi\r\n line\r\n value\r\n\r\n"); // the only parameter
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ("This is multi line value", testHdr.GetValue("X-Comment").c_str()) << "Wrong multiline value";
+
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\nX-Comment: This\n is\r\n multi\r\n line\n value\r\n\n"); // the only parameter with mixed ending style
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ("This is multi line value", testHdr.GetValue("X-Comment").c_str()) << "Wrong multiline value";
+
+ /* Check multiline parameter parsing as mixed one/many lines */
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\nX-Comment: This\n is\r\n multi\r\n");
+ testHdr.Parse(" line\n value\r\n\n"); // the only parameter with mixed ending style
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ("This is multi line value", testHdr.GetValue("X-Comment").c_str()) << "Wrong multiline value";
+
+ /* Check parsing of multiline parameter with ':' in value */
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\nX-Comment: This\r\n is:\r\n mul:ti\r\n");
+ testHdr.Parse(" :line\r\n valu:e\r\n\n"); // the only parameter
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ("This is: mul:ti :line valu:e", testHdr.GetValue("X-Comment").c_str()) << "Wrong multiline value";
+
+ /* Check multiline parameter parsing with trimming */
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n");
+ testHdr.Parse(CHECK_DATE_NAME ": " CHECK_DATE_VALUE3 "\r\n");
+ testHdr.Parse("Server: Apache/2.4.7 (Unix)\r\n"); // last parameter, line-by-line parsing
+ testHdr.Parse(" mod_wsgi/3.4 \r\n");
+ testHdr.Parse("\tPython/2.7.5\r\n");
+ testHdr.Parse("\t \t \tOpenSSL/1.0.1e\r\n");
+ testHdr.Parse("\r\n");
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_GE(strlen("Apache/2.4.7 (Unix) mod_wsgi/3.4 \tPython/2.7.5\t \t \tOpenSSL/1.0.1e"), testHdr.GetValue("Server").length()) << "Length of miltiline value is greater than length of original string";
+ EXPECT_LE(strlen("Apache/2.4.7 (Unix) mod_wsgi/3.4 Python/2.7.5 OpenSSL/1.0.1e"), testHdr.GetValue("Server").length()) << "Length of miltiline value is less than length of trimmed original string";
+ EXPECT_STREQ("Apache/2.4.7(Unix)mod_wsgi/3.4Python/2.7.5OpenSSL/1.0.1e", strReplc(strReplc(testHdr.GetValue("Server"), " ", ""), "\t", "").c_str()) << "Multiline value with removed whitespaces does not match original string with removed whitespaces";
+
+ testHdr.Clear();
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\n");
+ testHdr.Parse(CHECK_DATE_NAME ": " CHECK_DATE_VALUE3 "\r\n");
+ testHdr.Parse("Server: Apache/2.4.7 (Unix)\r\n mod_wsgi/3.4 \n"); // last parameter, mixed line-by-line/one line parsing, mixed line ending
+ testHdr.Parse("\tPython/2.7.5\n\t \t \tOpenSSL/1.0.1e\r\n");
+ testHdr.Parse("\r\n");
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_GE(strlen("Apache/2.4.7 (Unix) mod_wsgi/3.4 \tPython/2.7.5\t \t \tOpenSSL/1.0.1e"), testHdr.GetValue("Server").length()) << "Length of miltiline value is greater than length of original string";
+ EXPECT_LE(strlen("Apache/2.4.7 (Unix) mod_wsgi/3.4 Python/2.7.5 OpenSSL/1.0.1e"), testHdr.GetValue("Server").length()) << "Length of miltiline value is less than length of trimmed original string";
+ EXPECT_STREQ("Apache/2.4.7(Unix)mod_wsgi/3.4Python/2.7.5OpenSSL/1.0.1e", strReplc(strReplc(testHdr.GetValue("Server"), " ", ""), "\t", "").c_str()) << "Multiline value with removed whitespaces does not match original string with removed whitespaces";
+}
+
+TEST(TestHttpHeader, GetValue)
+{
+ CHttpHeader testHdr;
+
+ /* Check that all parameters values can be retrieved */
+ testHdr.Parse(CHECK_HEADER_R);
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ("nginx/1.4.4", testHdr.GetValue("Server").c_str()) << "Wrong parameter value";
+ EXPECT_STREQ(CHECK_DATE_VALUE2, testHdr.GetValue(CHECK_DATE_NAME).c_str()) << "Wrong parameter value";
+ EXPECT_STREQ(CHECK_CONTENT_TYPE_HTML, testHdr.GetValue(CHECK_CNT_TYPE_NAME).c_str()) << "Wrong parameter value";
+ EXPECT_STREQ("150", testHdr.GetValue("Content-Length").c_str()) << "Wrong parameter value";
+ EXPECT_STREQ("close", testHdr.GetValue("Connection").c_str()) << "Wrong parameter value";
+ EXPECT_STREQ("http://www.Example.Com", testHdr.GetValue("Location").c_str()) << "Wrong parameter value";
+ EXPECT_TRUE(testHdr.GetValue("foo").empty()) << "Some value is returned for non-existed parameter";
+
+ /* Check that all parameters values can be retrieved in random order */
+ testHdr.Parse(CHECK_HEADER_R);
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Completed header has \"parsing not finished\" state";
+ EXPECT_STREQ("http://www.Example.Com", testHdr.GetValue("Location").c_str()) << "Wrong parameter value";
+ EXPECT_STREQ(CHECK_CONTENT_TYPE_HTML, testHdr.GetValue(CHECK_CNT_TYPE_NAME).c_str()) << "Wrong parameter value";
+ EXPECT_STREQ("http://www.Example.Com", testHdr.GetValue("Location").c_str()) << "Wrong parameter value";
+ EXPECT_STREQ("close", testHdr.GetValue("Connection").c_str()) << "Wrong parameter value";
+ EXPECT_STREQ("nginx/1.4.4", testHdr.GetValue("Server").c_str()) << "Wrong parameter value";
+ EXPECT_STREQ("150", testHdr.GetValue("Content-Length").c_str()) << "Wrong parameter value";
+ EXPECT_STREQ(CHECK_DATE_VALUE2, testHdr.GetValue(CHECK_DATE_NAME).c_str()) << "Wrong parameter value";
+ EXPECT_STREQ("nginx/1.4.4", testHdr.GetValue("Server").c_str()) << "Wrong parameter value";
+ EXPECT_TRUE(testHdr.GetValue("foo").empty()) << "Some value is returned for non-existed parameter";
+
+ /* Check that parameters name is case-insensitive and value is case-sensitive*/
+ EXPECT_STREQ("http://www.Example.Com", testHdr.GetValue("location").c_str()) << "Wrong parameter value for lowercase name";
+ EXPECT_STREQ("http://www.Example.Com", testHdr.GetValue("LOCATION").c_str()) << "Wrong parameter value for UPPERCASE name";
+ EXPECT_STREQ("http://www.Example.Com", testHdr.GetValue("LoCAtIOn").c_str()) << "Wrong parameter value for MiXEdcASe name";
+
+ /* Check value of last added parameter with the same name is returned */
+ testHdr.Parse(CHECK_HEADER_L1);
+ EXPECT_STREQ("close", testHdr.GetValue("Connection").c_str()) << "Wrong parameter value";
+ EXPECT_STREQ("user_country=ot; expires=Thu, 09-Jan-2014 18:58:30 GMT; path=/; domain=.example.com", testHdr.GetValue("Set-Cookie").c_str()) << "Wrong parameter value";
+ EXPECT_STREQ("user_country=ot; expires=Thu, 09-Jan-2014 18:58:30 GMT; path=/; domain=.example.com", testHdr.GetValue("set-cookie").c_str()) << "Wrong parameter value for lowercase name";
+}
+
+TEST(TestHttpHeader, GetValues)
+{
+ CHttpHeader testHdr;
+
+ /* Check that all parameter values can be retrieved and order of values is correct */
+ testHdr.Parse(CHECK_HEADER_L1);
+ EXPECT_EQ(1, testHdr.GetValues("Server").size()) << "Wrong number of values for parameter \"Server\"";
+ EXPECT_STREQ("nginx/1.4.4", testHdr.GetValues("Server")[0].c_str()) << "Wrong parameter value";
+ EXPECT_EQ(2, testHdr.GetValues("Set-Cookie").size()) << "Wrong number of values for parameter \"Set-Cookie\"";
+ EXPECT_STREQ("PHPSESSID=90857d437518db8f0944ca012761048a; path=/; domain=example.com", testHdr.GetValues("Set-Cookie")[0].c_str()) << "Wrong parameter value";
+ EXPECT_STREQ("user_country=ot; expires=Thu, 09-Jan-2014 18:58:30 GMT; path=/; domain=.example.com", testHdr.GetValues("Set-Cookie")[1].c_str()) << "Wrong parameter value";
+ EXPECT_TRUE(testHdr.GetValues("foo").empty()) << "Some values are returned for non-existed parameter";
+}
+
+TEST(TestHttpHeader, AddParam)
+{
+ CHttpHeader testHdr;
+
+ /* General functionality */
+ testHdr.AddParam("server", "Microsoft-IIS/8.0");
+ EXPECT_STREQ("Microsoft-IIS/8.0", testHdr.GetValue("Server").c_str()) << "Wrong parameter value";
+
+ /* Interfere with parsing */
+ EXPECT_FALSE(testHdr.IsHeaderDone()) << "\"AddParam\" set \"parsing finished\" state";
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\nServer: nginx/1.4.4\r\n\r\n");
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Parsed header has \"parsing not finished\" state";
+ EXPECT_STREQ("nginx/1.4.4", testHdr.GetValue("Server").c_str()) << "Wrong parameter value";
+ testHdr.AddParam("server", "Apache/2.4.7");
+ EXPECT_STREQ("Apache/2.4.7", testHdr.GetValue("Server").c_str()) << "Wrong parameter value";
+ EXPECT_EQ(3, testHdr.GetValues("Server").size()) << "Wrong number of values for parameter \"Server\"";
+
+ /* Multiple values */
+ testHdr.AddParam("X-foo", "bar1");
+ testHdr.AddParam("x-foo", "bar2");
+ testHdr.AddParam("x-fOO", "bar3");
+ EXPECT_EQ(3, testHdr.GetValues("X-FOO").size()) << "Wrong number of values for parameter \"X-foo\"";
+ EXPECT_STREQ("bar1", testHdr.GetValues("X-FOo")[0].c_str()) << "Wrong parameter value";
+ EXPECT_STREQ("bar2", testHdr.GetValues("X-fOo")[1].c_str()) << "Wrong parameter value";
+ EXPECT_STREQ("bar3", testHdr.GetValues("x-fOo")[2].c_str()) << "Wrong parameter value";
+ EXPECT_STREQ("bar3", testHdr.GetValue("x-foo").c_str()) << "Wrong parameter value";
+
+ /* Overwrite value */
+ EXPECT_TRUE(testHdr.IsHeaderDone()) << "Parsed header has \"parsing not finished\" state";
+ testHdr.AddParam("x-fOO", "superbar", true);
+ EXPECT_EQ(1, testHdr.GetValues("X-FoO").size()) << "Wrong number of values for parameter \"X-foo\"";
+ EXPECT_STREQ("superbar", testHdr.GetValue("x-foo").c_str()) << "Wrong parameter value";
+
+ /* Check name trimming */
+ testHdr.AddParam("\tx-fOO\t ", "bar");
+ EXPECT_EQ(2, testHdr.GetValues("X-FoO").size()) << "Wrong number of values for parameter \"X-foo\"";
+ EXPECT_STREQ("bar", testHdr.GetValue("x-foo").c_str()) << "Wrong parameter value";
+ testHdr.AddParam(" SerVer \t ", "fakeSrv", true);
+ EXPECT_EQ(1, testHdr.GetValues("serveR").size()) << "Wrong number of values for parameter \"Server\"";
+ EXPECT_STREQ("fakeSrv", testHdr.GetValue("Server").c_str()) << "Wrong parameter value";
+
+ /* Check value trimming */
+ testHdr.AddParam("X-TestParam", " testValue1");
+ EXPECT_STREQ("testValue1", testHdr.GetValue("X-TestParam").c_str()) << "Wrong parameter value";
+ testHdr.AddParam("X-TestParam", "\ttestValue2 and more \t ");
+ EXPECT_STREQ("testValue2 and more", testHdr.GetValue("X-TestParam").c_str()) << "Wrong parameter value";
+
+ /* Empty name or value */
+ testHdr.Clear();
+ testHdr.AddParam("X-TestParam", " ");
+ EXPECT_TRUE(testHdr.GetHeader().empty()) << "Parameter with empty value was added";
+ testHdr.AddParam("\t\t", "value");
+ EXPECT_TRUE(testHdr.GetHeader().empty());
+ testHdr.AddParam(" ", "\t");
+ EXPECT_TRUE(testHdr.GetHeader().empty());
+}
+
+TEST(TestHttpHeader, GetMimeType)
+{
+ CHttpHeader testHdr;
+
+ /* General functionality */
+ EXPECT_TRUE(testHdr.GetMimeType().empty()) << "Newly created object has non-empty MIME-type";
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\nServer: nginx/1.4.4\r\n\r\n");
+ EXPECT_TRUE(testHdr.GetMimeType().empty()) << "Non-empty MIME-type for header without MIME-type";
+ testHdr.Parse(CHECK_HEADER_SMPL);
+ EXPECT_STREQ("text/html", testHdr.GetMimeType().c_str()) << "Wrong MIME-type";
+ testHdr.Parse(CHECK_HEADER_L1);
+ EXPECT_STREQ("text/html", testHdr.GetMimeType().c_str()) << "Wrong MIME-type";
+ testHdr.Parse(CHECK_HEADER_L2);
+ EXPECT_STREQ("text/xml", testHdr.GetMimeType().c_str()) << "Wrong MIME-type";
+ testHdr.Parse(CHECK_HEADER_R);
+ EXPECT_STREQ("text/html", testHdr.GetMimeType().c_str()) << "Wrong MIME-type";
+
+ /* Overwrite by AddParam */
+ testHdr.AddParam(CHECK_CNT_TYPE_NAME, CHECK_CONTENT_TYPE_TEXT);
+ EXPECT_STREQ(CHECK_CONTENT_TYPE_TEXT, testHdr.GetMimeType().c_str()) << "MIME-type was not overwritten by \"AddParam\"";
+
+ /* Correct trimming */
+ testHdr.AddParam(CHECK_CNT_TYPE_NAME, " "CHECK_CONTENT_TYPE_TEXT " \t ;foo=bar");
+ EXPECT_STREQ(CHECK_CONTENT_TYPE_TEXT, testHdr.GetMimeType().c_str()) << "MIME-type is not trimmed correctly";
+}
+
+
+TEST(TestHttpHeader, GetCharset)
+{
+ CHttpHeader testHdr;
+
+ /* General functionality */
+ EXPECT_TRUE(testHdr.GetCharset().empty()) << "Newly created object has non-empty charset";
+ testHdr.Parse(CHECK_PROT_LINE_200 "\r\nServer: nginx/1.4.4\r\n\r\n");
+ EXPECT_TRUE(testHdr.GetCharset().empty()) << "Non-empty charset for header without charset";
+ testHdr.Parse(CHECK_HEADER_SMPL);
+ EXPECT_TRUE(testHdr.GetCharset().empty()) << "Non-empty charset for header without charset";
+ testHdr.Parse(CHECK_HEADER_L1);
+ EXPECT_STREQ("WINDOWS-1251", testHdr.GetCharset().c_str()) << "Wrong charset value";
+ testHdr.Parse(CHECK_HEADER_L2);
+ EXPECT_STREQ("UTF-8", testHdr.GetCharset().c_str()) << "Wrong charset value";
+
+ /* Overwrite by AddParam */
+ testHdr.AddParam(CHECK_CNT_TYPE_NAME, CHECK_CONTENT_TYPE_TEXT "; charset=WINDOWS-1252");
+ EXPECT_STREQ("WINDOWS-1252", testHdr.GetCharset().c_str()) << "Charset was not overwritten by \"AddParam\"";
+
+ /* Correct trimming */
+ testHdr.AddParam(CHECK_CNT_TYPE_NAME, "text/plain;charset=WINDOWS-1251");
+ EXPECT_STREQ("WINDOWS-1251", testHdr.GetCharset().c_str()) << "Wrong charset value";
+ testHdr.AddParam(CHECK_CNT_TYPE_NAME, "text/plain ;\tcharset=US-AScII\t");
+ EXPECT_STREQ("US-ASCII", testHdr.GetCharset().c_str()) << "Wrong charset value";
+ testHdr.AddParam(CHECK_CNT_TYPE_NAME, "text/html ; \tcharset=\"uTF-8\"\t");
+ EXPECT_STREQ("UTF-8", testHdr.GetCharset().c_str()) << "Wrong charset value";
+ testHdr.AddParam(CHECK_CNT_TYPE_NAME, " \ttext/xml\t;\tcharset=uTF-16 ");
+ EXPECT_STREQ("UTF-16", testHdr.GetCharset().c_str()) << "Wrong charset value";
+}
diff --git a/src/utils/test/TestHttpParser.cpp b/src/utils/test/TestHttpParser.cpp
new file mode 100644
index 0000000000..75977f617b
--- /dev/null
+++ b/src/utils/test/TestHttpParser.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/HttpParser.h"
+#include "utils/StdString.h"
+
+#include "gtest/gtest.h"
+
+TEST(TestHttpParser, General)
+{
+ HttpParser a;
+ CStdString str = "POST /path/script.cgi HTTP/1.0\r\n"
+ "From: amejia@xbmc.org\r\n"
+ "User-Agent: XBMC/snapshot (compatible; MSIE 5.5; Windows NT"
+ " 4.0)\r\n"
+ "Content-Type: application/x-www-form-urlencoded\r\n"
+ "Content-Length: 35\r\n"
+ "\r\n"
+ "home=amejia&favorite+flavor=orange\r\n";
+ CStdString refstr, varstr;
+
+ EXPECT_EQ(a.Done, a.addBytes(str.c_str(), str.length()));
+
+ refstr = "POST";
+ varstr = a.getMethod();
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "/path/script.cgi";
+ varstr = a.getUri();
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "";
+ varstr = a.getQueryString();
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "home=amejia&favorite+flavor=orange\r\n";
+ varstr = a.getBody();
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "application/x-www-form-urlencoded";
+ varstr = a.getValue("content-type");
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ EXPECT_EQ((unsigned)35, a.getContentLength());
+}
diff --git a/src/utils/test/TestHttpResponse.cpp b/src/utils/test/TestHttpResponse.cpp
new file mode 100644
index 0000000000..d0fadeb649
--- /dev/null
+++ b/src/utils/test/TestHttpResponse.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/HttpResponse.h"
+
+#include "gtest/gtest.h"
+
+TEST(TestHttpResponse, General)
+{
+ CHttpResponse a(HTTP::POST, HTTP::OK);
+ char *buf = new char(100);
+ std::string response, content, refstr;
+ unsigned int size;
+ memset(buf, 0, sizeof(*buf));
+
+ a.AddHeader("date", "Sun, 01 Jul 2012 00:00:00 -0400");
+ a.AddHeader("content-type", "text/html");
+ content = "<html>\r\n"
+ " <body>\r\n"
+ " <h1>XBMC TestHttpResponse Page</h1>\r\n"
+ " <p>blah blah blah</p>\r\n"
+ " </body>\r\n"
+ "</html>\r\n";
+ a.SetContent(content.c_str(), content.length());
+
+ size = a.Create(buf);
+ EXPECT_EQ((unsigned int)210, size);
+
+ response = buf;
+ refstr = "HTTP/1.1 200 OK\r\n"
+ "date: Sun, 01 Jul 2012 00:00:00 -0400\r\n"
+ "content-type: text/html\r\n"
+ "Content-Length: 106\r\n"
+ "\r\n"
+ "<html>\r\n"
+ " <body>\r\n"
+ " <h1>XBMC TestHttpResponse Page</h1>\r\n"
+ " <p>blah blah blah</p>\r\n"
+ " </body>\r\n"
+ "</html>\r\n";
+ EXPECT_STREQ(refstr.c_str(), response.c_str());
+}
diff --git a/src/utils/test/TestJSONVariantParser.cpp b/src/utils/test/TestJSONVariantParser.cpp
new file mode 100644
index 0000000000..48d12cee37
--- /dev/null
+++ b/src/utils/test/TestJSONVariantParser.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/JSONVariantParser.h"
+
+#include "gtest/gtest.h"
+
+TEST(TestJSONVariantParser, Parse)
+{
+ CVariant variant;
+ unsigned char buf[100];
+
+ memset(buf, 0, sizeof(buf));
+ variant = CJSONVariantParser::Parse(buf, sizeof(buf));
+ EXPECT_TRUE(variant.isNull());
+}
diff --git a/src/utils/test/TestJSONVariantWriter.cpp b/src/utils/test/TestJSONVariantWriter.cpp
new file mode 100644
index 0000000000..cab124e74f
--- /dev/null
+++ b/src/utils/test/TestJSONVariantWriter.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/JSONVariantWriter.h"
+
+#include "gtest/gtest.h"
+
+TEST(TestJSONVariantWriter, Write)
+{
+ CVariant variant;
+ std::string str;
+
+ str = CJSONVariantWriter::Write(variant, false);
+ EXPECT_STREQ("null\n", str.c_str());
+}
diff --git a/src/utils/test/TestJobManager.cpp b/src/utils/test/TestJobManager.cpp
new file mode 100644
index 0000000000..073dcea270
--- /dev/null
+++ b/src/utils/test/TestJobManager.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/JobManager.h"
+#include "settings/Settings.h"
+#include "utils/SystemInfo.h"
+
+#include "gtest/gtest.h"
+
+/* CSysInfoJob::GetInternetState() will test for network connectivity. */
+class TestJobManager : public testing::Test
+{
+protected:
+ TestJobManager()
+ {
+ /* TODO
+ CSettingsCategory* net = CSettings::Get().AddCategory(4, "network", 798);
+ CSettings::Get().AddBool(net, "network.usehttpproxy", 708, false);
+ CSettings::Get().AddString(net, "network.httpproxyserver", 706, "",
+ EDIT_CONTROL_INPUT);
+ CSettings::Get().AddString(net, "network.httpproxyport", 730, "8080",
+ EDIT_CONTROL_NUMBER_INPUT, false, 707);
+ CSettings::Get().AddString(net, "network.httpproxyusername", 1048, "",
+ EDIT_CONTROL_INPUT);
+ CSettings::Get().AddString(net, "network.httpproxypassword", 733, "",
+ EDIT_CONTROL_HIDDEN_INPUT,true,733);
+ CSettings::Get().AddInt(net, "network.bandwidth", 14041, 0, 0, 512, 100*1024,
+ SPIN_CONTROL_INT_PLUS, 14048, 351);
+ */
+ }
+
+ ~TestJobManager()
+ {
+ /* Always cancel jobs test completion */
+ CJobManager::GetInstance().CancelJobs();
+ CJobManager::GetInstance().Restart();
+ CSettings::Get().Unload();
+ }
+};
+
+TEST_F(TestJobManager, AddJob)
+{
+ CJob* job = new CSysInfoJob();
+ CJobManager::GetInstance().AddJob(job, NULL);
+}
+
+TEST_F(TestJobManager, CancelJob)
+{
+ unsigned int id;
+ CJob* job = new CSysInfoJob();
+ id = CJobManager::GetInstance().AddJob(job, NULL);
+ CJobManager::GetInstance().CancelJob(id);
+}
+
+namespace
+{
+struct JobControlPackage
+{
+ JobControlPackage() :
+ ready (false)
+ {
+ // We're not ready to wait yet
+ jobCreatedMutex.lock();
+ }
+
+ ~JobControlPackage()
+ {
+ jobCreatedMutex.unlock();
+ }
+
+ bool ready;
+ XbmcThreads::ConditionVariable jobCreatedCond;
+ CCriticalSection jobCreatedMutex;
+};
+
+class BroadcastingJob :
+ public CJob
+{
+public:
+
+ BroadcastingJob(JobControlPackage &package) :
+ m_package(package),
+ m_finish(false)
+ {
+ }
+
+ void FinishAndStopBlocking()
+ {
+ CSingleLock lock(m_blockMutex);
+
+ m_finish = true;
+ m_block.notifyAll();
+ }
+
+ const char * GetType() const
+ {
+ return "BroadcastingJob";
+ }
+
+ bool DoWork()
+ {
+ {
+ CSingleLock lock(m_package.jobCreatedMutex);
+
+ m_package.ready = true;
+ m_package.jobCreatedCond.notifyAll();
+ }
+
+ CSingleLock blockLock(m_blockMutex);
+
+ // Block until we're told to go away
+ while (!m_finish)
+ m_block.wait(m_blockMutex);
+ return true;
+ }
+
+private:
+
+ JobControlPackage &m_package;
+
+ XbmcThreads::ConditionVariable m_block;
+ CCriticalSection m_blockMutex;
+ bool m_finish;
+};
+
+BroadcastingJob *
+WaitForJobToStartProcessing(CJob::PRIORITY priority, JobControlPackage &package)
+{
+ BroadcastingJob* job = new BroadcastingJob(package);
+ CJobManager::GetInstance().AddJob(job, NULL, priority);
+
+ // We're now ready to wait, wait and then unblock once ready
+ while (!package.ready)
+ package.jobCreatedCond.wait(package.jobCreatedMutex);
+
+ return job;
+}
+}
+
+TEST_F(TestJobManager, PauseLowPriorityJob)
+{
+ JobControlPackage package;
+ BroadcastingJob *job (WaitForJobToStartProcessing(CJob::PRIORITY_LOW_PAUSABLE, package));
+
+ EXPECT_TRUE(CJobManager::GetInstance().IsProcessing(CJob::PRIORITY_LOW_PAUSABLE));
+ CJobManager::GetInstance().PauseJobs();
+ EXPECT_FALSE(CJobManager::GetInstance().IsProcessing(CJob::PRIORITY_LOW_PAUSABLE));
+ CJobManager::GetInstance().UnPauseJobs();
+ EXPECT_TRUE(CJobManager::GetInstance().IsProcessing(CJob::PRIORITY_LOW_PAUSABLE));
+
+ job->FinishAndStopBlocking();
+}
+
+TEST_F(TestJobManager, IsProcessing)
+{
+ JobControlPackage package;
+ BroadcastingJob *job (WaitForJobToStartProcessing(CJob::PRIORITY_LOW_PAUSABLE, package));
+
+ EXPECT_EQ(0, CJobManager::GetInstance().IsProcessing(""));
+
+ job->FinishAndStopBlocking();
+}
diff --git a/src/utils/test/TestLabelFormatter.cpp b/src/utils/test/TestLabelFormatter.cpp
new file mode 100644
index 0000000000..d05d0238e1
--- /dev/null
+++ b/src/utils/test/TestLabelFormatter.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/LabelFormatter.h"
+#include "filesystem/File.h"
+#include "settings/Settings.h"
+#include "FileItem.h"
+
+#include "test/TestUtils.h"
+
+#include "gtest/gtest.h"
+
+/* Set default settings used by CLabelFormatter. */
+class TestLabelFormatter : public testing::Test
+{
+protected:
+ TestLabelFormatter()
+ {
+ /* TODO
+ CSettingsCategory* fl = CSettings::Get().AddCategory(7, "filelists", 14081);
+ CSettings::Get().AddBool(fl, "filelists.showparentdiritems", 13306, true);
+ CSettings::Get().AddBool(fl, "filelists.showextensions", 497, true);
+ CSettings::Get().AddBool(fl, "filelists.ignorethewhensorting", 13399, true);
+ CSettings::Get().AddBool(fl, "filelists.allowfiledeletion", 14071, false);
+ CSettings::Get().AddBool(fl, "filelists.showaddsourcebuttons", 21382, true);
+ CSettings::Get().AddBool(fl, "filelists.showhidden", 21330, false);
+ */
+ }
+
+ ~TestLabelFormatter()
+ {
+ CSettings::Get().Unload();
+ }
+};
+
+TEST_F(TestLabelFormatter, FormatLabel)
+{
+ XFILE::CFile *tmpfile;
+ std::string tmpfilepath, destpath;
+ LABEL_MASKS labelMasks;
+ CLabelFormatter formatter("", labelMasks.m_strLabel2File);
+
+ ASSERT_TRUE((tmpfile = XBMC_CREATETEMPFILE("")));
+ tmpfilepath = XBMC_TEMPFILEPATH(tmpfile);
+
+ CFileItemPtr item(new CFileItem(tmpfilepath));
+ item->SetPath(tmpfilepath);
+ item->m_bIsFolder = false;
+ item->Select(true);
+
+ formatter.FormatLabel(item.get());
+
+ EXPECT_TRUE(XBMC_DELETETEMPFILE(tmpfile));
+}
+
+TEST_F(TestLabelFormatter, FormatLabel2)
+{
+ XFILE::CFile *tmpfile;
+ std::string tmpfilepath, destpath;
+ LABEL_MASKS labelMasks;
+ CLabelFormatter formatter("", labelMasks.m_strLabel2File);
+
+ ASSERT_TRUE((tmpfile = XBMC_CREATETEMPFILE("")));
+ tmpfilepath = XBMC_TEMPFILEPATH(tmpfile);
+
+ CFileItemPtr item(new CFileItem(tmpfilepath));
+ item->SetPath(tmpfilepath);
+ item->m_bIsFolder = false;
+ item->Select(true);
+
+ formatter.FormatLabel2(item.get());
+
+ EXPECT_TRUE(XBMC_DELETETEMPFILE(tmpfile));
+}
diff --git a/src/utils/test/TestLangCodeExpander.cpp b/src/utils/test/TestLangCodeExpander.cpp
new file mode 100644
index 0000000000..a6c2db9557
--- /dev/null
+++ b/src/utils/test/TestLangCodeExpander.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/LangCodeExpander.h"
+
+#include "gtest/gtest.h"
+
+TEST(TestLangCodeExpander, ConvertTwoToThreeCharCode)
+{
+ std::string refstr, varstr;
+
+ refstr = "eng";
+ g_LangCodeExpander.ConvertTwoToThreeCharCode(varstr, "en");
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestLangCodeExpander, ConvertToThreeCharCode)
+{
+ std::string refstr, varstr;
+
+ refstr = "eng";
+ g_LangCodeExpander.ConvertToThreeCharCode(varstr, "en");
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
diff --git a/src/utils/test/TestMathUtils.cpp b/src/utils/test/TestMathUtils.cpp
new file mode 100644
index 0000000000..009c4fdf1a
--- /dev/null
+++ b/src/utils/test/TestMathUtils.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/MathUtils.h"
+
+#include "gtest/gtest.h"
+
+TEST(TestMathUtils, round_int)
+{
+ int refval, varval, i;
+
+ for (i = -8; i < 8; ++i)
+ {
+ double d = 0.25*i;
+ refval = (i < 0) ? (i - 1) / 4 : (i + 2) / 4;
+ varval = MathUtils::round_int(d);
+ EXPECT_EQ(refval, varval);
+ }
+}
+
+TEST(TestMathUtils, truncate_int)
+{
+ int refval, varval, i;
+
+ for (i = -8; i < 8; ++i)
+ {
+ double d = 0.25*i;
+ refval = i / 4;
+ varval = MathUtils::truncate_int(d);
+ EXPECT_EQ(refval, varval);
+ }
+}
+
+TEST(TestMathUtils, abs)
+{
+ int64_t refval, varval;
+
+ refval = 5;
+ varval = MathUtils::abs(-5);
+ EXPECT_EQ(refval, varval);
+}
+
+TEST(TestMathUtils, bitcount)
+{
+ unsigned refval, varval;
+
+ refval = 10;
+ varval = MathUtils::bitcount(0x03FF);
+ EXPECT_EQ(refval, varval);
+
+ refval = 8;
+ varval = MathUtils::bitcount(0x2AD5);
+ EXPECT_EQ(refval, varval);
+}
diff --git a/src/utils/test/TestMime.cpp b/src/utils/test/TestMime.cpp
new file mode 100644
index 0000000000..34cef46a26
--- /dev/null
+++ b/src/utils/test/TestMime.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/Mime.h"
+#include "FileItem.h"
+
+#include "gtest/gtest.h"
+
+TEST(TestMime, GetMimeType_string)
+{
+ EXPECT_STREQ("video/avi", CMime::GetMimeType("avi").c_str());
+ EXPECT_STRNE("video/x-msvideo", CMime::GetMimeType("avi").c_str());
+ EXPECT_STRNE("video/avi", CMime::GetMimeType("xvid").c_str());
+}
+
+TEST(TestMime, GetMimeType_CFileItem)
+{
+ std::string refstr, varstr;
+ CFileItem item("testfile.mp4", false);
+
+ refstr = "video/mp4";
+ varstr = CMime::GetMimeType(item);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
diff --git a/src/utils/test/TestPOUtils.cpp b/src/utils/test/TestPOUtils.cpp
new file mode 100644
index 0000000000..e28830d65d
--- /dev/null
+++ b/src/utils/test/TestPOUtils.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/POUtils.h"
+
+#include "test/TestUtils.h"
+
+#include "gtest/gtest.h"
+
+TEST(TestPOUtils, General)
+{
+ CPODocument a;
+
+ EXPECT_TRUE(a.LoadFile(XBMC_REF_FILE_PATH("/language/Spanish/strings.po")));
+
+ EXPECT_TRUE(a.GetNextEntry());
+ EXPECT_EQ(ID_FOUND, a.GetEntryType());
+ EXPECT_EQ((uint32_t)0, a.GetEntryID());
+ a.ParseEntry(false);
+ EXPECT_STREQ("", a.GetMsgctxt().c_str());
+ EXPECT_STREQ("Programs", a.GetMsgid().c_str());
+ EXPECT_STREQ("Programas", a.GetMsgstr().c_str());
+ EXPECT_STREQ("", a.GetPlurMsgstr(0).c_str());
+
+ EXPECT_TRUE(a.GetNextEntry());
+ EXPECT_EQ(ID_FOUND, a.GetEntryType());
+ EXPECT_EQ((uint32_t)1, a.GetEntryID());
+ a.ParseEntry(false);
+ EXPECT_STREQ("", a.GetMsgctxt().c_str());
+ EXPECT_STREQ("Pictures", a.GetMsgid().c_str());
+ EXPECT_STREQ("Imágenes", a.GetMsgstr().c_str());
+ EXPECT_STREQ("", a.GetPlurMsgstr(0).c_str());
+
+ EXPECT_TRUE(a.GetNextEntry());
+ EXPECT_EQ(ID_FOUND, a.GetEntryType());
+ EXPECT_EQ((uint32_t)2, a.GetEntryID());
+ a.ParseEntry(false);
+ EXPECT_STREQ("", a.GetMsgctxt().c_str());
+ EXPECT_STREQ("Music", a.GetMsgid().c_str());
+ EXPECT_STREQ("Música", a.GetMsgstr().c_str());
+ EXPECT_STREQ("", a.GetPlurMsgstr(0).c_str());
+}
diff --git a/src/utils/test/TestPerformanceSample.cpp b/src/utils/test/TestPerformanceSample.cpp
new file mode 100644
index 0000000000..be13685fff
--- /dev/null
+++ b/src/utils/test/TestPerformanceSample.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/PerformanceSample.h"
+
+#include "gtest/gtest.h"
+
+class MyCPerformanceSample : public CPerformanceSample
+{
+public:
+ MyCPerformanceSample(const std::string &statName, bool bCheckWhenDone = true)
+ : CPerformanceSample(statName, bCheckWhenDone)
+ {}
+ std::string getStatName() { return m_statName; }
+ bool getCheckWhenDown() { return m_bCheckWhenDone; }
+ int64_t getStart() { return m_tmStart; }
+ int64_t getFreq() { return m_tmFreq; }
+};
+
+TEST(TestPerformanceSample, General)
+{
+ MyCPerformanceSample a("test");
+
+ a.Reset();
+ a.CheckPoint();
+
+ EXPECT_STREQ("test", a.getStatName().c_str());
+ EXPECT_TRUE(a.getCheckWhenDown());
+ EXPECT_GT(a.getStart(), (int64_t)0);
+ EXPECT_GT(a.getFreq(), (int64_t)0);
+
+ std::cout << "Estimated Error: " <<
+ testing::PrintToString(a.GetEstimatedError()) << std::endl;
+ std::cout << "Start: " << testing::PrintToString(a.getStart()) << std::endl;
+ std::cout << "Frequency: " << testing::PrintToString(a.getFreq()) << std::endl;
+}
diff --git a/src/utils/test/TestRegExp.cpp b/src/utils/test/TestRegExp.cpp
new file mode 100644
index 0000000000..e4231afa29
--- /dev/null
+++ b/src/utils/test/TestRegExp.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/* TODO: gtest/gtest.h needs to come in before utils/RegExp.h.
+ * Investigate why.
+ */
+#include "gtest/gtest.h"
+
+#include "utils/RegExp.h"
+#include "utils/log.h"
+#include "utils/StdString.h"
+#include "filesystem/File.h"
+#include "filesystem/SpecialProtocol.h"
+#include "utils/StringUtils.h"
+#include "CompileInfo.h"
+
+TEST(TestRegExp, RegFind)
+{
+ CRegExp regex;
+
+ EXPECT_TRUE(regex.RegComp("^Test.*"));
+ EXPECT_EQ(0, regex.RegFind("Test string."));
+
+ EXPECT_TRUE(regex.RegComp("^string.*"));
+ EXPECT_EQ(-1, regex.RegFind("Test string."));
+}
+
+TEST(TestRegExp, GetReplaceString)
+{
+ CRegExp regex;
+
+ EXPECT_TRUE(regex.RegComp("^(Test)\\s*(.*)\\."));
+ EXPECT_EQ(0, regex.RegFind("Test string."));
+ EXPECT_STREQ("string", regex.GetReplaceString("\\2").c_str());
+}
+
+TEST(TestRegExp, GetFindLen)
+{
+ CRegExp regex;
+
+ EXPECT_TRUE(regex.RegComp("^(Test)\\s*(.*)\\."));
+ EXPECT_EQ(0, regex.RegFind("Test string."));
+ EXPECT_EQ(12, regex.GetFindLen());
+}
+
+TEST(TestRegExp, GetSubCount)
+{
+ CRegExp regex;
+
+ EXPECT_TRUE(regex.RegComp("^(Test)\\s*(.*)\\."));
+ EXPECT_EQ(0, regex.RegFind("Test string."));
+ EXPECT_EQ(2, regex.GetSubCount());
+}
+
+TEST(TestRegExp, GetSubStart)
+{
+ CRegExp regex;
+
+ EXPECT_TRUE(regex.RegComp("^(Test)\\s*(.*)\\."));
+ EXPECT_EQ(0, regex.RegFind("Test string."));
+ EXPECT_EQ(0, regex.GetSubStart(0));
+ EXPECT_EQ(0, regex.GetSubStart(1));
+ EXPECT_EQ(5, regex.GetSubStart(2));
+}
+
+TEST(TestRegExp, GetCaptureTotal)
+{
+ CRegExp regex;
+
+ EXPECT_TRUE(regex.RegComp("^(Test)\\s*(.*)\\."));
+ EXPECT_EQ(0, regex.RegFind("Test string."));
+ EXPECT_EQ(2, regex.GetCaptureTotal());
+}
+
+TEST(TestRegExp, GetMatch)
+{
+ CRegExp regex;
+
+ EXPECT_TRUE(regex.RegComp("^(Test)\\s*(.*)\\."));
+ EXPECT_EQ(0, regex.RegFind("Test string."));
+ EXPECT_STREQ("Test string.", regex.GetMatch(0).c_str());
+ EXPECT_STREQ("Test", regex.GetMatch(1).c_str());
+ EXPECT_STREQ("string", regex.GetMatch(2).c_str());
+}
+
+TEST(TestRegExp, GetPattern)
+{
+ CRegExp regex;
+
+ EXPECT_TRUE(regex.RegComp("^(Test)\\s*(.*)\\."));
+ EXPECT_STREQ("^(Test)\\s*(.*)\\.", regex.GetPattern().c_str());
+}
+
+TEST(TestRegExp, GetNamedSubPattern)
+{
+ CRegExp regex;
+ std::string match;
+
+ EXPECT_TRUE(regex.RegComp("^(?<first>Test)\\s*(?<second>.*)\\."));
+ EXPECT_EQ(0, regex.RegFind("Test string."));
+ EXPECT_TRUE(regex.GetNamedSubPattern("first", match));
+ EXPECT_STREQ("Test", match.c_str());
+ EXPECT_TRUE(regex.GetNamedSubPattern("second", match));
+ EXPECT_STREQ("string", match.c_str());
+}
+
+TEST(TestRegExp, operatorEqual)
+{
+ CRegExp regex, regexcopy;
+ std::string match;
+
+ EXPECT_TRUE(regex.RegComp("^(?<first>Test)\\s*(?<second>.*)\\."));
+ regexcopy = regex;
+ EXPECT_EQ(0, regexcopy.RegFind("Test string."));
+ EXPECT_TRUE(regexcopy.GetNamedSubPattern("first", match));
+ EXPECT_STREQ("Test", match.c_str());
+ EXPECT_TRUE(regexcopy.GetNamedSubPattern("second", match));
+ EXPECT_STREQ("string", match.c_str());
+}
+
+class TestRegExpLog : public testing::Test
+{
+protected:
+ TestRegExpLog(){}
+ ~TestRegExpLog()
+ {
+ CLog::Close();
+ }
+};
+
+TEST_F(TestRegExpLog, DumpOvector)
+{
+ CRegExp regex;
+ CStdString logfile, logstring;
+ char buf[100];
+ unsigned int bytesread;
+ XFILE::CFile file;
+
+ std::string appName = CCompileInfo::GetAppName();
+ StringUtils::ToLower(appName);
+ logfile = CSpecialProtocol::TranslatePath("special://temp/") + appName + ".log";
+ EXPECT_TRUE(CLog::Init(CSpecialProtocol::TranslatePath("special://temp/").c_str()));
+ EXPECT_TRUE(XFILE::CFile::Exists(logfile));
+
+ EXPECT_TRUE(regex.RegComp("^(?<first>Test)\\s*(?<second>.*)\\."));
+ EXPECT_EQ(0, regex.RegFind("Test string."));
+ regex.DumpOvector(LOGDEBUG);
+ CLog::Close();
+
+ EXPECT_TRUE(file.Open(logfile));
+ while ((bytesread = file.Read(buf, sizeof(buf) - 1)) > 0)
+ {
+ buf[bytesread] = '\0';
+ logstring.append(buf);
+ }
+ file.Close();
+ EXPECT_FALSE(logstring.empty());
+
+ EXPECT_STREQ("\xEF\xBB\xBF", logstring.substr(0, 3).c_str());
+
+ EXPECT_TRUE(regex.RegComp(".*DEBUG: regexp ovector=\\{\\[0,12\\],\\[0,4\\],"
+ "\\[5,11\\]\\}.*"));
+ EXPECT_GE(regex.RegFind(logstring), 0);
+
+ EXPECT_TRUE(XFILE::CFile::Delete(logfile));
+}
diff --git a/src/utils/test/TestRingBuffer.cpp b/src/utils/test/TestRingBuffer.cpp
new file mode 100644
index 0000000000..37a14b9935
--- /dev/null
+++ b/src/utils/test/TestRingBuffer.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/RingBuffer.h"
+
+#include "gtest/gtest.h"
+
+TEST(TestRingBuffer, General)
+{
+ CRingBuffer a;
+ char data[20];
+ unsigned int i;
+
+ EXPECT_TRUE(a.Create(20));
+ EXPECT_EQ((unsigned int)20, a.getSize());
+ memset(data, 0, sizeof(data));
+ for (i = 0; i < a.getSize(); i++)
+ EXPECT_TRUE(a.WriteData(data, 1));
+ a.Clear();
+
+ memcpy(data, "0123456789", sizeof("0123456789"));
+ EXPECT_TRUE(a.WriteData(data, sizeof("0123456789")));
+ EXPECT_STREQ("0123456789", a.getBuffer());
+
+ memset(data, 0, sizeof(data));
+ EXPECT_TRUE(a.ReadData(data, 5));
+ EXPECT_STREQ("01234", data);
+}
diff --git a/src/utils/test/TestScraperParser.cpp b/src/utils/test/TestScraperParser.cpp
new file mode 100644
index 0000000000..de2592d7a6
--- /dev/null
+++ b/src/utils/test/TestScraperParser.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/ScraperParser.h"
+
+#include "test/TestUtils.h"
+
+#include "gtest/gtest.h"
+
+TEST(TestScraperParser, General)
+{
+ CScraperParser a;
+
+ a.Clear();
+ EXPECT_TRUE(
+ a.Load(XBMC_REF_FILE_PATH("/addons/metadata.themoviedb.org/tmdb.xml")));
+
+ EXPECT_STREQ(
+ XBMC_REF_FILE_PATH("/addons/metadata.themoviedb.org/tmdb.xml").c_str(),
+ a.GetFilename().c_str());
+ EXPECT_STREQ("UTF-8", a.GetSearchStringEncoding().c_str());
+}
diff --git a/src/utils/test/TestScraperUrl.cpp b/src/utils/test/TestScraperUrl.cpp
new file mode 100644
index 0000000000..8f49060094
--- /dev/null
+++ b/src/utils/test/TestScraperUrl.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/ScraperUrl.h"
+
+#include "gtest/gtest.h"
+
+TEST(TestScraperUrl, General)
+{
+ CScraperUrl a;
+ std::string xmlstring;
+
+ xmlstring = "<data spoof=\"blah\" gzip=\"yes\">\n"
+ " <someurl>\n"
+ " </someurl>\n"
+ " <someotherurl>\n"
+ " </someotherurl>\n"
+ "</data>\n";
+ EXPECT_TRUE(a.ParseString(xmlstring));
+
+ EXPECT_STREQ("blah", a.GetFirstThumb().m_spoof.c_str());
+ EXPECT_STREQ("someurl", a.GetFirstThumb().m_url.c_str());
+ EXPECT_STREQ("", a.GetFirstThumb().m_cache.c_str());
+ EXPECT_EQ(CScraperUrl::URL_TYPE_GENERAL, a.GetFirstThumb().m_type);
+ EXPECT_FALSE(a.GetFirstThumb().m_post);
+ EXPECT_TRUE(a.GetFirstThumb().m_isgz);
+ EXPECT_EQ(-1, a.GetFirstThumb().m_season);
+}
diff --git a/src/utils/test/TestSortUtils.cpp b/src/utils/test/TestSortUtils.cpp
new file mode 100644
index 0000000000..659ff527b6
--- /dev/null
+++ b/src/utils/test/TestSortUtils.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/SortUtils.h"
+#include "utils/Variant.h"
+
+#include "gtest/gtest.h"
+
+TEST(TestSortUtils, Sort_SortBy)
+{
+ SortItems items;
+
+ CVariant variant1("M Artist");
+ SortItemPtr item1(new SortItem());
+ (*item1)[FieldArtist] = variant1;
+ CVariant variant2("B Artist");
+ SortItemPtr item2(new SortItem());
+ (*item2)[FieldArtist] = variant2;
+ CVariant variant3("R Artist");
+ SortItemPtr item3(new SortItem());
+ (*item3)[FieldArtist] = variant3;
+ CVariant variant4("R Artist");
+ SortItemPtr item4(new SortItem());
+ (*item4)[FieldArtist] = variant4;
+ CVariant variant5("I Artist");
+ SortItemPtr item5(new SortItem());
+ (*item5)[FieldArtist] = variant5;
+ CVariant variant6("A Artist");
+ SortItemPtr item6(new SortItem());
+ (*item6)[FieldArtist] = variant6;
+ CVariant variant7("G Artist");
+ SortItemPtr item7(new SortItem());
+ (*item7)[FieldArtist] = variant7;
+
+ items.push_back(item1);
+ items.push_back(item2);
+ items.push_back(item3);
+ items.push_back(item4);
+ items.push_back(item5);
+ items.push_back(item6);
+ items.push_back(item7);
+
+ SortUtils::Sort(SortByArtist, SortOrderAscending, SortAttributeNone, items);
+
+ EXPECT_STREQ("A Artist", (*items.at(0))[FieldArtist].asString().c_str());
+ EXPECT_STREQ("B Artist", (*items.at(1))[FieldArtist].asString().c_str());
+ EXPECT_STREQ("G Artist", (*items.at(2))[FieldArtist].asString().c_str());
+ EXPECT_STREQ("I Artist", (*items.at(3))[FieldArtist].asString().c_str());
+ EXPECT_STREQ("M Artist", (*items.at(4))[FieldArtist].asString().c_str());
+ EXPECT_STREQ("R Artist", (*items.at(5))[FieldArtist].asString().c_str());
+ EXPECT_STREQ("R Artist", (*items.at(6))[FieldArtist].asString().c_str());
+}
+
+TEST(TestSortUtils, Sort_SortDescription)
+{
+ SortItems items;
+
+ CVariant variant1("M Artist");
+ SortItemPtr item1(new SortItem());
+ (*item1)[FieldArtist] = variant1;
+ CVariant variant2("B Artist");
+ SortItemPtr item2(new SortItem());
+ (*item2)[FieldArtist] = variant2;
+ CVariant variant3("R Artist");
+ SortItemPtr item3(new SortItem());
+ (*item3)[FieldArtist] = variant3;
+ CVariant variant4("R Artist");
+ SortItemPtr item4(new SortItem());
+ (*item4)[FieldArtist] = variant4;
+ CVariant variant5("I Artist");
+ SortItemPtr item5(new SortItem());
+ (*item5)[FieldArtist] = variant5;
+ CVariant variant6("A Artist");
+ SortItemPtr item6(new SortItem());
+ (*item6)[FieldArtist] = variant6;
+ CVariant variant7("G Artist");
+ SortItemPtr item7(new SortItem());
+ (*item7)[FieldArtist] = variant7;
+
+ items.push_back(item1);
+ items.push_back(item2);
+ items.push_back(item3);
+ items.push_back(item4);
+ items.push_back(item5);
+ items.push_back(item6);
+ items.push_back(item7);
+
+ SortDescription desc;
+ desc.sortBy = SortByArtist;
+ SortUtils::Sort(desc, items);
+
+ EXPECT_STREQ("A Artist", (*items.at(0))[FieldArtist].asString().c_str());
+ EXPECT_STREQ("B Artist", (*items.at(1))[FieldArtist].asString().c_str());
+ EXPECT_STREQ("G Artist", (*items.at(2))[FieldArtist].asString().c_str());
+ EXPECT_STREQ("I Artist", (*items.at(3))[FieldArtist].asString().c_str());
+ EXPECT_STREQ("M Artist", (*items.at(4))[FieldArtist].asString().c_str());
+ EXPECT_STREQ("R Artist", (*items.at(5))[FieldArtist].asString().c_str());
+ EXPECT_STREQ("R Artist", (*items.at(6))[FieldArtist].asString().c_str());
+}
+
+TEST(TestSortUtils, GetFieldsForSorting)
+{
+ Fields fields;
+
+ fields = SortUtils::GetFieldsForSorting(SortByArtist);
+ Fields::iterator it;
+ it = fields.find(FieldAlbum);
+ EXPECT_EQ(FieldAlbum, *it);
+ it = fields.find(FieldArtist);
+ EXPECT_EQ(FieldArtist, *it);
+ it = fields.find(FieldYear);
+ EXPECT_EQ(FieldYear, *it);
+ it = fields.find(FieldTrackNumber);
+ EXPECT_EQ(FieldTrackNumber, *it);
+ EXPECT_EQ((unsigned int)4, fields.size());
+}
diff --git a/src/utils/test/TestStdString.cpp b/src/utils/test/TestStdString.cpp
new file mode 100644
index 0000000000..c613db28b3
--- /dev/null
+++ b/src/utils/test/TestStdString.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/StdString.h"
+
+#include "gtest/gtest.h"
+
+TEST(TestStdString, CStdString)
+{
+ CStdString ref, var;
+
+ ref = "CStdString test";
+ var = ref;
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+}
+
+TEST(TestStdString, CStdStringA)
+{
+ CStdStringA ref, var;
+
+ ref = "CStdStringA test";
+ var = ref;
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+}
+
+TEST(TestStdString, CStdStringW)
+{
+ CStdStringW ref, var;
+
+ ref = L"CStdStringW test";
+ var = ref;
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+}
diff --git a/src/utils/test/TestStopwatch.cpp b/src/utils/test/TestStopwatch.cpp
new file mode 100644
index 0000000000..4f8a607b91
--- /dev/null
+++ b/src/utils/test/TestStopwatch.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/Stopwatch.h"
+#include "threads/Thread.h"
+
+#include "gtest/gtest.h"
+
+class CTestStopWatchThread : public CThread
+{
+public:
+ CTestStopWatchThread() :
+ CThread("TestStopWatch"){}
+};
+
+TEST(TestStopWatch, Start)
+{
+ CStopWatch a;
+ CTestStopWatchThread thread;
+
+ EXPECT_FALSE(a.IsRunning());
+ EXPECT_EQ(0.0f, a.GetElapsedSeconds());
+ EXPECT_EQ(0.0f, a.GetElapsedMilliseconds());
+
+ std::cout << "Calling Start()" << std::endl;
+ a.Start();
+ thread.Sleep(1000);
+ EXPECT_TRUE(a.IsRunning());
+ std::cout << "Elapsed Seconds: " << a.GetElapsedSeconds() << std::endl;
+ std::cout << "Elapsed Milliseconds: " << a.GetElapsedMilliseconds() << std::endl;
+}
+
+TEST(TestStopWatch, Stop)
+{
+ CStopWatch a;
+ CTestStopWatchThread thread;
+
+ EXPECT_FALSE(a.IsRunning());
+ EXPECT_EQ(0.0f, a.GetElapsedSeconds());
+ EXPECT_EQ(0.0f, a.GetElapsedMilliseconds());
+
+ std::cout << "Calling Start()" << std::endl;
+ a.Start();
+ thread.Sleep(1000);
+ EXPECT_TRUE(a.IsRunning());
+ std::cout << "Elapsed Seconds: " << a.GetElapsedSeconds() << std::endl;
+ std::cout << "Elapsed Milliseconds: " << a.GetElapsedMilliseconds() << std::endl;
+
+ a.Stop();
+ EXPECT_FALSE(a.IsRunning());
+ EXPECT_GT(a.GetElapsedSeconds(), 0.0f);
+ EXPECT_GT(a.GetElapsedMilliseconds(), 0.0f);
+}
+
+TEST(TestStopWatch, StartZero)
+{
+ CStopWatch a;
+ CTestStopWatchThread thread;
+
+ EXPECT_FALSE(a.IsRunning());
+ EXPECT_EQ(0.0f, a.GetElapsedSeconds());
+ EXPECT_EQ(0.0f, a.GetElapsedMilliseconds());
+
+ std::cout << "Calling StartZero()" << std::endl;
+ a.StartZero();
+ thread.Sleep(1000);
+ EXPECT_TRUE(a.IsRunning());
+ std::cout << "Elapsed Seconds: " << a.GetElapsedSeconds() << std::endl;
+ std::cout << "Elapsed Milliseconds: " << a.GetElapsedMilliseconds() << std::endl;
+
+ std::cout << "Calling StartZero()" << std::endl;
+ a.StartZero();
+ thread.Sleep(1000);
+ EXPECT_TRUE(a.IsRunning());
+ std::cout << "Elapsed Seconds: " << a.GetElapsedSeconds() << std::endl;
+ std::cout << "Elapsed Milliseconds: " << a.GetElapsedMilliseconds() << std::endl;
+}
+
+TEST(TestStopWatch, Reset)
+{
+ CStopWatch a;
+ CTestStopWatchThread thread;
+
+ EXPECT_FALSE(a.IsRunning());
+ EXPECT_EQ(0.0f, a.GetElapsedSeconds());
+ EXPECT_EQ(0.0f, a.GetElapsedMilliseconds());
+
+ std::cout << "Calling StartZero()" << std::endl;
+ a.StartZero();
+ thread.Sleep(1000);
+ EXPECT_TRUE(a.IsRunning());
+ std::cout << "Elapsed Seconds: " << a.GetElapsedSeconds() << std::endl;
+ std::cout << "Elapsed Milliseconds: " << a.GetElapsedMilliseconds() << std::endl;
+
+ std::cout << "Calling Reset()" << std::endl;
+ a.Reset();
+ thread.Sleep(1000);
+ EXPECT_TRUE(a.IsRunning());
+ std::cout << "Elapsed Seconds: " << a.GetElapsedSeconds() << std::endl;
+ std::cout << "Elapsed Milliseconds: " << a.GetElapsedMilliseconds() << std::endl;
+}
diff --git a/src/utils/test/TestStreamDetails.cpp b/src/utils/test/TestStreamDetails.cpp
new file mode 100644
index 0000000000..1ebf688ae6
--- /dev/null
+++ b/src/utils/test/TestStreamDetails.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/StreamDetails.h"
+
+#include "gtest/gtest.h"
+
+TEST(TestStreamDetails, General)
+{
+ CStreamDetails a;
+ CStreamDetailVideo *video = new CStreamDetailVideo();
+ CStreamDetailAudio *audio = new CStreamDetailAudio();
+ CStreamDetailSubtitle *subtitle = new CStreamDetailSubtitle();
+
+ video->m_iWidth = 1920;
+ video->m_iHeight = 1080;
+ video->m_fAspect = 2.39f;
+ video->m_iDuration = 30;
+ video->m_strCodec = "h264";
+ video->m_strStereoMode = "left_right";
+
+ audio->m_iChannels = 2;
+ audio->m_strCodec = "aac";
+ audio->m_strLanguage = "eng";
+
+ subtitle->m_strLanguage = "eng";
+
+ a.AddStream(video);
+ a.AddStream(audio);
+
+ EXPECT_TRUE(a.HasItems());
+
+ EXPECT_EQ(1, a.GetStreamCount(CStreamDetail::VIDEO));
+ EXPECT_EQ(1, a.GetVideoStreamCount());
+ EXPECT_STREQ("", a.GetVideoCodec().c_str());
+ EXPECT_EQ(0.0f, a.GetVideoAspect());
+ EXPECT_EQ(0, a.GetVideoWidth());
+ EXPECT_EQ(0, a.GetVideoHeight());
+ EXPECT_EQ(0, a.GetVideoDuration());
+ EXPECT_STREQ("", a.GetStereoMode().c_str());
+
+ EXPECT_EQ(1, a.GetStreamCount(CStreamDetail::AUDIO));
+ EXPECT_EQ(1, a.GetAudioStreamCount());
+
+ EXPECT_EQ(0, a.GetStreamCount(CStreamDetail::SUBTITLE));
+ EXPECT_EQ(0, a.GetSubtitleStreamCount());
+
+ a.AddStream(subtitle);
+ EXPECT_EQ(1, a.GetStreamCount(CStreamDetail::SUBTITLE));
+ EXPECT_EQ(1, a.GetSubtitleStreamCount());
+
+ a.DetermineBestStreams();
+ EXPECT_STREQ("h264", a.GetVideoCodec().c_str());
+ EXPECT_EQ(2.39f, a.GetVideoAspect());
+ EXPECT_EQ(1920, a.GetVideoWidth());
+ EXPECT_EQ(1080, a.GetVideoHeight());
+ EXPECT_EQ(30, a.GetVideoDuration());
+ EXPECT_STREQ("left_right", a.GetStereoMode().c_str());
+}
+
+TEST(TestStreamDetails, VideoDimsToResolutionDescription)
+{
+ EXPECT_STREQ("1080",
+ CStreamDetails::VideoDimsToResolutionDescription(1920, 1080).c_str());
+}
+
+TEST(TestStreamDetails, VideoAspectToAspectDescription)
+{
+ EXPECT_STREQ("2.40", CStreamDetails::VideoAspectToAspectDescription(2.39f).c_str());
+}
diff --git a/src/utils/test/TestStreamUtils.cpp b/src/utils/test/TestStreamUtils.cpp
new file mode 100644
index 0000000000..026e4c4948
--- /dev/null
+++ b/src/utils/test/TestStreamUtils.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/StreamUtils.h"
+
+#include "gtest/gtest.h"
+
+TEST(TestStreamUtils, General)
+{
+ EXPECT_EQ(0, StreamUtils::GetCodecPriority(""));
+ EXPECT_EQ(1, StreamUtils::GetCodecPriority("ac3"));
+ EXPECT_EQ(2, StreamUtils::GetCodecPriority("dca"));
+ EXPECT_EQ(3, StreamUtils::GetCodecPriority("eac3"));
+ EXPECT_EQ(4, StreamUtils::GetCodecPriority("dtshd_hra"));
+ EXPECT_EQ(5, StreamUtils::GetCodecPriority("dtshd_ma"));
+ EXPECT_EQ(6, StreamUtils::GetCodecPriority("truehd"));
+ EXPECT_EQ(7, StreamUtils::GetCodecPriority("flac"));
+}
diff --git a/src/utils/test/TestStringUtils.cpp b/src/utils/test/TestStringUtils.cpp
new file mode 100644
index 0000000000..f9e25831c0
--- /dev/null
+++ b/src/utils/test/TestStringUtils.cpp
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/StringUtils.h"
+
+#include "gtest/gtest.h"
+
+TEST(TestStringUtils, Format)
+{
+ std::string refstr = "test 25 2.7 ff FF";
+
+ std::string varstr = StringUtils::Format("%s %d %.1f %x %02X", "test", 25, 2.743f, 0x00ff, 0x00ff);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ varstr = StringUtils::Format("", "test", 25, 2.743f, 0x00ff, 0x00ff);
+ EXPECT_STREQ("", varstr.c_str());
+}
+
+TEST(TestStringUtils, ToUpper)
+{
+ std::string refstr = "TEST";
+
+ std::string varstr = "TeSt";
+ StringUtils::ToUpper(varstr);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestStringUtils, ToLower)
+{
+ std::string refstr = "test";
+
+ std::string varstr = "TeSt";
+ StringUtils::ToLower(varstr);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestStringUtils, EqualsNoCase)
+{
+ std::string refstr = "TeSt";
+
+ EXPECT_TRUE(StringUtils::EqualsNoCase(refstr, "TeSt"));
+ EXPECT_TRUE(StringUtils::EqualsNoCase(refstr, "tEsT"));
+}
+
+TEST(TestStringUtils, Left)
+{
+ std::string refstr, varstr;
+ std::string origstr = "test";
+
+ refstr = "";
+ varstr = StringUtils::Left(origstr, 0);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "te";
+ varstr = StringUtils::Left(origstr, 2);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "test";
+ varstr = StringUtils::Left(origstr, 10);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestStringUtils, Mid)
+{
+ std::string refstr, varstr;
+ std::string origstr = "test";
+
+ refstr = "";
+ varstr = StringUtils::Mid(origstr, 0, 0);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "te";
+ varstr = StringUtils::Mid(origstr, 0, 2);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "test";
+ varstr = StringUtils::Mid(origstr, 0, 10);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "st";
+ varstr = StringUtils::Mid(origstr, 2);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "st";
+ varstr = StringUtils::Mid(origstr, 2, 2);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "es";
+ varstr = StringUtils::Mid(origstr, 1, 2);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestStringUtils, Right)
+{
+ std::string refstr, varstr;
+ std::string origstr = "test";
+
+ refstr = "";
+ varstr = StringUtils::Right(origstr, 0);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "st";
+ varstr = StringUtils::Right(origstr, 2);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ refstr = "test";
+ varstr = StringUtils::Right(origstr, 10);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestStringUtils, Trim)
+{
+ std::string refstr = "test test";
+
+ std::string varstr = " test test ";
+ StringUtils::Trim(varstr);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestStringUtils, TrimLeft)
+{
+ std::string refstr = "test test ";
+
+ std::string varstr = " test test ";
+ StringUtils::TrimLeft(varstr);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestStringUtils, TrimRight)
+{
+ std::string refstr = " test test";
+
+ std::string varstr = " test test ";
+ StringUtils::TrimRight(varstr);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestStringUtils, Replace)
+{
+ std::string refstr = "text text";
+
+ std::string varstr = "test test";
+ EXPECT_EQ(StringUtils::Replace(varstr, 's', 'x'), 2);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ EXPECT_EQ(StringUtils::Replace(varstr, 's', 'x'), 0);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ varstr = "test test";
+ EXPECT_EQ(StringUtils::Replace(varstr, "s", "x"), 2);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+
+ EXPECT_EQ(StringUtils::Replace(varstr, "s", "x"), 0);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestStringUtils, StartsWith)
+{
+ std::string refstr = "test";
+
+ EXPECT_FALSE(StringUtils::StartsWithNoCase(refstr, "x"));
+
+ EXPECT_TRUE(StringUtils::StartsWith(refstr, "te"));
+ EXPECT_TRUE(StringUtils::StartsWith(refstr, "test"));
+ EXPECT_FALSE(StringUtils::StartsWith(refstr, "Te"));
+
+ EXPECT_TRUE(StringUtils::StartsWithNoCase(refstr, "Te"));
+ EXPECT_TRUE(StringUtils::StartsWithNoCase(refstr, "TesT"));
+}
+
+TEST(TestStringUtils, EndsWith)
+{
+ std::string refstr = "test";
+
+ EXPECT_FALSE(StringUtils::EndsWithNoCase(refstr, "x"));
+
+ EXPECT_TRUE(StringUtils::EndsWith(refstr, "st"));
+ EXPECT_TRUE(StringUtils::EndsWith(refstr, "test"));
+ EXPECT_FALSE(StringUtils::EndsWith(refstr, "sT"));
+
+ EXPECT_TRUE(StringUtils::EndsWithNoCase(refstr, "sT"));
+ EXPECT_TRUE(StringUtils::EndsWithNoCase(refstr, "TesT"));
+}
+
+TEST(TestStringUtils, Join)
+{
+ CStdString refstr, varstr;
+ std::vector<std::string> strarray;
+
+ strarray.push_back("a");
+ strarray.push_back("b");
+ strarray.push_back("c");
+ strarray.push_back("de");
+ strarray.push_back(",");
+ strarray.push_back("fg");
+ strarray.push_back(",");
+ refstr = "a,b,c,de,,,fg,,";
+ varstr = StringUtils::Join(strarray, ",");
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestStringUtils, Split)
+{
+ std::vector<std::string> varresults;
+
+ // test overload with string as delimiter
+ varresults = StringUtils::Split("g,h,ij,k,lm,,n", ",");
+ EXPECT_STREQ("g", varresults.at(0).c_str());
+ EXPECT_STREQ("h", varresults.at(1).c_str());
+ EXPECT_STREQ("ij", varresults.at(2).c_str());
+ EXPECT_STREQ("k", varresults.at(3).c_str());
+ EXPECT_STREQ("lm", varresults.at(4).c_str());
+ EXPECT_STREQ("", varresults.at(5).c_str());
+ EXPECT_STREQ("n", varresults.at(6).c_str());
+
+ EXPECT_TRUE(StringUtils::Split("", "|").empty());
+
+ EXPECT_EQ(4, StringUtils::Split("a bc d ef ghi ", " ", 4).size());
+ EXPECT_STREQ("d ef ghi ", StringUtils::Split("a bc d ef ghi ", " ", 4).at(3).c_str()) << "Last part must include rest of the input string";
+ EXPECT_EQ(7, StringUtils::Split("a bc d ef ghi ", " ").size()) << "Result must be 7 strings including two empty strings";
+ EXPECT_STREQ("bc", StringUtils::Split("a bc d ef ghi ", " ").at(1).c_str());
+ EXPECT_STREQ("", StringUtils::Split("a bc d ef ghi ", " ").at(2).c_str());
+ EXPECT_STREQ("", StringUtils::Split("a bc d ef ghi ", " ").at(6).c_str());
+
+ EXPECT_EQ(2, StringUtils::Split("a bc d ef ghi ", " ").size());
+ EXPECT_EQ(2, StringUtils::Split("a bc d ef ghi ", " ", 10).size());
+ EXPECT_STREQ("a bc", StringUtils::Split("a bc d ef ghi ", " ", 10).at(0).c_str());
+
+ EXPECT_EQ(1, StringUtils::Split("a bc d ef ghi ", " z").size());
+ EXPECT_STREQ("a bc d ef ghi ", StringUtils::Split("a bc d ef ghi ", " z").at(0).c_str());
+
+ EXPECT_EQ(1, StringUtils::Split("a bc d ef ghi ", "").size());
+ EXPECT_STREQ("a bc d ef ghi ", StringUtils::Split("a bc d ef ghi ", "").at(0).c_str());
+
+ // test overload with char as delimiter
+ EXPECT_EQ(4, StringUtils::Split("a bc d ef ghi ", ' ', 4).size());
+ EXPECT_STREQ("d ef ghi ", StringUtils::Split("a bc d ef ghi ", ' ', 4).at(3).c_str());
+ EXPECT_EQ(7, StringUtils::Split("a bc d ef ghi ", ' ').size()) << "Result must be 7 strings including two empty strings";
+ EXPECT_STREQ("bc", StringUtils::Split("a bc d ef ghi ", ' ').at(1).c_str());
+ EXPECT_STREQ("", StringUtils::Split("a bc d ef ghi ", ' ').at(2).c_str());
+ EXPECT_STREQ("", StringUtils::Split("a bc d ef ghi ", ' ').at(6).c_str());
+
+ EXPECT_EQ(1, StringUtils::Split("a bc d ef ghi ", 'z').size());
+ EXPECT_STREQ("a bc d ef ghi ", StringUtils::Split("a bc d ef ghi ", 'z').at(0).c_str());
+
+ EXPECT_EQ(1, StringUtils::Split("a bc d ef ghi ", "").size());
+ EXPECT_STREQ("a bc d ef ghi ", StringUtils::Split("a bc d ef ghi ", 'z').at(0).c_str());
+}
+
+TEST(TestStringUtils, FindNumber)
+{
+ EXPECT_EQ(3, StringUtils::FindNumber("aabcaadeaa", "aa"));
+ EXPECT_EQ(1, StringUtils::FindNumber("aabcaadeaa", "b"));
+}
+
+TEST(TestStringUtils, AlphaNumericCompare)
+{
+ int64_t ref, var;
+
+ ref = 0;
+ var = StringUtils::AlphaNumericCompare(L"123abc", L"abc123");
+ EXPECT_LT(var, ref);
+}
+
+TEST(TestStringUtils, TimeStringToSeconds)
+{
+ EXPECT_EQ(77455, StringUtils::TimeStringToSeconds("21:30:55"));
+ EXPECT_EQ(7*60, StringUtils::TimeStringToSeconds("7 min"));
+ EXPECT_EQ(7*60, StringUtils::TimeStringToSeconds("7 min\t"));
+ EXPECT_EQ(154*60, StringUtils::TimeStringToSeconds(" 154 min"));
+ EXPECT_EQ(1*60+1, StringUtils::TimeStringToSeconds("1:01"));
+ EXPECT_EQ(4*60+3, StringUtils::TimeStringToSeconds("4:03"));
+ EXPECT_EQ(2*3600+4*60+3, StringUtils::TimeStringToSeconds("2:04:03"));
+ EXPECT_EQ(2*3600+4*60+3, StringUtils::TimeStringToSeconds(" 2:4:3"));
+ EXPECT_EQ(2*3600+4*60+3, StringUtils::TimeStringToSeconds(" \t\t 02:04:03 \n "));
+ EXPECT_EQ(1*3600+5*60+2, StringUtils::TimeStringToSeconds("01:05:02:04:03 \n "));
+ EXPECT_EQ(0, StringUtils::TimeStringToSeconds("blah"));
+ EXPECT_EQ(0, StringUtils::TimeStringToSeconds("ля-ля"));
+}
+
+TEST(TestStringUtils, RemoveCRLF)
+{
+ CStdString refstr, varstr;
+
+ refstr = "test\r\nstring\nblah blah";
+ varstr = "test\r\nstring\nblah blah\n";
+ StringUtils::RemoveCRLF(varstr);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+}
+
+TEST(TestStringUtils, utf8_strlen)
+{
+ int ref, var;
+
+ ref = 9;
+ var = StringUtils::utf8_strlen("test_UTF8");
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestStringUtils, SecondsToTimeString)
+{
+ CStdString ref, var;
+
+ ref = "21:30:55";
+ var = StringUtils::SecondsToTimeString(77455);
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+}
+
+TEST(TestStringUtils, IsNaturalNumber)
+{
+ EXPECT_TRUE(StringUtils::IsNaturalNumber("10"));
+ EXPECT_TRUE(StringUtils::IsNaturalNumber(" 10"));
+ EXPECT_TRUE(StringUtils::IsNaturalNumber("0"));
+ EXPECT_FALSE(StringUtils::IsNaturalNumber(" 1 0"));
+ EXPECT_FALSE(StringUtils::IsNaturalNumber("1.0"));
+ EXPECT_FALSE(StringUtils::IsNaturalNumber("1.1"));
+ EXPECT_FALSE(StringUtils::IsNaturalNumber("0x1"));
+ EXPECT_FALSE(StringUtils::IsNaturalNumber("blah"));
+ EXPECT_FALSE(StringUtils::IsNaturalNumber("120 h"));
+ EXPECT_FALSE(StringUtils::IsNaturalNumber(" "));
+ EXPECT_FALSE(StringUtils::IsNaturalNumber(""));
+}
+
+TEST(TestStringUtils, IsInteger)
+{
+ EXPECT_TRUE(StringUtils::IsInteger("10"));
+ EXPECT_TRUE(StringUtils::IsInteger(" -10"));
+ EXPECT_TRUE(StringUtils::IsInteger("0"));
+ EXPECT_FALSE(StringUtils::IsInteger(" 1 0"));
+ EXPECT_FALSE(StringUtils::IsInteger("1.0"));
+ EXPECT_FALSE(StringUtils::IsInteger("1.1"));
+ EXPECT_FALSE(StringUtils::IsInteger("0x1"));
+ EXPECT_FALSE(StringUtils::IsInteger("blah"));
+ EXPECT_FALSE(StringUtils::IsInteger("120 h"));
+ EXPECT_FALSE(StringUtils::IsInteger(" "));
+ EXPECT_FALSE(StringUtils::IsInteger(""));
+}
+
+TEST(TestStringUtils, SizeToString)
+{
+ CStdString ref, var;
+
+ ref = "2.00 GB";
+ var = StringUtils::SizeToString(2147483647);
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+}
+
+TEST(TestStringUtils, EmptyString)
+{
+ EXPECT_STREQ("", StringUtils::EmptyString.c_str());
+}
+
+TEST(TestStringUtils, FindWords)
+{
+ int ref, var;
+
+ ref = 5;
+ var = StringUtils::FindWords("test string", "string");
+ EXPECT_EQ(ref, var);
+ var = StringUtils::FindWords("12345string", "string");
+ EXPECT_EQ(ref, var);
+ var = StringUtils::FindWords("apple2012", "2012");
+ EXPECT_EQ(ref, var);
+ ref = -1;
+ var = StringUtils::FindWords("12345string", "ring");
+ EXPECT_EQ(ref, var);
+ var = StringUtils::FindWords("12345string", "345");
+ EXPECT_EQ(ref, var);
+ var = StringUtils::FindWords("apple2012", "e2012");
+ EXPECT_EQ(ref, var);
+ var = StringUtils::FindWords("apple2012", "12");
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestStringUtils, FindWords_NonAscii)
+{
+ int ref, var;
+
+ ref = 6;
+ var = StringUtils::FindWords("我的视频", "视频");
+ EXPECT_EQ(ref, var);
+ var = StringUtils::FindWords("我的视频", "视");
+ EXPECT_EQ(ref, var);
+ var = StringUtils::FindWords("Apple ple", "ple");
+ EXPECT_EQ(ref, var);
+ ref = 7;
+ var = StringUtils::FindWords("Äpfel.pfel", "pfel");
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestStringUtils, FindEndBracket)
+{
+ int ref, var;
+
+ ref = 11;
+ var = StringUtils::FindEndBracket("atest testbb test", 'a', 'b');
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestStringUtils, DateStringToYYYYMMDD)
+{
+ int ref, var;
+
+ ref = 20120706;
+ var = StringUtils::DateStringToYYYYMMDD("2012-07-06");
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestStringUtils, WordToDigits)
+{
+ std::string ref, var;
+
+ ref = "8378 787464";
+ var = "test string";
+ StringUtils::WordToDigits(var);
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+}
+
+TEST(TestStringUtils, CreateUUID)
+{
+ std::cout << "CreateUUID(): " << StringUtils::CreateUUID() << std::endl;
+}
+
+TEST(TestStringUtils, ValidateUUID)
+{
+ EXPECT_TRUE(StringUtils::ValidateUUID(StringUtils::CreateUUID()));
+}
+
+TEST(TestStringUtils, CompareFuzzy)
+{
+ double ref, var;
+
+ ref = 6.25f;
+ var = StringUtils::CompareFuzzy("test string", "string test");
+ EXPECT_EQ(ref, var);
+}
+
+TEST(TestStringUtils, FindBestMatch)
+{
+ double refdouble, vardouble;
+ int refint, varint;
+ std::vector<std::string> strarray;
+
+ refint = 3;
+ refdouble = 0.5625f;
+ strarray.push_back("");
+ strarray.push_back("a");
+ strarray.push_back("e");
+ strarray.push_back("es");
+ strarray.push_back("t");
+ varint = StringUtils::FindBestMatch("test", strarray, vardouble);
+ EXPECT_EQ(refint, varint);
+ EXPECT_EQ(refdouble, vardouble);
+}
+
+TEST(TestStringUtils, Paramify)
+{
+ const char *input = "some, very \\ odd \"string\"";
+ const char *ref = "\"some, very \\\\ odd \\\"string\\\"\"";
+
+ std::string result = StringUtils::Paramify(input);
+ EXPECT_STREQ(ref, result.c_str());
+}
diff --git a/src/utils/test/TestSystemInfo.cpp b/src/utils/test/TestSystemInfo.cpp
new file mode 100644
index 0000000000..f19d4d0c7d
--- /dev/null
+++ b/src/utils/test/TestSystemInfo.cpp
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/SystemInfo.h"
+#include "settings/Settings.h"
+#include "GUIInfoManager.h"
+
+#include "gtest/gtest.h"
+
+class TestSystemInfo : public testing::Test
+{
+protected:
+ TestSystemInfo()
+ {
+ }
+ ~TestSystemInfo()
+ {
+ }
+};
+
+TEST_F(TestSystemInfo, Print_System_Info)
+{
+ std::cout << "'GetKernelName(false)': \"" << g_sysinfo.GetKernelName(true) << "\"\n";
+ std::cout << "'GetKernelVersion()': \"" << g_sysinfo.GetKernelVersion() << "\"\n";
+ std::cout << "'GetKernelVersionFull()': \"" << g_sysinfo.GetKernelVersionFull() << "\"\n";
+ std::cout << "'GetOsPrettyNameWithVersion()': \"" << g_sysinfo.GetOsPrettyNameWithVersion() << "\"\n";
+ std::cout << "'GetOsName(false)': \"" << g_sysinfo.GetOsName(false) << "\"\n";
+ std::cout << "'GetOsVersion()': \"" << g_sysinfo.GetOsVersion() << "\"\n";
+ std::cout << "'GetKernelCpuFamily()': \"" << g_sysinfo.GetKernelCpuFamily() << "\"\n";
+ std::cout << "'GetKernelBitness()': \"" << g_sysinfo.GetKernelBitness() << "\"\n";
+ std::cout << "'GetBuildTargetPlatformName()': \"" << g_sysinfo.GetBuildTargetPlatformName() << "\"\n";
+ std::cout << "'GetBuildTargetPlatformVersionDecoded()': \"" << g_sysinfo.GetBuildTargetPlatformVersionDecoded() << "\"\n";
+ std::cout << "'GetBuildTargetPlatformVersion()': \"" << g_sysinfo.GetBuildTargetPlatformVersion() << "\"\n";
+ std::cout << "'GetBuildTargetCpuFamily()': \"" << g_sysinfo.GetBuildTargetCpuFamily() << "\"\n";
+ std::cout << "'GetXbmcBitness()': \"" << g_sysinfo.GetXbmcBitness() << "\"\n";
+ std::cout << "'GetUsedCompilerNameAndVer()': \"" << g_sysinfo.GetUsedCompilerNameAndVer() << "\"\n";
+ std::cout << "'GetManufacturerName()': \"" << g_sysinfo.GetManufacturerName() << "\"\n";
+ std::cout << "'GetModelName()': \"" << g_sysinfo.GetModelName() << "\"\n";
+ std::cout << "'IsAppleTV2()': \"" << g_sysinfo.IsAppleTV2() << "\"\n";
+ std::cout << "'GetUserAgent()': \"" << g_sysinfo.GetUserAgent() << "\"\n";
+}
+
+TEST_F(TestSystemInfo, GetKernelName)
+{
+ EXPECT_FALSE(g_sysinfo.GetKernelName(true).empty()) << "'GetKernelName(true)' must not return empty kernel name";
+ EXPECT_FALSE(g_sysinfo.GetKernelName(false).empty()) << "'GetKernelName(false)' must not return empty kernel name";
+ EXPECT_STRCASENE("Unknown kernel", g_sysinfo.GetKernelName(true).c_str()) << "'GetKernelName(true)' must not return 'Unknown kernel'";
+ EXPECT_STRCASENE("Unknown kernel", g_sysinfo.GetKernelName(false).c_str()) << "'GetKernelName(false)' must not return 'Unknown kernel'";
+#ifndef TARGET_DARWIN
+ EXPECT_EQ(g_sysinfo.GetBuildTargetPlatformName(), g_sysinfo.GetKernelName(true)) << "'GetKernelName(true)' must match GetBuildTargetPlatformName()";
+ EXPECT_EQ(g_sysinfo.GetBuildTargetPlatformName(), g_sysinfo.GetKernelName(false)) << "'GetKernelName(false)' must match GetBuildTargetPlatformName()";
+#endif // !TARGET_DARWIN
+#if defined(TARGET_WINDOWS)
+ EXPECT_NE(std::string::npos, g_sysinfo.GetKernelName(true).find("Windows")) << "'GetKernelName(true)' must contain 'Windows'";
+ EXPECT_NE(std::string::npos, g_sysinfo.GetKernelName(false).find("Windows")) << "'GetKernelName(false)' must contain 'Windows'";
+#elif defined(TARGET_FREEBSD)
+ EXPECT_STREQ("FreeBSD", g_sysinfo.GetKernelName(true).c_str()) << "'GetKernelName(true)' must return 'FreeBSD'";
+ EXPECT_STREQ("FreeBSD", g_sysinfo.GetKernelName(false).c_str()) << "'GetKernelName(false)' must return 'FreeBSD'";
+#elif defined(TARGET_DARWIN)
+ EXPECT_STREQ("Darwin", g_sysinfo.GetKernelName(true).c_str()) << "'GetKernelName(true)' must return 'Darwin'";
+ EXPECT_STREQ("Darwin", g_sysinfo.GetKernelName(false).c_str()) << "'GetKernelName(false)' must return 'Darwin'";
+#elif defined(TARGET_LINUX)
+ EXPECT_STREQ("Linux", g_sysinfo.GetKernelName(true).c_str()) << "'GetKernelName(true)' must return 'Linux'";
+ EXPECT_STREQ("Linux", g_sysinfo.GetKernelName(false).c_str()) << "'GetKernelName(false)' must return 'Linux'";
+#endif
+}
+
+TEST_F(TestSystemInfo, GetKernelVersionFull)
+{
+ EXPECT_FALSE(g_sysinfo.GetKernelVersionFull().empty()) << "'GetKernelVersionFull()' must not return empty string";
+ EXPECT_STRNE("0.0.0", g_sysinfo.GetKernelVersionFull().c_str()) << "'GetKernelVersionFull()' must not return '0.0.0'";
+ EXPECT_STRNE("0.0", g_sysinfo.GetKernelVersionFull().c_str()) << "'GetKernelVersionFull()' must not return '0.0'";
+ EXPECT_EQ(0, g_sysinfo.GetKernelVersionFull().find_first_of("0123456789")) << "'GetKernelVersionFull()' must not return version not starting from digit";
+}
+
+TEST_F(TestSystemInfo, GetKernelVersion)
+{
+ EXPECT_FALSE(g_sysinfo.GetKernelVersion().empty()) << "'GetKernelVersion()' must not return empty string";
+ EXPECT_STRNE("0.0.0", g_sysinfo.GetKernelVersion().c_str()) << "'GetKernelVersion()' must not return '0.0.0'";
+ EXPECT_STRNE("0.0", g_sysinfo.GetKernelVersion().c_str()) << "'GetKernelVersion()' must not return '0.0'";
+ EXPECT_EQ(0, g_sysinfo.GetKernelVersion().find_first_of("0123456789")) << "'GetKernelVersion()' must not return version not starting from digit";
+ EXPECT_EQ(std::string::npos, g_sysinfo.GetKernelVersion().find_first_not_of("0123456789.")) << "'GetKernelVersion()' must not return version with not only digits and dots";
+}
+
+TEST_F(TestSystemInfo, GetOsName)
+{
+ EXPECT_FALSE(g_sysinfo.GetOsName(true).empty()) << "'GetOsName(true)' must not return empty OS name";
+ EXPECT_FALSE(g_sysinfo.GetOsName(false).empty()) << "'GetOsName(false)' must not return empty OS name";
+ EXPECT_STRCASENE("Unknown OS", g_sysinfo.GetOsName(true).c_str()) << "'GetOsName(true)' must not return 'Unknown OS'";
+ EXPECT_STRCASENE("Unknown OS", g_sysinfo.GetOsName(false).c_str()) << "'GetOsName(false)' must not return 'Unknown OS'";
+#if defined(TARGET_WINDOWS)
+ EXPECT_NE(std::string::npos, g_sysinfo.GetOsName(true).find("Windows")) << "'GetOsName(true)' must contain 'Windows'";
+ EXPECT_NE(std::string::npos, g_sysinfo.GetOsName(false).find("Windows")) << "'GetOsName(false)' must contain 'Windows'";
+#elif defined(TARGET_FREEBSD)
+ EXPECT_STREQ("FreeBSD", g_sysinfo.GetOsName(true).c_str()) << "'GetOsName(true)' must return 'FreeBSD'";
+ EXPECT_STREQ("FreeBSD", g_sysinfo.GetOsName(false).c_str()) << "'GetOsName(false)' must return 'FreeBSD'";
+#elif defined(TARGET_DARWIN_IOS)
+ EXPECT_STREQ("iOS", g_sysinfo.GetOsName(true).c_str()) << "'GetOsName(true)' must return 'iOS'";
+ EXPECT_STREQ("iOS", g_sysinfo.GetOsName(false).c_str()) << "'GetOsName(false)' must return 'iOS'";
+#elif defined(TARGET_DARWIN_OSX)
+ EXPECT_STREQ("OS X", g_sysinfo.GetOsName(true).c_str()) << "'GetOsName(true)' must return 'OS X'";
+ EXPECT_STREQ("OS X", g_sysinfo.GetOsName(false).c_str()) << "'GetOsName(false)' must return 'OS X'";
+#elif defined(TARGET_ANDROID)
+ EXPECT_STREQ("Android", g_sysinfo.GetOsName(true).c_str()) << "'GetOsName(true)' must return 'Android'";
+ EXPECT_STREQ("Android", g_sysinfo.GetOsName(false).c_str()) << "'GetOsName(false)' must return 'Android'";
+#endif
+#ifdef TARGET_DARWIN
+ EXPECT_EQ(g_sysinfo.GetBuildTargetPlatformName(), g_sysinfo.GetOsName(true)) << "'GetOsName(true)' must match GetBuildTargetPlatformName()";
+ EXPECT_EQ(g_sysinfo.GetBuildTargetPlatformName(), g_sysinfo.GetOsName(false)) << "'GetOsName(false)' must match GetBuildTargetPlatformName()";
+#endif // TARGET_DARWIN
+}
+
+TEST_F(TestSystemInfo, GetOsVersion)
+{
+ EXPECT_FALSE(g_sysinfo.GetOsVersion().empty()) << "'GetOsVersion()' must not return empty string";
+ EXPECT_STRNE("0.0.0", g_sysinfo.GetOsVersion().c_str()) << "'GetOsVersion()' must not return '0.0.0'";
+ EXPECT_STRNE("0.0", g_sysinfo.GetOsVersion().c_str()) << "'GetOsVersion()' must not return '0.0'";
+ EXPECT_EQ(0, g_sysinfo.GetOsVersion().find_first_of("0123456789")) << "'GetOsVersion()' must not return version not starting from digit";
+ EXPECT_EQ(std::string::npos, g_sysinfo.GetOsVersion().find_first_not_of("0123456789.")) << "'GetOsVersion()' must not return version with not only digits and dots";
+}
+
+TEST_F(TestSystemInfo, GetOsPrettyNameWithVersion)
+{
+ EXPECT_FALSE(g_sysinfo.GetOsPrettyNameWithVersion().empty()) << "'GetOsPrettyNameWithVersion()' must not return empty string";
+ EXPECT_EQ(std::string::npos, g_sysinfo.GetOsPrettyNameWithVersion().find("Unknown")) << "'GetOsPrettyNameWithVersion()' must not contain 'Unknown'";
+ EXPECT_EQ(std::string::npos, g_sysinfo.GetOsPrettyNameWithVersion().find("unknown")) << "'GetOsPrettyNameWithVersion()' must not contain 'unknown'";
+#ifdef TARGET_WINDOWS
+ EXPECT_NE(std::string::npos, g_sysinfo.GetOsPrettyNameWithVersion().find("Windows")) << "'GetOsPrettyNameWithVersion()' must contain 'Windows'";
+#else // ! TARGET_WINDOWS
+ EXPECT_NE(std::string::npos, g_sysinfo.GetOsPrettyNameWithVersion().find(g_sysinfo.GetOsVersion())) << "'GetOsPrettyNameWithVersion()' must contain OS version";
+#endif // ! TARGET_WINDOWS
+}
+
+TEST_F(TestSystemInfo, GetManufacturerName)
+{
+ EXPECT_STRCASENE("unknown", g_sysinfo.GetManufacturerName().c_str()) << "'GetManufacturerName()' must return empty string instead of 'Unknown'";
+}
+
+TEST_F(TestSystemInfo, GetModelName)
+{
+ EXPECT_STRCASENE("unknown", g_sysinfo.GetModelName().c_str()) << "'GetModelName()' must return empty string instead of 'Unknown'";
+}
+
+#ifndef TARGET_WINDOWS
+TEST_F(TestSystemInfo, IsAeroDisabled)
+{
+ EXPECT_FALSE(g_sysinfo.IsAeroDisabled()) << "'IsAeroDisabled()' must return 'false'";
+}
+#endif // ! TARGET_WINDOWS
+
+TEST_F(TestSystemInfo, IsWindowsVersion)
+{
+ EXPECT_FALSE(g_sysinfo.IsWindowsVersion(CSysInfo::WindowsVersionUnknown)) << "'IsWindowsVersion()' must return 'false' for 'WindowsVersionUnknown'";
+#ifndef TARGET_WINDOWS
+ EXPECT_FALSE(g_sysinfo.IsWindowsVersion(CSysInfo::WindowsVersionWin7)) << "'IsWindowsVersion()' must return 'false'";
+#endif // ! TARGET_WINDOWS
+}
+
+TEST_F(TestSystemInfo, IsWindowsVersionAtLeast)
+{
+ EXPECT_FALSE(g_sysinfo.IsWindowsVersionAtLeast(CSysInfo::WindowsVersionUnknown)) << "'IsWindowsVersionAtLeast()' must return 'false' for 'WindowsVersionUnknown'";
+ EXPECT_FALSE(g_sysinfo.IsWindowsVersionAtLeast(CSysInfo::WindowsVersionFuture)) << "'IsWindowsVersionAtLeast()' must return 'false' for 'WindowsVersionFuture'";
+#ifndef TARGET_WINDOWS
+ EXPECT_FALSE(g_sysinfo.IsWindowsVersion(CSysInfo::WindowsVersionWin7)) << "'IsWindowsVersionAtLeast()' must return 'false'";
+#endif // ! TARGET_WINDOWS
+}
+
+TEST_F(TestSystemInfo, GetWindowsVersion)
+{
+#ifdef TARGET_WINDOWS
+ EXPECT_NE(CSysInfo::WindowsVersionUnknown, g_sysinfo.GetWindowsVersion()) << "'GetWindowsVersion()' must not return 'WindowsVersionUnknown'";
+ EXPECT_NE(CSysInfo::WindowsVersionFuture, g_sysinfo.GetWindowsVersion()) << "'GetWindowsVersion()' must not return 'WindowsVersionFuture'";
+#else // ! TARGET_WINDOWS
+ EXPECT_EQ(CSysInfo::WindowsVersionUnknown, g_sysinfo.GetWindowsVersion()) << "'GetWindowsVersion()' must return 'WindowsVersionUnknown'";
+#endif // ! TARGET_WINDOWS
+}
+
+TEST_F(TestSystemInfo, GetKernelBitness)
+{
+ EXPECT_TRUE(g_sysinfo.GetKernelBitness() == 32 || g_sysinfo.GetKernelBitness() == 64) << "'GetKernelBitness()' must return '32' or '64', but not '" << g_sysinfo.GetKernelBitness() << "'";
+ EXPECT_LE(g_sysinfo.GetXbmcBitness(), g_sysinfo.GetKernelBitness()) << "'GetKernelBitness()' must be greater or equal to 'GetXbmcBitness()'";
+}
+
+TEST_F(TestSystemInfo, GetKernelCpuFamily)
+{
+ EXPECT_STRNE("unknown CPU family", g_sysinfo.GetKernelCpuFamily().c_str()) << "'GetKernelCpuFamily()' must not return 'unknown CPU family'";
+#if defined(__thumb__) || defined(_M_ARMT) || defined(__arm__) || defined(_M_ARM) || defined (__aarch64__)
+ EXPECT_STREQ("ARM", g_sysinfo.GetKernelCpuFamily().c_str()) << "'GetKernelCpuFamily()' must return 'ARM'";
+#else // ! ARM
+ EXPECT_EQ(g_sysinfo.GetBuildTargetCpuFamily(), g_sysinfo.GetKernelCpuFamily()) << "'GetKernelCpuFamily()' must match 'GetBuildTargetCpuFamily()'";
+#endif // ! ARM
+}
+
+TEST_F(TestSystemInfo, GetXbmcBitness)
+{
+ EXPECT_TRUE(g_sysinfo.GetXbmcBitness() == 32 || g_sysinfo.GetXbmcBitness() == 64) << "'GetXbmcBitness()' must return '32' or '64', but not '" << g_sysinfo.GetXbmcBitness() << "'";
+ EXPECT_GE(g_sysinfo.GetKernelBitness(), g_sysinfo.GetXbmcBitness()) << "'GetXbmcBitness()' must be not greater than 'GetKernelBitness()'";
+}
+
+TEST_F(TestSystemInfo, GetUserAgent)
+{
+ EXPECT_STREQ(g_sysinfo.GetAppName().c_str(), g_sysinfo.GetUserAgent().substr(0, g_sysinfo.GetAppName().size()).c_str()) << "'GetUserAgent()' string must start with app name'";
+ EXPECT_NE(std::string::npos, g_sysinfo.GetUserAgent().find('(')) << "'GetUserAgent()' must contain brackets around second parameter";
+ EXPECT_NE(std::string::npos, g_sysinfo.GetUserAgent().find(')')) << "'GetUserAgent()' must contain brackets around second parameter";
+ EXPECT_EQ(g_sysinfo.GetUserAgent().find(' '), g_sysinfo.GetUserAgent().find(" (")) << "Second parameter in 'GetUserAgent()' string must be in brackets";
+ EXPECT_EQ(g_sysinfo.GetUserAgent().find(" (") + 1, g_sysinfo.GetUserAgent().find('(')) << "'GetUserAgent()' string must not contain any opening brackets before second parameter";
+ EXPECT_GT(g_sysinfo.GetUserAgent().find(')'), g_sysinfo.GetUserAgent().find('(')) << "'GetUserAgent()' string must not contain any closing brackets before second parameter";
+ EXPECT_EQ(g_sysinfo.GetUserAgent().find(") "), g_sysinfo.GetUserAgent().find(')')) << "'GetUserAgent()' string must not contain any closing brackets before end of second parameter";
+#if defined(TARGET_WINDOWS)
+ EXPECT_EQ(g_sysinfo.GetUserAgent().find('('), g_sysinfo.GetUserAgent().find("(Windows")) << "Second parameter in 'GetUserAgent()' string must start from `Windows`";
+ EXPECT_NE(std::string::npos, g_sysinfo.GetUserAgent().find("Windows")) << "'GetUserAgent()' must contain 'Windows'";
+#elif defined(TARGET_DARWIN_IOS)
+ EXPECT_NE(std::string::npos, g_sysinfo.GetUserAgent().find("like Mac OS X")) << "'GetUserAgent()' must contain ' like Mac OS X'";
+#ifdef TARGET_DARWIN_IOS_ATV2
+ EXPECT_NE(std::string::npos, g_sysinfo.GetUserAgent().find("CPU OS ")) << "'GetUserAgent()' must contain 'CPU OS '";
+#else // ! TARGET_DARWIN_IOS_ATV2
+ EXPECT_TRUE(g_sysinfo.GetUserAgent().find("CPU OS ") != std::string::npos || g_sysinfo.GetUserAgent().find("CPU iPhone OS ") != std::string::npos) << "'GetUserAgent()' must contain 'CPU OS ' or 'CPU iPhone OS '";
+#endif // ! TARGET_DARWIN_IOS_ATV2
+#elif defined(TARGET_DARWIN_OSX)
+ EXPECT_EQ(g_sysinfo.GetUserAgent().find('('), g_sysinfo.GetUserAgent().find("(Macintosh; ")) << "Second parameter in 'GetUserAgent()' string must start from 'Macintosh; '";
+#elif defined(TARGET_ANDROID)
+ EXPECT_EQ(g_sysinfo.GetUserAgent().find('('), g_sysinfo.GetUserAgent().find("(Linux; Android ")) << "Second parameter in 'GetUserAgent()' string must start from 'Linux; Android '";
+#elif defined(TARGET_POSIX)
+ EXPECT_EQ(g_sysinfo.GetUserAgent().find('('), g_sysinfo.GetUserAgent().find("(X11; ")) << "Second parameter in 'GetUserAgent()' string must start from 'X11; '";
+#if defined(TARGET_FREEBSD)
+ EXPECT_EQ(g_sysinfo.GetUserAgent().find('('), g_sysinfo.GetUserAgent().find("(X11; FreeBSD ")) << "Second parameter in 'GetUserAgent()' string must start from 'X11; FreeBSD '";
+#elif defined(TARGET_LINUX)
+ EXPECT_EQ(g_sysinfo.GetUserAgent().find('('), g_sysinfo.GetUserAgent().find("(X11; Linux ")) << "Second parameter in 'GetUserAgent()' string must start from 'X11; Linux '";
+#endif // defined(TARGET_LINUX)
+#endif // defined(TARGET_POSIX)
+
+#ifdef TARGET_RASPBERRY_PI
+ EXPECT_NE(std::string::npos, g_sysinfo.GetUserAgent().find(" XBMC_HW_RaspberryPi/")) << "'GetUserAgent()' must contain ' XBMC_HW_RaspberryPi/'";
+#endif // TARGET_RASPBERRY_PI
+
+ EXPECT_NE(std::string::npos, g_sysinfo.GetUserAgent().find(" App_Bitness/")) << "'GetUserAgent()' must contain ' App_Bitness/'";
+ EXPECT_NE(std::string::npos, g_sysinfo.GetUserAgent().find(" Version/")) << "'GetUserAgent()' must contain ' Version/'";
+}
+
+TEST_F(TestSystemInfo, IsAppleTV2)
+{
+#ifdef TARGET_DARWIN_IOS_ATV2
+ EXPECT_TRUE(g_sysinfo.IsAppleTV2()) << "'IsAppleTV2()' must return 'true'";
+#else
+ EXPECT_FALSE(g_sysinfo.IsAppleTV2()) << "'IsAppleTV2()' must return 'false'";
+#endif
+}
+
+// FIXME: TARGET_DARWIN_IOS_ATV2?
+#ifndef TARGET_DARWIN
+TEST_F(TestSystemInfo, HasVideoToolBoxDecoder)
+{
+ EXPECT_FALSE(g_sysinfo.HasVideoToolBoxDecoder()) << "'HasVideoToolBoxDecoder()' must return 'false'";
+}
+#endif
+
+TEST_F(TestSystemInfo, GetBuildTargetPlatformName)
+{
+ EXPECT_EQ(std::string::npos, g_sysinfo.GetBuildTargetPlatformName().find("Unknown")) << "'GetBuildTargetPlatformName()' must not contain 'Unknown', actual value: '" << g_sysinfo.GetBuildTargetPlatformName() << "'";
+ EXPECT_EQ(std::string::npos, g_sysinfo.GetBuildTargetPlatformName().find("unknown")) << "'GetBuildTargetPlatformName()' must not contain 'unknown', actual value: '" << g_sysinfo.GetBuildTargetPlatformName() << "'";
+}
+
+TEST_F(TestSystemInfo, GetBuildTargetPlatformVersion)
+{
+ EXPECT_EQ(std::string::npos, g_sysinfo.GetBuildTargetPlatformVersion().find("Unknown")) << "'GetBuildTargetPlatformVersion()' must not contain 'Unknown', actual value: '" << g_sysinfo.GetBuildTargetPlatformVersion() << "'";
+ EXPECT_EQ(std::string::npos, g_sysinfo.GetBuildTargetPlatformVersion().find("unknown")) << "'GetBuildTargetPlatformVersion()' must not contain 'unknown', actual value: '" << g_sysinfo.GetBuildTargetPlatformVersion() << "'";
+}
+
+TEST_F(TestSystemInfo, GetBuildTargetPlatformVersionDecoded)
+{
+ EXPECT_EQ(std::string::npos, g_sysinfo.GetBuildTargetPlatformVersionDecoded().find("Unknown")) << "'GetBuildTargetPlatformVersionDecoded()' must not contain 'Unknown', actual value: '" << g_sysinfo.GetBuildTargetPlatformVersion() << "'";
+ EXPECT_EQ(std::string::npos, g_sysinfo.GetBuildTargetPlatformVersionDecoded().find("unknown")) << "'GetBuildTargetPlatformVersionDecoded()' must not contain 'unknown', actual value: '" << g_sysinfo.GetBuildTargetPlatformVersion() << "'";
+#ifdef TARGET_ANDROID
+ EXPECT_STREQ("API level ", g_sysinfo.GetBuildTargetPlatformVersionDecoded().substr(0, 10).c_str()) << "'GetBuildTargetPlatformVersionDecoded()' must start from 'API level '";
+#else
+ EXPECT_STREQ("version ", g_sysinfo.GetBuildTargetPlatformVersionDecoded().substr(0, 8).c_str()) << "'GetBuildTargetPlatformVersionDecoded()' must start from 'version'";
+#endif
+}
+
+TEST_F(TestSystemInfo, GetBuildTargetCpuFamily)
+{
+ EXPECT_STRNE("unknown CPU family", g_sysinfo.GetBuildTargetCpuFamily().c_str()) << "'GetBuildTargetCpuFamily()' must not return 'unknown CPU family'";
+#if defined(__thumb__) || defined(_M_ARMT) || defined(__arm__) || defined(_M_ARM) || defined (__aarch64__)
+ EXPECT_STREQ("ARM", g_sysinfo.GetBuildTargetCpuFamily().substr(0, 3).c_str()) << "'GetKernelCpuFamily()' string must start from 'ARM'";
+#else // ! ARM
+ EXPECT_EQ(g_sysinfo.GetKernelCpuFamily(), g_sysinfo.GetBuildTargetCpuFamily()) << "'GetBuildTargetCpuFamily()' must match 'GetKernelCpuFamily()'";
+#endif // ! ARM
+}
+
+TEST_F(TestSystemInfo, GetUsedCompilerNameAndVer)
+{
+ EXPECT_STRNE("unknown compiler", g_sysinfo.GetUsedCompilerNameAndVer().c_str()) << "'GetUsedCompilerNameAndVer()' must not return 'unknown compiler'";
+}
+
+TEST_F(TestSystemInfo, GetDiskSpace)
+{
+ int iTotal, iTotalFree, iTotalUsed, iPercentFree, iPercentUsed;
+
+ iTotal = iTotalFree = iTotalUsed = iPercentFree = iPercentUsed = 0;
+ EXPECT_TRUE(g_sysinfo.GetDiskSpace("*", iTotal, iTotalFree, iTotalUsed, iPercentFree, iPercentUsed)) << "'GetDiskSpace()' return 'false' for disk '*'";
+ EXPECT_NE(0, iTotal) << "'GetDiskSpace()' return zero total space for disk '*'";
+ EXPECT_EQ(iTotal, iTotalFree + iTotalUsed) << "'GetDiskSpace()' return 'TotalFree + TotalUsed' not equal to 'Total' for disk '*'";
+ EXPECT_EQ(100, iPercentFree + iPercentUsed) << "'GetDiskSpace()' return 'PercentFree + PercentUsed' not equal to '100' for disk '*'";
+
+ iTotal = iTotalFree = iTotalUsed = iPercentFree = iPercentUsed = 0;
+ EXPECT_TRUE(g_sysinfo.GetDiskSpace("", iTotal, iTotalFree, iTotalUsed, iPercentFree, iPercentUsed)) << "'GetDiskSpace()' return 'false' for disk ''";
+ EXPECT_NE(0, iTotal) << "'GetDiskSpace()' return zero total space for disk ''";
+ EXPECT_EQ(iTotal, iTotalFree + iTotalUsed) << "'GetDiskSpace()' return 'TotalFree + TotalUsed' not equal to 'Total' for disk ''";
+ EXPECT_EQ(100, iPercentFree + iPercentUsed) << "'GetDiskSpace()' return 'PercentFree + PercentUsed' not equal to '100' for disk ''";
+
+#ifdef TARGET_WINDOWS
+ char sysDrive[300];
+ DWORD res = GetEnvironmentVariableA("SystemDrive", sysDrive, sizeof(sysDrive) / sizeof(char));
+ std::string sysDriveLtr;
+ if (res != 0 && res <= sizeof(sysDrive) / sizeof(char))
+ sysDriveLtr.assign(sysDrive, 1);
+ else
+ sysDriveLtr = "C"; // fallback
+
+ iTotal = iTotalFree = iTotalUsed = iPercentFree = iPercentUsed = 0;
+ EXPECT_TRUE(g_sysinfo.GetDiskSpace(sysDriveLtr, iTotal, iTotalFree, iTotalUsed, iPercentFree, iPercentUsed)) << "'GetDiskSpace()' return 'false' for disk '" << sysDriveLtr << ":'";
+ EXPECT_NE(0, iTotal) << "'GetDiskSpace()' return zero total space for disk '" << sysDriveLtr << ":'";
+ EXPECT_EQ(iTotal, iTotalFree + iTotalUsed) << "'GetDiskSpace()' return 'TotalFree + TotalUsed' not equal to 'Total' for disk '" << sysDriveLtr << ":'";
+ EXPECT_EQ(100, iPercentFree + iPercentUsed) << "'GetDiskSpace()' return 'PercentFree + PercentUsed' not equal to '100' for disk '" << sysDriveLtr << ":'";
+#elif defined(TARGET_POSIX)
+ iTotal = iTotalFree = iTotalUsed = iPercentFree = iPercentUsed = 0;
+ EXPECT_TRUE(g_sysinfo.GetDiskSpace("/", iTotal, iTotalFree, iTotalUsed, iPercentFree, iPercentUsed)) << "'GetDiskSpace()' return 'false' for directory '/'";
+ EXPECT_NE(0, iTotal) << "'GetDiskSpace()' return zero total space for directory '/'";
+ EXPECT_EQ(iTotal, iTotalFree + iTotalUsed) << "'GetDiskSpace()' return 'TotalFree + TotalUsed' not equal to 'Total' for directory '/'";
+ EXPECT_EQ(100, iPercentFree + iPercentUsed) << "'GetDiskSpace()' return 'PercentFree + PercentUsed' not equal to '100' for directory '/'";
+#endif
+}
diff --git a/src/utils/test/TestTimeSmoother.cpp b/src/utils/test/TestTimeSmoother.cpp
new file mode 100644
index 0000000000..c59e62f3e5
--- /dev/null
+++ b/src/utils/test/TestTimeSmoother.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/TimeSmoother.h"
+#include "threads/SystemClock.h"
+
+#include "gtest/gtest.h"
+
+TEST(TestTimeSmoother, General)
+{
+ CTimeSmoother a;
+
+ unsigned int currentTime, frameTime;
+ currentTime = XbmcThreads::SystemClockMillis();
+ std::cout << "currentTime: " << testing::PrintToString(currentTime) << "\n";
+ frameTime = a.GetNextFrameTime(currentTime);
+ std::cout << "frameTime: " << testing::PrintToString(frameTime) << "\n";
+
+ std::cout << "Calling AddTimeStamp()\n";
+ a.AddTimeStamp(currentTime);
+ std::cout << "currentTime: " << testing::PrintToString(currentTime) << "\n";
+ frameTime = a.GetNextFrameTime(currentTime);
+ std::cout << "frameTime: " << testing::PrintToString(frameTime) << "\n";
+}
diff --git a/src/utils/test/TestTimeUtils.cpp b/src/utils/test/TestTimeUtils.cpp
new file mode 100644
index 0000000000..f4ed08cc4f
--- /dev/null
+++ b/src/utils/test/TestTimeUtils.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/TimeUtils.h"
+#include "XBDateTime.h"
+
+#include "gtest/gtest.h"
+
+TEST(TestTimeUtils, CurrentHostCounter)
+{
+ std::cout << "CurrentHostCounter(): " <<
+ testing::PrintToString(CurrentHostCounter()) << std::endl;
+}
+
+TEST(TestTimeUtils, CurrentHostFrequency)
+{
+ std::cout << "CurrentHostFrequency(): " <<
+ testing::PrintToString(CurrentHostFrequency()) << std::endl;
+}
+
+TEST(TestTimeUtils, GetFrameTime)
+{
+ std::cout << "GetFrameTime(): " <<
+ testing::PrintToString(CTimeUtils::GetFrameTime()) << std::endl;
+
+ std::cout << "Calling UpdateFrameTime()" << std::endl;
+ CTimeUtils::UpdateFrameTime(true);
+ std::cout << "GetFrameTime(): " <<
+ testing::PrintToString(CTimeUtils::GetFrameTime()) << std::endl;
+}
+
+TEST(TestTimeUtils, GetLocalTime)
+{
+ CDateTime cdatetime, cdatetime2;
+ time_t timer;
+
+ time(&timer);
+
+ cdatetime = CTimeUtils::GetLocalTime(timer);
+ std::cout << "cdatetime.GetAsLocalizedDateTime(): " <<
+ cdatetime.GetAsLocalizedDateTime() << std::endl;
+
+ cdatetime2 = timer;
+ std::cout << "time: " <<
+ cdatetime2.GetAsLocalizedDateTime() << std::endl;
+}
diff --git a/src/utils/test/TestURIUtils.cpp b/src/utils/test/TestURIUtils.cpp
new file mode 100644
index 0000000000..cd6dbec802
--- /dev/null
+++ b/src/utils/test/TestURIUtils.cpp
@@ -0,0 +1,622 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/URIUtils.h"
+#include "settings/AdvancedSettings.h"
+#include "filesystem/MultiPathDirectory.h"
+#include "URL.h"
+
+#include "gtest/gtest.h"
+
+using namespace XFILE;
+
+class TestURIUtils : public testing::Test
+{
+protected:
+ TestURIUtils(){}
+ ~TestURIUtils()
+ {
+ g_advancedSettings.m_pathSubstitutions.clear();
+ }
+};
+
+TEST_F(TestURIUtils, IsInPath)
+{
+ EXPECT_TRUE(URIUtils::IsInPath("/path/to/movie.avi", "/path/to/"));
+ EXPECT_FALSE(URIUtils::IsInPath("/path/to/movie.avi", "/path/2/"));
+}
+
+TEST_F(TestURIUtils, GetDirectory)
+{
+ EXPECT_STREQ("/path/to/", URIUtils::GetDirectory("/path/to/movie.avi"));
+ EXPECT_STREQ("/path/to/", URIUtils::GetDirectory("/path/to/"));
+ EXPECT_STREQ("/path/to/|option=foo", URIUtils::GetDirectory("/path/to/movie.avi|option=foo"));
+ EXPECT_STREQ("/path/to/|option=foo", URIUtils::GetDirectory("/path/to/|option=foo"));
+ EXPECT_STREQ("", URIUtils::GetDirectory("movie.avi"));
+ EXPECT_STREQ("", URIUtils::GetDirectory("movie.avi|option=foo"));
+ EXPECT_STREQ("", URIUtils::GetDirectory(""));
+
+ // Make sure it works when assigning to the same str as the reference parameter
+ CStdString var = "/path/to/movie.avi|option=foo";
+ var = URIUtils::GetDirectory(var);
+ EXPECT_STREQ("/path/to/|option=foo", var);
+}
+
+TEST_F(TestURIUtils, GetExtension)
+{
+ EXPECT_STREQ(".avi",
+ URIUtils::GetExtension("/path/to/movie.avi").c_str());
+}
+
+TEST_F(TestURIUtils, HasExtension)
+{
+ EXPECT_TRUE (URIUtils::HasExtension("/path/to/movie.AvI"));
+ EXPECT_FALSE(URIUtils::HasExtension("/path/to/movie"));
+ EXPECT_FALSE(URIUtils::HasExtension("/path/.to/movie"));
+ EXPECT_FALSE(URIUtils::HasExtension(""));
+
+ EXPECT_TRUE (URIUtils::HasExtension("/path/to/movie.AvI", ".avi"));
+ EXPECT_FALSE(URIUtils::HasExtension("/path/to/movie.AvI", ".mkv"));
+ EXPECT_FALSE(URIUtils::HasExtension("/path/.avi/movie", ".avi"));
+ EXPECT_FALSE(URIUtils::HasExtension("", ".avi"));
+
+ EXPECT_TRUE (URIUtils::HasExtension("/path/movie.AvI", ".avi|.mkv|.mp4"));
+ EXPECT_TRUE (URIUtils::HasExtension("/path/movie.AvI", ".mkv|.avi|.mp4"));
+ EXPECT_FALSE(URIUtils::HasExtension("/path/movie.AvI", ".mpg|.mkv|.mp4"));
+ EXPECT_FALSE(URIUtils::HasExtension("/path.mkv/movie.AvI", ".mpg|.mkv|.mp4"));
+ EXPECT_FALSE(URIUtils::HasExtension("", ".avi|.mkv|.mp4"));
+}
+
+TEST_F(TestURIUtils, GetFileName)
+{
+ EXPECT_STREQ("movie.avi",
+ URIUtils::GetFileName("/path/to/movie.avi").c_str());
+}
+
+TEST_F(TestURIUtils, RemoveExtension)
+{
+ CStdString ref, var;
+
+ /* NOTE: CSettings need to be set to find other extensions. */
+ ref = "/path/to/file";
+ var = "/path/to/file.xml";
+ URIUtils::RemoveExtension(var);
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+}
+
+TEST_F(TestURIUtils, ReplaceExtension)
+{
+ CStdString ref, var;
+
+ ref = "/path/to/file.xsd";
+ var = URIUtils::ReplaceExtension("/path/to/file.xml", ".xsd");
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+}
+
+TEST_F(TestURIUtils, Split)
+{
+ CStdString refpath, reffile, varpath, varfile;
+
+ refpath = "/path/to/";
+ reffile = "movie.avi";
+ URIUtils::Split("/path/to/movie.avi", varpath, varfile);
+ EXPECT_STREQ(refpath.c_str(), varpath.c_str());
+ EXPECT_STREQ(reffile.c_str(), varfile.c_str());
+}
+
+TEST_F(TestURIUtils, SplitPath)
+{
+ std::vector<std::string> strarray;
+
+ strarray = URIUtils::SplitPath("http://www.test.com/path/to/movie.avi");
+
+ EXPECT_STREQ("http://www.test.com/", strarray.at(0).c_str());
+ EXPECT_STREQ("path", strarray.at(1).c_str());
+ EXPECT_STREQ("to", strarray.at(2).c_str());
+ EXPECT_STREQ("movie.avi", strarray.at(3).c_str());
+}
+
+TEST_F(TestURIUtils, SplitPathLocal)
+{
+#ifndef TARGET_LINUX
+ const char *path = "C:\\path\\to\\movie.avi";
+#else
+ const char *path = "/path/to/movie.avi";
+#endif
+ std::vector<std::string> strarray;
+
+ strarray = URIUtils::SplitPath(path);
+
+#ifndef TARGET_LINUX
+ EXPECT_STREQ("C:", strarray.at(0).c_str());
+#else
+ EXPECT_STREQ("", strarray.at(0).c_str());
+#endif
+ EXPECT_STREQ("path", strarray.at(1).c_str());
+ EXPECT_STREQ("to", strarray.at(2).c_str());
+ EXPECT_STREQ("movie.avi", strarray.at(3).c_str());
+}
+
+TEST_F(TestURIUtils, GetCommonPath)
+{
+ CStdString ref, var;
+
+ ref = "/path/";
+ var = "/path/2/movie.avi";
+ URIUtils::GetCommonPath(var, "/path/to/movie.avi");
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+}
+
+TEST_F(TestURIUtils, GetParentPath)
+{
+ CStdString ref, var;
+
+ ref = "/path/to/";
+ var = URIUtils::GetParentPath("/path/to/movie.avi");
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+
+ var.clear();
+ EXPECT_TRUE(URIUtils::GetParentPath("/path/to/movie.avi", var));
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+}
+
+TEST_F(TestURIUtils, SubstitutePath)
+{
+ CStdString from, to, ref, var;
+
+ from = "C:\\My Videos";
+ to = "https://myserver/some%20other%20path";
+ g_advancedSettings.m_pathSubstitutions.push_back(std::make_pair(from, to));
+
+ from = "/this/path1";
+ to = "/some/other/path2";
+ g_advancedSettings.m_pathSubstitutions.push_back(std::make_pair(from, to));
+
+ from = "davs://otherserver/my%20music%20path";
+ to = "D:\\Local Music\\MP3 Collection";
+ g_advancedSettings.m_pathSubstitutions.push_back(std::make_pair(from, to));
+
+ ref = "https://myserver/some%20other%20path/sub%20dir/movie%20name.avi";
+ var = URIUtils::SubstitutePath("C:\\My Videos\\sub dir\\movie name.avi");
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+
+ ref = "C:\\My Videos\\sub dir\\movie name.avi";
+ var = URIUtils::SubstitutePath("https://myserver/some%20other%20path/sub%20dir/movie%20name.avi", true);
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+
+ ref = "D:\\Local Music\\MP3 Collection\\Phil Collins\\Some CD\\01 - Two Hearts.mp3";
+ var = URIUtils::SubstitutePath("davs://otherserver/my%20music%20path/Phil%20Collins/Some%20CD/01%20-%20Two%20Hearts.mp3");
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+
+ ref = "davs://otherserver/my%20music%20path/Phil%20Collins/Some%20CD/01%20-%20Two%20Hearts.mp3";
+ var = URIUtils::SubstitutePath("D:\\Local Music\\MP3 Collection\\Phil Collins\\Some CD\\01 - Two Hearts.mp3", true);
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+
+ ref = "/some/other/path2/to/movie.avi";
+ var = URIUtils::SubstitutePath("/this/path1/to/movie.avi");
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+
+ ref = "/this/path1/to/movie.avi";
+ var = URIUtils::SubstitutePath("/some/other/path2/to/movie.avi", true);
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+
+ ref = "/no/translation path/";
+ var = URIUtils::SubstitutePath(ref);
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+
+ ref = "/no/translation path/";
+ var = URIUtils::SubstitutePath(ref, true);
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+
+ ref = "c:\\no\\translation path";
+ var = URIUtils::SubstitutePath(ref);
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+
+ ref = "c:\\no\\translation path";
+ var = URIUtils::SubstitutePath(ref, true);
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+}
+
+TEST_F(TestURIUtils, IsAddonsPath)
+{
+ EXPECT_TRUE(URIUtils::IsAddonsPath("addons://path/to/addons"));
+}
+
+TEST_F(TestURIUtils, IsSourcesPath)
+{
+ EXPECT_TRUE(URIUtils::IsSourcesPath("sources://path/to/sources"));
+}
+
+TEST_F(TestURIUtils, IsCDDA)
+{
+ EXPECT_TRUE(URIUtils::IsCDDA("cdda://path/to/cdda"));
+}
+
+TEST_F(TestURIUtils, IsDAAP)
+{
+ EXPECT_TRUE(URIUtils::IsDAAP("daap://path/to/daap"));
+}
+
+TEST_F(TestURIUtils, IsDOSPath)
+{
+ EXPECT_TRUE(URIUtils::IsDOSPath("C://path/to/dosfile"));
+}
+
+TEST_F(TestURIUtils, IsDVD)
+{
+ EXPECT_TRUE(URIUtils::IsDVD("dvd://path/in/video_ts.ifo"));
+#if defined(TARGET_WINDOWS)
+ EXPECT_TRUE(URIUtils::IsDVD("dvd://path/in/file"));
+#else
+ EXPECT_TRUE(URIUtils::IsDVD("iso9660://path/in/video_ts.ifo"));
+ EXPECT_TRUE(URIUtils::IsDVD("udf://path/in/video_ts.ifo"));
+ EXPECT_TRUE(URIUtils::IsDVD("dvd://1"));
+#endif
+}
+
+TEST_F(TestURIUtils, IsFTP)
+{
+ EXPECT_TRUE(URIUtils::IsFTP("ftp://path/in/ftp"));
+}
+
+TEST_F(TestURIUtils, IsHD)
+{
+ EXPECT_TRUE(URIUtils::IsHD("/path/to/file"));
+ EXPECT_TRUE(URIUtils::IsHD("file:///path/to/file"));
+ EXPECT_TRUE(URIUtils::IsHD("special://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsHD("stack://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsHD("zip://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsHD("rar://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsHDHomeRun)
+{
+ EXPECT_TRUE(URIUtils::IsHDHomeRun("hdhomerun://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsSlingbox)
+{
+ EXPECT_TRUE(URIUtils::IsSlingbox("sling://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsHTSP)
+{
+ EXPECT_TRUE(URIUtils::IsHTSP("htsp://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsInArchive)
+{
+ EXPECT_TRUE(URIUtils::IsInArchive("zip://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsInArchive("rar://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsInRAR)
+{
+ EXPECT_TRUE(URIUtils::IsInRAR("rar://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsInternetStream)
+{
+ CURL url1("http://path/to/file");
+ CURL url2("https://path/to/file");
+ EXPECT_TRUE(URIUtils::IsInternetStream(url1));
+ EXPECT_TRUE(URIUtils::IsInternetStream(url2));
+}
+
+TEST_F(TestURIUtils, IsInZIP)
+{
+ EXPECT_TRUE(URIUtils::IsInZIP("zip://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsISO9660)
+{
+ EXPECT_TRUE(URIUtils::IsISO9660("iso9660://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsLiveTV)
+{
+ EXPECT_TRUE(URIUtils::IsLiveTV("tuxbox://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsLiveTV("vtp://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsLiveTV("hdhomerun://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsLiveTV("sling://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsLiveTV("htsp://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsLiveTV("sap://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsLiveTV("myth://path/channels/"));
+}
+
+TEST_F(TestURIUtils, IsMultiPath)
+{
+ EXPECT_TRUE(URIUtils::IsMultiPath("multipath://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsMusicDb)
+{
+ EXPECT_TRUE(URIUtils::IsMusicDb("musicdb://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsMythTV)
+{
+ EXPECT_TRUE(URIUtils::IsMythTV("myth://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsNfs)
+{
+ EXPECT_TRUE(URIUtils::IsNfs("nfs://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsNfs("stack://nfs://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsAfp)
+{
+ EXPECT_TRUE(URIUtils::IsAfp("afp://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsAfp("stack://afp://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsOnDVD)
+{
+ EXPECT_TRUE(URIUtils::IsOnDVD("dvd://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsOnDVD("udf://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsOnDVD("iso9660://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsOnDVD("cdda://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsOnLAN)
+{
+ std::vector<std::string> multiVec;
+ multiVec.push_back("daap://path/to/file");
+ EXPECT_TRUE(URIUtils::IsOnLAN(CMultiPathDirectory::ConstructMultiPath(multiVec)));
+ EXPECT_TRUE(URIUtils::IsOnLAN("stack://daap://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsOnLAN("daap://path/to/file"));
+ EXPECT_FALSE(URIUtils::IsOnLAN("plugin://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsOnLAN("tuxbox://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsOnLAN("upnp://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsPlugin)
+{
+ EXPECT_TRUE(URIUtils::IsPlugin("plugin://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsScript)
+{
+ EXPECT_TRUE(URIUtils::IsScript("script://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsRAR)
+{
+ EXPECT_TRUE(URIUtils::IsRAR("/path/to/rarfile.rar"));
+ EXPECT_TRUE(URIUtils::IsRAR("/path/to/rarfile.cbr"));
+ EXPECT_FALSE(URIUtils::IsRAR("/path/to/file"));
+ EXPECT_FALSE(URIUtils::IsRAR("rar://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsRemote)
+{
+ EXPECT_TRUE(URIUtils::IsRemote("http://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsRemote("https://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsSmb)
+{
+ EXPECT_TRUE(URIUtils::IsSmb("smb://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsSmb("stack://smb://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsSpecial)
+{
+ EXPECT_TRUE(URIUtils::IsSpecial("special://path/to/file"));
+ EXPECT_TRUE(URIUtils::IsSpecial("stack://special://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsStack)
+{
+ EXPECT_TRUE(URIUtils::IsStack("stack://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsTuxBox)
+{
+ EXPECT_TRUE(URIUtils::IsTuxBox("tuxbox://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsUPnP)
+{
+ EXPECT_TRUE(URIUtils::IsUPnP("upnp://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsURL)
+{
+ EXPECT_TRUE(URIUtils::IsURL("someprotocol://path/to/file"));
+ EXPECT_FALSE(URIUtils::IsURL("/path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsVideoDb)
+{
+ EXPECT_TRUE(URIUtils::IsVideoDb("videodb://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsVTP)
+{
+ EXPECT_TRUE(URIUtils::IsVTP("vtp://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsZIP)
+{
+ EXPECT_TRUE(URIUtils::IsZIP("/path/to/zipfile.zip"));
+ EXPECT_TRUE(URIUtils::IsZIP("/path/to/zipfile.cbz"));
+ EXPECT_FALSE(URIUtils::IsZIP("/path/to/file"));
+ EXPECT_FALSE(URIUtils::IsZIP("zip://path/to/file"));
+}
+
+TEST_F(TestURIUtils, IsBluray)
+{
+ EXPECT_TRUE(URIUtils::IsBluray("bluray://path/to/file"));
+}
+
+TEST_F(TestURIUtils, AddSlashAtEnd)
+{
+ CStdString ref, var;
+
+ ref = "bluray://path/to/file/";
+ var = "bluray://path/to/file/";
+ URIUtils::AddSlashAtEnd(var);
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+}
+
+TEST_F(TestURIUtils, HasSlashAtEnd)
+{
+ EXPECT_TRUE(URIUtils::HasSlashAtEnd("bluray://path/to/file/"));
+ EXPECT_FALSE(URIUtils::HasSlashAtEnd("bluray://path/to/file"));
+}
+
+TEST_F(TestURIUtils, RemoveSlashAtEnd)
+{
+ CStdString ref, var;
+
+ ref = "bluray://path/to/file";
+ var = "bluray://path/to/file/";
+ URIUtils::RemoveSlashAtEnd(var);
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+}
+
+TEST_F(TestURIUtils, CreateArchivePath)
+{
+ CStdString ref, var;
+
+ ref = "zip://%2fpath%2fto%2f/file";
+ var = URIUtils::CreateArchivePath("zip", CURL("/path/to/"), "file").Get();
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+}
+
+TEST_F(TestURIUtils, AddFileToFolder)
+{
+ CStdString ref = "/path/to/file";
+ CStdString var = URIUtils::AddFileToFolder("/path/to", "file");
+ EXPECT_STREQ(ref.c_str(), var.c_str());
+}
+
+TEST_F(TestURIUtils, HasParentInHostname)
+{
+ EXPECT_TRUE(URIUtils::HasParentInHostname(CURL("zip://")));
+ EXPECT_TRUE(URIUtils::HasParentInHostname(CURL("rar://")));
+ EXPECT_TRUE(URIUtils::HasParentInHostname(CURL("bluray://")));
+}
+
+TEST_F(TestURIUtils, HasEncodedHostname)
+{
+ EXPECT_TRUE(URIUtils::HasEncodedHostname(CURL("zip://")));
+ EXPECT_TRUE(URIUtils::HasEncodedHostname(CURL("rar://")));
+ EXPECT_TRUE(URIUtils::HasEncodedHostname(CURL("bluray://")));
+ EXPECT_TRUE(URIUtils::HasEncodedHostname(CURL("musicsearch://")));
+}
+
+TEST_F(TestURIUtils, HasEncodedFilename)
+{
+ EXPECT_TRUE(URIUtils::HasEncodedFilename(CURL("shout://")));
+ EXPECT_TRUE(URIUtils::HasEncodedFilename(CURL("daap://")));
+ EXPECT_TRUE(URIUtils::HasEncodedFilename(CURL("dav://")));
+ EXPECT_TRUE(URIUtils::HasEncodedFilename(CURL("tuxbox://")));
+ EXPECT_TRUE(URIUtils::HasEncodedFilename(CURL("rss://")));
+ EXPECT_TRUE(URIUtils::HasEncodedFilename(CURL("davs://")));
+}
+
+TEST_F(TestURIUtils, GetRealPath)
+{
+ std::string ref;
+
+ ref = "/path/to/file/";
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath(ref).c_str());
+
+ ref = "path/to/file";
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath("../path/to/file").c_str());
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath("./path/to/file").c_str());
+
+ ref = "/path/to/file";
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath(ref).c_str());
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath("/path/to/./file").c_str());
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath("/./path/to/./file").c_str());
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath("/path/to/some/../file").c_str());
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath("/../path/to/some/../file").c_str());
+
+ ref = "/path/to";
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath("/path/to/some/../file/..").c_str());
+
+#ifdef TARGET_WINDOWS
+ ref = "\\\\path\\to\\file\\";
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath(ref).c_str());
+
+ ref = "path\\to\\file";
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath("..\\path\\to\\file").c_str());
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath(".\\path\\to\\file").c_str());
+
+ ref = "\\\\path\\to\\file";
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath(ref).c_str());
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath("\\\\path\\to\\.\\file").c_str());
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath("\\\\.\\path/to\\.\\file").c_str());
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath("\\\\path\\to\\some\\..\\file").c_str());
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath("\\\\..\\path\\to\\some\\..\\file").c_str());
+
+ ref = "\\\\path\\to";
+ EXPECT_STREQ(ref.c_str(), URIUtils::GetRealPath("\\\\path\\to\\some\\..\\file\\..").c_str());
+#endif
+
+ // test rar/zip paths
+ ref = "rar://%2fpath%2fto%2frar/subpath/to/file";
+ EXPECT_STRCASEEQ(ref.c_str(), URIUtils::GetRealPath(ref).c_str());
+
+ // test rar/zip paths
+ ref = "rar://%2fpath%2fto%2frar/subpath/to/file";
+ EXPECT_STRCASEEQ(ref.c_str(), URIUtils::GetRealPath("rar://%2fpath%2fto%2frar/../subpath/to/file").c_str());
+ EXPECT_STRCASEEQ(ref.c_str(), URIUtils::GetRealPath("rar://%2fpath%2fto%2frar/./subpath/to/file").c_str());
+ EXPECT_STRCASEEQ(ref.c_str(), URIUtils::GetRealPath("rar://%2fpath%2fto%2frar/subpath/to/./file").c_str());
+ EXPECT_STRCASEEQ(ref.c_str(), URIUtils::GetRealPath("rar://%2fpath%2fto%2frar/subpath/to/some/../file").c_str());
+
+ EXPECT_STRCASEEQ(ref.c_str(), URIUtils::GetRealPath("rar://%2fpath%2fto%2f.%2frar/subpath/to/file").c_str());
+ EXPECT_STRCASEEQ(ref.c_str(), URIUtils::GetRealPath("rar://%2fpath%2fto%2fsome%2f..%2frar/subpath/to/file").c_str());
+
+ // test rar/zip path in rar/zip path
+ ref ="zip://rar%3A%2F%2F%252Fpath%252Fto%252Frar%2Fpath%2Fto%2Fzip/subpath/to/file";
+ EXPECT_STRCASEEQ(ref.c_str(), URIUtils::GetRealPath("zip://rar%3A%2F%2F%252Fpath%252Fto%252Fsome%252F..%252Frar%2Fpath%2Fto%2Fsome%2F..%2Fzip/subpath/to/some/../file").c_str());
+}
+
+TEST_F(TestURIUtils, UpdateUrlEncoding)
+{
+ std::string oldUrl = "stack://rar://%2fpath%2fto%2farchive%2fsome%2darchive%2dfile%2eCD1%2erar/video.avi , rar://%2fpath%2fto%2farchive%2fsome%2darchive%2dfile%2eCD2%2erar/video.avi";
+ std::string newUrl = "stack://rar://%2fpath%2fto%2farchive%2fsome-archive-file.CD1.rar/video.avi , rar://%2fpath%2fto%2farchive%2fsome-archive-file.CD2.rar/video.avi";
+
+ EXPECT_TRUE(URIUtils::UpdateUrlEncoding(oldUrl));
+ EXPECT_STRCASEEQ(newUrl.c_str(), oldUrl.c_str());
+
+ oldUrl = "rar://%2fpath%2fto%2farchive%2fsome%2darchive%2efile%2erar/video.avi";
+ newUrl = "rar://%2fpath%2fto%2farchive%2fsome-archive.file.rar/video.avi";
+
+ EXPECT_TRUE(URIUtils::UpdateUrlEncoding(oldUrl));
+ EXPECT_STRCASEEQ(newUrl.c_str(), oldUrl.c_str());
+
+ oldUrl = "/path/to/some/long%2dnamed%2efile";
+ newUrl = "/path/to/some/long%2dnamed%2efile";
+
+ EXPECT_FALSE(URIUtils::UpdateUrlEncoding(oldUrl));
+ EXPECT_STRCASEEQ(newUrl.c_str(), oldUrl.c_str());
+
+ oldUrl = "/path/to/some/long-named.file";
+ newUrl = "/path/to/some/long-named.file";
+
+ EXPECT_FALSE(URIUtils::UpdateUrlEncoding(oldUrl));
+ EXPECT_STRCASEEQ(newUrl.c_str(), oldUrl.c_str());
+}
diff --git a/src/utils/test/TestUrlOptions.cpp b/src/utils/test/TestUrlOptions.cpp
new file mode 100644
index 0000000000..ade5855b95
--- /dev/null
+++ b/src/utils/test/TestUrlOptions.cpp
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/UrlOptions.h"
+
+#include "gtest/gtest.h"
+
+TEST(TestUrlOptions, Clear)
+{
+ const char *key = "foo";
+
+ CUrlOptions urlOptions;
+ urlOptions.AddOption(key, "bar");
+ EXPECT_TRUE(urlOptions.HasOption(key));
+
+ urlOptions.Clear();
+ EXPECT_FALSE(urlOptions.HasOption(key));
+}
+
+TEST(TestUrlOptions, AddOption)
+{
+ const char *keyChar = "char";
+ const char *keyString = "string";
+ const char *keyEmpty = "empty";
+ const char *keyInt = "int";
+ const char *keyFloat = "float";
+ const char *keyDouble = "double";
+ const char *keyBool = "bool";
+
+ const char *valueChar = "valueChar";
+ const std::string valueString = "valueString";
+ const char *valueEmpty = "";
+ int valueInt = 1;
+ float valueFloat = 1.0f;
+ double valueDouble = 1.0;
+ bool valueBool = true;
+
+ CVariant variantValue;
+
+ CUrlOptions urlOptions;
+ urlOptions.AddOption(keyChar, valueChar);
+ {
+ CVariant variantValue;
+ EXPECT_TRUE(urlOptions.GetOption(keyChar, variantValue));
+ EXPECT_TRUE(variantValue.isString());
+ EXPECT_STREQ(valueChar, variantValue.asString().c_str());
+ }
+
+ urlOptions.AddOption(keyString, valueString);
+ {
+ CVariant variantValue;
+ EXPECT_TRUE(urlOptions.GetOption(keyString, variantValue));
+ EXPECT_TRUE(variantValue.isString());
+ EXPECT_STREQ(valueString.c_str(), variantValue.asString().c_str());
+ }
+
+ urlOptions.AddOption(keyEmpty, valueEmpty);
+ {
+ CVariant variantValue;
+ EXPECT_TRUE(urlOptions.GetOption(keyEmpty, variantValue));
+ EXPECT_TRUE(variantValue.isString());
+ EXPECT_STREQ(valueEmpty, variantValue.asString().c_str());
+ }
+
+ urlOptions.AddOption(keyInt, valueInt);
+ {
+ CVariant variantValue;
+ EXPECT_TRUE(urlOptions.GetOption(keyInt, variantValue));
+ EXPECT_TRUE(variantValue.isInteger());
+ EXPECT_EQ(valueInt, (int)variantValue.asInteger());
+ }
+
+ urlOptions.AddOption(keyFloat, valueFloat);
+ {
+ CVariant variantValue;
+ EXPECT_TRUE(urlOptions.GetOption(keyFloat, variantValue));
+ EXPECT_TRUE(variantValue.isDouble());
+ EXPECT_EQ(valueFloat, variantValue.asFloat());
+ }
+
+ urlOptions.AddOption(keyDouble, valueDouble);
+ {
+ CVariant variantValue;
+ EXPECT_TRUE(urlOptions.GetOption(keyDouble, variantValue));
+ EXPECT_TRUE(variantValue.isDouble());
+ EXPECT_EQ(valueDouble, variantValue.asDouble());
+ }
+
+ urlOptions.AddOption(keyBool, valueBool);
+ {
+ CVariant variantValue;
+ EXPECT_TRUE(urlOptions.GetOption(keyBool, variantValue));
+ EXPECT_TRUE(variantValue.isBoolean());
+ EXPECT_EQ(valueBool, variantValue.asBoolean());
+ }
+}
+
+TEST(TestUrlOptions, AddOptions)
+{
+ std::string ref = "foo=bar&key=value";
+
+ CUrlOptions urlOptions(ref);
+ {
+ CVariant value;
+ EXPECT_TRUE(urlOptions.GetOption("foo", value));
+ EXPECT_TRUE(value.isString());
+ EXPECT_STREQ("bar", value.asString().c_str());
+ }
+ {
+ CVariant value;
+ EXPECT_TRUE(urlOptions.GetOption("key", value));
+ EXPECT_TRUE(value.isString());
+ EXPECT_STREQ("value", value.asString().c_str());
+ }
+
+ ref = "foo=bar&key";
+ urlOptions.Clear();
+ urlOptions.AddOptions(ref);
+ {
+ CVariant value;
+ EXPECT_TRUE(urlOptions.GetOption("foo", value));
+ EXPECT_TRUE(value.isString());
+ EXPECT_STREQ("bar", value.asString().c_str());
+ }
+ {
+ CVariant value;
+ EXPECT_TRUE(urlOptions.GetOption("key", value));
+ EXPECT_TRUE(value.isString());
+ EXPECT_TRUE(value.empty());
+ }
+}
+
+TEST(TestUrlOptions, RemoveOption)
+{
+ const char *key = "foo";
+
+ CUrlOptions urlOptions;
+ urlOptions.AddOption(key, "bar");
+ EXPECT_TRUE(urlOptions.HasOption(key));
+
+ urlOptions.RemoveOption(key);
+ EXPECT_FALSE(urlOptions.HasOption(key));
+}
+
+TEST(TestUrlOptions, HasOption)
+{
+ const char *key = "foo";
+
+ CUrlOptions urlOptions;
+ urlOptions.AddOption(key, "bar");
+ EXPECT_TRUE(urlOptions.HasOption(key));
+ EXPECT_FALSE(urlOptions.HasOption("bar"));
+}
+
+TEST(TestUrlOptions, GetOptions)
+{
+ const char *key1 = "foo";
+ const char *key2 = "key";
+ const char *value1 = "bar";
+ const char *value2 = "value";
+
+ CUrlOptions urlOptions;
+ urlOptions.AddOption(key1, value1);
+ urlOptions.AddOption(key2, value2);
+ const CUrlOptions::UrlOptions &options = urlOptions.GetOptions();
+ EXPECT_FALSE(options.empty());
+ EXPECT_EQ(2, options.size());
+
+ CUrlOptions::UrlOptions::const_iterator it1 = options.find(key1);
+ EXPECT_TRUE(it1 != options.end());
+ CUrlOptions::UrlOptions::const_iterator it2 = options.find(key2);
+ EXPECT_TRUE(it2 != options.end());
+ EXPECT_FALSE(options.find("wrong") != options.end());
+ EXPECT_TRUE(it1->second.isString());
+ EXPECT_TRUE(it2->second.isString());
+ EXPECT_STREQ(value1, it1->second.asString().c_str());
+ EXPECT_STREQ(value2, it2->second.asString().c_str());
+}
+
+TEST(TestUrlOptions, GetOptionsString)
+{
+ const char *ref = "foo=bar&key";
+
+ CUrlOptions urlOptions(ref);
+ std::string value = urlOptions.GetOptionsString();
+ EXPECT_STREQ(ref, value.c_str());
+}
diff --git a/src/utils/test/TestVariant.cpp b/src/utils/test/TestVariant.cpp
new file mode 100644
index 0000000000..45ee687758
--- /dev/null
+++ b/src/utils/test/TestVariant.cpp
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/Variant.h"
+
+#include "gtest/gtest.h"
+
+TEST(TestVariant, VariantTypeInteger)
+{
+ CVariant a((int)0), b((int64_t)1);
+
+ EXPECT_TRUE(a.isInteger());
+ EXPECT_EQ(CVariant::VariantTypeInteger, a.type());
+ EXPECT_TRUE(b.isInteger());
+ EXPECT_EQ(CVariant::VariantTypeInteger, b.type());
+
+ EXPECT_EQ((int64_t)1, b.asInteger());
+}
+
+TEST(TestVariant, VariantTypeUnsignedInteger)
+{
+ CVariant a((unsigned int)0), b((uint64_t)1);
+
+ EXPECT_TRUE(a.isUnsignedInteger());
+ EXPECT_EQ(CVariant::VariantTypeUnsignedInteger, a.type());
+ EXPECT_TRUE(b.isUnsignedInteger());
+ EXPECT_EQ(CVariant::VariantTypeUnsignedInteger, b.type());
+
+ EXPECT_EQ((uint64_t)1, b.asUnsignedInteger());
+}
+
+TEST(TestVariant, VariantTypeBoolean)
+{
+ CVariant a(true);
+
+ EXPECT_TRUE(a.isBoolean());
+ EXPECT_EQ(CVariant::VariantTypeBoolean, a.type());
+
+ EXPECT_TRUE(a.asBoolean());
+}
+
+TEST(TestVariant, VariantTypeString)
+{
+ CVariant a("VariantTypeString");
+ CVariant b("VariantTypeString2", sizeof("VariantTypeString2") - 1);
+ std::string str("VariantTypeString3");
+ CVariant c(str);
+
+ EXPECT_TRUE(a.isString());
+ EXPECT_EQ(CVariant::VariantTypeString, a.type());
+ EXPECT_TRUE(b.isString());
+ EXPECT_EQ(CVariant::VariantTypeString, b.type());
+ EXPECT_TRUE(c.isString());
+ EXPECT_EQ(CVariant::VariantTypeString, c.type());
+
+ EXPECT_STREQ("VariantTypeString", a.asString().c_str());
+ EXPECT_STREQ("VariantTypeString2", b.asString().c_str());
+ EXPECT_STREQ("VariantTypeString3", c.asString().c_str());
+}
+
+TEST(TestVariant, VariantTypeWideString)
+{
+ CVariant a(L"VariantTypeWideString");
+ CVariant b(L"VariantTypeWideString2", sizeof(L"VariantTypeWideString2") - 1);
+ std::wstring str(L"VariantTypeWideString3");
+ CVariant c(str);
+
+ EXPECT_TRUE(a.isWideString());
+ EXPECT_EQ(CVariant::VariantTypeWideString, a.type());
+ EXPECT_TRUE(b.isWideString());
+ EXPECT_EQ(CVariant::VariantTypeWideString, b.type());
+ EXPECT_TRUE(c.isWideString());
+ EXPECT_EQ(CVariant::VariantTypeWideString, c.type());
+
+ EXPECT_STREQ(L"VariantTypeWideString", a.asWideString().c_str());
+ EXPECT_STREQ(L"VariantTypeWideString2", b.asWideString().c_str());
+ EXPECT_STREQ(L"VariantTypeWideString3", c.asWideString().c_str());
+}
+
+TEST(TestVariant, VariantTypeDouble)
+{
+ CVariant a((float)0.0f), b((double)0.1f);
+
+ EXPECT_TRUE(a.isDouble());
+ EXPECT_EQ(CVariant::VariantTypeDouble, a.type());
+ EXPECT_TRUE(b.isDouble());
+ EXPECT_EQ(CVariant::VariantTypeDouble, b.type());
+
+ EXPECT_EQ((float)0.0f, a.asDouble());
+ EXPECT_EQ((double)0.1f, b.asDouble());
+}
+
+TEST(TestVariant, VariantTypeArray)
+{
+ std::vector<std::string> strarray;
+ strarray.push_back("string1");
+ strarray.push_back("string2");
+ strarray.push_back("string3");
+ strarray.push_back("string4");
+ CVariant a(strarray);
+
+ EXPECT_TRUE(a.isArray());
+ EXPECT_EQ(CVariant::VariantTypeArray, a.type());
+}
+
+TEST(TestVariant, VariantTypeObject)
+{
+ CVariant a;
+ a["key"] = "value";
+
+ EXPECT_TRUE(a.isObject());
+ EXPECT_EQ(CVariant::VariantTypeObject, a.type());
+}
+
+TEST(TestVariant, VariantTypeNull)
+{
+ CVariant a;
+
+ EXPECT_TRUE(a.isNull());
+ EXPECT_EQ(CVariant::VariantTypeNull, a.type());
+}
+
+TEST(TestVariant, VariantFromMap)
+{
+ std::map<std::string, std::string> strMap;
+ strMap["key"] = "value";
+ CVariant a = strMap;
+
+ EXPECT_TRUE(a.isObject());
+ EXPECT_TRUE(a.size() == 1);
+ EXPECT_EQ(CVariant::VariantTypeObject, a.type());
+ EXPECT_TRUE(a.isMember("key"));
+ EXPECT_TRUE(a["key"].isString());
+ EXPECT_STREQ(a["key"].asString().c_str(), "value");
+
+ std::map<std::string, CVariant> variantMap;
+ variantMap["key"] = CVariant("value");
+ CVariant b = variantMap;
+
+ EXPECT_TRUE(b.isObject());
+ EXPECT_TRUE(b.size() == 1);
+ EXPECT_EQ(CVariant::VariantTypeObject, b.type());
+ EXPECT_TRUE(b.isMember("key"));
+ EXPECT_TRUE(b["key"].isString());
+ EXPECT_STREQ(b["key"].asString().c_str(), "value");
+}
+
+TEST(TestVariant, operatorTest)
+{
+ std::vector<std::string> strarray;
+ strarray.push_back("string1");
+ CVariant a, b, c(strarray), d;
+ a["key"] = "value";
+ b = a;
+ c[0] = "value2";
+ d = c;
+
+ EXPECT_TRUE(a.isObject());
+ EXPECT_EQ(CVariant::VariantTypeObject, a.type());
+ EXPECT_TRUE(b.isObject());
+ EXPECT_EQ(CVariant::VariantTypeObject, b.type());
+ EXPECT_TRUE(c.isArray());
+ EXPECT_EQ(CVariant::VariantTypeArray, c.type());
+ EXPECT_TRUE(d.isArray());
+ EXPECT_EQ(CVariant::VariantTypeArray, d.type());
+
+ EXPECT_TRUE(a == b);
+ EXPECT_TRUE(c == d);
+ EXPECT_FALSE(a == d);
+
+ EXPECT_STREQ("value", a["key"].asString().c_str());
+ EXPECT_STREQ("value2", c[0].asString().c_str());
+}
+
+TEST(TestVariant, push_back)
+{
+ CVariant a, b("variant1"), c("variant2"), d("variant3");
+ a.push_back(b);
+ a.push_back(c);
+ a.push_back(d);
+
+ EXPECT_TRUE(a.isArray());
+ EXPECT_EQ(CVariant::VariantTypeArray, a.type());
+ EXPECT_STREQ("variant1", a[0].asString().c_str());
+ EXPECT_STREQ("variant2", a[1].asString().c_str());
+ EXPECT_STREQ("variant3", a[2].asString().c_str());
+}
+
+TEST(TestVariant, append)
+{
+ CVariant a, b("variant1"), c("variant2"), d("variant3");
+ a.append(b);
+ a.append(c);
+ a.append(d);
+
+ EXPECT_TRUE(a.isArray());
+ EXPECT_EQ(CVariant::VariantTypeArray, a.type());
+ EXPECT_STREQ("variant1", a[0].asString().c_str());
+ EXPECT_STREQ("variant2", a[1].asString().c_str());
+ EXPECT_STREQ("variant3", a[2].asString().c_str());
+}
+
+TEST(TestVariant, c_str)
+{
+ CVariant a("variant");
+
+ EXPECT_STREQ("variant", a.c_str());
+}
+
+TEST(TestVariant, swap)
+{
+ CVariant a((int)0), b("variant");
+
+ EXPECT_TRUE(a.isInteger());
+ EXPECT_TRUE(b.isString());
+
+ a.swap(b);
+ EXPECT_TRUE(b.isInteger());
+ EXPECT_TRUE(a.isString());
+}
+
+TEST(TestVariant, interator_array)
+{
+ std::vector<std::string> strarray;
+ strarray.push_back("string");
+ strarray.push_back("string");
+ strarray.push_back("string");
+ strarray.push_back("string");
+ CVariant a(strarray);
+
+ EXPECT_TRUE(a.isArray());
+ EXPECT_EQ(CVariant::VariantTypeArray, a.type());
+
+ CVariant::iterator_array it;
+ for (it = a.begin_array(); it < a.end_array(); it++)
+ {
+ EXPECT_STREQ("string", it->c_str());
+ }
+
+ CVariant::const_iterator_array const_it;
+ for (const_it = a.begin_array(); const_it < a.end_array(); const_it++)
+ {
+ EXPECT_STREQ("string", const_it->c_str());
+ }
+}
+
+TEST(TestVariant, iterator_map)
+{
+ CVariant a;
+ a["key1"] = "string";
+ a["key2"] = "string";
+ a["key3"] = "string";
+ a["key4"] = "string";
+
+ EXPECT_TRUE(a.isObject());
+ EXPECT_EQ(CVariant::VariantTypeObject, a.type());
+
+ CVariant::iterator_map it;
+ for (it = a.begin_map(); it != a.end_map(); it++)
+ {
+ EXPECT_STREQ("string", it->second.c_str());
+ }
+
+ CVariant::const_iterator_map const_it;
+ for (const_it = a.begin_map(); const_it != a.end_map(); const_it++)
+ {
+ EXPECT_STREQ("string", const_it->second.c_str());
+ }
+}
+
+TEST(TestVariant, size)
+{
+ std::vector<std::string> strarray;
+ strarray.push_back("string");
+ strarray.push_back("string");
+ strarray.push_back("string");
+ strarray.push_back("string");
+ CVariant a(strarray);
+
+ EXPECT_EQ((unsigned int)4, a.size());
+}
+
+TEST(TestVariant, empty)
+{
+ std::vector<std::string> strarray;
+ CVariant a(strarray);
+
+ EXPECT_TRUE(a.empty());
+}
+
+TEST(TestVariant, clear)
+{
+ std::vector<std::string> strarray;
+ strarray.push_back("string");
+ strarray.push_back("string");
+ strarray.push_back("string");
+ strarray.push_back("string");
+ CVariant a(strarray);
+
+ EXPECT_FALSE(a.empty());
+ a.clear();
+ EXPECT_TRUE(a.empty());
+}
+
+TEST(TestVariant, erase)
+{
+ std::vector<std::string> strarray;
+ strarray.push_back("string1");
+ strarray.push_back("string2");
+ strarray.push_back("string3");
+ strarray.push_back("string4");
+ CVariant a, b(strarray);
+ a["key1"] = "string1";
+ a["key2"] = "string2";
+ a["key3"] = "string3";
+ a["key4"] = "string4";
+
+ EXPECT_STREQ("string2", a["key2"].c_str());
+ EXPECT_STREQ("string2", b[1].c_str());
+ a.erase("key2");
+ b.erase(1);
+ EXPECT_FALSE(a["key2"].c_str());
+ EXPECT_STREQ("string3", b[1].c_str());
+}
+
+TEST(TestVariant, isMember)
+{
+ CVariant a;
+ a["key1"] = "string1";
+
+ EXPECT_TRUE(a.isMember("key1"));
+ EXPECT_FALSE(a.isMember("key2"));
+}
diff --git a/src/utils/test/TestXBMCTinyXML.cpp b/src/utils/test/TestXBMCTinyXML.cpp
new file mode 100644
index 0000000000..0072d1b4f3
--- /dev/null
+++ b/src/utils/test/TestXBMCTinyXML.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/XBMCTinyXML.h"
+#include "utils/StringUtils.h"
+#include "test/TestUtils.h"
+
+#include "gtest/gtest.h"
+
+TEST(TestXBMCTinyXML, ParseFromString)
+{
+ bool retval = false;
+ // scraper results with unescaped &
+ CXBMCTinyXML doc;
+ std::string data("<details><url function=\"ParseTMDBRating\" "
+ "cache=\"tmdb-en-12244.json\">"
+ "http://api.themoviedb.org/3/movie/12244"
+ "?api_key=57983e31fb435df4df77afb854740ea9"
+ "&language=en&#x3f;&#x003F;&#0063;</url></details>");
+ doc.Parse(data.c_str());
+ TiXmlNode *root = doc.RootElement();
+ if (root && root->ValueStr() == "details")
+ {
+ TiXmlElement *url = root->FirstChildElement("url");
+ if (url && url->FirstChild())
+ {
+ retval = (url->FirstChild()->ValueStr() == "http://api.themoviedb.org/3/movie/12244?api_key=57983e31fb435df4df77afb854740ea9&language=en???");
+ }
+ }
+ EXPECT_TRUE(retval);
+}
+
+TEST(TestXBMCTinyXML, ParseFromFileHandle)
+{
+ bool retval = false;
+ // scraper results with unescaped &
+ CXBMCTinyXML doc;
+ FILE *f = fopen(XBMC_REF_FILE_PATH("/xbmc/utils/test/CXBMCTinyXML-test.xml").c_str(), "r");
+ ASSERT_TRUE(f);
+ doc.LoadFile(f);
+ fclose(f);
+ TiXmlNode *root = doc.RootElement();
+ if (root && root->ValueStr() == "details")
+ {
+ TiXmlElement *url = root->FirstChildElement("url");
+ if (url && url->FirstChild())
+ {
+ std::string str = url->FirstChild()->ValueStr();
+ retval = (StringUtils::Trim(str) == "http://api.themoviedb.org/3/movie/12244?api_key=57983e31fb435df4df77afb854740ea9&language=en???");
+ }
+ }
+ EXPECT_TRUE(retval);
+}
diff --git a/src/utils/test/TestXMLUtils.cpp b/src/utils/test/TestXMLUtils.cpp
new file mode 100644
index 0000000000..7170c425dc
--- /dev/null
+++ b/src/utils/test/TestXMLUtils.cpp
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/XMLUtils.h"
+#include "utils/StringUtils.h"
+#include "XBDateTime.h"
+
+#include "gtest/gtest.h"
+
+TEST(TestXMLUtils, GetHex)
+{
+ CXBMCTinyXML a;
+ uint32_t ref, val;
+
+ a.Parse("<root><node>0xFF</node></root>");
+ EXPECT_TRUE(XMLUtils::GetHex(a.RootElement(), "node", val));
+
+ ref = 0xFF;
+ EXPECT_EQ(ref, val);
+}
+
+TEST(TestXMLUtils, GetUInt)
+{
+ CXBMCTinyXML a;
+ uint32_t ref, val;
+
+ a.Parse("<root><node>1000</node></root>");
+ EXPECT_TRUE(XMLUtils::GetUInt(a.RootElement(), "node", val));
+
+ ref = 1000;
+ EXPECT_EQ(ref, val);
+}
+
+TEST(TestXMLUtils, GetLong)
+{
+ CXBMCTinyXML a;
+ long ref, val;
+
+ a.Parse("<root><node>1000</node></root>");
+ EXPECT_TRUE(XMLUtils::GetLong(a.RootElement(), "node", val));
+
+ ref = 1000;
+ EXPECT_EQ(ref, val);
+}
+
+TEST(TestXMLUtils, GetFloat)
+{
+ CXBMCTinyXML a;
+ float ref, val;
+
+ a.Parse("<root><node>1000.1f</node></root>");
+ EXPECT_TRUE(XMLUtils::GetFloat(a.RootElement(), "node", val));
+ EXPECT_TRUE(XMLUtils::GetFloat(a.RootElement(), "node", val, 1000.0f,
+ 1000.2f));
+ ref = 1000.1f;
+ EXPECT_EQ(ref, val);
+}
+
+TEST(TestXMLUtils, GetDouble)
+{
+ CXBMCTinyXML a;
+ double val;
+ std::string refstr, valstr;
+
+ a.Parse("<root><node>1000.1f</node></root>");
+ EXPECT_TRUE(XMLUtils::GetDouble(a.RootElement(), "node", val));
+
+ refstr = "1000.100000";
+ valstr = StringUtils::Format("%f", val);
+ EXPECT_STREQ(refstr.c_str(), valstr.c_str());
+}
+
+TEST(TestXMLUtils, GetInt)
+{
+ CXBMCTinyXML a;
+ int ref, val;
+
+ a.Parse("<root><node>1000</node></root>");
+ EXPECT_TRUE(XMLUtils::GetInt(a.RootElement(), "node", val));
+ EXPECT_TRUE(XMLUtils::GetInt(a.RootElement(), "node", val, 999, 1001));
+
+ ref = 1000;
+ EXPECT_EQ(ref, val);
+}
+
+TEST(TestXMLUtils, GetBoolean)
+{
+ CXBMCTinyXML a;
+ bool ref, val;
+
+ a.Parse("<root><node>true</node></root>");
+ EXPECT_TRUE(XMLUtils::GetBoolean(a.RootElement(), "node", val));
+
+ ref = true;
+ EXPECT_EQ(ref, val);
+}
+
+TEST(TestXMLUtils, GetString)
+{
+ CXBMCTinyXML a;
+ std::string ref, val;
+
+ a.Parse("<root><node>some string</node></root>");
+ EXPECT_TRUE(XMLUtils::GetString(a.RootElement(), "node", val));
+
+ ref = "some string";
+ EXPECT_STREQ(ref.c_str(), val.c_str());
+}
+
+TEST(TestXMLUtils, GetAdditiveString)
+{
+ CXBMCTinyXML a, b;
+ std::string ref, val;
+
+ a.Parse("<root>\n"
+ " <node>some string1</node>\n"
+ " <node>some string2</node>\n"
+ " <node>some string3</node>\n"
+ " <node>some string4</node>\n"
+ " <node>some string5</node>\n"
+ "</root>\n");
+ EXPECT_TRUE(XMLUtils::GetAdditiveString(a.RootElement(), "node", ",", val));
+
+ ref = "some string1,some string2,some string3,some string4,some string5";
+ EXPECT_STREQ(ref.c_str(), val.c_str());
+
+ val.clear();
+ b.Parse("<root>\n"
+ " <node>some string1</node>\n"
+ " <node>some string2</node>\n"
+ " <node clear=\"true\">some string3</node>\n"
+ " <node>some string4</node>\n"
+ " <node>some string5</node>\n"
+ "</root>\n");
+ EXPECT_TRUE(XMLUtils::GetAdditiveString(b.RootElement(), "node", ",", val));
+
+ ref = "some string3,some string4,some string5";
+ EXPECT_STREQ(ref.c_str(), val.c_str());
+}
+
+TEST(TestXMLUtils, GetStringArray)
+{
+ CXBMCTinyXML a;
+ std::vector<std::string> strarray;
+
+ a.Parse("<root>\n"
+ " <node>some string1</node>\n"
+ " <node>some string2</node>\n"
+ " <node>some string3</node>\n"
+ " <node>some string4</node>\n"
+ " <node>some string5</node>\n"
+ "</root>\n");
+ EXPECT_TRUE(XMLUtils::GetStringArray(a.RootElement(), "node", strarray));
+
+ EXPECT_STREQ("some string1", strarray.at(0).c_str());
+ EXPECT_STREQ("some string2", strarray.at(1).c_str());
+ EXPECT_STREQ("some string3", strarray.at(2).c_str());
+ EXPECT_STREQ("some string4", strarray.at(3).c_str());
+ EXPECT_STREQ("some string5", strarray.at(4).c_str());
+}
+
+TEST(TestXMLUtils, GetPath)
+{
+ CXBMCTinyXML a, b;
+ std::string ref, val;
+
+ a.Parse("<root><node urlencoded=\"yes\">special://xbmc/</node></root>");
+ EXPECT_TRUE(XMLUtils::GetPath(a.RootElement(), "node", val));
+
+ ref = "special://xbmc/";
+ EXPECT_STREQ(ref.c_str(), val.c_str());
+
+ val.clear();
+ b.Parse("<root><node>special://xbmcbin/</node></root>");
+ EXPECT_TRUE(XMLUtils::GetPath(b.RootElement(), "node", val));
+
+ ref = "special://xbmcbin/";
+ EXPECT_STREQ(ref.c_str(), val.c_str());
+}
+
+TEST(TestXMLUtils, GetDate)
+{
+ CXBMCTinyXML a;
+ CDateTime ref, val;
+
+ a.Parse("<root><node>2012-07-08</node></root>");
+ EXPECT_TRUE(XMLUtils::GetDate(a.RootElement(), "node", val));
+ ref.SetDate(2012, 7, 8);
+ EXPECT_TRUE(ref == val);
+}
+
+TEST(TestXMLUtils, GetDateTime)
+{
+ CXBMCTinyXML a;
+ CDateTime ref, val;
+
+ a.Parse("<root><node>2012-07-08 01:02:03</node></root>");
+ EXPECT_TRUE(XMLUtils::GetDateTime(a.RootElement(), "node", val));
+ ref.SetDateTime(2012, 7, 8, 1, 2, 3);
+ EXPECT_TRUE(ref == val);
+}
+
+TEST(TestXMLUtils, SetString)
+{
+ CXBMCTinyXML a;
+ std::string ref, val;
+
+ a.Parse("<root></root>");
+ XMLUtils::SetString(a.RootElement(), "node", "some string");
+ EXPECT_TRUE(XMLUtils::GetString(a.RootElement(), "node", val));
+
+ ref = "some string";
+ EXPECT_STREQ(ref.c_str(), val.c_str());
+}
+
+TEST(TestXMLUtils, SetAdditiveString)
+{
+ CXBMCTinyXML a;
+ std::string ref, val;
+
+ a.Parse("<root></root>");
+ XMLUtils::SetAdditiveString(a.RootElement(), "node", ",",
+ "some string1,some string2,some string3,some string4,some string5");
+ EXPECT_TRUE(XMLUtils::GetAdditiveString(a.RootElement(), "node", ",", val));
+
+ ref = "some string1,some string2,some string3,some string4,some string5";
+ EXPECT_STREQ(ref.c_str(), val.c_str());
+}
+
+TEST(TestXMLUtils, SetStringArray)
+{
+ CXBMCTinyXML a;
+ std::vector<std::string> strarray;
+ strarray.push_back("some string1");
+ strarray.push_back("some string2");
+ strarray.push_back("some string3");
+ strarray.push_back("some string4");
+ strarray.push_back("some string5");
+
+ a.Parse("<root></root>");
+ XMLUtils::SetStringArray(a.RootElement(), "node", strarray);
+ EXPECT_TRUE(XMLUtils::GetStringArray(a.RootElement(), "node", strarray));
+
+ EXPECT_STREQ("some string1", strarray.at(0).c_str());
+ EXPECT_STREQ("some string2", strarray.at(1).c_str());
+ EXPECT_STREQ("some string3", strarray.at(2).c_str());
+ EXPECT_STREQ("some string4", strarray.at(3).c_str());
+ EXPECT_STREQ("some string5", strarray.at(4).c_str());
+}
+
+TEST(TestXMLUtils, SetInt)
+{
+ CXBMCTinyXML a;
+ int ref, val;
+
+ a.Parse("<root></root>");
+ XMLUtils::SetInt(a.RootElement(), "node", 1000);
+ EXPECT_TRUE(XMLUtils::GetInt(a.RootElement(), "node", val));
+
+ ref = 1000;
+ EXPECT_EQ(ref, val);
+}
+
+TEST(TestXMLUtils, SetFloat)
+{
+ CXBMCTinyXML a;
+ float ref, val;
+
+ a.Parse("<root></root>");
+ XMLUtils::SetFloat(a.RootElement(), "node", 1000.1f);
+ EXPECT_TRUE(XMLUtils::GetFloat(a.RootElement(), "node", val));
+
+ ref = 1000.1f;
+ EXPECT_EQ(ref, val);
+}
+
+TEST(TestXMLUtils, SetBoolean)
+{
+ CXBMCTinyXML a;
+ bool ref, val;
+
+ a.Parse("<root></root>");
+ XMLUtils::SetBoolean(a.RootElement(), "node", true);
+ EXPECT_TRUE(XMLUtils::GetBoolean(a.RootElement(), "node", val));
+
+ ref = true;
+ EXPECT_EQ(ref, val);
+}
+
+TEST(TestXMLUtils, SetHex)
+{
+ CXBMCTinyXML a;
+ uint32_t ref, val;
+
+ a.Parse("<root></root>");
+ XMLUtils::SetHex(a.RootElement(), "node", 0xFF);
+ EXPECT_TRUE(XMLUtils::GetHex(a.RootElement(), "node", val));
+
+ ref = 0xFF;
+ EXPECT_EQ(ref, val);
+}
+
+TEST(TestXMLUtils, SetPath)
+{
+ CXBMCTinyXML a;
+ std::string ref, val;
+
+ a.Parse("<root></root>");
+ XMLUtils::SetPath(a.RootElement(), "node", "special://xbmc/");
+ EXPECT_TRUE(XMLUtils::GetPath(a.RootElement(), "node", val));
+
+ ref = "special://xbmc/";
+ EXPECT_STREQ(ref.c_str(), val.c_str());
+}
+
+TEST(TestXMLUtils, SetLong)
+{
+ CXBMCTinyXML a;
+ long ref, val;
+
+ a.Parse("<root></root>");
+ XMLUtils::SetLong(a.RootElement(), "node", 1000);
+ EXPECT_TRUE(XMLUtils::GetLong(a.RootElement(), "node", val));
+
+ ref = 1000;
+ EXPECT_EQ(ref, val);
+}
+
+TEST(TestXMLUtils, SetDate)
+{
+ CXBMCTinyXML a;
+ CDateTime ref, val;
+
+ a.Parse("<root></root>");
+ ref.SetDate(2012, 7, 8);
+ XMLUtils::SetDate(a.RootElement(), "node", ref);
+ EXPECT_TRUE(XMLUtils::GetDate(a.RootElement(), "node", val));
+ EXPECT_TRUE(ref == val);
+}
+
+TEST(TestXMLUtils, SetDateTime)
+{
+ CXBMCTinyXML a;
+ CDateTime ref, val;
+
+ a.Parse("<root></root>");
+ ref.SetDateTime(2012, 7, 8, 1, 2, 3);
+ XMLUtils::SetDateTime(a.RootElement(), "node", ref);
+ EXPECT_TRUE(XMLUtils::GetDateTime(a.RootElement(), "node", val));
+ EXPECT_TRUE(ref == val);
+}
diff --git a/src/utils/test/Testfastmemcpy.cpp b/src/utils/test/Testfastmemcpy.cpp
new file mode 100644
index 0000000000..3299f732d5
--- /dev/null
+++ b/src/utils/test/Testfastmemcpy.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stddef.h> // TODO: This should go in fastmemcpy.h instead.
+#include "utils/fastmemcpy.h"
+
+#include "gtest/gtest.h"
+
+static const char refdata[] = "\x01\x02\x03\x04\x05\x06\x07\x08"
+ "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
+ "\x11\x12\x13\x14\x15\x16\x17\x18"
+ "\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
+ "\x21\x22\x23\x24\x25\x26\x27\x28"
+ "\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30";
+
+TEST(Testfastmemcpy, General)
+{
+ char vardata[sizeof(refdata)];
+ memset(vardata, 0, sizeof(vardata));
+ EXPECT_TRUE(fast_memcpy(vardata, refdata, sizeof(refdata)));
+ EXPECT_TRUE(!memcmp(refdata, vardata, sizeof(refdata)));
+}
diff --git a/src/utils/test/Testfft.cpp b/src/utils/test/Testfft.cpp
new file mode 100644
index 0000000000..25bd324f3f
--- /dev/null
+++ b/src/utils/test/Testfft.cpp
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/fft.h"
+#include "utils/StdString.h"
+#include "utils/StringUtils.h"
+
+#include "gtest/gtest.h"
+
+/* refdata[] below was generated using the following Python script.
+
+import math
+import wave
+import struct
+import sys
+
+if __name__=='__main__':
+ # http://stackoverflow.com/questions/3637350/how-to-write-stereo-wav-files-in-python
+ # http://www.sonicspot.com/guide/wavefiles.html
+ freq=440.0
+ data_size=128
+ fname="test.wav"
+ frate=11025.0
+ amp=64000.0
+ nchannels=1
+ sampwidth=2
+ framerate=int(frate)
+ nframes=data_size
+ comptype="NONE"
+ compname="not compressed"
+ data=[math.sin(2*math.pi*freq*(x/frate))
+ for x in range(data_size)]
+ count = 0
+ sys.stdout.write("static const float refdata[] = {\n")
+ for v in data:
+ sys.stdout.write(str(v) + "f,")
+ count += 1
+ if count % 4 == 0:
+ sys.stdout.write("\n")
+ else:
+ sys.stdout.write(" ")
+ sys.stdout.write("};\n")
+*/
+
+static const float refdata[] = {
+0.0f, 0.248137847944f, 0.480754541017f, 0.683299780871f,
+0.843104254616f, 0.950172107096f, 0.997806186976f, 0.983026957126f,
+0.906758866265f, 0.773772524197f, 0.592386297618f, 0.373945991846f,
+0.132115164631f, -0.117979536667f, -0.360694554958f, -0.580847936259f,
+-0.764668969029f, -0.900659549526f, -0.980313394334f, -0.998648112931f,
+-0.954516858881f, -0.850680065687f, -0.693632780202f, -0.493198393981f,
+-0.261914184895f, -0.0142471037071f, 0.234311141425f, 0.46821309956f,
+0.672828078337f, 0.835357301588f, 0.945634479622f, 0.996761716077f,
+0.985540975016f, 0.912674119782f, 0.782719011074f, 0.603804410325f,
+0.387121521337f, 0.146223974503f, -0.103819960006f, -0.347369900587f,
+-0.569191668423f, -0.755410193505f, -0.894377407666f, -0.977400837465f,
+-0.999287323041f, -0.958667853037f, -0.858083196987f, -0.703824978831f,
+-0.505542132473f, -0.275637355817f, -0.0284913153908f, 0.220436872025f,
+0.455576615403f, 0.662219798253f, 0.827440779159f, 0.940904897555f,
+0.995514912254f, 0.987854937681f, 0.918404109336f, 0.791506613611f,
+0.615099956615f, 0.400218468928f, 0.160303102331f, -0.0896393089034f,
+-0.333974733507f, -0.557419860219f, -0.745998077066f, -0.887913715899f,
+-0.974289877741f, -0.999723687553f, -0.96262424695f, -0.865312145751f,
+-0.71387430784f, -0.517783250833f, -0.289304575039f, -0.0427297436149f,
+0.206517856086f, 0.442847653627f, 0.651477093995f, 0.819356294308f,
+0.935984320952f, 0.994066028594f, 0.989968375411f, 0.923947671797f,
+0.800133548011f, 0.626270643602f, 0.413234176067f, 0.17434969019f,
+-0.0754404618937f, -0.320511772808f, -0.545534901211f, -0.736434530278f,
+-0.881269786291f, -0.970981146657f, -0.999957117888f, -0.966385237512f,
+-0.872365444572f, -0.723778727314f, -0.529919264233f, -0.302913068248f,
+-0.056959498117f, 0.192556919032f, 0.430028798089f, 0.64060214623f,
+0.811105488104f, 0.930873748644f, 0.992415359208f, 0.991880859198f,
+0.929303681875f, 0.80859806309f, 0.637314203745f, 0.42616600069f,
+0.188360886759f, -0.0612263012046f, -0.306983751339f, -0.533539203927f,
+-0.72672149445f, -0.874446967495f, -0.967475315852f, -0.999987566662f,
+-0.969950061278f, -0.879241661701f, -0.733536226752f, -0.541947709182f,
+-0.316460073053f, -0.0711776903954f, 0.178556894799f, 0.417122650891f
+};
+
+#define REFDATA_NUMELEMENTS 128 /*(sizeof(refdata)/sizeof(float))*/
+
+/* All reference data below were generated by using the following C++ code.
+
+ fprintf(stdout, "static const float reffftdata[] = {\n");
+ for (i = 0; i < REFDATA_NUMELEMENTS; i++)
+ {
+ fprintf(stdout, "%.6ff,", vardata[i]);
+ if ((i + 1) % 4 == 0)
+ fprintf(stdout, "\n");
+ else
+ fprintf(stdout, " ");
+ }
+ fprintf(stdout, "};\n");
+
+*/
+
+static const float reffftdata[] = {
+0.000000f, 0.449505f, 0.120648f, 0.233138f,
+0.392939f, 0.043552f, 0.833054f, -0.148657f,
+1.675773f, -0.424927f, 3.999525f, -3.602860f,
+48.666092f, 0.123507f, -6.779173f, -0.179830f,
+-3.556417f, -0.323241f, -2.528245f, -0.424007f,
+-1.999831f, -0.503748f, -1.662579f, -0.569499f,
+-1.417722f, -0.624251f, -1.224108f, -0.669501f,
+-1.061725f, -0.706093f, -0.919814f, -0.734565f,
+-0.792209f, -0.755297f, -0.675225f, -0.768604f,
+-0.566617f, -0.774772f, -0.465016f, -0.774090f,
+-0.369611f, -0.766866f, -0.279953f, -0.753430f,
+-0.195832f, -0.734147f, -0.117191f, -0.709420f,
+-0.044083f, -0.679685f, 0.023383f, -0.645416f,
+0.085060f, -0.607121f, 0.140793f, -0.565342f,
+0.190426f, -0.520648f, 0.233821f, -0.473637f,
+0.270868f, -0.424926f, 0.301492f, -0.375151f,
+0.325655f, -0.324962f, 0.343369f, -0.275019f,
+0.354691f, -0.225984f, 0.359731f, -0.178525f,
+0.358652f, -0.133307f, 0.351671f, -0.090984f,
+0.339060f, -0.052212f, 0.321141f, -0.017629f,
+0.298293f, 0.012125f, 0.270944f, 0.036423f,
+0.239573f, 0.054633f, 0.204705f, 0.066113f,
+0.166914f, 0.070200f, 0.126814f, 0.066174f,
+0.085068f, 0.053236f, 0.042377f, 0.030439f,
+-0.000509f, -0.003392f, -0.042792f, -0.049815f,
+-0.083613f, -0.111011f, -0.122037f, -0.190212f,
+-0.157020f, -0.292521f, -0.187344f, -0.426526f,
+-0.211503f, -0.607937f, -0.227426f, -0.868800f,
+-0.231823f, -1.285804f, -0.218194f, -2.098434f,
+-0.168627f, -4.622009f, 0.009162f, 38.381283f,
+-2.935104f, 3.676884f, -0.513415f, 1.829207f,
+-0.334315f, 1.124193f, -0.207913f, 0.725023f,
+};
+
+static const float reffftinversedata[] = {
+0.000000f, 0.449505f, 0.120648f, 0.725023f,
+-0.066419f, 1.124193f, -0.207913f, 1.829207f,
+-0.334315f, 3.676884f, -0.513415f, 38.381283f,
+-2.935104f, -4.622009f, 0.009162f, -2.098434f,
+-0.168627f, -1.285804f, -0.218194f, -0.868800f,
+-0.231823f, -0.607937f, -0.227426f, -0.426526f,
+-0.211503f, -0.292521f, -0.187344f, -0.190212f,
+-0.157020f, -0.111011f, -0.122037f, -0.049815f,
+-0.083613f, -0.003392f, -0.042792f, 0.030439f,
+-0.000509f, 0.053236f, 0.042377f, 0.066174f,
+0.085068f, 0.070200f, 0.126814f, 0.066113f,
+0.166914f, 0.054633f, 0.204705f, 0.036423f,
+0.239573f, 0.012125f, 0.270944f, -0.017629f,
+0.298293f, -0.052212f, 0.321141f, -0.090984f,
+0.339060f, -0.133307f, 0.351671f, -0.178525f,
+0.358652f, -0.225984f, 0.359731f, -0.275019f,
+0.354691f, -0.324962f, 0.343369f, -0.375151f,
+0.325655f, -0.424926f, 0.301492f, -0.473637f,
+0.270868f, -0.520648f, 0.233821f, -0.565342f,
+0.190426f, -0.607121f, 0.140793f, -0.645416f,
+0.085060f, -0.679685f, 0.023383f, -0.709420f,
+-0.044083f, -0.734147f, -0.117191f, -0.753430f,
+-0.195832f, -0.766866f, -0.279953f, -0.774090f,
+-0.369611f, -0.774772f, -0.465016f, -0.768604f,
+-0.566617f, -0.755297f, -0.675225f, -0.734565f,
+-0.792209f, -0.706093f, -0.919814f, -0.669501f,
+-1.061725f, -0.624251f, -1.224108f, -0.569499f,
+-1.417722f, -0.503748f, -1.662579f, -0.424007f,
+-1.999831f, -0.323241f, -2.528245f, -0.179830f,
+-3.556417f, 0.123507f, -6.779173f, -3.602860f,
+48.666092f, -0.424927f, 3.999525f, -0.148657f,
+1.675773f, 0.043552f, 0.833054f, 0.233138f,
+};
+
+static const float reftwochannelrfftdata[] = {
+0.014556f, 0.202055f, 0.174283f, 0.564540f,
+0.779294f, 1.223621f, 2.855724f, 3.432353f,
+14.488905f, 15.470927f, 1926.995483f, 1936.110718f,
+34.176479f, 33.159004f, 8.778496f, 8.333804f,
+4.234725f, 3.962682f, 2.589059f, 2.398601f,
+1.791486f, 1.647755f, 1.337403f, 1.223514f,
+1.051121f, 0.957675f, 0.857526f, 0.778798f,
+0.719788f, 0.652054f, 0.617972f, 0.558680f,
+0.540455f, 0.487790f, 0.480051f, 0.432682f,
+0.432111f, 0.389031f, 0.393506f, 0.353940f,
+0.362065f, 0.325402f, 0.336244f, 0.301994f,
+0.314917f, 0.282679f, 0.297250f, 0.266693f,
+0.282615f, 0.253461f, 0.270538f, 0.242548f,
+0.260654f, 0.233622f, 0.252682f, 0.226430f,
+0.246418f, 0.220773f, 0.241694f, 0.216511f,
+0.238397f, 0.213538f, 0.236449f, 0.211782f,
+0.117902f, 0.105600f, -0.357960f, -0.308602f,
+-0.372784f, -0.292395f, -0.387933f, -0.276250f,
+-0.403502f, -0.260078f, -0.419604f, -0.243792f,
+-0.436362f, -0.227290f, -0.453916f, -0.210478f,
+-0.472433f, -0.193249f, -0.492110f, -0.175478f,
+-0.513182f, -0.157028f, -0.535943f, -0.137741f,
+-0.560756f, -0.117426f, -0.588082f, -0.095855f,
+-0.618519f, -0.072741f, -0.652857f, -0.047725f,
+-0.692169f, -0.020336f, -0.737947f, 0.010054f,
+-0.792340f, 0.044317f, -0.858569f, 0.083711f,
+-0.941691f, 0.130146f, -1.050159f, 0.186711f,
+-1.199277f, 0.258840f, -1.419963f, 0.357229f,
+-1.785721f, 0.506807f, -2.525070f, 0.783990f,
+-4.890463f, 1.604234f, 36.070564f, -11.902861f,
+3.081995f, -0.935849f, 1.359818f, -0.300774f,
+0.721637f, -0.007045f, 0.368046f, 0.218320f,
+};
+
+static const float reftwochanwithwindowdata[] = {
+0.000078f, 0.000219f, 0.000856f, 0.001174f,
+0.007080f, 0.007566f, 0.107324f, 0.108530f,
+90.137039f, 90.161346f, 504.375732f, 504.333801f,
+173.107437f, 173.125870f, 0.244316f, 0.243784f,
+0.012957f, 0.012861f, 0.001967f, 0.001938f,
+0.000483f, 0.000471f, 0.000157f, 0.000152f,
+0.000062f, 0.000059f, 0.000028f, 0.000026f,
+0.000014f, 0.000013f, 0.000007f, 0.000007f,
+0.000004f, 0.000004f, 0.000003f, 0.000002f,
+0.000002f, 0.000001f, 0.000001f, 0.000001f,
+0.000001f, 0.000001f, 0.000000f, 0.000000f,
+0.000000f, 0.000000f, 0.000000f, 0.000000f,
+0.000000f, 0.000000f, 0.000000f, 0.000000f,
+0.000000f, 0.000000f, 0.000000f, 0.000000f,
+0.000000f, 0.000000f, 0.000000f, 0.000000f,
+0.000000f, 0.000000f, 0.000000f, 0.000000f,
+0.000000f, 0.000000f, 0.000058f, 0.000038f,
+0.000081f, 0.000016f, 0.000106f, -0.000007f,
+0.000133f, -0.000029f, 0.000164f, -0.000052f,
+0.000199f, -0.000077f, 0.000241f, -0.000105f,
+0.000290f, -0.000135f, 0.000349f, -0.000169f,
+0.000422f, -0.000210f, 0.000513f, -0.000257f,
+0.000628f, -0.000314f, 0.000778f, -0.000385f,
+0.000975f, -0.000475f, 0.001243f, -0.000593f,
+0.001616f, -0.000750f, 0.002154f, -0.000969f,
+0.002959f, -0.001283f, 0.004224f, -0.001760f,
+0.006336f, -0.002532f, 0.010161f, -0.003890f,
+0.017893f, -0.006565f, 0.036268f, -0.012797f,
+0.093398f, -0.031901f, 0.406511f, -0.135766f,
+-10.831606f, 3.581835f, 18.487400f, -6.118526f,
+-7.816598f, 2.582984f, -0.270999f, 0.085336f,
+-0.071148f, 0.017091f, -0.026548f, -0.001455f,
+};
+
+TEST(Testfft, fft)
+{
+ int i;
+ float vardata[REFDATA_NUMELEMENTS];
+ float res;
+
+ memcpy(vardata, refdata, sizeof(refdata));
+ fft(vardata - 1, REFDATA_NUMELEMENTS/2, 1);
+ // let's see if it's okay enough
+ fft(vardata -1, REFDATA_NUMELEMENTS/2, -1);
+ for (i = 0; i < REFDATA_NUMELEMENTS; i++)
+ {
+ res = vardata[i] / (REFDATA_NUMELEMENTS / 2);
+ EXPECT_NEAR(res, refdata[i], 0.000001);
+ }
+}
+
+TEST(Testfft, twochannelrfft)
+{
+ int i;
+ float vardata[REFDATA_NUMELEMENTS];
+ CStdString refstr, varstr;
+
+ memcpy(vardata, refdata, sizeof(refdata));
+ twochannelrfft(vardata, REFDATA_NUMELEMENTS/2);
+ for (i = 0; i < REFDATA_NUMELEMENTS; i++)
+ {
+ refstr = StringUtils::Format("%.6f", reftwochannelrfftdata[i]);
+ varstr = StringUtils::Format("%.6f", vardata[i]);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+ }
+}
+
+TEST(Testfft, twochanwithwindow)
+{
+ int i;
+ float vardata[REFDATA_NUMELEMENTS];
+ CStdString refstr, varstr;
+
+ memcpy(vardata, refdata, sizeof(refdata));
+ twochanwithwindow(vardata, REFDATA_NUMELEMENTS/2);
+ for (i = 0; i < REFDATA_NUMELEMENTS; i++)
+ {
+ refstr = StringUtils::Format("%.6f", reftwochanwithwindowdata[i]);
+ varstr = StringUtils::Format("%.6f", vardata[i]);
+ EXPECT_STREQ(refstr.c_str(), varstr.c_str());
+ }
+}
diff --git a/src/utils/test/Testfstrcmp.cpp b/src/utils/test/Testfstrcmp.cpp
new file mode 100644
index 0000000000..be4605f8d8
--- /dev/null
+++ b/src/utils/test/Testfstrcmp.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/fstrcmp.h"
+#include "utils/StdString.h"
+#include "utils/StringUtils.h"
+
+#include "gtest/gtest.h"
+
+TEST(Testfstrcmp, General)
+{
+ CStdString refstr, varstr, refresult, varresult;
+ refstr = "Testfstrcmp test string";
+ varstr = refstr;
+
+ /* NOTE: Third parameter is not used at all in fstrcmp. */
+ refresult = "1.000000";
+ varresult = StringUtils::Format("%.6f", fstrcmp(refstr.c_str(), varstr.c_str(), 0.0));
+ EXPECT_STREQ(refresult.c_str(), varresult.c_str());
+
+ varstr = "Testfstrcmp_test_string";
+ refresult = "0.913043";
+ varresult = StringUtils::Format("%.6f", fstrcmp(refstr.c_str(), varstr.c_str(), 0.0));
+ EXPECT_STREQ(refresult.c_str(), varresult.c_str());
+
+ varstr = "";
+ refresult = "0.000000";
+ varresult = StringUtils::Format("%.6f", fstrcmp(refstr.c_str(), varstr.c_str(), 0.0));
+ EXPECT_STREQ(refresult.c_str(), varresult.c_str());
+}
diff --git a/src/utils/test/Testlog.cpp b/src/utils/test/Testlog.cpp
new file mode 100644
index 0000000000..656201f959
--- /dev/null
+++ b/src/utils/test/Testlog.cpp
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/log.h"
+#include "utils/RegExp.h"
+#include "filesystem/File.h"
+#include "filesystem/SpecialProtocol.h"
+#include "utils/StdString.h"
+#include "utils/StringUtils.h"
+#include "CompileInfo.h"
+
+#include "test/TestUtils.h"
+
+#include "gtest/gtest.h"
+
+class Testlog : public testing::Test
+{
+protected:
+ Testlog(){}
+ ~Testlog()
+ {
+ CLog::Close();
+ }
+};
+
+TEST_F(Testlog, Log)
+{
+ CStdString logfile, logstring;
+ char buf[100];
+ unsigned int bytesread;
+ XFILE::CFile file;
+ CRegExp regex;
+
+ std::string appName = CCompileInfo::GetAppName();
+ StringUtils::ToLower(appName);
+ logfile = CSpecialProtocol::TranslatePath("special://temp/") + appName + ".log";
+ EXPECT_TRUE(CLog::Init(CSpecialProtocol::TranslatePath("special://temp/").c_str()));
+ EXPECT_TRUE(XFILE::CFile::Exists(logfile));
+
+ CLog::Log(LOGDEBUG, "debug log message");
+ CLog::Log(LOGINFO, "info log message");
+ CLog::Log(LOGNOTICE, "notice log message");
+ CLog::Log(LOGWARNING, "warning log message");
+ CLog::Log(LOGERROR, "error log message");
+ CLog::Log(LOGSEVERE, "severe log message");
+ CLog::Log(LOGFATAL, "fatal log message");
+ CLog::Log(LOGNONE, "none type log message");
+ CLog::Close();
+
+ EXPECT_TRUE(file.Open(logfile));
+ while ((bytesread = file.Read(buf, sizeof(buf) - 1)) > 0)
+ {
+ buf[bytesread] = '\0';
+ logstring.append(buf);
+ }
+ file.Close();
+ EXPECT_FALSE(logstring.empty());
+
+ EXPECT_STREQ("\xEF\xBB\xBF", logstring.substr(0, 3).c_str());
+
+ EXPECT_TRUE(regex.RegComp(".*DEBUG: debug log message.*"));
+ EXPECT_GE(regex.RegFind(logstring), 0);
+ EXPECT_TRUE(regex.RegComp(".*INFO: info log message.*"));
+ EXPECT_GE(regex.RegFind(logstring), 0);
+ EXPECT_TRUE(regex.RegComp(".*NOTICE: notice log message.*"));
+ EXPECT_GE(regex.RegFind(logstring), 0);
+ EXPECT_TRUE(regex.RegComp(".*WARNING: warning log message.*"));
+ EXPECT_GE(regex.RegFind(logstring), 0);
+ EXPECT_TRUE(regex.RegComp(".*ERROR: error log message.*"));
+ EXPECT_GE(regex.RegFind(logstring), 0);
+ EXPECT_TRUE(regex.RegComp(".*SEVERE: severe log message.*"));
+ EXPECT_GE(regex.RegFind(logstring), 0);
+ EXPECT_TRUE(regex.RegComp(".*FATAL: fatal log message.*"));
+ EXPECT_GE(regex.RegFind(logstring), 0);
+ EXPECT_TRUE(regex.RegComp(".*NONE: none type log message.*"));
+ EXPECT_GE(regex.RegFind(logstring), 0);
+
+ EXPECT_TRUE(XFILE::CFile::Delete(logfile));
+}
+
+TEST_F(Testlog, MemDump)
+{
+ CStdString logfile, logstring;
+ char buf[100];
+ unsigned int bytesread;
+ XFILE::CFile file;
+ CRegExp regex;
+ char refdata[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+
+ std::string appName = CCompileInfo::GetAppName();
+ StringUtils::ToLower(appName);
+ logfile = CSpecialProtocol::TranslatePath("special://temp/") + appName + ".log";
+ EXPECT_TRUE(CLog::Init(CSpecialProtocol::TranslatePath("special://temp/").c_str()));
+ EXPECT_TRUE(XFILE::CFile::Exists(logfile));
+
+ CLog::MemDump(refdata, sizeof(refdata));
+ CLog::Close();
+
+ EXPECT_TRUE(file.Open(logfile));
+ while ((bytesread = file.Read(buf, sizeof(buf) - 1)) > 0)
+ {
+ buf[bytesread] = '\0';
+ logstring.append(buf);
+ }
+ file.Close();
+ EXPECT_FALSE(logstring.empty());
+
+ EXPECT_STREQ("\xEF\xBB\xBF", logstring.substr(0, 3).c_str());
+
+ EXPECT_TRUE(regex.RegComp(".*DEBUG: MEM_DUMP: Dumping from.*"));
+ EXPECT_GE(regex.RegFind(logstring), 0);
+ EXPECT_TRUE(regex.RegComp(".*DEBUG: MEM_DUMP: 0000 30 31 32 33.*"));
+ EXPECT_GE(regex.RegFind(logstring), 0);
+ EXPECT_TRUE(regex.RegComp(".*73 74 75 76 ghijklmnopqrstuv.*"));
+ EXPECT_GE(regex.RegFind(logstring), 0);
+
+ EXPECT_TRUE(XFILE::CFile::Delete(logfile));
+}
+
+TEST_F(Testlog, SetLogLevel)
+{
+ CStdString logfile;
+
+ std::string appName = CCompileInfo::GetAppName();
+ StringUtils::ToLower(appName);
+ logfile = CSpecialProtocol::TranslatePath("special://temp/") + appName + ".log";
+ EXPECT_TRUE(CLog::Init(CSpecialProtocol::TranslatePath("special://temp/").c_str()));
+ EXPECT_TRUE(XFILE::CFile::Exists(logfile));
+
+ EXPECT_EQ(LOG_LEVEL_DEBUG, CLog::GetLogLevel());
+ CLog::SetLogLevel(LOG_LEVEL_MAX);
+ EXPECT_EQ(LOG_LEVEL_MAX, CLog::GetLogLevel());
+
+ CLog::Close();
+ EXPECT_TRUE(XFILE::CFile::Delete(logfile));
+}
diff --git a/src/utils/test/Testmd5.cpp b/src/utils/test/Testmd5.cpp
new file mode 100644
index 0000000000..d19ecc749d
--- /dev/null
+++ b/src/utils/test/Testmd5.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/md5.h"
+
+#include "gtest/gtest.h"
+
+TEST(Testmd5, ZeroLengthString)
+{
+ XBMC::XBMC_MD5 a;
+ std::string refdigest, vardigest;
+
+ refdigest = "D41D8CD98F00B204E9800998ECF8427E";
+ a.append("");
+ vardigest = a.getDigest();
+ EXPECT_STREQ(refdigest.c_str(), vardigest.c_str());
+}
+
+TEST(Testmd5, String1)
+{
+ XBMC::XBMC_MD5 a;
+ std::string refdigest, vardigest;
+
+ refdigest = "9E107D9D372BB6826BD81D3542A419D6";
+ a.append("The quick brown fox jumps over the lazy dog");
+ vardigest = a.getDigest();
+ EXPECT_STREQ(refdigest.c_str(), vardigest.c_str());
+}
+
+TEST(Testmd5, String2)
+{
+ XBMC::XBMC_MD5 a;
+ std::string refdigest, vardigest;
+
+ refdigest = "E4D909C290D0FB1CA068FFADDF22CBD0";
+ a.append("The quick brown fox jumps over the lazy dog.");
+ vardigest = a.getDigest();
+ EXPECT_STREQ(refdigest.c_str(), vardigest.c_str());
+}
diff --git a/src/utils/uXstrings.h b/src/utils/uXstrings.h
new file mode 100644
index 0000000000..0b2e12c303
--- /dev/null
+++ b/src/utils/uXstrings.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/** @file utils/uXstrings.h
+ * Declarations of std::u16string and std::u32string for systems without those declarations
+ */
+#pragma once
+
+#include <string>
+
+#ifndef TARGET_WINDOWS
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif // HAVE_CONFIG_H
+
+#if !defined(HAVE_STD__U16STRING) || !defined(HAVE_STD__U32STRING)
+#if defined(HAVE_STDINT_H)
+#include <stdint.h>
+#elif defined(HAVE_INTTYPES_H)
+#include <inttypes.h>
+#endif // defined(HAVE_INTTYPES_H)
+
+#ifndef HAVE_STD__U16STRING
+#ifndef HAVE_CHAR16_T
+typedef uint_least16_t char16_t;
+#endif // HAVE_CHAR16_T
+namespace std
+{
+ typedef basic_string<char16_t> u16string;
+}
+#endif // HAVE_STD__U16STRING
+
+#ifndef HAVE_STD__U32STRING
+#ifndef HAVE_CHAR32_T
+typedef uint_least32_t char32_t;
+#endif // HAVE_CHAR32_T
+namespace std
+{
+ typedef basic_string<char32_t> u32string;
+}
+#endif // HAVE_STD__U32STRING
+
+#endif // !defined(HAVE_STD__U16STRING) || !defined(HAVE_STD__U32STRING)
+#endif // TARGET_WINDOWS
diff --git a/src/utils/win32/Win32InterfaceForCLog.cpp b/src/utils/win32/Win32InterfaceForCLog.cpp
new file mode 100644
index 0000000000..6daf40f9ed
--- /dev/null
+++ b/src/utils/win32/Win32InterfaceForCLog.cpp
@@ -0,0 +1,120 @@
+/*
+* Copyright (C) 2014 Team XBMC
+* http://xbmc.org
+*
+* This Program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2, or (at your option)
+* any later version.
+*
+* This Program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with XBMC; see the file COPYING. If not, see
+* <http://www.gnu.org/licenses/>.
+*
+*/
+
+#ifndef TARGET_WINDOWS
+#error This file is for win32 platforms only
+#endif //!TARGET_WINDOWS
+
+#include "Win32InterfaceForCLog.h"
+#include "win32/WIN32Util.h"
+#include "utils/StringUtils.h"
+#include "utils/auto_buffer.h"
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif // WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+CWin32InterfaceForCLog::CWin32InterfaceForCLog() :
+ m_hFile(INVALID_HANDLE_VALUE)
+{ }
+
+CWin32InterfaceForCLog::~CWin32InterfaceForCLog()
+{
+ if (m_hFile != INVALID_HANDLE_VALUE)
+ CloseHandle(m_hFile);
+}
+
+bool CWin32InterfaceForCLog::OpenLogFile(const std::string& logFilename, const std::string& backupOldLogToFilename)
+{
+ if (m_hFile != INVALID_HANDLE_VALUE)
+ return false; // file was already opened
+
+ std::wstring strLogFileW(CWIN32Util::ConvertPathToWin32Form(CWIN32Util::SmbToUnc(logFilename)));
+ std::wstring strLogFileOldW(CWIN32Util::ConvertPathToWin32Form(CWIN32Util::SmbToUnc(backupOldLogToFilename)));
+
+ if (strLogFileW.empty())
+ return false;
+
+ if (!strLogFileOldW.empty())
+ {
+ (void)DeleteFileW(strLogFileOldW.c_str()); // if it's failed, try to continue
+ (void)MoveFileW(strLogFileW.c_str(), strLogFileOldW.c_str()); // if it's failed, try to continue
+ }
+
+ m_hFile = CreateFileW(strLogFileW.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL,
+ CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (m_hFile == INVALID_HANDLE_VALUE)
+ return false;
+
+ static const unsigned char BOM[3] = { 0xEF, 0xBB, 0xBF };
+ DWORD written;
+ (void)WriteFile(m_hFile, BOM, sizeof(BOM), &written, NULL); // write BOM, ignore possible errors
+
+ return true;
+}
+
+void CWin32InterfaceForCLog::CloseLogFile(void)
+{
+ if (m_hFile != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(m_hFile);
+ m_hFile = INVALID_HANDLE_VALUE;
+ }
+}
+
+bool CWin32InterfaceForCLog::WriteStringToLog(const std::string& logString)
+{
+ if (m_hFile == INVALID_HANDLE_VALUE)
+ return false;
+
+ std::string strData(logString);
+ StringUtils::Replace(strData, "\n", "\r\n");
+ strData += "\r\n";
+
+ DWORD written;
+ const bool ret = (WriteFile(m_hFile, strData.c_str(), strData.length(), &written, NULL) != 0) && written == strData.length();
+ (void)FlushFileBuffers(m_hFile);
+
+ return ret;
+}
+
+void CWin32InterfaceForCLog::PrintDebugString(const std::string& debugString)
+{
+#ifdef _DEBUG
+ ::OutputDebugStringW(L"Debug Print: ");
+ int bufSize = MultiByteToWideChar(CP_UTF8, 0, debugString.c_str(), debugString.length(), NULL, 0);
+ XUTILS::auto_buffer buf(sizeof(wchar_t) * (bufSize + 1)); // '+1' for extra safety
+ if (MultiByteToWideChar(CP_UTF8, 0, debugString.c_str(), debugString.length(), (wchar_t*)buf.get(), buf.size() / sizeof(wchar_t)) == bufSize)
+ ::OutputDebugStringW(std::wstring((wchar_t*)buf.get(), bufSize).c_str());
+ else
+ ::OutputDebugStringA(debugString.c_str());
+ ::OutputDebugStringW(L"\n");
+#endif // _DEBUG
+}
+
+void CWin32InterfaceForCLog::GetCurrentLocalTime(int& hour, int& minute, int& second)
+{
+ SYSTEMTIME time;
+ GetLocalTime(&time);
+ hour = time.wHour;
+ minute = time.wMinute;
+ second = time.wSecond;
+}
diff --git a/src/utils/win32/Win32InterfaceForCLog.h b/src/utils/win32/Win32InterfaceForCLog.h
new file mode 100644
index 0000000000..a2909d3b52
--- /dev/null
+++ b/src/utils/win32/Win32InterfaceForCLog.h
@@ -0,0 +1,38 @@
+#pragma once
+/*
+* Copyright (C) 2014 Team XBMC
+* http://xbmc.org
+*
+* This Program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2, or (at your option)
+* any later version.
+*
+* This Program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with XBMC; see the file COPYING. If not, see
+* <http://www.gnu.org/licenses/>.
+*
+*/
+
+#include <string>
+
+typedef void* HANDLE; // forward declaration, to avoid inclusion of whole Windows.h
+
+class CWin32InterfaceForCLog
+{
+public:
+ CWin32InterfaceForCLog();
+ ~CWin32InterfaceForCLog();
+ bool OpenLogFile(const std::string& logFilename, const std::string& backupOldLogToFilename);
+ void CloseLogFile(void);
+ bool WriteStringToLog(const std::string& logString);
+ void PrintDebugString(const std::string& debugString);
+ static void GetCurrentLocalTime(int& hour, int& minute, int& second);
+private:
+ HANDLE m_hFile;
+};
diff --git a/src/utils/win32/Win32Log.cpp b/src/utils/win32/Win32Log.cpp
new file mode 100644
index 0000000000..ef5264ca67
--- /dev/null
+++ b/src/utils/win32/Win32Log.cpp
@@ -0,0 +1,65 @@
+/*
+* Copyright (C) 2014 Team XBMC
+* http://xbmc.org
+*
+* This Program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2, or (at your option)
+* any later version.
+*
+* This Program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with XBMC; see the file COPYING. If not, see
+* <http://www.gnu.org/licenses/>.
+*
+*/
+
+#include "Win32Log.h"
+#include "utils/StringUtils.h"
+#include "utils/CharsetConverter.h"
+
+void CWin32Log::LogW(int loglevel, const wchar_t* format, ...)
+{
+ if (IsLogLevelLogged(loglevel))
+ {
+ va_list va;
+ va_start(va, format);
+ std::wstring strDataW(StringUtils::FormatV(format, va));
+ va_end(va);
+ if (!strDataW.empty())
+ {
+ std::string strDataUtf8;
+ if (g_charsetConverter.wToUTF8(strDataW, strDataUtf8, false) && !strDataUtf8.empty())
+ LogString(loglevel, strDataUtf8);
+ else
+ PrintDebugString(__FUNCTION__ ": Can't convert log wide string to UTF-8");
+ }
+ }
+}
+
+void CWin32Log::LogFunctionW(int loglevel, const char* functionName, const wchar_t* format, ...)
+{
+ if (IsLogLevelLogged(loglevel))
+ {
+ va_list va;
+ va_start(va, format);
+ std::wstring strDataW(StringUtils::FormatV(format, va));
+ va_end(va);
+ if (!strDataW.empty())
+ {
+ std::string funcNameStr;
+ if (functionName && functionName[0])
+ funcNameStr.assign(functionName).append(": ");
+
+ std::string strDataUtf8;
+ if (g_charsetConverter.wToUTF8(strDataW, strDataUtf8, false) && !strDataUtf8.empty())
+ LogString(loglevel, funcNameStr + strDataUtf8);
+ else
+ PrintDebugString(__FUNCTION__ ": Can't convert log wide string to UTF-8");
+ }
+ }
+}
diff --git a/src/utils/win32/Win32Log.h b/src/utils/win32/Win32Log.h
new file mode 100644
index 0000000000..53ee0fedae
--- /dev/null
+++ b/src/utils/win32/Win32Log.h
@@ -0,0 +1,40 @@
+#pragma once
+
+/*
+* Copyright (C) 2014 Team XBMC
+* http://xbmc.org
+*
+* This Program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2, or (at your option)
+* any later version.
+*
+* This Program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with XBMC; see the file COPYING. If not, see
+* <http://www.gnu.org/licenses/>.
+*
+*/
+
+#include "utils/log.h"
+
+#ifndef TARGET_WINDOWS
+#error This file is for Win32 platfrom only
+#endif // TARGET_WINDOWS
+
+
+// CLog version for Win32 with additional widestring logging capabilities
+class CWin32Log : public CLog
+{
+public:
+ static void LogW(int loglevel, PRINTF_FORMAT_STRING const wchar_t *format, ...);
+ static void LogFunctionW(int loglevel, IN_OPT_STRING const char* functionName, PRINTF_FORMAT_STRING const wchar_t* format, ...);
+#define LogFW(loglevel,format,...) LogFunctionW((loglevel),__FUNCTION__,(format),##__VA_ARGS__)
+};
+
+// substitute CWin32Log instead of CLog for Win32
+#define CLog CWin32Log