summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Lancaster <alexlan[AT]fedoraproject org>2012-05-16 20:19:21 -0400
committerAlex Lancaster <alexlan[AT]fedoraproject org>2012-05-16 20:19:21 -0400
commitd5c86c6ed87a63b6cd963799904510eadcc41bdb (patch)
treeeeefb182a1610530bb44794d48fcd9570c1b8b46
parent06ea05f47e4d9f067b2ca39350ecd573bb7d4a44 (diff)
downloadxbmc-rpm-d5c86c6ed87a63b6cd963799904510eadcc41bdb.tar.gz
xbmc-rpm-d5c86c6ed87a63b6cd963799904510eadcc41bdb.tar.xz
xbmc-rpm-d5c86c6ed87a63b6cd963799904510eadcc41bdb.zip
- Add support for PVR add-ons (backported from tsp's PVR branch to
Eden), including MythTV - Workaround bug in compiling against afpfs-ng-devel, will hopefully get AirPlay support working (needs testing) - Drop references to obsolete patches now in Eden - Add patch from dteirney's branch for MythTV 0.25 support
-rw-r--r--xbmc-10.1-libpng-1.5.patch596
-rw-r--r--xbmc-11.0-dteirney-myth-0.25.patch660
-rw-r--r--xbmc-11.0-libpng-1.5-fix-plt-trn-get.patch51
-rw-r--r--xbmc-11.0-tsp-Eden-pvr.patch126913
-rw-r--r--xbmc-Dharma-10.1-gcc-4.6-fixes-0.1.patch92
-rw-r--r--xbmc.spec53
6 files changed, 127610 insertions, 755 deletions
diff --git a/xbmc-10.1-libpng-1.5.patch b/xbmc-10.1-libpng-1.5.patch
deleted file mode 100644
index da331d2..0000000
--- a/xbmc-10.1-libpng-1.5.patch
+++ /dev/null
@@ -1,596 +0,0 @@
-fix building with newer libpng. patch by Ian Stakenvicius.
-
-https://bugs.gentoo.org/380127
-
---- a/xbmc/lib/cximage-6.0/CxImage/ximapng.h
-+++ b/xbmc/lib/cximage-6.0/CxImage/ximapng.h
-@@ -69,8 +69,13 @@
-
- static void PNGAPI user_error_fn(png_structp png_ptr,png_const_charp error_msg)
- {
-+#if PNG_LIBPNG_VER > 10399
-+ strncpy((char*)png_get_error_ptr(png_ptr),error_msg,255);
-+ longjmp(png_jmpbuf(png_ptr), 1);
-+#else
- strncpy((char*)png_ptr->error_ptr,error_msg,255);
- longjmp(png_ptr->jmpbuf, 1);
-+#endif
- }
- };
-
---- a/xbmc/lib/cximage-6.0/CxImage/ximapng.cpp
-+++ b/xbmc/lib/cximage-6.0/CxImage/ximapng.cpp
-@@ -15,7 +15,11 @@
- void CxImagePNG::ima_png_error(png_struct *png_ptr, char *message)
- {
- strcpy(info.szLastError,message);
-+#if PNG_LIBPNG_VER > 10399
-+ longjmp(png_jmpbuf(png_ptr), 1);
-+#else
- longjmp(png_ptr->jmpbuf, 1);
-+#endif
- }
- ////////////////////////////////////////////////////////////////////////////////
- #if CXIMAGE_SUPPORT_DECODE
-@@ -62,7 +66,11 @@
- /* Set error handling if you are using the setjmp/longjmp method (this is
- * the normal method of doing things with libpng). REQUIRED unless you
- * set up your own error handlers in the png_create_read_struct() earlier. */
-+#if PNG_LIBPNG_VER > 10399
-+ if (setjmp(png_jmpbuf(png_ptr))) {
-+#else
- if (setjmp(png_ptr->jmpbuf)) {
-+#endif
- /* Free all of the memory associated with the png_ptr and info_ptr */
- delete [] row_pointers;
- png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
-@@ -70,16 +78,35 @@
- /* read the file information */
- png_read_info(png_ptr, info_ptr);
-
-+ png_uint_32 _width,_height;
-+ int _bit_depth,_color_type,_interlace_type,_compression_type,_filter_type;
-+#if PNG_LIBPNG_VER > 10399
-+ png_get_IHDR(png_ptr,info_ptr,&_width,&_height,&_bit_depth,&_color_type,
-+ &_interlace_type,&_compression_type,&_filter_type);
-+#else
-+ _width=info_ptr->width;
-+ _height=info_ptr->height;
-+ _bit_depth=info_ptr->bit_depth;
-+ _color_type=info_ptr->color_type;
-+ _interlace_type=info_ptr->interlace_type;
-+ _compression_type=info_ptr->compression_type;
-+ _filter_type=info_ptr->filter_type;
-+#endif
-+
- if (info.nEscape == -1){
-- head.biWidth = info_ptr->width;
-- head.biHeight= info_ptr->height;
-+ head.biWidth = _width;
-+ head.biHeight= _height;
- info.dwType = CXIMAGE_FORMAT_PNG;
-+#if PNG_LIBPNG_VER > 10399
-+ longjmp(png_jmpbuf(png_ptr), 1);
-+#else
- longjmp(png_ptr->jmpbuf, 1);
-+#endif
- }
-
- /* calculate new number of channels */
- int channels=0;
-- switch(info_ptr->color_type){
-+ switch(_color_type){
- case PNG_COLOR_TYPE_GRAY:
- case PNG_COLOR_TYPE_PALETTE:
- channels = 1;
-@@ -101,71 +128,108 @@
- break;
- default:
- strcpy(info.szLastError,"unknown PNG color type");
-+#if PNG_LIBPNG_VER > 10399
-+ longjmp(png_jmpbuf(png_ptr), 1);
-+#else
- longjmp(png_ptr->jmpbuf, 1);
-+#endif
- }
-
- //find the right pixel depth used for cximage
-+#if PNG_LIBPNG_VER > 10399
-+ int pixel_depth = _bit_depth * png_get_channels(png_ptr,info_ptr);
-+#else
- int pixel_depth = info_ptr->pixel_depth;
-+#endif
- if (channels == 1 && pixel_depth>8) pixel_depth=8;
- if (channels == 2) pixel_depth=8;
- if (channels >= 3) pixel_depth=24;
-
-- if (!Create(info_ptr->width, info_ptr->height, pixel_depth, CXIMAGE_FORMAT_PNG)){
-+ if (!Create(_width, _height, pixel_depth, CXIMAGE_FORMAT_PNG)){
-+#if PNG_LIBPNG_VER > 10399
-+ longjmp(png_jmpbuf(png_ptr), 1);
-+#else
- longjmp(png_ptr->jmpbuf, 1);
-+#endif
- }
-
- /* get metrics */
-- switch (info_ptr->phys_unit_type)
-+ png_uint_32 _x_pixels_per_unit,_y_pixels_per_unit;
-+ int _phys_unit_type;
-+#if PNG_LIBPNG_VER > 10399
-+ png_get_pHYs(png_ptr,info_ptr,&_x_pixels_per_unit,&_y_pixels_per_unit,&_phys_unit_type);
-+#else
-+ _x_pixels_per_unit=info_ptr->x_pixels_per_unit;
-+ _y_pixels_per_unit=info_ptr->y_pixels_per_unit;
-+ _phys_unit_type=info_ptr->phys_unit_type;
-+#endif
-+ switch (_phys_unit_type)
- {
- case PNG_RESOLUTION_UNKNOWN:
-- SetXDPI(info_ptr->x_pixels_per_unit);
-- SetYDPI(info_ptr->y_pixels_per_unit);
-+ SetXDPI(_x_pixels_per_unit);
-+ SetYDPI(_y_pixels_per_unit);
- break;
- case PNG_RESOLUTION_METER:
-- SetXDPI((long)floor(info_ptr->x_pixels_per_unit * 254.0 / 10000.0 + 0.5));
-- SetYDPI((long)floor(info_ptr->y_pixels_per_unit * 254.0 / 10000.0 + 0.5));
-+ SetXDPI((long)floor(_x_pixels_per_unit * 254.0 / 10000.0 + 0.5));
-+ SetYDPI((long)floor(_y_pixels_per_unit * 254.0 / 10000.0 + 0.5));
- break;
- }
-
-- if (info_ptr->num_palette>0){
-- SetPalette((rgb_color*)info_ptr->palette,info_ptr->num_palette);
-- SetClrImportant(info_ptr->num_palette);
-- } else if (info_ptr->bit_depth ==2) { //<DP> needed for 2 bpp grayscale PNGs
-+ int _num_palette;
-+ png_colorp _palette;
-+#if PNG_LIBPNG_VER > 10399
-+ png_get_PLTE(png_ptr,info_ptr,&_palette,&_num_palette);
-+#else
-+ _num_palette=info_ptr->num_palette;
-+ _palette=info_ptr->palette;
-+#endif
-+ if (_num_palette>0){
-+ SetPalette((rgb_color*)_palette,_num_palette);
-+ SetClrImportant(_num_palette);
-+ } else if (_bit_depth ==2) { //<DP> needed for 2 bpp grayscale PNGs
- SetPaletteColor(0,0,0,0);
- SetPaletteColor(1,85,85,85);
- SetPaletteColor(2,170,170,170);
- SetPaletteColor(3,255,255,255);
- } else SetGrayPalette(); //<DP> needed for grayscale PNGs
-
-- int nshift = max(0,(info_ptr->bit_depth>>3)-1)<<3;
-+ int nshift = max(0,(_bit_depth>>3)-1)<<3;
-
-- if (info_ptr->num_trans!=0){ //palette transparency
-- if (info_ptr->num_trans==1){
-- if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE){
-+ png_bytep _trans_alpha;
-+ int _num_trans;
-+ png_color_16p _trans_color;
-+#if PNG_LIBPNG_VER > 10399
-+ png_get_tRNS(png_ptr,info_ptr,&_trans_alpha,&_num_trans,&_trans_color);
-+#else
-+ _num_trans=info_ptr->num_trans;
-+#endif
-+ if (_num_trans!=0){ //palette transparency
-+ if (_num_trans==1){
-+ if (_color_type == PNG_COLOR_TYPE_PALETTE){
- #if PNG_LIBPNG_VER > 10399
-- info.nBkgndIndex = info_ptr->trans_color.index;
-+ info.nBkgndIndex = _trans_color->index;
- #else
- info.nBkgndIndex = info_ptr->trans_values.index;
- #endif
- } else{
- #if PNG_LIBPNG_VER > 10399
-- info.nBkgndIndex = info_ptr->trans_color.gray>>nshift;
-+ info.nBkgndIndex = _trans_color->gray>>nshift;
- #else
- info.nBkgndIndex = info_ptr->trans_values.gray>>nshift;
- #endif
- }
- }
-- if (info_ptr->num_trans>1){
-+ if (_num_trans>1){
- RGBQUAD* pal=GetPalette();
- if (pal){
- DWORD ip;
-- for (ip=0;ip<min(head.biClrUsed,(unsigned long)info_ptr->num_trans);ip++)
-+ for (ip=0;ip<min(head.biClrUsed,(unsigned long)_num_trans);ip++)
- #if PNG_LIBPNG_VER > 10399
-- pal[ip].rgbReserved=info_ptr->trans_alpha[ip];
-+ pal[ip].rgbReserved=_trans_alpha[ip];
- #else
- pal[ip].rgbReserved=info_ptr->trans[ip];
- #endif
-- for (ip=info_ptr->num_trans;ip<head.biClrUsed;ip++){
-+ for (ip=_num_trans;ip<head.biClrUsed;ip++){
- pal[ip].rgbReserved=255;
- }
- info.bAlphaPaletteEnabled=true;
-@@ -174,14 +238,12 @@
- }
-
- if (channels == 3){ //check RGB binary transparency
-- png_bytep trans;
-- int num_trans;
-- png_color_16 *image_background;
-- if (png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &image_background)){
--#if PNG_LIBPNG_VER > 10399
-- info.nBkgndColor.rgbRed = (BYTE)(info_ptr->trans_color.red>>nshift);
-- info.nBkgndColor.rgbGreen = (BYTE)(info_ptr->trans_color.green>>nshift);
-- info.nBkgndColor.rgbBlue = (BYTE)(info_ptr->trans_color.blue>>nshift);
-+ /* seems unnecessary to call again, but the conditional must be important so... */
-+ if (png_get_tRNS(png_ptr,info_ptr,&_trans_alpha,&_num_trans,&_trans_color)){
-+#if PNG_LIBPNG_VER > 10399
-+ info.nBkgndColor.rgbRed = (BYTE)(_trans_color->red>>nshift);
-+ info.nBkgndColor.rgbGreen = (BYTE)(_trans_color->green>>nshift);
-+ info.nBkgndColor.rgbBlue = (BYTE)(_trans_color->blue>>nshift);
- #else
- info.nBkgndColor.rgbRed = (BYTE)(info_ptr->trans_values.red>>nshift);
- info.nBkgndColor.rgbGreen = (BYTE)(info_ptr->trans_values.green>>nshift);
-@@ -202,15 +264,24 @@
- }
-
- // <vho> - flip the RGB pixels to BGR (or RGBA to BGRA)
-- if (info_ptr->color_type & PNG_COLOR_MASK_COLOR){
-+ if (_color_type & PNG_COLOR_MASK_COLOR){
- png_set_bgr(png_ptr);
- }
-
- // <vho> - handle cancel
-- if (info.nEscape) longjmp(png_ptr->jmpbuf, 1);
-+ if (info.nEscape)
-+#if PNG_LIBPNG_VER > 10399
-+ longjmp(png_jmpbuf(png_ptr), 1);
-+#else
-+ longjmp(png_ptr->jmpbuf, 1);
-+#endif
-
- // row_bytes is the width x number of channels x (bit-depth / 8)
-+#if PNG_LIBPNG_VER > 10399
-+ row_pointers = new BYTE[png_get_rowbytes(png_ptr,info_ptr) + 8];
-+#else
- row_pointers = new BYTE[info_ptr->rowbytes + 8];
-+#endif
-
- // turn on interlace handling
- int number_passes = png_set_interlace_handling(png_ptr);
-@@ -221,8 +292,12 @@
- SetCodecOption(0);
- }
-
-- int chan_offset = info_ptr->bit_depth >> 3;
-+ int chan_offset = _bit_depth >> 3;
-+#if PNG_LIBPNG_VER > 10399
-+ int pixel_offset = (_bit_depth * png_get_channels(png_ptr,info_ptr)) >> 3;
-+#else
- int pixel_offset = info_ptr->pixel_depth >> 3;
-+#endif
-
- for (int pass=0; pass < number_passes; pass++) {
- iter.Upset();
-@@ -230,7 +305,12 @@
- do {
-
- // <vho> - handle cancel
-- if (info.nEscape) longjmp(png_ptr->jmpbuf, 1);
-+ if (info.nEscape)
-+#if PNG_LIBPNG_VER > 10399
-+ longjmp(png_jmpbuf(png_ptr), 1);
-+#else
-+ longjmp(png_ptr->jmpbuf, 1);
-+#endif
-
- #if CXIMAGE_SUPPORT_ALPHA // <vho>
- if (AlphaIsValid()) {
-@@ -241,7 +321,7 @@
- BYTE* prow= iter.GetRow(ay);
-
- //recover data from previous scan
-- if (info_ptr->interlace_type && pass>0 && pass!=7){
-+ if (_interlace_type && pass>0 && pass!=7){
- for(ax=0;ax<head.biWidth;ax++){
- long px = ax * pixel_offset;
- if (channels == 2){
-@@ -278,10 +358,14 @@
- #endif // CXIMAGE_SUPPORT_ALPHA // vho
- {
- //recover data from previous scan
-- if (info_ptr->interlace_type && pass>0){
-+ if (_interlace_type && pass>0){
-+#if PNG_LIBPNG_VER > 10399
-+ iter.GetRow(row_pointers, png_get_rowbytes(png_ptr,info_ptr));
-+#else
- iter.GetRow(row_pointers, info_ptr->rowbytes);
-+#endif
- //re-expand buffer for images with bit depth > 8
-- if (info_ptr->bit_depth > 8){
-+ if (_bit_depth > 8){
- for(long ax=(head.biWidth*channels-1);ax>=0;ax--)
- row_pointers[ax*chan_offset] = row_pointers[ax];
- }
-@@ -291,15 +375,19 @@
- png_read_row(png_ptr, row_pointers, NULL);
-
- //shrink 16 bit depth images down to 8 bits
-- if (info_ptr->bit_depth > 8){
-+ if (_bit_depth > 8){
- for(long ax=0;ax<(head.biWidth*channels);ax++)
- row_pointers[ax] = row_pointers[ax*chan_offset];
- }
-
- //copy the pixels
-+#if PNG_LIBPNG_VER > 10399
-+ iter.SetRow(row_pointers, png_get_rowbytes(png_ptr,info_ptr));
-+#else
- iter.SetRow(row_pointers, info_ptr->rowbytes);
-+#endif
- //<DP> expand 2 bpp images only in the last pass
-- if (info_ptr->bit_depth==2 && pass==(number_passes-1))
-+ if (_bit_depth==2 && pass==(number_passes-1))
- expand2to4bpp(iter.GetRow());
-
- //go on
-@@ -361,9 +449,13 @@
- /* Set error handling. REQUIRED if you aren't supplying your own
- * error hadnling functions in the png_create_write_struct() call.
- */
-+#if PNG_LIBPNG_VER > 10399
-+ if (setjmp(png_jmpbuf(png_ptr))){
-+#else
- if (setjmp(png_ptr->jmpbuf)){
- /* If we get here, we had a problem reading the file */
- if (info_ptr->palette) free(info_ptr->palette);
-+#endif
- png_destroy_write_struct(&png_ptr, (png_infopp)&info_ptr);
- cx_throw("Error saving PNG file");
- }
-@@ -372,9 +464,23 @@
- //png_init_io(png_ptr, hFile);
-
- // use custom I/O functions
-- png_set_write_fn(png_ptr,hFile,/*(png_rw_ptr)*/user_write_data,/*(png_flush_ptr)*/user_flush_data);
-+ png_set_write_fn(png_ptr,hFile,/*(png_rw_ptr)*/user_write_data,/*(png_flush_ptr)*/user_flush_data);
-
- /* set the file information here */
-+#if PNG_LIBPNG_VER > 10399
-+ /* use variables to hold the values so it isnt necessary to png_get them later */
-+ png_uint_32 _width,_height;
-+ int _bit_depth,_color_type,_interlace_type,_compression_type,_filter_type;
-+ png_byte _channels,_pixel_depth;
-+
-+ _width = GetWidth();
-+ _height = GetHeight();
-+ _pixel_depth = (BYTE)GetBpp();
-+ _channels = (GetBpp()>8) ? (BYTE)3: (BYTE)1;
-+ _bit_depth = (BYTE)(GetBpp()/_channels);
-+ _compression_type = PNG_COMPRESSION_TYPE_DEFAULT;
-+ _filter_type = PNG_FILTER_TYPE_DEFAULT;
-+#else
- info_ptr->width = GetWidth();
- info_ptr->height = GetHeight();
- info_ptr->pixel_depth = (BYTE)GetBpp();
-@@ -382,13 +488,22 @@
- info_ptr->bit_depth = (BYTE)(GetBpp()/info_ptr->channels);
- info_ptr->compression_type = info_ptr->filter_type = 0;
- info_ptr->valid = 0;
-+#endif
-
- switch(GetCodecOption(CXIMAGE_FORMAT_PNG)){
- case 1:
-+#if PNG_LIBPNG_VER > 10399
-+ _interlace_type = PNG_INTERLACE_ADAM7;
-+#else
- info_ptr->interlace_type = PNG_INTERLACE_ADAM7;
-+#endif
- break;
- default:
-+#if PNG_LIBPNG_VER > 10399
-+ _interlace_type = PNG_INTERLACE_NONE;
-+#else
- info_ptr->interlace_type = PNG_INTERLACE_NONE;
-+#endif
- }
-
- /* set compression level */
-@@ -398,22 +513,47 @@
-
- if (GetNumColors()){
- if (bGrayScale){
-+#if PNG_LIBPNG_VER > 10399
-+ _color_type = PNG_COLOR_TYPE_GRAY;
-+#else
- info_ptr->color_type = PNG_COLOR_TYPE_GRAY;
-+#endif
- } else {
-+#if PNG_LIBPNG_VER > 10399
-+ _color_type = PNG_COLOR_TYPE_PALETTE;
-+#else
- info_ptr->color_type = PNG_COLOR_TYPE_PALETTE;
-+#endif
- }
- } else {
-+#if PNG_LIBPNG_VER > 10399
-+ _color_type = PNG_COLOR_TYPE_RGB;
-+#else
- info_ptr->color_type = PNG_COLOR_TYPE_RGB;
-+#endif
- }
- #if CXIMAGE_SUPPORT_ALPHA
- if (AlphaIsValid()){
-+#if PNG_LIBPNG_VER > 10399
-+ _color_type |= PNG_COLOR_MASK_ALPHA;
-+ _channels++;
-+ _bit_depth = 8;
-+ _pixel_depth += 8;
-+#else
- info_ptr->color_type |= PNG_COLOR_MASK_ALPHA;
- info_ptr->channels++;
- info_ptr->bit_depth = 8;
- info_ptr->pixel_depth += 8;
-+#endif
- }
- #endif
-
-+#if PNG_LIBPNG_VER > 10399
-+ /* set the header here, since we're done modifying these values */
-+ png_set_IHDR(png_ptr,info_ptr,_width,_height,_bit_depth,_color_type,_interlace_type,
-+ _compression_type,_filter_type);
-+#endif
-+
- /* set background */
- png_color_16 image_background={ 0, 255, 255, 255, 0 };
- RGBQUAD tc = GetTransColor();
-@@ -427,22 +567,24 @@
- /* set metrics */
- png_set_pHYs(png_ptr, info_ptr, head.biXPelsPerMeter, head.biYPelsPerMeter, PNG_RESOLUTION_METER);
-
-+#if PNG_LIBPNG_VER <= 10399
- png_set_IHDR(png_ptr, info_ptr, info_ptr->width, info_ptr->height, info_ptr->bit_depth,
- info_ptr->color_type, info_ptr->interlace_type,
- PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
-+#endif
-
- //<DP> simple transparency
- if (info.nBkgndIndex >= 0){
-- info_ptr->num_trans = 1;
-- info_ptr->valid |= PNG_INFO_tRNS;
- #if PNG_LIBPNG_VER > 10399
-- info_ptr->trans_alpha = trans;
-- info_ptr->trans_color.index = (BYTE)info.nBkgndIndex;
-- info_ptr->trans_color.red = tc.rgbRed;
-- info_ptr->trans_color.green = tc.rgbGreen;
-- info_ptr->trans_color.blue = tc.rgbBlue;
-- info_ptr->trans_color.gray = info_ptr->trans_color.index;
-+ png_color_16 _trans_color;
-+ _trans_color.index = (BYTE)info.nBkgndIndex;
-+ _trans_color.red = tc.rgbRed;
-+ _trans_color.green = tc.rgbGreen;
-+ _trans_color.blue = tc.rgbBlue;
-+ _trans_color.gray = _trans_color.index;
- #else
-+ info_ptr->num_trans = 1;
-+ info_ptr->valid |= PNG_INFO_tRNS;
- info_ptr->trans = trans;
- info_ptr->trans_values.index = (BYTE)info.nBkgndIndex;
- info_ptr->trans_values.red = tc.rgbRed;
-@@ -454,34 +596,53 @@
- // the transparency indexes start from 0 for non grayscale palette
- if (!bGrayScale && head.biClrUsed && info.nBkgndIndex)
- SwapIndex(0,(BYTE)info.nBkgndIndex);
-+
-+#if PNG_LIBPNG_VER > 10399
-+ png_set_tRNS(png_ptr,info_ptr,(png_bytep)trans,1,&_trans_color);
-+#endif
- }
-
- /* set the palette if there is one */
-+#if PNG_LIBPNG_VER > 10399
-+ png_colorp _palette;
-+#endif
- if (GetPalette()){
-+#if PNG_LIBPNG_VER <= 10399
- if (!bGrayScale){
- info_ptr->valid |= PNG_INFO_PLTE;
- }
-+#endif
-
- int nc = GetClrImportant();
- if (nc==0) nc = GetNumColors();
-
-+ // copy the palette colors
-+#if PNG_LIBPNG_VER > 10399
-+ _palette = new png_color[nc];
-+#else
-+ info_ptr->palette = new png_color[nc];
-+ info_ptr->num_palette = (png_uint_16) nc;
-+#endif
-+ for (int i=0; i<nc; i++)
-+#if PNG_LIBPNG_VER > 10399
-+ GetPaletteColor(i, &_palette[i].red, &_palette[i].green, &_palette[i].blue);
-+
-+ png_set_PLTE(png_ptr,info_ptr,_palette,nc);
-+#else
-+ GetPaletteColor(i, &info_ptr->palette[i].red, &info_ptr->palette[i].green, &info_ptr->palette[i].blue);
-+#endif
-+
- if (info.bAlphaPaletteEnabled){
- for(WORD ip=0; ip<nc;ip++)
- trans[ip]=GetPaletteColor((BYTE)ip).rgbReserved;
-- info_ptr->num_trans = (WORD)nc;
-- info_ptr->valid |= PNG_INFO_tRNS;
- #if PNG_LIBPNG_VER > 10399
-- info_ptr->trans_alpha = trans;
-+ png_set_tRNS(png_ptr,info_ptr,(png_bytep)trans,nc,NULL);
- #else
-+ info_ptr->num_trans = (WORD)nc;
-+ info_ptr->valid |= PNG_INFO_tRNS;
- info_ptr->trans = trans;
- #endif
- }
--
-- // copy the palette colors
-- info_ptr->palette = new png_color[nc];
-- info_ptr->num_palette = (png_uint_16) nc;
-- for (int i=0; i<nc; i++)
-- GetPaletteColor(i, &info_ptr->palette[i].red, &info_ptr->palette[i].green, &info_ptr->palette[i].blue);
- }
-
- #if CXIMAGE_SUPPORT_ALPHA // <vho>
-@@ -495,8 +656,12 @@
- } } }
- #endif // CXIMAGE_SUPPORT_ALPHA // <vho>
-
-+#if PNG_LIBPNG_VER > 10399
-+ int row_size = max(info.dwEffWidth, (_width * _channels * _bit_depth / 8));
-+#else
- int row_size = max(info.dwEffWidth, info_ptr->width*info_ptr->channels*(info_ptr->bit_depth/8));
- info_ptr->rowbytes = row_size;
-+#endif
- BYTE *row_pointers = new BYTE[row_size];
-
- /* write the file information */
-@@ -514,7 +679,11 @@
- if (AlphaIsValid()){
- for (long ax=head.biWidth-1; ax>=0;ax--){
- c = BlindGetPixelColor(ax,ay);
-+#if PNG_LIBPNG_VER > 10399
-+ int px = ax * _channels;
-+#else
- int px = ax * info_ptr->channels;
-+#endif
- if (!bGrayScale){
- row_pointers[px++]=c.rgbRed;
- row_pointers[px++]=c.rgbGreen;
-@@ -529,7 +698,11 @@
- #endif //CXIMAGE_SUPPORT_ALPHA // <vho>
- {
- iter.GetRow(row_pointers, row_size);
-+#if PNG_LIBPNG_VER > 10399
-+ if (_color_type == PNG_COLOR_TYPE_RGB) //HACK BY OP
-+#else
- if (info_ptr->color_type == PNG_COLOR_TYPE_RGB) //HACK BY OP
-+#endif
- RGBtoBGR(row_pointers, row_size);
- png_write_row(png_ptr, row_pointers);
- }
-@@ -546,9 +719,14 @@
- png_write_end(png_ptr, info_ptr);
-
- /* if you malloced the palette, free it here */
-+#if PNG_LIBPNG_VER > 10399
-+ if (_palette){
-+ delete [] (_palette);
-+#else
- if (info_ptr->palette){
- delete [] (info_ptr->palette);
- info_ptr->palette = NULL;
-+#endif
- }
-
- /* clean up after the write, and free any memory allocated */
diff --git a/xbmc-11.0-dteirney-myth-0.25.patch b/xbmc-11.0-dteirney-myth-0.25.patch
new file mode 100644
index 0000000..dfd12d6
--- /dev/null
+++ b/xbmc-11.0-dteirney-myth-0.25.patch
@@ -0,0 +1,660 @@
+diff --git a/lib/cmyth/include/cmyth/cmyth.h b/lib/cmyth/include/cmyth/cmyth.h
+index 52f2f68..d8cb364 100644
+--- a/lib/cmyth/include/cmyth/cmyth.h
++++ b/lib/cmyth/include/cmyth/cmyth.h
+@@ -749,6 +749,20 @@ extern char *cmyth_proginfo_subtitle(cmyth_proginfo_t prog);
+ extern char *cmyth_proginfo_description(cmyth_proginfo_t prog);
+
+ /**
++ * Retrieve the season of a program.
++ * \param prog proginfo handle
++ * \return season
++ */
++extern unsigned short cmyth_proginfo_season(cmyth_proginfo_t prog);
++
++/**
++ * Retrieve the episode of a program.
++ * \param prog proginfo handle
++ * \return episode
++ */
++extern unsigned short cmyth_proginfo_episode(cmyth_proginfo_t prog);
++
++/**
+ * Retrieve the category of a program.
+ * \param prog proginfo handle
+ * \return null-terminated string
+@@ -805,6 +819,13 @@ extern char *cmyth_proginfo_seriesid(cmyth_proginfo_t prog);
+ extern long cmyth_proginfo_priority(cmyth_proginfo_t prog);
+
+ /**
++ * Retrieve the inetref of a program.
++ * \param prog proginfo handle
++ * \return null-terminated string
++ */
++extern char *cmyth_proginfo_inetref(cmyth_proginfo_t prog);
++
++/**
+ * Retrieve the critics rating (number of stars) of a program.
+ * \param prog proginfo handle
+ * \return null-terminated string
+diff --git a/lib/cmyth/libcmyth/bookmark.c b/lib/cmyth/libcmyth/bookmark.c
+index af7580f..4681c21 100644
+--- a/lib/cmyth/libcmyth/bookmark.c
++++ b/lib/cmyth/libcmyth/bookmark.c
+@@ -20,6 +20,7 @@
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
++#include <inttypes.h>
+ #include <errno.h>
+ #include <cmyth_local.h>
+
+@@ -59,7 +60,7 @@ long long cmyth_get_bookmark(cmyth_conn_t conn, cmyth_proginfo_t prog)
+ }
+ if ((r=cmyth_rcv_long_long(conn, &err, &ll, count)) < 0) {
+ cmyth_dbg(CMYTH_DBG_ERROR,
+- "%s: cmyth_rcv_longlong() failed (%d)\n",
++ "%s: cmyth_rcv_long_long() failed (%d)\n",
+ __FUNCTION__, r);
+ ret = err;
+ goto out;
+@@ -74,7 +75,7 @@ long long cmyth_get_bookmark(cmyth_conn_t conn, cmyth_proginfo_t prog)
+ int cmyth_set_bookmark(cmyth_conn_t conn, cmyth_proginfo_t prog, long long bookmark)
+ {
+ char *buf;
+- unsigned int len = CMYTH_TIMESTAMP_LEN + CMYTH_LONGLONG_LEN + 18;
++ unsigned int len = CMYTH_TIMESTAMP_LEN + CMYTH_LONGLONG_LEN * 2 + 18;
+ char resultstr[3];
+ int r,err;
+ int ret;
+@@ -85,8 +86,18 @@ int cmyth_set_bookmark(cmyth_conn_t conn, cmyth_proginfo_t prog, long long bookm
+ if (!buf) {
+ return -ENOMEM;
+ }
+- sprintf(buf,"%s %ld %s %lld %lld","SET_BOOKMARK",prog->proginfo_chanId,
+- start_ts_dt, bookmark >> 32,(bookmark & 0xffffffff));
++ if (conn->conn_version >= 66) {
++ /*
++ * Since protocol 66 mythbackend expects a single 64 bit integer rather than two 32 bit
++ * hi and lo integers.
++ */
++ sprintf(buf, "SET_BOOKMARK %ld %s %"PRIu64, prog->proginfo_chanId,
++ start_ts_dt, (int64_t)bookmark);
++ }
++ else {
++ sprintf(buf, "SET_BOOKMARK %ld %s %d %d", prog->proginfo_chanId,
++ start_ts_dt, (int32_t)(bookmark >> 32), (int32_t)(bookmark & 0xffffffff));
++ }
+ pthread_mutex_lock(&mutex);
+ if ((err = cmyth_send_message(conn,buf)) < 0) {
+ cmyth_dbg(CMYTH_DBG_ERROR,
+diff --git a/lib/cmyth/libcmyth/cmyth_local.h b/lib/cmyth/libcmyth/cmyth_local.h
+index 40bed11..f7b85fb 100644
+--- a/lib/cmyth/libcmyth/cmyth_local.h
++++ b/lib/cmyth/libcmyth/cmyth_local.h
+@@ -224,6 +224,8 @@ struct cmyth_proginfo {
+ char *proginfo_title;
+ char *proginfo_subtitle;
+ char *proginfo_description;
++ unsigned short proginfo_season; /* new in V67 */
++ unsigned short proginfo_episode; /* new in V67 */
+ char *proginfo_category;
+ long proginfo_chanId;
+ char *proginfo_chanstr;
+@@ -258,6 +260,7 @@ struct cmyth_proginfo {
+ char *proginfo_chan_output_filters; /* new in V8 */
+ char *proginfo_seriesid; /* new in V8 */
+ char *proginfo_programid; /* new in V12 */
++ char *proginfo_inetref; /* new in V67 */
+ cmyth_timestamp_t proginfo_lastmodified; /* new in V12 */
+ char *proginfo_stars; /* new in V12 */
+ cmyth_timestamp_t proginfo_originalairdate; /* new in V12 */
+diff --git a/lib/cmyth/libcmyth/connection.c b/lib/cmyth/libcmyth/connection.c
+index 9d5fb0d..9d13721 100644
+--- a/lib/cmyth/libcmyth/connection.c
++++ b/lib/cmyth/libcmyth/connection.c
+@@ -56,6 +56,15 @@ static myth_protomap_t protomap[] = {
+ {62, "78B5631E"},
+ {63, "3875641D"},
+ {64, "8675309J"},
++ {65, "D2BB94C2"},
++ {66, "0C0FFEE0"},
++ {67, "0G0G0G0"},
++ {68, "90094EAD"},
++ {69, "63835135"},
++ {70, "53153836"},
++ {71, "05e82186"},
++ {72, "D78EFD6F"},
++ {73, "D7FE8D6F"},
+ {0, 0}
+ };
+
+@@ -527,7 +536,7 @@ cmyth_conn_connect_file(cmyth_proginfo_t prog, cmyth_conn_t control,
+ int err = 0;
+ int count = 0;
+ int r;
+- int ann_size = sizeof("ANN FileTransfer []:[][]:[]");
++ int ann_size = sizeof("ANN FileTransfer 0[]:[][]:[]");
+ cmyth_file_t ret = NULL;
+
+ if (!prog) {
+@@ -575,6 +584,12 @@ cmyth_conn_connect_file(cmyth_proginfo_t prog, cmyth_conn_t control,
+ myth_host, prog->proginfo_port, buflen);
+ goto shut;
+ }
++ /*
++ * Explicitly set the conn version to the control version as cmyth_connect() doesn't and some of
++ * the cmyth_rcv_* functions expect it to be the same as the protocol version used by mythbackend.
++ */
++ conn->conn_version = control->conn_version;
++
+ ann_size += strlen(prog->proginfo_pathname) + strlen(my_hostname);
+ announcement = malloc(ann_size);
+ if (!announcement) {
+@@ -584,7 +599,7 @@ cmyth_conn_connect_file(cmyth_proginfo_t prog, cmyth_conn_t control,
+ goto shut;
+ }
+ if (control->conn_version >= 44) {
+- sprintf(announcement, "ANN FileTransfer %s[]:[]%s[]:[]",
++ sprintf(announcement, "ANN FileTransfer %s 0[]:[]%s[]:[]", // write = false
+ my_hostname, prog->proginfo_pathname);
+ }
+ else {
+@@ -631,7 +646,7 @@ cmyth_conn_connect_file(cmyth_proginfo_t prog, cmyth_conn_t control,
+ r = cmyth_rcv_u_long_long(conn, &err, &ret->file_length, count);
+ if (err) {
+ cmyth_dbg(CMYTH_DBG_ERROR,
+- "%s: (length) cmyth_rcv_longlong() failed (%d)\n",
++ "%s: (length) cmyth_rcv_u_long_long() failed (%d)\n",
+ __FUNCTION__, err);
+ goto shut;
+ }
+@@ -683,7 +698,7 @@ cmyth_conn_connect_path(char* path, cmyth_conn_t control,
+ int err = 0;
+ int count = 0;
+ int r, port;
+- int ann_size = sizeof("ANN FileTransfer []:[][]:[]");
++ int ann_size = sizeof("ANN FileTransfer 0[]:[][]:[]");
+ struct sockaddr_in addr;
+ socklen_t addr_size = sizeof(addr);
+ cmyth_file_t ret = NULL;
+@@ -716,6 +731,11 @@ cmyth_conn_connect_path(char* path, cmyth_conn_t control,
+ __FUNCTION__, host, port, buflen);
+ goto shut;
+ }
++ /*
++ * Explicitly set the conn version to the control version as cmyth_connect() doesn't and some of
++ * the cmyth_rcv_* functions expect it to be the same as the protocol version used by mythbackend.
++ */
++ conn->conn_version = control->conn_version;
+
+ ann_size += strlen(path) + strlen(my_hostname) + strlen(sgToGetFrom) + 6;
+ announcement = malloc(ann_size);
+@@ -771,7 +792,7 @@ cmyth_conn_connect_path(char* path, cmyth_conn_t control,
+ r = cmyth_rcv_u_long_long(conn, &err, &ret->file_length, count);
+ if (err) {
+ cmyth_dbg(CMYTH_DBG_ERROR,
+- "%s: (length) cmyth_rcv_longlong() failed (%d)\n",
++ "%s: (length) cmyth_rcv_u_long_long() failed (%d)\n",
+ __FUNCTION__, err);
+ goto shut;
+ }
+diff --git a/lib/cmyth/libcmyth/file.c b/lib/cmyth/libcmyth/file.c
+index 74408ed..640f299 100644
+--- a/lib/cmyth/libcmyth/file.c
++++ b/lib/cmyth/libcmyth/file.c
+@@ -20,6 +20,7 @@
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <errno.h>
++#include <inttypes.h>
+ #ifndef _MSC_VER
+ #include <sys/socket.h>
+ #endif
+@@ -449,14 +450,28 @@ cmyth_file_seek(cmyth_file_t file, long long offset, int whence)
+
+ pthread_mutex_lock(&mutex);
+
+- snprintf(msg, sizeof(msg),
+- "QUERY_FILETRANSFER %ld[]:[]SEEK[]:[]%d[]:[]%d[]:[]%d[]:[]%d[]:[]%d",
+- file->file_id,
+- (int32_t)(offset >> 32),
+- (int32_t)(offset & 0xffffffff),
+- whence,
+- (int32_t)(file->file_pos >> 32),
+- (int32_t)(file->file_pos & 0xffffffff));
++ if (file->file_control->conn_version >= 66) {
++ /*
++ * Since protocol 66 mythbackend expects to receive a single 64 bit integer rather than
++ * two 32 bit hi and lo integers.
++ */
++ snprintf(msg, sizeof(msg),
++ "QUERY_FILETRANSFER %ld[]:[]SEEK[]:[]%"PRIu64"[]:[]%d[]:[]%"PRIu64,
++ file->file_id,
++ (int64_t)offset,
++ whence,
++ (int64_t)file->file_pos);
++ }
++ else {
++ snprintf(msg, sizeof(msg),
++ "QUERY_FILETRANSFER %ld[]:[]SEEK[]:[]%d[]:[]%d[]:[]%d[]:[]%d[]:[]%d",
++ file->file_id,
++ (int32_t)(offset >> 32),
++ (int32_t)(offset & 0xffffffff),
++ whence,
++ (int32_t)(file->file_pos >> 32),
++ (int32_t)(file->file_pos & 0xffffffff));
++ }
+
+ if ((err = cmyth_send_message(file->file_control, msg)) < 0) {
+ cmyth_dbg(CMYTH_DBG_ERROR,
+diff --git a/lib/cmyth/libcmyth/proginfo.c b/lib/cmyth/libcmyth/proginfo.c
+index b175637..f6f9347 100644
+--- a/lib/cmyth/libcmyth/proginfo.c
++++ b/lib/cmyth/libcmyth/proginfo.c
+@@ -116,6 +116,9 @@ cmyth_proginfo_destroy(cmyth_proginfo_t p)
+ if (p->proginfo_programid) {
+ ref_release(p->proginfo_programid);
+ }
++ if (p->proginfo_inetref) {
++ ref_release(p->proginfo_inetref);
++ }
+ if (p->proginfo_stars) {
+ ref_release(p->proginfo_stars);
+ }
+@@ -217,6 +220,8 @@ cmyth_proginfo_create(void)
+ ret->proginfo_title = NULL;
+ ret->proginfo_subtitle = NULL;
+ ret->proginfo_description = NULL;
++ ret->proginfo_season = 0;
++ ret->proginfo_episode = 0;
+ ret->proginfo_category = NULL;
+ ret->proginfo_chanId = 0;
+ ret->proginfo_chanstr = NULL;
+@@ -250,6 +255,7 @@ cmyth_proginfo_create(void)
+ ret->proginfo_chan_output_filters = NULL;
+ ret->proginfo_seriesid = NULL;
+ ret->proginfo_programid = NULL;
++ ret->proginfo_inetref = NULL;
+ ret->proginfo_stars = NULL;
+ ret->proginfo_version = 12;
+ ret->proginfo_hasairdate = 0;
+@@ -307,6 +313,8 @@ cmyth_proginfo_dup(cmyth_proginfo_t p)
+ ret->proginfo_title = ref_hold(p->proginfo_title);
+ ret->proginfo_subtitle = ref_hold(p->proginfo_subtitle);
+ ret->proginfo_description = ref_hold(p->proginfo_description);
++ ret->proginfo_season = p->proginfo_season;
++ ret->proginfo_episode = p->proginfo_episode;
+ ret->proginfo_category = ref_hold(p->proginfo_category);
+ ret->proginfo_chanId = p->proginfo_chanId;
+ ret->proginfo_chanstr = ref_hold(p->proginfo_chanstr);
+@@ -340,6 +348,7 @@ cmyth_proginfo_dup(cmyth_proginfo_t p)
+ ret->proginfo_chan_output_filters = ref_hold(p->proginfo_chan_output_filters);
+ ret->proginfo_seriesid = ref_hold(p->proginfo_seriesid);
+ ret->proginfo_programid = ref_hold(p->proginfo_programid);
++ ret->proginfo_inetref = ref_hold(p->proginfo_inetref);
+ ret->proginfo_stars = ref_hold(p->proginfo_stars);
+ ret->proginfo_version = p->proginfo_version;
+ ret->proginfo_hasairdate = p->proginfo_hasairdate;
+@@ -386,7 +395,7 @@ delete_command(cmyth_conn_t control, cmyth_proginfo_t prog, char *cmd)
+ char *buf;
+ unsigned int len = ((2 * CMYTH_LONGLONG_LEN) +
+ (6 * CMYTH_TIMESTAMP_LEN) +
+- (14 * CMYTH_LONG_LEN));
++ (16 * CMYTH_LONG_LEN));
+ char start_ts[CMYTH_TIMESTAMP_LEN + 1];
+ char end_ts[CMYTH_TIMESTAMP_LEN + 1];
+ char rec_start_ts[CMYTH_TIMESTAMP_LEN + 1];
+@@ -415,8 +424,12 @@ delete_command(cmyth_conn_t control, cmyth_proginfo_t prog, char *cmd)
+ len += strlen(S(prog->proginfo_url));
+ len += strlen(S(prog->proginfo_hostname));
+ len += strlen(S(prog->proginfo_playgroup));
++ len += strlen(S(prog->proginfo_seriesid));
++ len += strlen(S(prog->proginfo_programid));
++ len += strlen(S(prog->proginfo_inetref));
+ len += strlen(S(prog->proginfo_recpriority_2));
+ len += strlen(S(prog->proginfo_storagegroup));
++ len += strlen(S(prog->proginfo_prodyear));
+
+ buf = alloca(len + 1+2048);
+ if (!buf) {
+@@ -462,6 +475,10 @@ delete_command(cmyth_conn_t control, cmyth_proginfo_t prog, char *cmd)
+ sprintf(buf + strlen(buf), "%s[]:[]", S(prog->proginfo_title));
+ sprintf(buf + strlen(buf), "%s[]:[]", S(prog->proginfo_subtitle));
+ sprintf(buf + strlen(buf), "%s[]:[]", S(prog->proginfo_description));
++ if (control->conn_version >= 67) {
++ sprintf(buf + strlen(buf), "%u[]:[]", prog->proginfo_season);
++ sprintf(buf + strlen(buf), "%u[]:[]", prog->proginfo_episode);
++ }
+ sprintf(buf + strlen(buf), "%s[]:[]", S(prog->proginfo_category));
+ sprintf(buf + strlen(buf), "%ld[]:[]", prog->proginfo_chanId);
+ sprintf(buf + strlen(buf), "%s[]:[]", S(prog->proginfo_chanstr));
+@@ -504,6 +521,9 @@ delete_command(cmyth_conn_t control, cmyth_proginfo_t prog, char *cmd)
+ sprintf(buf + strlen(buf), "%s[]:[]", S(prog->proginfo_chan_output_filters));
+ sprintf(buf + strlen(buf), "%s[]:[]", S(prog->proginfo_seriesid));
+ sprintf(buf + strlen(buf), "%s[]:[]", S(prog->proginfo_programid));
++ if (control->conn_version >= 67) {
++ sprintf(buf + strlen(buf), "%s[]:[]", S(prog->proginfo_inetref));
++ }
+ sprintf(buf + strlen(buf), "%s[]:[]", lastmodified);
+ sprintf(buf + strlen(buf), "%s[]:[]", S(prog->proginfo_stars));
+ sprintf(buf + strlen(buf), "%s[]:[]", originalairdate);
+@@ -773,6 +793,24 @@ cmyth_proginfo_description(cmyth_proginfo_t prog)
+ return ref_hold(prog->proginfo_description);
+ }
+
++unsigned short
++cmyth_proginfo_season(cmyth_proginfo_t prog)
++{
++ if (!prog) {
++ return 0;
++ }
++ return prog->proginfo_season;
++}
++
++unsigned short
++cmyth_proginfo_episode(cmyth_proginfo_t prog)
++{
++ if (!prog) {
++ return 0;
++ }
++ return prog->proginfo_episode;
++}
++
+ /*
+ * cmyth_proginfo_category(cmyth_proginfo_t prog)
+ *
+@@ -827,6 +865,17 @@ cmyth_proginfo_programid(cmyth_proginfo_t prog)
+ }
+
+ char *
++cmyth_proginfo_inetref(cmyth_proginfo_t prog)
++{
++ if (!prog) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: NULL inetref\n",
++ __FUNCTION__);
++ return NULL;
++ }
++ return ref_hold(prog->proginfo_inetref);
++}
++
++char *
+ cmyth_proginfo_stars(cmyth_proginfo_t prog)
+ {
+ if (!prog) {
+@@ -1260,7 +1309,7 @@ fill_command(cmyth_conn_t control, cmyth_proginfo_t prog, char *cmd)
+ char *buf;
+ unsigned int len = ((2 * CMYTH_LONGLONG_LEN) +
+ (6 * CMYTH_TIMESTAMP_LEN) +
+- (14 * CMYTH_LONG_LEN));
++ (16 * CMYTH_LONG_LEN));
+ char start_ts[CMYTH_TIMESTAMP_LEN + 1];
+ char end_ts[CMYTH_TIMESTAMP_LEN + 1];
+ char rec_start_ts[CMYTH_TIMESTAMP_LEN + 1];
+@@ -1288,6 +1337,9 @@ fill_command(cmyth_conn_t control, cmyth_proginfo_t prog, char *cmd)
+ len += strlen(S(prog->proginfo_url));
+ len += strlen(S(prog->proginfo_hostname));
+ len += strlen(S(prog->proginfo_playgroup));
++ len += strlen(S(prog->proginfo_seriesid));
++ len += strlen(S(prog->proginfo_programid));
++ len += strlen(S(prog->proginfo_inetref));
+ len += strlen(S(prog->proginfo_recpriority_2));
+ len += strlen(S(prog->proginfo_storagegroup));
+ len += strlen(S(prog->proginfo_prodyear));
+@@ -1336,6 +1388,10 @@ fill_command(cmyth_conn_t control, cmyth_proginfo_t prog, char *cmd)
+ sprintf(buf + strlen(buf), "%s[]:[]", S(prog->proginfo_title));
+ sprintf(buf + strlen(buf), "%s[]:[]", S(prog->proginfo_subtitle));
+ sprintf(buf + strlen(buf), "%s[]:[]", S(prog->proginfo_description));
++ if (control->conn_version >= 67) {
++ sprintf(buf + strlen(buf), "%u[]:[]", prog->proginfo_season);
++ sprintf(buf + strlen(buf), "%u[]:[]", prog->proginfo_episode);
++ }
+ sprintf(buf + strlen(buf), "%s[]:[]", S(prog->proginfo_category));
+ sprintf(buf + strlen(buf), "%ld[]:[]", prog->proginfo_chanId);
+ sprintf(buf + strlen(buf), "%s[]:[]", S(prog->proginfo_chanstr));
+@@ -1378,6 +1434,9 @@ fill_command(cmyth_conn_t control, cmyth_proginfo_t prog, char *cmd)
+ sprintf(buf + strlen(buf), "%s[]:[]", S(prog->proginfo_chan_output_filters));
+ sprintf(buf + strlen(buf), "%s[]:[]", S(prog->proginfo_seriesid));
+ sprintf(buf + strlen(buf), "%s[]:[]", S(prog->proginfo_programid));
++ if (control->conn_version >= 67) {
++ sprintf(buf + strlen(buf), "%s[]:[]", S(prog->proginfo_inetref));
++ }
+ sprintf(buf + strlen(buf), "%s[]:[]", lastmodified);
+ sprintf(buf + strlen(buf), "%s[]:[]", S(prog->proginfo_stars));
+ sprintf(buf + strlen(buf), "%s[]:[]", originalairdate);
+diff --git a/lib/cmyth/libcmyth/proglist.c b/lib/cmyth/libcmyth/proglist.c
+index 28e742c..fe75577 100644
+--- a/lib/cmyth/libcmyth/proglist.c
++++ b/lib/cmyth/libcmyth/proglist.c
+@@ -315,6 +315,7 @@ cmyth_proglist_get_list(cmyth_conn_t conn,
+ cmyth_proglist_t
+ cmyth_proglist_get_all_recorded(cmyth_conn_t control)
+ {
++ char query[32];
+ cmyth_proglist_t proglist = cmyth_proglist_create();
+
+ if (proglist == NULL) {
+@@ -324,8 +325,14 @@ cmyth_proglist_get_all_recorded(cmyth_conn_t control)
+ return NULL;
+ }
+
++ if (control->conn_version < 65) {
++ strcpy(query, "QUERY_RECORDINGS Play");
++ }
++ else {
++ strcpy(query, "QUERY_RECORDINGS Ascending");
++ }
+ if (cmyth_proglist_get_list(control, proglist,
+- "QUERY_RECORDINGS Play",
++ query,
+ __FUNCTION__) < 0) {
+ cmyth_dbg(CMYTH_DBG_ERROR,
+ "%s: cmyth_proglist_get_list() failed\n",
+diff --git a/lib/cmyth/libcmyth/socket.c b/lib/cmyth/libcmyth/socket.c
+index f1f9a45..552b88d 100644
+--- a/lib/cmyth/libcmyth/socket.c
++++ b/lib/cmyth/libcmyth/socket.c
+@@ -882,23 +882,38 @@ cmyth_rcv_long_long(cmyth_conn_t conn, int *err, long long *buf, int count)
+ *err = EINVAL;
+ return 0;
+ }
+- consumed = cmyth_rcv_u_long(conn, err, &hi, count);
+- if (*err) {
+- cmyth_dbg(CMYTH_DBG_ERROR,
+- "%s: cmyth_rcv_long_long() failed (%d)\n",
+- __FUNCTION__, consumed);
+- return consumed;
++
++ if (conn->conn_version >= 66) {
++ /*
++ * Since protocol 66 mythbackend now sends a single 64 bit integer rather than two hi and lo
++ * 32 bit integers for ALL 64 bit values.
++ */
++ consumed = cmyth_rcv_int64(conn, err, &val, count);
++ if (*err) {
++ cmyth_dbg(CMYTH_DBG_ERROR,
++ "%s: cmyth_rcv_int64() failed (%d)\n",
++ __FUNCTION__, consumed);
++ return consumed;
++ }
+ }
+- consumed += cmyth_rcv_u_long(conn, err, &lo, count-consumed);
+- if (*err) {
+- cmyth_dbg(CMYTH_DBG_ERROR,
+- "%s: cmyth_rcv_long_long() failed (%d)\n",
+- __FUNCTION__, consumed);
+- return consumed;
++ else {
++ consumed = cmyth_rcv_u_long(conn, err, &hi, count);
++ if (*err) {
++ cmyth_dbg(CMYTH_DBG_ERROR,
++ "%s: cmyth_rcv_u_long_long() failed (%d)\n",
++ __FUNCTION__, consumed);
++ return consumed;
++ }
++ consumed += cmyth_rcv_u_long(conn, err, &lo, count-consumed);
++ if (*err) {
++ cmyth_dbg(CMYTH_DBG_ERROR,
++ "%s: cmyth_rcv_u_long_long() failed (%d)\n",
++ __FUNCTION__, consumed);
++ return consumed;
++ }
++ val = (((long long)hi) << 32) | ((long long)(lo & 0xFFFFFFFF));
+ }
+
+- val = (((long long)hi) << 32) | ((long long)(lo & 0xFFFFFFFF));
+-
+ *err = 0;
+ *buf = val;
+
+@@ -1172,6 +1187,7 @@ cmyth_rcv_ulong_long(cmyth_conn_t conn, int *err,
+ unsigned long long *buf, int count)
+ {
+ unsigned long long val;
++ long long val64;
+ unsigned long hi, lo;
+ int consumed;
+ int tmp;
+@@ -1186,23 +1202,45 @@ cmyth_rcv_ulong_long(cmyth_conn_t conn, int *err,
+ *err = EINVAL;
+ return 0;
+ }
+- consumed = cmyth_rcv_u_long(conn, err, &hi, count);
+- if (*err) {
+- cmyth_dbg(CMYTH_DBG_ERROR,
+- "%s: cmyth_rcv_ulong_long() failed (%d)\n",
+- __FUNCTION__, consumed);
+- return consumed;
++
++ if (conn->conn_version >= 66) {
++ /*
++ * Since protocol 66 mythbackend now sends a single 64 bit integer rather than two hi and lo
++ * 32 bit integers for ALL 64 bit values.
++ */
++ consumed = cmyth_rcv_int64(conn, err, &val64, count);
++ if (*err) {
++ cmyth_dbg(CMYTH_DBG_ERROR,
++ "%s: cmyth_rcv_int64() failed (%d)\n",
++ __FUNCTION__, consumed);
++ return consumed;
++ }
++ if (val64 < 0) {
++ cmyth_dbg(CMYTH_DBG_ERROR,
++ "%s: cmyth_rcv_int64() failed as signed 64 bit integer received\n",
++ __FUNCTION__, consumed);
++ *err = EINVAL;
++ return consumed;
++ }
++ val = (unsigned long long)val64;
+ }
+- consumed += cmyth_rcv_u_long(conn, err, &lo, count);
+- if (*err) {
+- cmyth_dbg(CMYTH_DBG_ERROR,
+- "%s: cmyth_rcv_ulong_long() failed (%d)\n",
+- __FUNCTION__, consumed);
+- return consumed;
++ else {
++ consumed = cmyth_rcv_u_long(conn, err, &hi, count);
++ if (*err) {
++ cmyth_dbg(CMYTH_DBG_ERROR,
++ "%s: cmyth_rcv_u_long_long() failed (%d)\n",
++ __FUNCTION__, consumed);
++ return consumed;
++ }
++ consumed += cmyth_rcv_u_long(conn, err, &lo, count);
++ if (*err) {
++ cmyth_dbg(CMYTH_DBG_ERROR,
++ "%s: cmyth_rcv_u_long_long() failed (%d)\n",
++ __FUNCTION__, consumed);
++ return consumed;
++ }
++ val = (((unsigned long long)hi) << 32) | ((unsigned long long)(lo & 0xFFFFFFFF));
+ }
+-
+- val = (((unsigned long long)hi) << 32) | ((unsigned long long)(lo & 0xFFFFFFFF));
+-
+ *err = 0;
+ *buf = val;
+
+@@ -1485,7 +1523,8 @@ cmyth_rcv_proginfo(cmyth_conn_t conn, int *err, cmyth_proginfo_t buf,
+
+ if (buf->proginfo_version >= 57) {
+ /*
+- * Myth now sends a single 64bit int, rather than 2 32bit ints
++ * Since protocol 57 mythbackend now sends a single 64 bit integer rather than two 32 bit
++ * hi and lo integers for the proginfo length.
+ */
+ rcv_64 = &cmyth_rcv_int64;
+ } else {
+@@ -1536,6 +1575,29 @@ cmyth_rcv_proginfo(cmyth_conn_t conn, int *err, cmyth_proginfo_t buf,
+ ref_release(buf->proginfo_description);
+ buf->proginfo_description = ref_strdup(tmp_str);
+
++ if (buf->proginfo_version >= 67) {
++ /*
++ * Get season and episode (unsigned int)
++ */
++ consumed = cmyth_rcv_ushort(conn, err,
++ &buf->proginfo_season, count);
++ count -= consumed;
++ total += consumed;
++ if (*err) {
++ failed = "cmyth_rcv_ushort";
++ goto fail;
++ }
++
++ consumed = cmyth_rcv_ushort(conn, err,
++ &buf->proginfo_episode, count);
++ count -= consumed;
++ total += consumed;
++ if (*err) {
++ failed = "cmyth_rcv_ushort";
++ goto fail;
++ }
++ }
++
+ /*
+ * Get proginfo_category (string)
+ */
+@@ -1649,7 +1711,7 @@ cmyth_rcv_proginfo(cmyth_conn_t conn, int *err, cmyth_proginfo_t buf,
+ count -= consumed;
+ total += consumed;
+ if (*err) {
+- failed = "cmyth_rcv_long_long";
++ failed = "rcv_64";
+ goto fail;
+ }
+
+@@ -2013,7 +2075,7 @@ cmyth_rcv_proginfo(cmyth_conn_t conn, int *err, cmyth_proginfo_t buf,
+ count -= consumed;
+ total += consumed;
+ if (*err) {
+- failed = "cmyth_rcv_timestamp";
++ failed = "cmyth_rcv_string";
+ goto fail;
+ }
+ if (buf->proginfo_programid)
+@@ -2021,6 +2083,23 @@ cmyth_rcv_proginfo(cmyth_conn_t conn, int *err, cmyth_proginfo_t buf,
+ buf->proginfo_programid = ref_strdup(tmp_str);
+ }
+
++ if (buf->proginfo_version >= 67) {
++ /*
++ * Get inetref (string)
++ */
++ consumed = cmyth_rcv_string(conn, err, tmp_str,
++ sizeof(tmp_str) - 1, count);
++ count -= consumed;
++ total += consumed;
++ if (*err) {
++ failed = "cmyth_rcv_string";
++ goto fail;
++ }
++ if (buf->proginfo_inetref)
++ ref_release(buf->proginfo_inetref);
++ buf->proginfo_inetref = ref_strdup(tmp_str);
++ }
++
+ if (buf->proginfo_version >= 12) {
+ /*
+ * Get lastmodified (string)
diff --git a/xbmc-11.0-libpng-1.5-fix-plt-trn-get.patch b/xbmc-11.0-libpng-1.5-fix-plt-trn-get.patch
deleted file mode 100644
index 7e6ce67..0000000
--- a/xbmc-11.0-libpng-1.5-fix-plt-trn-get.patch
+++ /dev/null
@@ -1,51 +0,0 @@
-
-Update libpng 1.5 patch: check return values of png_get_PLTE() and
-png_get_tRNS() before using the values to avoid using uninitialized values.
-
---- a/lib/cximage-6.0/CxImage/ximapng.cpp
-+++ b/lib/cximage-6.0/CxImage/ximapng.cpp
-@@ -178,12 +178,14 @@ bool CxImagePNG::Decode(CxFile *hFile)
- int _num_palette;
- png_colorp _palette;
- #if PNG_LIBPNG_VER > 10399
-- png_get_PLTE(png_ptr,info_ptr,&_palette,&_num_palette);
-+ png_uint_32 _palette_ret;
-+ _palette_ret = png_get_PLTE(png_ptr,info_ptr,&_palette,&_num_palette);
-+ if (_palette_ret && _num_palette>0){
- #else
- _num_palette=info_ptr->num_palette;
- _palette=info_ptr->palette;
--#endif
- if (_num_palette>0){
-+#endif
- SetPalette((rgb_color*)_palette,_num_palette);
- SetClrImportant(_num_palette);
- } else if (_bit_depth ==2) { //<DP> needed for 2 bpp grayscale PNGs
-@@ -199,11 +201,13 @@ bool CxImagePNG::Decode(CxFile *hFile)
- int _num_trans;
- png_color_16p _trans_color;
- #if PNG_LIBPNG_VER > 10399
-- png_get_tRNS(png_ptr,info_ptr,&_trans_alpha,&_num_trans,&_trans_color);
-+ png_uint_32 _trans_ret;
-+ _trans_ret = png_get_tRNS(png_ptr,info_ptr,&_trans_alpha,&_num_trans,&_trans_color);
-+ if (_trans_ret && _num_trans!=0){ //palette transparency
- #else
- _num_trans=info_ptr->num_trans;
--#endif
- if (_num_trans!=0){ //palette transparency
-+#endif
- if (_num_trans==1){
- if (_color_type == PNG_COLOR_TYPE_PALETTE){
- #if PNG_LIBPNG_VER > 10399
-@@ -219,7 +223,11 @@ bool CxImagePNG::Decode(CxFile *hFile)
- #endif
- }
- }
-+#if PNG_LIBPNG_VER > 10399
-+ if (_num_trans>1 && _trans_alpha!=NULL){
-+#else
- if (_num_trans>1){
-+#endif
- RGBQUAD* pal=GetPalette();
- if (pal){
- DWORD ip;
diff --git a/xbmc-11.0-tsp-Eden-pvr.patch b/xbmc-11.0-tsp-Eden-pvr.patch
new file mode 100644
index 0000000..2fa4c15
--- /dev/null
+++ b/xbmc-11.0-tsp-Eden-pvr.patch
@@ -0,0 +1,126913 @@
+diff --git a/.gitattributes b/.gitattributes
+new file mode 100644
+index 0000000..ffc7f0e
+--- /dev/null
++++ b/.gitattributes
+@@ -0,0 +1 @@
++*.pbxproj -crlf -diff
+diff --git a/.gitignore b/.gitignore
+index bcc3307..a82fba6 100644
+--- a/.gitignore
++++ b/.gitignore
+@@ -42,6 +42,7 @@ config.log
+ *.vcxproj.*.user
+ *.vcxproj.user
+ *.obj
++*.idb
+ *ReSharper*
+ *.idb
+
+@@ -62,16 +63,22 @@ config.log
+ .libs/
+ .deps/
+
++# Eclipse project files. Not needed as they are generated in two clicks if needed.
++.settings
++.project
++.cproject
++
+ # /
+ /.dummy
+ /.dummy.in
+ /Makefile
+ /Makefile.include
+ /aclocal.m4
+-/autom4te.cache/
++autom4te.cache
+ /build-aux/config.guess
+ /build-aux/config.sub
+ /build-aux/install-sh
++/build-aux/ltmain.sh
+ /build-aux/missing
+ /build-aux/ltmain.sh
+ /autotools
+@@ -110,6 +117,7 @@ config.log
+ /addons/visualization.waveform/Waveform_win32.vis
+ /addons/visualization.itunes/iTunes.mvis
+ /addons/script.module.pil/
++/addons/pvr.*/*.pvr
+
+ # /xbmc/guilib/
+ /xbmc/guilib/Makefile
+@@ -145,6 +153,17 @@ config.log
+ /lib/asap/xbmc/xbmc_asap.res
+
+
++# /lib/addons/
++/lib/addons/library.xbmc.addon/Makefile
++/lib/addons/library.xbmc.gui/Makefile
++/lib/addons/library.xbmc.pvr/Makefile
++/lib/addons/library.xbmc.addon/project/VS2010Express/Release
++/lib/addons/library.xbmc.addon/project/VS2010Express/Debug
++/lib/addons/library.xbmc.gui/project/VS2010Express/Release
++/lib/addons/library.xbmc.gui/project/VS2010Express/Debug
++/lib/addons/library.xbmc.pvr/project/VS2010Express/Release
++/lib/addons/library.xbmc.pvr/project/VS2010Express/Debug
++
+ # /lib/cmyth/
+ lib/cmyth/Makefile
+
+@@ -212,6 +231,11 @@ lib/cmyth/Makefile
+ /lib/libass/libass/Makefile.in
+ /lib/libass/libtool
+ /lib/libass/ltmain.sh
++/lib/libass/m4/libtool.m4
++/lib/libass/m4/ltoptions.m4
++/lib/libass/m4/ltsugar.m4
++/lib/libass/m4/ltversion.m4
++/lib/libass/m4/lt~obsolete.m4
+ /lib/libass/missing
+ /lib/libass/shave/libtool.m4
+ /lib/libass/shave/ltoptions.m4
+@@ -295,6 +319,7 @@ lib/cmyth/Makefile
+ /lib/libapetag/config.h
+ /lib/libapetag/install-sh
+ /lib/libapetag/libtool
++/lib/libapetag/m4/lt~obsolete.m4
+ /lib/libapetag/stamp-h1
+
+ # /project
+@@ -1123,6 +1148,17 @@ lib/cmyth/Makefile
+ /lib/libXDAAP/libXDAAP_win32/Debug
+ /lib/libXDAAP/libXDAAP_win32/Release
+
++# /xbmc/osx/
++/xbmc/osx/Makefile
++
++# /portable_data
++/portable_data
++
++# /xbmc/pvrclients
++/xbmc/pvrclients/Makefile.include
++/xbmc/pvrclients/*/.dependencies
++/xbmc/pvrclients/*/Makefile
++
+ /xbmc/screensavers/Makefile
+
+ /xbmc/screensavers/rsxs-0.9/Makefile
+@@ -1220,3 +1256,21 @@ lib/cmyth/Makefile
+ /xbmc/visualizations/XBMCProjectM/libprojectM/config.inp
+
+ /xbmc/win32/git_rev.h
++
++/addons/library.xbmc.addon/libXBMC_addon.dll
++/addons/library.xbmc.addon/libXBMC_addon.lib
++/addons/library.xbmc.gui/libXBMC_gui.dll
++/addons/library.xbmc.gui/libXBMC_gui.lib
++/addons/library.xbmc.pvr/libXBMC_pvr.dll
++/addons/library.xbmc.pvr/libXBMC_pvr.lib
++
++/addons/pvr.team-mediaportal.tvserver/XBMC_MPTV.lib
++/addons/pvr.hts/XBMC_Tvheadend_win32.lib
++/addons/pvr.vdr.streamdev/XBMC_VDR_win32.lib
++
++/xbmc/pvrclients/MediaPortal/project/VS2010Express/Debug
++/xbmc/pvrclients/MediaPortal/project/VS2010Express/Release
++/xbmc/pvrclients/tvheadend/project/VS2010Express/Debug
++/xbmc/pvrclients/tvheadend/project/VS2010Express/Release
++/xbmc/pvrclients/vdr-streamdev/project/VS2010Express/Debug
++/xbmc/pvrclients/vdr-streamdev/project/VS2010Express/Release
+diff --git a/Makefile.in b/Makefile.in
+index 9d850e4..bfd10e0 100755
+--- a/Makefile.in
++++ b/Makefile.in
+@@ -42,6 +42,7 @@ DIRECTORY_ARCHIVES=$(DVDPLAYER_ARCHIVES) \
+ xbmc/cores/playercorefactory/playercorefactory.a \
+ xbmc/dbwrappers/dbwrappers.a \
+ xbmc/dialogs/dialogs.a \
++ xbmc/epg/epg.a \
+ xbmc/filesystem/MusicDatabaseDirectory/musicdatabasedirectory.a \
+ xbmc/filesystem/VideoDatabaseDirectory/videodatabasedirectory.a \
+ xbmc/filesystem/filesystem.a \
+@@ -70,6 +71,13 @@ DIRECTORY_ARCHIVES=$(DVDPLAYER_ARCHIVES) \
+ xbmc/playlists/playlists.a \
+ xbmc/powermanagement/powermanagement.a \
+ xbmc/programs/programs.a \
++ xbmc/pvr/addons/pvraddons.a \
++ xbmc/pvr/channels/pvrchannels.a \
++ xbmc/pvr/dialogs/pvrdialogs.a \
++ xbmc/pvr/pvr.a \
++ xbmc/pvr/recordings/pvrrecordings.a \
++ xbmc/pvr/timers/pvrtimers.a \
++ xbmc/pvr/windows/pvrwindows.a \
+ xbmc/rendering/rendering.a \
+ xbmc/settings/settings.a \
+ xbmc/storage/storage.a \
+@@ -157,6 +165,19 @@ ifneq (@DISABLE_GOOM@,1)
+ VIS_DIRS+=xbmc/visualizations/Goom
+ endif
+
++PVR_DIRS=\
++ xbmc/pvrclients/MediaPortal \
++ xbmc/pvrclients/mythtv \
++ xbmc/pvrclients/mythtv-cmyth \
++ xbmc/pvrclients/vdr-vnsi \
++ xbmc/pvrclients/tvheadend \
++ xbmc/pvrclients/pvr-demo
++
++LIBADDON_DIRS=\
++ lib/addons/library.xbmc.addon \
++ lib/addons/library.xbmc.pvr \
++ lib/addons/library.xbmc.gui \
++
+ CONFLUENCE_MEDIA=addons/skin.confluence/media
+ SKIN_DIRS=$(CONFLUENCE_MEDIA)
+
+@@ -166,7 +187,7 @@ SKIN_DIRS+=$(TOUCHED_MEDIA)
+ endif
+
+ DIRS= $(BIN_DIRS) $(EC_DIRS) $(XBMCTEX_DIRS) $(DVDPCODECS_DIRS) $(PAPCODECS_DIRS) \
+- $(LIB_DIRS) $(SS_DIRS) $(VIS_DIRS) $(SKIN_DIRS)
++ $(LIB_DIRS) $(SS_DIRS) $(VIS_DIRS) $(PVR_DIRS) $(LIBADDON_DIRS) $(SKIN_DIRS)
+
+ LIBS=@LIBS@
+ CFLAGS=@CFLAGS@
+@@ -187,8 +208,8 @@ all : Makefile externals xbmc.bin xbmc-xrandr skins
+
+ include Makefile.include
+
+-.PHONY : dllloader exports visualizations screensavers eventclients papcodecs \
+- dvdpcodecs imagelib codecs externals force skins
++.PHONY : dllloader exports pvrclients visualizations screensavers eventclients \
++ papcodecs dvdpcodecs imagelib codecs externals force libaddon skins
+
+ # hack targets to keep build system up to date
+ Makefile : config.status $(addsuffix .in, $(AUTOGENERATED_MAKEFILES))
+@@ -249,6 +270,17 @@ visualizations: $(VIS_DIRS)
+
+ screensavers: $(SS_DIRS)
+
++pvrclients: exports
++ $(MAKE) -C xbmc/pvrclients/MediaPortal
++ $(MAKE) -C xbmc/pvrclients/mythtv
++ $(MAKE) -C xbmc/pvrclients/mythtv-cmyth
++ $(MAKE) -C xbmc/pvrclients/vdr-vnsi
++ $(MAKE) -C xbmc/pvrclients/tvheadend
++ $(MAKE) -C xbmc/pvrclients/pvr-demo
++libaddon: exports
++ $(MAKE) -C lib/addons/library.xbmc.addon
++ $(MAKE) -C lib/addons/library.xbmc.gui
++ $(MAKE) -C lib/addons/library.xbmc.pvr
+ libpython: dllloader
+ $(MAKE) -C xbmc/interfaces/python
+ $(MAKE) -C xbmc/interfaces/python/xbmcmodule
+@@ -294,17 +326,16 @@ imagelib: dllloader
+
+ codecs: papcodecs dvdpcodecs
+ libs: cmyth libid3tag imagelib libexif system/libcpluff-@ARCH@.so
+-externals: codecs libs visualizations screensavers
++externals: libaddon codecs libs pvrclients visualizations screensavers
+
+ xcode_depends: \
+- codecs libs visualizations screensavers eventclients skins \
++ codecs libs pvrclients visualizations screensavers eventclients libaddon skins \
+ lib/libsquish/libsquish.a \
+ lib/libapetag/.libs/libapetag.a \
+ lib/libRTV/librtv.a \
+ lib/libXDAAP/libxdaap.a \
+ lib/SlingboxLib/SlingboxLib.a
+
+-
+ OBJSXBMC =$(DIRECTORY_ARCHIVES)
+ OBJSXBMC+=lib/libapetag/.libs/libapetag.a
+
+@@ -317,6 +348,8 @@ DYNOBJSXBMC= \
+ xbmc/cores/DllLoader/exports/exports.a \
+ xbmc/settings/settings.a \
+ xbmc/video/video.a \
++ xbmc/pvr/addons/pvraddons.a \
++ xbmc/pvr/windows/pvrwindows.a \
+ xbmc/guilib/guilib.a # must be dynamic to avoid linker errors
+
+ OBJSXBMC:=$(filter-out $(DYNOBJSXBMC), $(OBJSXBMC))
+@@ -373,13 +406,13 @@ install-arch:
+ @# Arch dependent files
+ ifeq ($(findstring freebsd,@ARCH@), freebsd)
+ @find -E system addons -type f -not -iregex ".*svn.*" \
+- -iregex ".*@ARCH@.*|.*\.vis|.*\.xbs" \
++ -iregex ".*@ARCH@.*|.*\.pvr|.*\.vis|.*\.xbs" \
+ -exec sh -c "install -d \"$(DESTDIR)$(libdir)/xbmc/\`dirname '{}'\`\"" \; \
+ -and \
+ -exec install "{}" $(DESTDIR)$(libdir)/xbmc/"{}" \; \
+ -exec printf " -- %-75.75s\r" "{}" \;
+ else
+- @find system addons -regextype posix-extended -type f -not -iregex ".*svn.*" -iregex ".*@ARCH@.*|.*\.vis|.*\.xbs" -exec install -D "{}" $(DESTDIR)$(libdir)/xbmc/"{}" \; -printf " -- %-75.75f\r"
++ @find system addons -regextype posix-extended -type f -not -iregex ".*svn.*" -iregex ".*@ARCH@.*|.*\.pvr|.*\.vis|.*\.xbs" -exec install -D "{}" $(DESTDIR)$(libdir)/xbmc/"{}" \; -printf " -- %-75.75f\r"
+ endif
+
+ install-scripts:
+@@ -402,13 +435,14 @@ install-datas: install-scripts
+ @# Arch independent files
+ ifeq ($(findstring bsd,@ARCH@), bsd)
+ @find -E addons language media sounds userdata system -type f \
++ -not -iregex ".*@ARCH@.*|.*\.pvr|.*\.vis|.*\.xbs|.*svn.*|.*\.so|.*\.dll" \
+ -not -iregex ".*@ARCH@.*|.*\.vis|.*\.xbs|.*svn.*|.*\.so|.*\.dll" \
+ -exec sh -c "install -d \"$(DESTDIR)$(datarootdir)/xbmc/\`dirname '{}'\`\"" \; \
+ -and \
+ -exec install -m 0644 "{}" $(DESTDIR)$(datarootdir)/xbmc/"{}" \; \
+ -exec printf " -- %-75.75s\r" "{}" \;
+ else
+- @find addons language media sounds userdata system -regextype posix-extended -type f -not -iregex ".*@ARCH@.*|.*\.vis|.*\.xbs|.*svn.*|.*\.so|.*\.dll" -exec install -D -m 0644 "{}" $(DESTDIR)$(datarootdir)/xbmc/"{}" \; -printf " -- %-75.75f\r"
++ @find addons language media sounds userdata system -regextype posix-extended -type f -not -iregex ".*@ARCH@.*|.*\.pvr|.*\.vis|.*\.xbs|.*svn.*|.*\.so|.*\.dll" -exec install -D -m 0644 "{}" $(DESTDIR)$(datarootdir)/xbmc/"{}" \; -printf " -- %-75.75f\r"
+ endif
+ @# Icons and links
+ @install -d $(DESTDIR)$(datarootdir)/applications
+@@ -425,6 +459,8 @@ uninstall:
+ @rm -rf $(DESTDIR)$(datarootdir)/xbmc $(DESTDIR)$(bindir)/xbmc
+ @rm -rf $(DESTDIR)$(bindir)/xbmc-standalone
+ @rm -rf $(DESTDIR)$(datarootdir)/xsessions/XBMC.desktop
++ @rm -rf $(libdir)/libXBMC_*
++ @rm -rf $(prefix)/include/xbmc
+ @echo "Done!"
+
+ clean-xbmc.bin:
+@@ -444,8 +480,12 @@ clean-screensavers:
+ for d in $(SS_DIRS); do if test -f $$d/Makefile; then $(MAKE) -C $$d clean; fi; done
+ clean-visualisations:
+ for d in $(VIS_DIRS); do if test -f $$d/Makefile; then $(MAKE) -C $$d clean; fi; done
++clean-pvrclients:
++ for d in $(PVR_DIRS); do if test -f $$d/Makefile; then $(MAKE) -C $$d clean; fi; done
++clean-libaddons:
++ for d in $(LIBADDON_DIRS); do if test -f $$d/Makefile; then $(MAKE) -C $$d clean; fi; done
+
+ clean-codecs: clean-dvdpcodecs clean-papcodecs
+
+ clean-externals: clean-codecs clean-eventclients clean-xbmctex clean-libs \
+- clean-screensavers clean-visualisations
++ clean-pvrclients clean-screensavers clean-visualisations clean-libaddons
+diff --git a/XBMC-ATV2.xcodeproj/project.pbxproj b/XBMC-ATV2.xcodeproj/project.pbxproj
+index c606ca0..f1737df 100644
+Binary files a/XBMC-ATV2.xcodeproj/project.pbxproj and b/XBMC-ATV2.xcodeproj/project.pbxproj differ
+diff --git a/XBMC-IOS.xcodeproj/project.pbxproj b/XBMC-IOS.xcodeproj/project.pbxproj
+index 9ba2d9f..cc04f37 100644
+Binary files a/XBMC-IOS.xcodeproj/project.pbxproj and b/XBMC-IOS.xcodeproj/project.pbxproj differ
+diff --git a/XBMC.xcodeproj/project.pbxproj b/XBMC.xcodeproj/project.pbxproj
+index 114d104..66f0833 100644
+Binary files a/XBMC.xcodeproj/project.pbxproj and b/XBMC.xcodeproj/project.pbxproj differ
+diff --git a/addons/library.xbmc.addon/dlfcn-win32.cpp b/addons/library.xbmc.addon/dlfcn-win32.cpp
+new file mode 100644
+index 0000000..a6cf2c2
+--- /dev/null
++++ b/addons/library.xbmc.addon/dlfcn-win32.cpp
+@@ -0,0 +1,263 @@
++/*
++ * dlfcn-win32
++ * Copyright (c) 2007 Ramiro Polla
++ *
++ * 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 <windows.h>
++#include <stdio.h>
++
++#include "dlfcn-win32.h"
++
++/* Note:
++ * MSDN says these functions are not thread-safe. We make no efforts to have
++ * any kind of thread safety.
++ */
++
++/* I have no special reason to have set MAX_GLOBAL_OBJECTS to this value. Any
++ * comments are welcome.
++ */
++#define MAX_OBJECTS 255
++
++static HMODULE global_objects[MAX_OBJECTS];
++
++/* This function adds an object to the list of global objects.
++ * The implementation is very simple and slow.
++ * TODO: should failing this function be enough to fail the call to dlopen( )?
++ */
++static void global_object_add( HMODULE hModule )
++{
++ int i;
++
++ for( i = 0 ; i < MAX_OBJECTS ; i++ )
++ {
++ if( !global_objects[i] )
++ {
++ global_objects[i] = hModule;
++ break;
++ }
++ }
++}
++
++static void global_object_rem( HMODULE hModule )
++{
++ int i;
++
++ for( i = 0 ; i < MAX_OBJECTS ; i++ )
++ {
++ if( global_objects[i] == hModule )
++ {
++ global_objects[i] = 0;
++ break;
++ }
++ }
++}
++
++/* Argument to last function. Used in dlerror( ) */
++static char last_name[MAX_PATH];
++
++static int copy_string( char *dest, int dest_size, const char *src )
++{
++ int i = 0;
++
++ if( src && dest )
++ {
++ for( i = 0 ; i < dest_size-1 ; i++ )
++ {
++ if( !src[i] )
++ break;
++ else
++ dest[i] = src[i];
++ }
++ }
++ dest[i] = '\0';
++
++ return i;
++}
++
++void *dlopen( const char *file, int mode )
++{
++ HMODULE hModule;
++ UINT uMode;
++
++ /* Do not let Windows display the critical-error-handler message box */
++ uMode = SetErrorMode( SEM_FAILCRITICALERRORS );
++
++ if( file == 0 )
++ {
++ /* Save NULL pointer for error message */
++ _snprintf_s( last_name, MAX_PATH, MAX_PATH, "0x%p", file );
++
++ /* POSIX says that if the value of file is 0, a handle on a global
++ * symbol object must be provided. That object must be able to access
++ * all symbols from the original program file, and any objects loaded
++ * with the RTLD_GLOBAL flag.
++ * The return value from GetModuleHandle( ) allows us to retrieve
++ * symbols only from the original program file. For objects loaded with
++ * the RTLD_GLOBAL flag, we create our own list later on.
++ */
++ hModule = GetModuleHandle( NULL );
++ }
++ else
++ {
++ char lpFileName[MAX_PATH];
++ int i;
++
++ /* MSDN says backslashes *must* be used instead of forward slashes. */
++ for( i = 0 ; i < sizeof(lpFileName)-1 ; i++ )
++ {
++ if( !file[i] )
++ break;
++ else if( file[i] == '/' )
++ lpFileName[i] = '\\';
++ else
++ lpFileName[i] = file[i];
++ }
++ lpFileName[i] = '\0';
++
++ /* Save file name for error message */
++ copy_string( last_name, sizeof(last_name), lpFileName );
++
++ /* POSIX says the search path is implementation-defined.
++ * LOAD_WITH_ALTERED_SEARCH_PATH is used to make it behave more closely
++ * to UNIX's search paths (start with system folders instead of current
++ * folder).
++ */
++ hModule = LoadLibraryEx( (LPSTR) lpFileName, NULL,
++ LOAD_WITH_ALTERED_SEARCH_PATH );
++ /* If the object was loaded with RTLD_GLOBAL, add it to list of global
++ * objects, so that its symbols may be retrieved even if the handle for
++ * the original program file is passed. POSIX says that if the same
++ * file is specified in multiple invocations, and any of them are
++ * RTLD_GLOBAL, even if any further invocations use RTLD_LOCAL, the
++ * symbols will remain global.
++ */
++
++ if( hModule && (mode & RTLD_GLOBAL) )
++ global_object_add( hModule );
++ }
++
++ /* Return to previous state of the error-mode bit flags. */
++ SetErrorMode( uMode );
++
++ return (void *) hModule;
++}
++
++int dlclose( void *handle )
++{
++ HMODULE hModule = (HMODULE) handle;
++ BOOL ret;
++
++ /* Save handle for error message */
++ _snprintf_s( last_name, MAX_PATH, MAX_PATH, "0x%p", handle );
++
++ ret = FreeLibrary( hModule );
++
++ /* If the object was loaded with RTLD_GLOBAL, remove it from list of global
++ * objects.
++ */
++ if( ret )
++ global_object_rem( hModule );
++
++ /* dlclose's return value in inverted in relation to FreeLibrary's. */
++ ret = !ret;
++
++ return (int) ret;
++}
++
++void *dlsym( void *handle, const char *name )
++{
++ FARPROC symbol;
++ HMODULE myhandle = (HMODULE) handle;
++
++ /* Save symbol name for error message */
++ copy_string( last_name, sizeof(last_name), name );
++
++ symbol = GetProcAddress( myhandle, name );
++#if 0
++ if( symbol == NULL )
++ {
++ HMODULE hModule;
++
++ /* If the handle for the original program file is passed, also search
++ * in all globally loaded objects.
++ */
++
++ hModule = GetModuleHandle( NULL );
++
++ if( hModule == handle )
++ {
++ int i;
++
++ for( i = 0 ; i < MAX_OBJECTS ; i++ )
++ {
++ if( global_objects[i] != 0 )
++ {
++ symbol = GetProcAddress( global_objects[i], name );
++ if( symbol != NULL )
++ break;
++ }
++ }
++ }
++
++
++ CloseHandle( hModule );
++ }
++#endif
++ return (void*) symbol;
++}
++
++char *dlerror( void )
++{
++ DWORD dwMessageId;
++ /* POSIX says this function doesn't have to be thread-safe, so we use one
++ * static buffer.
++ * MSDN says the buffer cannot be larger than 64K bytes, so we set it to
++ * the limit.
++ */
++ static char lpBuffer[65535];
++ DWORD ret;
++
++ dwMessageId = GetLastError( );
++
++ if( dwMessageId == 0 )
++ return NULL;
++
++ /* Format error message to:
++ * "<argument to function that failed>": <Windows localized error message>
++ */
++ ret = copy_string( lpBuffer, sizeof(lpBuffer), "\"" );
++ ret += copy_string( lpBuffer+ret, sizeof(lpBuffer)-ret, last_name );
++ ret += copy_string( lpBuffer+ret, sizeof(lpBuffer)-ret, "\": " );
++ ret += FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwMessageId,
++ MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
++ lpBuffer+ret, sizeof(lpBuffer)-ret, NULL );
++
++ if( ret > 1 )
++ {
++ /* POSIX says the string must not have trailing <newline> */
++ if( lpBuffer[ret-2] == '\r' && lpBuffer[ret-1] == '\n' )
++ lpBuffer[ret-2] = '\0';
++ }
++
++ /* POSIX says that invoking dlerror( ) a second time, immediately following
++ * a prior invocation, shall result in NULL being returned.
++ */
++ SetLastError(0);
++
++ return lpBuffer;
++}
++
+diff --git a/addons/library.xbmc.addon/dlfcn-win32.h b/addons/library.xbmc.addon/dlfcn-win32.h
+new file mode 100644
+index 0000000..f906343
+--- /dev/null
++++ b/addons/library.xbmc.addon/dlfcn-win32.h
+@@ -0,0 +1,46 @@
++#pragma once
++/*
++ * dlfcn-win32
++ * Copyright (c) 2007 Ramiro Polla
++ *
++ * 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
++ */
++
++#ifndef DLFCN_H
++#define DLFCN_H
++
++/* POSIX says these are implementation-defined.
++ * To simplify use with Windows API, we treat them the same way.
++ */
++
++#define RTLD_LAZY 0
++#define RTLD_NOW 0
++
++#define RTLD_GLOBAL (1 << 1)
++#define RTLD_LOCAL (1 << 2)
++
++/* These two were added in The Open Group Base Specifications Issue 6.
++ * Note: All other RTLD_* flags in any dlfcn.h are not standard compliant.
++ */
++
++#define RTLD_DEFAULT 0
++#define RTLD_NEXT 0
++
++void *dlopen ( const char *file, int mode );
++int dlclose( void *handle );
++void *dlsym ( void *handle, const char *name );
++char *dlerror( void );
++
++#endif /* DLFCN-WIN32_H */
+diff --git a/addons/library.xbmc.addon/libXBMC_addon.h b/addons/library.xbmc.addon/libXBMC_addon.h
+new file mode 100644
+index 0000000..de727b7
+--- /dev/null
++++ b/addons/library.xbmc.addon/libXBMC_addon.h
+@@ -0,0 +1,192 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <string>
++#include <vector>
++#include <string.h>
++#include <stdlib.h>
++#include <stdio.h>
++
++#ifdef _WIN32 // windows
++#include "dlfcn-win32.h"
++#define ADDON_DLL "\\library.xbmc.addon\\libXBMC_addon" ADDON_HELPER_EXT
++#define ADDON_HELPER_PLATFORM "win32"
++#define ADDON_HELPER_EXT ".dll"
++#else
++#if defined(__APPLE__) // osx
++#define ADDON_HELPER_PLATFORM "osx"
++#if defined(__POWERPC__)
++#define ADDON_HELPER_ARCH "powerpc"
++#elif defined(__arm__)
++#define ADDON_HELPER_ARCH "arm"
++#else
++#define ADDON_HELPER_ARCH "x86"
++#endif
++#else // linux
++#define ADDON_HELPER_PLATFORM "linux"
++#if defined(__x86_64__)
++#define ADDON_HELPER_ARCH "x86_64"
++#elif defined(_POWERPC)
++#define ADDON_HELPER_ARCH "powerpc"
++#elif defined(_POWERPC64)
++#define ADDON_HELPER_ARCH "powerpc64"
++#elif defined(_ARMEL)
++#define ADDON_HELPER_ARCH "arm"
++#elif defined(_MIPSEL)
++#define ADDON_HELPER_ARCH "mipsel"
++#else
++#define ADDON_HELPER_ARCH "i486"
++#endif
++#endif
++#include <dlfcn.h> // linux+osx
++#define ADDON_HELPER_EXT ".so"
++#define ADDON_DLL "/library.xbmc.addon/libXBMC_addon-" ADDON_HELPER_ARCH "-" ADDON_HELPER_PLATFORM ADDON_HELPER_EXT
++#endif
++
++#ifdef LOG_DEBUG
++#undef LOG_DEBUG
++#endif
++#ifdef LOG_INFO
++#undef LOG_INFO
++#endif
++#ifdef LOG_NOTICE
++#undef LOG_NOTICE
++#endif
++#ifdef LOG_ERROR
++#undef LOG_ERROR
++#endif
++
++namespace ADDON
++{
++ typedef enum addon_log
++ {
++ LOG_DEBUG,
++ LOG_INFO,
++ LOG_NOTICE,
++ LOG_ERROR
++ } addon_log_t;
++
++ typedef enum queue_msg
++ {
++ QUEUE_INFO,
++ QUEUE_WARNING,
++ QUEUE_ERROR
++ } queue_msg_t;
++
++ class CHelper_libXBMC_addon
++ {
++ public:
++ CHelper_libXBMC_addon()
++ {
++ m_libXBMC_addon = NULL;
++ m_Handle = NULL;
++ }
++
++ ~CHelper_libXBMC_addon()
++ {
++ if (m_libXBMC_addon)
++ {
++ XBMC_unregister_me();
++ dlclose(m_libXBMC_addon);
++ }
++ }
++
++ bool RegisterMe(void *Handle)
++ {
++ m_Handle = Handle;
++
++ std::string libBasePath;
++ libBasePath = ((cb_array*)m_Handle)->libPath;
++ libBasePath += ADDON_DLL;
++
++ m_libXBMC_addon = dlopen(libBasePath.c_str(), RTLD_LAZY);
++ if (m_libXBMC_addon == NULL)
++ {
++ fprintf(stderr, "Unable to load %s\n", dlerror());
++ return false;
++ }
++
++ XBMC_register_me = (int (*)(void *HANDLE))
++ dlsym(m_libXBMC_addon, "XBMC_register_me");
++ if (XBMC_register_me == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ XBMC_unregister_me = (void (*)())
++ dlsym(m_libXBMC_addon, "XBMC_unregister_me");
++ if (XBMC_unregister_me == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ Log = (void (*)(const addon_log_t loglevel, const char *format, ... ))
++ dlsym(m_libXBMC_addon, "XBMC_log");
++ if (Log == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ GetSetting = (bool (*)(const char* settingName, void *settingValue))
++ dlsym(m_libXBMC_addon, "XBMC_get_setting");
++ if (GetSetting == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ QueueNotification = (void (*)(const queue_msg_t loglevel, const char *format, ... ))
++ dlsym(m_libXBMC_addon, "XBMC_queue_notification");
++ if (QueueNotification == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ UnknownToUTF8 = (void (*)(std::string &str))
++ dlsym(m_libXBMC_addon, "XBMC_unknown_to_utf8");
++ if (UnknownToUTF8 == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ GetLocalizedString = (const char* (*)(int dwCode))
++ dlsym(m_libXBMC_addon, "XBMC_get_localized_string");
++ if (GetLocalizedString == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ GetDVDMenuLanguage = (const char* (*)())
++ dlsym(m_libXBMC_addon, "XBMC_get_dvd_menu_language");
++ if (GetDVDMenuLanguage == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ GetLocalizedDate = (const char* (*)(time_t time, bool bLongDate, bool bWithShortNames))
++ dlsym(m_libXBMC_addon, "XBMC_get_localized_date");
++ if (GetLocalizedDate == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ GetLocalizedTime = (const char* (*)(time_t time, bool bWithSeconds))
++ dlsym(m_libXBMC_addon, "XBMC_get_localized_time");
++ if (GetLocalizedTime == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ return XBMC_register_me(m_Handle) > 0;
++ }
++
++ void (*Log)(const addon_log_t loglevel, const char *format, ... );
++ bool (*GetSetting)(const char* settingName, void *settingValue);
++ void (*QueueNotification)(const queue_msg_t type, const char *format, ... );
++ void (*UnknownToUTF8)(std::string &str);
++ const char* (*GetLocalizedString)(int dwCode);
++ const char* (*GetDVDMenuLanguage)();
++ const char* (*GetLocalizedDate)(time_t time, bool bLongDate, bool bWithShortNames);
++ const char* (*GetLocalizedTime)(time_t time, bool bWithSeconds);
++
++ protected:
++ int (*XBMC_register_me)(void *HANDLE);
++ void (*XBMC_unregister_me)();
++
++ private:
++ void *m_libXBMC_addon;
++ void *m_Handle;
++ struct cb_array
++ {
++ const char* libPath;
++ };
++ };
++};
+diff --git a/addons/library.xbmc.gui/libXBMC_gui.h b/addons/library.xbmc.gui/libXBMC_gui.h
+new file mode 100644
+index 0000000..bb9d658
+--- /dev/null
++++ b/addons/library.xbmc.gui/libXBMC_gui.h
+@@ -0,0 +1,352 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <string>
++#include <vector>
++#include <string.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include "../library.xbmc.addon/libXBMC_addon.h"
++
++typedef void* GUIHANDLE;
++
++#ifdef _WIN32
++#define GUI_HELPER_DLL "\\library.xbmc.gui\\libXBMC_gui" ADDON_HELPER_EXT
++#else
++#define GUI_HELPER_DLL "/library.xbmc.gui/libXBMC_gui-" ADDON_HELPER_ARCH "-" ADDON_HELPER_PLATFORM ADDON_HELPER_EXT
++#endif
++
++#define ADDON_ACTION_PREVIOUS_MENU 10
++#define ADDON_ACTION_CLOSE_DIALOG 51
++
++class CAddonGUIWindow;
++class CAddonGUISpinControl;
++class CAddonGUIListContainer;
++class CAddonGUIRadioButton;
++class CAddonGUIProgressControl;
++class CAddonListItem;
++
++class CHelper_libXBMC_gui
++{
++public:
++ CHelper_libXBMC_gui()
++ {
++ m_libXBMC_gui = NULL;
++ m_Handle = NULL;
++ }
++
++ ~CHelper_libXBMC_gui()
++ {
++ if (m_libXBMC_gui)
++ {
++ GUI_unregister_me();
++ dlclose(m_libXBMC_gui);
++ }
++ }
++
++ bool RegisterMe(void *Handle)
++ {
++ m_Handle = Handle;
++
++ std::string libBasePath;
++ libBasePath = ((cb_array*)m_Handle)->libPath;
++ libBasePath += GUI_HELPER_DLL;
++
++ m_libXBMC_gui = dlopen(libBasePath.c_str(), RTLD_LAZY);
++ if (m_libXBMC_gui == NULL)
++ {
++ fprintf(stderr, "Unable to load %s\n", dlerror());
++ return false;
++ }
++
++ GUI_register_me = (int (*)(void *HANDLE))
++ dlsym(m_libXBMC_gui, "GUI_register_me");
++ if (GUI_register_me == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ GUI_unregister_me = (void (*)())
++ dlsym(m_libXBMC_gui, "GUI_unregister_me");
++ if (GUI_unregister_me == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ Lock = (void (*)())
++ dlsym(m_libXBMC_gui, "GUI_lock");
++ if (Lock == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ Unlock = (void (*)())
++ dlsym(m_libXBMC_gui, "GUI_unlock");
++ if (Unlock == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ GetScreenHeight = (int (*)())
++ dlsym(m_libXBMC_gui, "GUI_get_screen_height");
++ if (GetScreenHeight == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ GetScreenWidth = (int (*)())
++ dlsym(m_libXBMC_gui, "GUI_get_screen_width");
++ if (GetScreenWidth == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ GetVideoResolution = (int (*)())
++ dlsym(m_libXBMC_gui, "GUI_get_video_resolution");
++ if (GetVideoResolution == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ Dialog_showYesNo = (int (*)(const char* heading, const char* line0, const char* line1, const char* line2, int* bCanceled, const char* noLabel, const char* yesLabel))
++ dlsym(m_libXBMC_gui, "GUI_Dialog_ShowYesNo");
++ if (Dialog_showYesNo == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ Window_create = (CAddonGUIWindow* (*)(const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog))
++ dlsym(m_libXBMC_gui, "GUI_Window_create");
++ if (Window_create == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ Window_destroy = (void (*)(CAddonGUIWindow* p))
++ dlsym(m_libXBMC_gui, "GUI_Window_destroy");
++ if (Window_destroy == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ Control_getSpin = (CAddonGUISpinControl* (*)(CAddonGUIWindow *window, int controlId))
++ dlsym(m_libXBMC_gui, "GUI_control_get_spin");
++ if (Control_getSpin == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ Control_releaseSpin = (void (*)(CAddonGUISpinControl* p))
++ dlsym(m_libXBMC_gui, "GUI_control_release_spin");
++ if (Control_releaseSpin == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ Control_getListContainer= (CAddonGUIListContainer* (*)(CAddonGUIWindow *window, int controlId))
++ dlsym(m_libXBMC_gui, "GUI_control_get_listcontainer");
++ if (Control_getListContainer == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ Control_releaseListContainer = (void (*)(CAddonGUIListContainer* p))
++ dlsym(m_libXBMC_gui, "GUI_control_release_listcontainer");
++ if (Control_releaseListContainer == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ Control_getRadioButton = (CAddonGUIRadioButton* (*)(CAddonGUIWindow *window, int controlId))
++ dlsym(m_libXBMC_gui, "GUI_control_get_radiobutton");
++ if (Control_getRadioButton == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ Control_releaseRadioButton = (void (*)(CAddonGUIRadioButton* p))
++ dlsym(m_libXBMC_gui, "GUI_control_release_radiobutton");
++ if (Control_releaseRadioButton == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ Control_getProgress = (CAddonGUIProgressControl* (*)(CAddonGUIWindow *window, int controlId))
++ dlsym(m_libXBMC_gui, "GUI_control_get_progress");
++ if (Control_getProgress == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ Control_releaseProgress = (void (*)(CAddonGUIProgressControl* p))
++ dlsym(m_libXBMC_gui, "GUI_control_release_progress");
++ if (Control_releaseProgress == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ListItem_create = (CAddonListItem* (*)(const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path))
++ dlsym(m_libXBMC_gui, "GUI_ListItem_create");
++ if (ListItem_create == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ListItem_destroy = (void (*)(CAddonListItem* p))
++ dlsym(m_libXBMC_gui, "GUI_ListItem_destroy");
++ if (ListItem_destroy == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ return GUI_register_me(m_Handle) > 0;
++ }
++
++ void (*Lock)();
++ void (*Unlock)();
++ int (*GetScreenHeight)();
++ int (*GetScreenWidth)();
++ int (*GetVideoResolution)();
++ int (*Dialog_showYesNo)(const char* heading, const char* line0, const char* line1, const char* line2, int* bCanceled, const char* noLabel, const char* yesLabel);
++ CAddonGUIWindow* (*Window_create)(const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog);
++ void (*Window_destroy)(CAddonGUIWindow* p);
++ CAddonGUISpinControl* (*Control_getSpin)(CAddonGUIWindow *window, int controlId);
++ void (*Control_releaseSpin)(CAddonGUISpinControl* p);
++ CAddonGUIListContainer* (*Control_getListContainer)(CAddonGUIWindow *window, int controlId);
++ void (*Control_releaseListContainer)(CAddonGUIListContainer* p);
++ CAddonGUIRadioButton* (*Control_getRadioButton)(CAddonGUIWindow *window, int controlId);
++ void (*Control_releaseRadioButton)(CAddonGUIRadioButton* p);
++ CAddonGUIProgressControl* (*Control_getProgress)(CAddonGUIWindow *window, int controlId);
++ void (*Control_releaseProgress)(CAddonGUIProgressControl* p);
++ CAddonListItem* (*ListItem_create)(const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path);
++ void (*ListItem_destroy)(CAddonListItem* p);
++
++protected:
++ int (*GUI_register_me)(void *HANDLE);
++ void (*GUI_unregister_me)();
++
++private:
++ void *m_libXBMC_gui;
++ void *m_Handle;
++ struct cb_array
++ {
++ const char* libPath;
++ };
++};
++
++class CAddonGUISpinControl
++{
++public:
++ CAddonGUISpinControl(CAddonGUIWindow *window, int controlId);
++ virtual ~CAddonGUISpinControl(void) {}
++
++ virtual void SetVisible(bool yesNo);
++ virtual void SetText(const char *label);
++ virtual void Clear();
++ virtual void AddLabel(const char *label, int iValue);
++ virtual int GetValue();
++ virtual void SetValue(int iValue);
++
++private:
++ CAddonGUIWindow *m_Window;
++ int m_ControlId;
++ GUIHANDLE m_SpinHandle;
++};
++
++class CAddonGUIListContainer
++{
++public:
++ CAddonGUIListContainer(CAddonGUIWindow *window, int controlId);
++ ~CAddonGUIListContainer() {}
++
++ virtual void SetVisible(bool yesNo);
++ virtual void AddItem(CAddonListItem *item);
++ virtual void AddItems(CAddonListItem* items[],int size);
++ virtual CAddonListItem* GetItem(int index);
++ virtual int GetSelected();
++ virtual void ResetList();
++
++private:
++ CAddonGUIWindow *m_Window;
++ int m_ControlId;
++ GUIHANDLE m_ListHandle;
++ GUIHANDLE m_Items;
++};
++
++class CAddonGUIRadioButton
++{
++public:
++ CAddonGUIRadioButton(CAddonGUIWindow *window, int controlId);
++ ~CAddonGUIRadioButton() {}
++
++ virtual void SetVisible(bool yesNo);
++ virtual void SetText(const char *label);
++ virtual void SetSelected(bool yesNo);
++ virtual bool IsSelected();
++
++private:
++ CAddonGUIWindow *m_Window;
++ int m_ControlId;
++ GUIHANDLE m_ButtonHandle;
++};
++
++class CAddonGUIProgressControl
++{
++public:
++ CAddonGUIProgressControl(CAddonGUIWindow *window, int controlId);
++ virtual ~CAddonGUIProgressControl(void) {}
++
++ virtual void SetPercentage(float fPercent);
++ virtual float GetPercentage() const;
++ virtual void SetInfo(int iInfo);
++ virtual int GetInfo() const;
++ virtual std::string GetDescription() const;
++
++private:
++ CAddonGUIWindow *m_Window;
++ int m_ControlId;
++ GUIHANDLE m_ProgressHandle;
++};
++
++/*Please note that CAddonListItem will leak memory if it is not assigned to a list.*/
++class CAddonListItem
++{
++friend class CAddonGUIWindow;
++friend class CAddonGUIListContainer;
++
++public:
++ CAddonListItem(const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path);
++ virtual ~CAddonListItem(void){}
++
++ virtual const char *GetLabel();
++ virtual void SetLabel(const char *label);
++ virtual const char *GetLabel2();
++ virtual void SetLabel2(const char *label);
++ virtual void SetIconImage(const char *image);
++ virtual void SetThumbnailImage(const char *image);
++ virtual void SetInfo(const char *Info);
++ virtual void SetProperty(const char *key, const char *value);
++ virtual const char *GetProperty(const char *key) const;
++ virtual void SetPath(const char *Path);
++
++// {(char*)"select();
++// {(char*)"isSelected();
++protected:
++ CAddonListItem(GUIHANDLE ListItemHandle);
++ GUIHANDLE m_ListItemHandle;
++};
++
++class CAddonGUIWindow
++{
++friend class CAddonGUISpinControl;
++friend class CAddonGUIListContainer;
++friend class CAddonGUIRadioButton;
++friend class CAddonGUIProgressControl;
++
++public:
++ CAddonGUIWindow(const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog);
++ ~CAddonGUIWindow();
++
++ virtual bool Show();
++ virtual void Close();
++ virtual void DoModal();
++ virtual bool SetFocusId(int iControlId);
++ virtual int GetFocusId();
++ virtual bool SetCoordinateResolution(int res);
++ virtual void SetProperty(const char *key, const char *value);
++ virtual void SetPropertyInt(const char *key, int value);
++ virtual void SetPropertyBool(const char *key, bool value);
++ virtual void SetPropertyDouble(const char *key, double value);
++ virtual const char *GetProperty(const char *key) const;
++ virtual int GetPropertyInt(const char *key) const;
++ virtual bool GetPropertyBool(const char *key) const;
++ virtual double GetPropertyDouble(const char *key) const;
++ virtual void ClearProperties();
++ virtual int GetListSize();
++ virtual void ClearList();
++ virtual GUIHANDLE AddStringItem(const char *name, int itemPosition = -1);
++ virtual void AddItem(GUIHANDLE item, int itemPosition = -1);
++ virtual void AddItem(CAddonListItem *item, int itemPosition = -1);
++ virtual void RemoveItem(int itemPosition);
++ virtual GUIHANDLE GetListItem(int listPos);
++ virtual void SetCurrentListPosition(int listPos);
++ virtual int GetCurrentListPosition();
++ virtual void SetControlLabel(int controlId, const char *label);
++ virtual void AddContextMenuButton(int controlId,unsigned int contextButtonId,const char* label);
++
++ virtual bool OnClick(int controlId);
++ virtual bool OnFocus(int controlId);
++ virtual bool OnInit();
++ virtual bool OnAction(int actionId);
++ virtual bool OnContextMenu(int controlId,int itemNumber, unsigned int contextButtonId);
++
++ GUIHANDLE m_cbhdl;
++ bool (*CBOnInit)(GUIHANDLE cbhdl);
++ bool (*CBOnFocus)(GUIHANDLE cbhdl, int controlId);
++ bool (*CBOnClick)(GUIHANDLE cbhdl, int controlId);
++ bool (*CBOnAction)(GUIHANDLE cbhdl, int actionId);
++ bool (*CBOnContextMenu)(GUIHANDLE cbhdl,int controlId,int itemNumber, unsigned int contextButtonId);
++
++protected:
++ GUIHANDLE m_WindowHandle;
++};
++
+diff --git a/addons/library.xbmc.pvr/libXBMC_pvr.h b/addons/library.xbmc.pvr/libXBMC_pvr.h
+new file mode 100644
+index 0000000..aa679b8
+--- /dev/null
++++ b/addons/library.xbmc.pvr/libXBMC_pvr.h
+@@ -0,0 +1,170 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <string>
++#include <vector>
++#include <string.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include "xbmc_pvr_types.h"
++#include "../library.xbmc.addon/libXBMC_addon.h"
++
++#ifdef _WIN32
++#define PVR_HELPER_DLL "\\library.xbmc.pvr\\libXBMC_pvr" ADDON_HELPER_EXT
++#else
++#define PVR_HELPER_DLL "/library.xbmc.pvr/libXBMC_pvr-" ADDON_HELPER_ARCH "-" ADDON_HELPER_PLATFORM ADDON_HELPER_EXT
++#endif
++
++#define DVD_TIME_BASE 1000000
++#define DVD_NOPTS_VALUE (-1LL<<52) // should be possible to represent in both double and __int64
++
++class CHelper_libXBMC_pvr
++{
++public:
++ CHelper_libXBMC_pvr()
++ {
++ m_libXBMC_pvr = NULL;
++ m_Handle = NULL;
++ }
++
++ ~CHelper_libXBMC_pvr()
++ {
++ if (m_libXBMC_pvr)
++ {
++ PVR_unregister_me();
++ dlclose(m_libXBMC_pvr);
++ }
++ }
++
++ bool RegisterMe(void *Handle)
++ {
++ m_Handle = Handle;
++
++ std::string libBasePath;
++ libBasePath = ((cb_array*)m_Handle)->libPath;
++ libBasePath += PVR_HELPER_DLL;
++
++ m_libXBMC_pvr = dlopen(libBasePath.c_str(), RTLD_LAZY);
++ if (m_libXBMC_pvr == NULL)
++ {
++ fprintf(stderr, "Unable to load %s\n", dlerror());
++ return false;
++ }
++
++ PVR_register_me = (int (*)(void *HANDLE))
++ dlsym(m_libXBMC_pvr, "PVR_register_me");
++ if (PVR_register_me == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ PVR_unregister_me = (void (*)())
++ dlsym(m_libXBMC_pvr, "PVR_unregister_me");
++ if (PVR_unregister_me == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TransferEpgEntry = (void (*)(const PVR_HANDLE handle, const EPG_TAG *epgentry))
++ dlsym(m_libXBMC_pvr, "PVR_transfer_epg_entry");
++ if (TransferEpgEntry == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TransferChannelEntry = (void (*)(const PVR_HANDLE handle, const PVR_CHANNEL *chan))
++ dlsym(m_libXBMC_pvr, "PVR_transfer_channel_entry");
++ if (TransferChannelEntry == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TransferTimerEntry = (void (*)(const PVR_HANDLE handle, const PVR_TIMER *timer))
++ dlsym(m_libXBMC_pvr, "PVR_transfer_timer_entry");
++ if (TransferTimerEntry == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TransferRecordingEntry = (void (*)(const PVR_HANDLE handle, const PVR_RECORDING *recording))
++ dlsym(m_libXBMC_pvr, "PVR_transfer_recording_entry");
++ if (TransferRecordingEntry == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ AddMenuHook = (void (*)(PVR_MENUHOOK *hook))
++ dlsym(m_libXBMC_pvr, "PVR_add_menu_hook");
++ if (AddMenuHook == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ Recording = (void (*)(const char *Name, const char *FileName, bool On))
++ dlsym(m_libXBMC_pvr, "PVR_recording");
++ if (Recording == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TriggerTimerUpdate = (void (*)())
++ dlsym(m_libXBMC_pvr, "PVR_trigger_timer_update");
++ if (TriggerTimerUpdate == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TriggerRecordingUpdate = (void (*)())
++ dlsym(m_libXBMC_pvr, "PVR_trigger_recording_update");
++ if (TriggerRecordingUpdate == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TriggerChannelUpdate = (void (*)())
++ dlsym(m_libXBMC_pvr, "PVR_trigger_channel_update");
++ if (TriggerChannelUpdate == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TriggerChannelGroupsUpdate = (void (*)())
++ dlsym(m_libXBMC_pvr, "PVR_trigger_channel_groups_update");
++ if (TriggerChannelGroupsUpdate == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TransferChannelGroup = (void (*)(const PVR_HANDLE handle, const PVR_CHANNEL_GROUP *group))
++ dlsym(m_libXBMC_pvr, "PVR_transfer_channel_group");
++ if (TransferChannelGroup == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TransferChannelGroupMember = (void (*)(const PVR_HANDLE handle, const PVR_CHANNEL_GROUP_MEMBER *member))
++ dlsym(m_libXBMC_pvr, "PVR_transfer_channel_group_member");
++ if (TransferChannelGroupMember == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++#ifdef USE_DEMUX
++ FreeDemuxPacket = (void (*)(DemuxPacket* pPacket))
++ dlsym(m_libXBMC_pvr, "PVR_free_demux_packet");
++ if (FreeDemuxPacket == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ AllocateDemuxPacket = (DemuxPacket* (*)(int iDataSize))
++ dlsym(m_libXBMC_pvr, "PVR_allocate_demux_packet");
++ if (AllocateDemuxPacket == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++#endif
++
++ return PVR_register_me(m_Handle) > 0;
++ }
++
++ void (*TransferEpgEntry)(const PVR_HANDLE handle, const EPG_TAG *epgentry);
++ void (*TransferChannelEntry)(const PVR_HANDLE handle, const PVR_CHANNEL *chan);
++ void (*TransferTimerEntry)(const PVR_HANDLE handle, const PVR_TIMER *timer);
++ void (*TransferRecordingEntry)(const PVR_HANDLE handle, const PVR_RECORDING *recording);
++ void (*AddMenuHook)(PVR_MENUHOOK *hook);
++ void (*Recording)(const char *Name, const char *FileName, bool On);
++ void (*TriggerTimerUpdate)();
++ void (*TriggerRecordingUpdate)();
++ void (*TriggerChannelUpdate)();
++ void (*TriggerChannelGroupsUpdate)();
++ void (*TransferChannelGroup)(const PVR_HANDLE handle, const PVR_CHANNEL_GROUP *group);
++ void (*TransferChannelGroupMember)(const PVR_HANDLE handle, const PVR_CHANNEL_GROUP_MEMBER *member);
++#ifdef USE_DEMUX
++ void (*FreeDemuxPacket)(DemuxPacket* pPacket);
++ DemuxPacket* (*AllocateDemuxPacket)(int iDataSize);
++#endif
++
++protected:
++ int (*PVR_register_me)(void *HANDLE);
++ void (*PVR_unregister_me)();
++
++private:
++ void *m_libXBMC_pvr;
++ void *m_Handle;
++ struct cb_array
++ {
++ const char* libPath;
++ };
++};
+diff --git a/addons/pvr.demo/addon.xml b/addons/pvr.demo/addon.xml
+new file mode 100644
+index 0000000..6a850be
+--- /dev/null
++++ b/addons/pvr.demo/addon.xml
+@@ -0,0 +1,19 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<addon
++ id="pvr.demo"
++ version="1.0.0"
++ name="PVR Demo Client"
++ provider-name="Pulse-Eight Ltd.">
++ <requires>
++ <c-pluff version="0.1"/>
++ </requires>
++ <extension
++ point="xbmc.pvrclient"
++ library_linux="XBMC_demo.pvr" />
++ <extension point="xbmc.addon.metadata">
++ <summary>Pulse-Eight Demo PVR Client</summary>
++ <description>Pulse-Eight Demo PVR Client</description>
++ <disclaimer>Just contains stubs</disclaimer>
++ <platform>linux</platform>
++ </extension>
++</addon>
+diff --git a/addons/pvr.hts/addon.xml b/addons/pvr.hts/addon.xml
+new file mode 100644
+index 0000000..66cf72b
+--- /dev/null
++++ b/addons/pvr.hts/addon.xml
+@@ -0,0 +1,22 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<addon
++ id="pvr.hts"
++ version="1.0.0"
++ name="Tvheadend HTSP Client"
++ provider-name="Alwin Esch, Team XBMC">
++ <requires>
++ <c-pluff version="0.1"/>
++ </requires>
++ <extension
++ point="xbmc.pvrclient"
++ library_linux="XBMC_Tvheadend.pvr"
++ library_osx="XBMC_Tvheadend.pvr"
++ library_wingl="XBMC_Tvheadend_win32.pvr"
++ library_windx="XBMC_Tvheadend_win32.pvr"/>
++ <extension point="xbmc.addon.metadata">
++ <summary>XBMC's frontend for Tvheadend</summary>
++ <description>Tvheadend frontend; supporting streaming of Live TV &amp; Recordings, EPG, Timers</description>
++ <disclaimer>This is unstable software! The authors are in no way responsible for failed recordings, incorrect timers, wasted hours, or any other undesirable effects..</disclaimer>
++ <platform>all</platform>
++ </extension>
++</addon>
+diff --git a/addons/pvr.hts/icon.png b/addons/pvr.hts/icon.png
+new file mode 100644
+index 0000000..a990063
+Binary files /dev/null and b/addons/pvr.hts/icon.png differ
+diff --git a/addons/pvr.hts/pthreadVC2.dll b/addons/pvr.hts/pthreadVC2.dll
+new file mode 100644
+index 0000000..fdea676
+Binary files /dev/null and b/addons/pvr.hts/pthreadVC2.dll differ
+diff --git a/addons/pvr.hts/pthreadVC2d.dll b/addons/pvr.hts/pthreadVC2d.dll
+new file mode 100644
+index 0000000..6fffdc4
+Binary files /dev/null and b/addons/pvr.hts/pthreadVC2d.dll differ
+diff --git a/addons/pvr.hts/resources/language/Dutch/strings.xml b/addons/pvr.hts/resources/language/Dutch/strings.xml
+new file mode 100644
+index 0000000..a6a3455
+--- /dev/null
++++ b/addons/pvr.hts/resources/language/Dutch/strings.xml
+@@ -0,0 +1,15 @@
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<strings>
++ <!-- settings labels -->
++ <string id="30000">Tvheadend server naam of IP adres</string>
++ <string id="30001">HTTP poort</string>
++ <string id="30002">HTSP poort</string>
++ <string id="30003">Gebruikersnaam</string>
++ <string id="30004">Wachtwoord</string>
++ <string id="30006">Verbinding timeout in seconden</string>
++ <string id="30007">Antwoord timeout in seconden</string>
++
++ <!-- notifications -->
++ <string id="30500">Verbinding met '%s' verbroken</string>
++ <string id="30501">Verbinding met '%s' hersteld</string>
++</strings>
+diff --git a/addons/pvr.hts/resources/language/English/strings.xml b/addons/pvr.hts/resources/language/English/strings.xml
+new file mode 100644
+index 0000000..5b69906
+--- /dev/null
++++ b/addons/pvr.hts/resources/language/English/strings.xml
+@@ -0,0 +1,15 @@
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<strings>
++ <!-- settings labels -->
++ <string id="30000">Tvheadend hostname or IP address</string>
++ <string id="30001">HTTP port</string>
++ <string id="30002">HTSP port</string>
++ <string id="30003">Username</string>
++ <string id="30004">Password</string>
++ <string id="30006">Connect timeout in seconds</string>
++ <string id="30007">Response timeout in seconds</string>
++
++ <!-- notifications -->
++ <string id="30500">Disconnected from '%s'</string>
++ <string id="30501">Reconnected to '%s'</string>
++</strings>
+diff --git a/addons/pvr.hts/resources/language/Finnish/strings.xml b/addons/pvr.hts/resources/language/Finnish/strings.xml
+new file mode 100644
+index 0000000..d3bb391
+--- /dev/null
++++ b/addons/pvr.hts/resources/language/Finnish/strings.xml
+@@ -0,0 +1,15 @@
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<strings>
++ <!-- settings labels -->
++ <string id="30000">Tvheadend-palvelimen nimi tai IP-osoite</string>
++ <string id="30001">HTTP-portti</string>
++ <string id="30002">HTSP-portti</string>
++ <string id="30003">Käyttäjänimi</string>
++ <string id="30004">Salasana</string>
++ <string id="30006">Yhteyden aikakatkaisu sekunneissa</string>
++ <string id="30007">Vastauksen aikakatkaisu sekunneissa</string>
++
++ <!-- notifications -->
++ <string id="30500">Yhteys katkaistu palvelimeen '%s'</string>
++ <string id="30501">Yhdistetty palvelimeen '%s'</string>
++</strings>
+diff --git a/addons/pvr.hts/resources/language/German/strings.xml b/addons/pvr.hts/resources/language/German/strings.xml
+new file mode 100644
+index 0000000..b2ba2ea
+--- /dev/null
++++ b/addons/pvr.hts/resources/language/German/strings.xml
+@@ -0,0 +1,16 @@
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<strings>
++ <!-- settings labels -->
++ <string id="30000">Tvheadend Hostname oder IP</string>
++ <string id="30001">HTTP Port</string>
++ <string id="30002">HTSP Port</string>
++ <string id="30003">Benutzername</string>
++ <string id="30004">Passwort</string>
++
++ <string id="30006">Verbindungszeit Ãœberschreitung in Sekunden</string>
++ <string id="30007">Antwortzeit Ãœberschreitung in Sekunden</string>
++
++ <!-- notifications -->
++ <string id="30500">Getrennt von '%s'</string>
++ <string id="30501">Wiederverbunden mit '%s'</string>
++</strings>
+diff --git a/addons/pvr.hts/resources/settings.xml b/addons/pvr.hts/resources/settings.xml
+new file mode 100644
+index 0000000..3ef0839
+--- /dev/null
++++ b/addons/pvr.hts/resources/settings.xml
+@@ -0,0 +1,10 @@
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<settings>
++ <setting id="host" type="text" label="30000" default="127.0.0.1" />
++ <setting id="http_port" type="number" label="30001" default="9981" />
++ <setting id="htsp_port" type="number" label="30002" default="9982" />
++ <setting id="user" type="text" label="30003" default="" />
++ <setting id="pass" type="text" label="30004" option="hidden" default="" />
++ <setting id="connect_timeout" type="enum" label="30006" values="1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45|46|47|48|49|50|51|52|53|54|55|56|57|58|59|60" default="29" />
++ <setting id="response_timeout" type="enum" label="30007" values="1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45|46|47|48|49|50|51|52|53|54|55|56|57|58|59|60" default="1" />
++</settings>
+diff --git a/addons/pvr.mythtv.cmyth/Thumbs.db b/addons/pvr.mythtv.cmyth/Thumbs.db
+new file mode 100644
+index 0000000..e742505
+Binary files /dev/null and b/addons/pvr.mythtv.cmyth/Thumbs.db differ
+diff --git a/addons/pvr.mythtv.cmyth/addon.xml b/addons/pvr.mythtv.cmyth/addon.xml
+new file mode 100644
+index 0000000..a5c28f5
+--- /dev/null
++++ b/addons/pvr.mythtv.cmyth/addon.xml
+@@ -0,0 +1,23 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<addon
++ id="pvr.mythtv.cmyth"
++ version="0.1.0"
++ name="MythTV cmyth PVR Client"
++ provider-name="Tonny Petersen">
++ <requires>
++ <c-pluff version="0.1"/>
++ </requires>
++ <extension
++ point="xbmc.pvrclient"
++ library_linux="XBMC_MythTV_cmyth.pvr"
++ library_osx="XBMC_MythTV_cmyth.pvr"
++ library_wingl="XBMC_MythTV_cmyth_win32.pvr"
++ library_windx="XBMC_MythTV_cmyth_win32.pvr"/>
++ <extension point="xbmc.addon.metadata">
++ <summary>XBMC's frontend for MythTV (using cmyth lib)</summary>
++ <description>MythTV frontend; supporting streaming of Live TV &amp; Recordings, EPG, Timers</description>
++ <disclaimer>This is unstable software! The authors are in no way responsible for failed recordings, incorrect timers, wasted hours, or any other undesirable effects..</disclaimer>
++ <platform>all</platform>
++ </extension>
++</addon>
++
+diff --git a/addons/pvr.mythtv.cmyth/icon.png b/addons/pvr.mythtv.cmyth/icon.png
+new file mode 100644
+index 0000000..821f8c5
+Binary files /dev/null and b/addons/pvr.mythtv.cmyth/icon.png differ
+diff --git a/addons/pvr.mythtv.cmyth/pthreadVC2.dll b/addons/pvr.mythtv.cmyth/pthreadVC2.dll
+new file mode 100644
+index 0000000..fdea676
+Binary files /dev/null and b/addons/pvr.mythtv.cmyth/pthreadVC2.dll differ
+diff --git a/addons/pvr.mythtv.cmyth/pthreadVC2d.dll b/addons/pvr.mythtv.cmyth/pthreadVC2d.dll
+new file mode 100644
+index 0000000..2b4f50e
+Binary files /dev/null and b/addons/pvr.mythtv.cmyth/pthreadVC2d.dll differ
+diff --git a/addons/pvr.mythtv.cmyth/resources/language/Danish/strings.xml b/addons/pvr.mythtv.cmyth/resources/language/Danish/strings.xml
+new file mode 100644
+index 0000000..8abb3b3
+--- /dev/null
++++ b/addons/pvr.mythtv.cmyth/resources/language/Danish/strings.xml
+@@ -0,0 +1,11 @@
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<strings>
++ <!-- settings labels -->
++ <string id="30000">MythTV Backend Hostname eller IP</string>
++ <string id="30001">MythTV Backend Port</string>
++ <string id="30002">MythTV Database Brugernavn</string>
++ <string id="30003">MythTV Database Adgangskode</string>
++ <string id="30004">MythTV Database Navn</string>
++ <string id="30005">Inkluder mere fejlfindings-information i log-filen</string>
++</strings>
++
+diff --git a/addons/pvr.mythtv.cmyth/resources/language/English/strings.xml b/addons/pvr.mythtv.cmyth/resources/language/English/strings.xml
+new file mode 100644
+index 0000000..36d4806
+--- /dev/null
++++ b/addons/pvr.mythtv.cmyth/resources/language/English/strings.xml
+@@ -0,0 +1,18 @@
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<strings>
++ <!-- settings labels -->
++ <string id="30000">MythTV Backend Hostname or IP</string>
++ <string id="30001">MythTV Backend Port</string>
++ <string id="30002">MythTV Database Username</string>
++ <string id="30003">MythTV Database Password</string>
++ <string id="30004">MythTV Database name</string>
++ <string id="30005">Include more debug information in the log</string>
++ <string id="30006">Recording Rules</string>
++ <string id="30007">Do you still want to delete?</string>
++ <string id="30008">This will delete the recording rule and &#xA;an additional %i timer(s).</string>
++ <string id="30009">Allow live TV to move scheduled shows</string>
++ <string id="30010">Minimum duration of a recording to be considered a movie</string>
++ <string id="30011">Perl Regular Expression used to extract the foldernames and title for series</string>
++ <string id="30012">Perl Regular Expression used to detect if the title+subtitle is a series</string>
++</strings>
++
+diff --git a/addons/pvr.mythtv.cmyth/resources/language/German/strings.xml b/addons/pvr.mythtv.cmyth/resources/language/German/strings.xml
+new file mode 100644
+index 0000000..ffb17c3
+--- /dev/null
++++ b/addons/pvr.mythtv.cmyth/resources/language/German/strings.xml
+@@ -0,0 +1,14 @@
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<strings>
++ <!-- settings labels -->
++ <string id="30000">MythTV Backend Hostname oder IP</string>
++ <string id="30001">MythTV Backend Port</string>
++ <string id="30002">MythTV Datenbank Benutzer</string>
++ <string id="30003">MythTV Datenbank Passwort</string>
++ <string id="30004">MythTV Datenbank Name</string>
++ <string id="30005">Schreibe ausführlichere debugging Informationen in die Log-Datei</string>
++ <string id="30006">Aufnahme-Regeln</string>
++ <string id="30007">Möchtest du immer noch löschen?</string>
++ <string id="30008">Dieser Vorgang löscht die Aufnahmeregel und &#xA;zusätzlich %i Aufnahmetermine.</string>
++ <string id="30009">Erlaube dem Live-TV etwaige geplante Aufnahmen zu verschieben</string>
++</strings>
+diff --git a/addons/pvr.mythtv.cmyth/resources/settings.xml b/addons/pvr.mythtv.cmyth/resources/settings.xml
+new file mode 100644
+index 0000000..6664b7a
+--- /dev/null
++++ b/addons/pvr.mythtv.cmyth/resources/settings.xml
+@@ -0,0 +1,14 @@
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<settings>
++ <setting id="host" type="text" label="30000" default="127.0.0.1" />
++ <setting id="port" type="number" label="30001" default="6543" />
++ <setting id="db_user" type="text" label="30002" default="mythtv" />
++ <setting id="db_password" type="text" label="30003" default="mythtv" />
++ <setting id="db_name" type="text" label="30004" default="mythconverg" />
++ <setting id="extradebug" type="bool" label="30005" default="false" />
++ <setting id="livetv_priority" type="bool" label="30009" default="false" />
++ <setting id="min_movie_length" type="number" label="30010" default="65" />
++ <setting id="series_regex" type="text" label="30011" default="^(?<folder>.+?)::(?<title>.+)" />
++ <setting id="series_regex_id" type="text" label="30012" default="" />
++</settings>
++
+diff --git a/addons/pvr.mythtv.cmyth/resources/skins/skin.confluence/720p/DialogRecordingRulesSettings.xml b/addons/pvr.mythtv.cmyth/resources/skins/skin.confluence/720p/DialogRecordingRulesSettings.xml
+new file mode 100644
+index 0000000..24316d6
+--- /dev/null
++++ b/addons/pvr.mythtv.cmyth/resources/skins/skin.confluence/720p/DialogRecordingRulesSettings.xml
+@@ -0,0 +1,155 @@
++<window>
++ <defaultcontrol>15</defaultcontrol>
++ <coordinates>
++ <system>1</system>
++ <posx>275</posx>
++ <posy>30</posy>
++ </coordinates>
++ <include>dialogeffect</include>
++ <controls>
++ <control type="image">
++ <description>background image</description>
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>730</width>
++ <height>660</height>
++ <texture border="40">DialogBack.png</texture>
++ </control>
++ <control type="image">
++ <description>Dialog Header image</description>
++ <posx>40</posx>
++ <posy>16</posy>
++ <width>650</width>
++ <height>40</height>
++ <texture>dialogheader.png</texture>
++ </control>
++ <control type="label" id="2">
++ <description>header label</description>
++ <posx>40</posx>
++ <posy>20</posy>
++ <width>650</width>
++ <height>30</height>
++ <font>font13_title</font>
++ <label>-</label>
++ <align>center</align>
++ <aligny>center</aligny>
++ <textcolor>selected</textcolor>
++ <shadowcolor>black</shadowcolor>
++ </control>
++ <control type="button">
++ <description>Close Window button</description>
++ <posx>640</posx>
++ <posy>15</posy>
++ <width>64</width>
++ <height>32</height>
++ <label>-</label>
++ <font>-</font>
++ <onclick>PreviousMenu</onclick>
++ <texturefocus>DialogCloseButton-focus.png</texturefocus>
++ <texturenofocus>DialogCloseButton.png</texturenofocus>
++ <onleft>10</onleft>
++ <onright>10</onright>
++ <onup>10</onup>
++ <ondown>10</ondown>
++ <visible>system.getbool(input.enablemouse)</visible>
++ </control>
++ <control type="grouplist" id="5">
++ <description>control area</description>
++ <posx>30</posx>
++ <posy>70</posy>
++ <width>670</width>
++ <height>510</height>
++ <itemgap>5</itemgap>
++ <onup>9001</onup>
++ <ondown>9001</ondown>
++ <onleft>9001</onleft>
++ <onright>9001</onright>
++ </control>
++ <control type="group" id="9001">
++ <posx>160</posx>
++ <posy>590</posy>
++ <control type="button" id="28">
++ <description>OK Button</description>
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>200</width>
++ <height>40</height>
++ <align>center</align>
++ <aligny>center</aligny>
++ <font>font12_title</font>
++ <label>186</label>
++ <onleft>29</onleft>
++ <onright>29</onright>
++ <onup>5</onup>
++ <ondown>5</ondown>
++ </control>
++ <control type="button" id="29">
++ <description>Cancel Button</description>
++ <posx>210</posx>
++ <posy>0</posy>
++ <width>200</width>
++ <height>40</height>
++ <align>center</align>
++ <aligny>center</aligny>
++ <font>font12_title</font>
++ <label>222</label>
++ <onleft>28</onleft>
++ <onright>28</onright>
++ <onup>5</onup>
++ <ondown>5</ondown>
++ </control>
++ </control>
++ <control type="button" id="7">
++ <description>Default Button</description>
++ <height>40</height>
++ <font>font13</font>
++ <textcolor>grey2</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ </control>
++ <control type="radiobutton" id="8">
++ <description>Default RadioButton</description>
++ <height>40</height>
++ <font>font13</font>
++ <textcolor>grey2</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ </control>
++ <control type="spincontrolex" id="9">
++ <description>Default spincontrolex</description>
++ <height>40</height>
++ <textcolor>grey2</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <font>font13</font>
++ <aligny>center</aligny>
++ <reverse>yes</reverse>
++ </control>
++ <control type="sliderex" id="10">
++ <description>Default Slider</description>
++ <height>40</height>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <font>font13</font>
++ <textcolor>grey2</textcolor>
++ <focusedcolor>white</focusedcolor>
++ </control>
++ <control type="image" id="11">
++ <description>Default Seperator</description>
++ <height>2</height>
++ <texture>separator2.png</texture>
++ </control>
++ <control type="edit" id="12">
++ <description>Default Edit</description>
++ <height>40</height>
++ <font>font13</font>
++ <textcolor>grey2</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ </control>
++ </controls>
++</window>
+\ No newline at end of file
+diff --git a/addons/pvr.mythtv.cmyth/resources/skins/skin.confluence/720p/RecordingRules.xml b/addons/pvr.mythtv.cmyth/resources/skins/skin.confluence/720p/RecordingRules.xml
+new file mode 100644
+index 0000000..f2fb9fb
+--- /dev/null
++++ b/addons/pvr.mythtv.cmyth/resources/skins/skin.confluence/720p/RecordingRules.xml
+@@ -0,0 +1,339 @@
++<window>
++ <defaultcontrol>14</defaultcontrol>
++
++ <controls>
++ <include>CommonTVBackground</include>
++ <control type="group">
++ <visible>true</visible>
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>1280</width>
++ <height>720</height>
++ <texture>special://skin/backgrounds/media-overlay.png</texture>
++ <visible>Player.HasVideo + !Skin.HasSetting(ShowBackgroundVideo)</visible>
++ </control>
++ <control type="visualisation">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>1280</width>
++ <height>720</height>
++ <visible>Player.HasAudio + !Skin.HasSetting(ShowBackgroundVis)</visible>
++ </control>
++ <control type="videowindow">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>1280</width>
++ <height>720</height>
++ <visible>Player.HasVideo + !Skin.HasSetting(ShowBackgroundVideo)</visible>
++ </control>
++ </control>
++ <include>ContentPanelBackgroundsPVR</include>
++ <control type="group">
++ <description>Recording Rules group</description>
++ <include>VisibleFadeEffect</include>
++ <control type="group">
++ <posx>80</posx>
++ <posy>60</posy>
++ <control type="label">
++ <description>Channel header label</description>
++ <posx>0</posx>
++ <posy>20</posy>
++ <width>220</width>
++ <height>20</height>
++ <font>font13_title</font>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <align>center</align>
++ <aligny>center</aligny>
++ <label>19029</label>
++ </control>
++ <control type="label">
++ <description>Time header label</description>
++ <posx>580</posx>
++ <posy>20</posy>
++ <width>300</width>
++ <height>20</height>
++ <font>font13_title</font>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <align>center</align>
++ <aligny>center</aligny>
++ <label>555</label>
++ </control>
++ <control type="label">
++ <description>Search header label</description>
++ <posx>220</posx>
++ <posy>20</posy>
++ <width>300</width>
++ <height>20</height>
++ <font>font13_title</font>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <align>center</align>
++ <aligny>center</aligny>
++ <label>137</label>
++ </control>
++ <control type="label">
++ <description>Status header label</description>
++ <posx>940</posx>
++ <posy>20</posy>
++ <width>150</width>
++ <height>20</height>
++ <font>font13_title</font>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <align>center</align>
++ <aligny>center</aligny>
++ <label>126</label>
++ </control>
++ <control type="image">
++ <description>separator image</description>
++ <posx>0</posx>
++ <posy>50</posy>
++ <width>1100</width>
++ <height>1</height>
++ <colordiffuse>88FFFFFF</colordiffuse>
++ <texture>separator2.png</texture>
++ </control>
++ <control type="list" id="14">
++ <posx>0</posx>
++ <posy>55</posy>
++ <width>1100</width>
++ <height>480</height>
++ <onup>14</onup>
++ <ondown>14</ondown>
++ <onleft>35</onleft>
++ <onright>73</onright>
++ <pagecontrol>73</pagecontrol>
++ <scrolltime>200</scrolltime>
++ <itemlayout height="40">
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>1100</width>
++ <height>41</height>
++ <texture border="5">MenuItemNF.png</texture>
++ </control>
++ <control type="image">
++ <posx>220</posx>
++ <posy>0</posy>
++ <width>300</width>
++ <height>40</height>
++ <colordiffuse>33FFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ </control>
++ <control type="image">
++ <posx>940</posx>
++ <posy>0</posy>
++ <width>155</width>
++ <height>40</height>
++ <colordiffuse>33FFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ </control>
++ <control type="image">
++ <posx>0</posx>
++ <posy>8</posy>
++ <width>50</width>
++ <height>26</height>
++ <visible>!IsEmpty(ListItem.Date)</visible>
++ <texture border="1">$INFO[ListItem.Icon]</texture>
++ </control>
++ <control type="label">
++ <posx>50</posx>
++ <posy>0</posy>
++ <width>150</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.Property(channel)</info>
++ </control>
++ <control type="label">
++ <posx>370</posx>
++ <posy>0</posy>
++ <width>290</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>center</align>
++ <aligny>center</aligny>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.Property(search)</info>
++ </control>
++ <control type="label">
++ <posx>730</posx>
++ <posy>0</posy>
++ <width>400</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>center</align>
++ <aligny>center</aligny>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.Property(time)</info>
++ </control>
++ <control type="label">
++ <posx>1018</posx>
++ <posy>0</posy>
++ <width>170</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>center</align>
++ <aligny>center</aligny>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.Property(status)</info>
++ </control>
++ </itemlayout>
++ <focusedlayout height="40">
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>1100</width>
++ <height>41</height>
++ <texture border="5">MenuItemNF.png</texture>
++ </control>
++ <control type="image">
++ <posx>220</posx>
++ <posy>0</posy>
++ <width>300</width>
++ <height>40</height>
++ <colordiffuse>33FFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ <visible>!Control.HasFocus(14)</visible>
++ </control>
++ <control type="image">
++ <posx>980</posx>
++ <posy>0</posy>
++ <width>120</width>
++ <height>40</height>
++ <colordiffuse>33FFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ <visible>!Control.HasFocus(14)</visible>
++ </control>
++ <control type="image">
++ <posx>220</posx>
++ <posy>0</posy>
++ <width>300</width>
++ <height>40</height>
++ <colordiffuse>88FFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ <visible>Control.HasFocus(14)</visible>
++ </control>
++ <control type="image">
++ <posx>940</posx>
++ <posy>0</posy>
++ <width>155</width>
++ <height>40</height>
++ <colordiffuse>88FFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ <visible>Control.HasFocus(14)</visible>
++ </control>
++ <control type="image">
++ <posx>0</posx>
++ <posy>8</posy>
++ <width>50</width>
++ <height>26</height>
++ <visible>!IsEmpty(ListItem.Date)</visible>
++ <texture border="1">$INFO[ListItem.Icon]</texture>
++ </control>
++ <control type="label">
++ <posx>50</posx>
++ <posy>0</posy>
++ <width>150</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.Property(channel)</info>
++ </control>
++ <control type="label">
++ <posx>370</posx>
++ <posy>0</posy>
++ <width>290</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>center</align>
++ <aligny>center</aligny>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.Property(search)</info>
++ </control>
++ <control type="label">
++ <posx>730</posx>
++ <posy>0</posy>
++ <width>400</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>center</align>
++ <aligny>center</aligny>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.Property(time)</info>
++ </control>
++ <control type="label">
++ <posx>1018</posx>
++ <posy>0</posy>
++ <width>150</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>center</align>
++ <aligny>center</aligny>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.Property(status)</info>
++ </control>
++ </focusedlayout>
++ </control>
++ <control type="scrollbar" id="73">
++ <posx>1105</posx>
++ <posy>50</posy>
++ <width>25</width>
++ <height>480</height>
++ <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
++ <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
++ <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
++ <textureslidernib>ScrollBarNib.png</textureslidernib>
++ <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
++ <onleft>11</onleft>
++ <onright>35</onright>
++ <showonepage>false</showonepage>
++ <orientation>vertical</orientation>
++ </control>
++ <control type="image">
++ <description>separator image</description>
++ <posx>55</posx>
++ <posy>540</posy>
++ <width>1010</width>
++ <height>1</height>
++ <colordiffuse>88FFFFFF</colordiffuse>
++ <texture>separator2.png</texture>
++ </control>
++ <control type="button" id="32">
++ <description>Done</description>
++ <posx>55</posx>
++ <posy>545</posy>
++ <width>128</width>
++ <height>30</height>
++ <label>20177</label>
++ <font>font13_title</font>
++ <align>right</align>
++ <aligny>center</aligny>
++ <texturenofocus border="5">MenuItemNF.png</texturenofocus>
++ <texturefocus border="5">MenuItemFO.png</texturefocus>
++ </control>
++ </control>
++ <control type="label">
++ <description>Page Count Label</description>
++ <posx>40r</posx>
++ <posy>53r</posy>
++ <width>500</width>
++ <height>20</height>
++ <font>font12</font>
++ <textcolor>grey</textcolor>
++ <scroll>false</scroll>
++ <align>right</align>
++ <aligny>center</aligny>
++ <label>([COLOR=blue]$INFO[Container(14).NumItems][/COLOR]) $LOCALIZE[19040] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(14).CurrentPage]/$INFO[Container(14).NumPages][/COLOR])</label>
++ <include>Window_OpenClose_Animation</include>
++ </control>
++ </control>
++ </controls>
++</window>
+\ No newline at end of file
+diff --git a/addons/pvr.mythtv/addon.xml b/addons/pvr.mythtv/addon.xml
+new file mode 100644
+index 0000000..dac8f16
+--- /dev/null
++++ b/addons/pvr.mythtv/addon.xml
+@@ -0,0 +1,23 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<addon
++ id="pvr.mythtv"
++ version="1.0.0"
++ name="MythTV PVR Client"
++ provider-name="Miscelleaneous People">
++ <requires>
++ <c-pluff version="0.1"/>
++ </requires>
++ <extension
++ point="xbmc.pvrclient"
++ library_linux="XBMC_MythTV.pvr"
++ library_osx="XBMC_MythTV.pvr"
++ library_wingl="XBMC_MythTV_win32.pvr"
++ library_windx="XBMC_MythTV_win32.pvr"/>
++ <extension point="xbmc.addon.metadata">
++ <summary>XBMC's frontend for MythTV</summary>
++ <description>MythTV frontend; supporting streaming of Live TV &amp; Recordings, EPG, Timers</description>
++ <disclaimer>This is unstable software! The authors are in no way responsible for failed recordings, incorrect timers, wasted hours, or any other undesirable effects..</disclaimer>
++ <platform>all</platform>
++ </extension>
++</addon>
++
+diff --git a/addons/pvr.mythtv/icon.png b/addons/pvr.mythtv/icon.png
+new file mode 100644
+index 0000000..821f8c5
+Binary files /dev/null and b/addons/pvr.mythtv/icon.png differ
+diff --git a/addons/pvr.mythtv/resources/language/English/strings.xml b/addons/pvr.mythtv/resources/language/English/strings.xml
+new file mode 100644
+index 0000000..bb3167c
+--- /dev/null
++++ b/addons/pvr.mythtv/resources/language/English/strings.xml
+@@ -0,0 +1,7 @@
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<strings>
++ <!-- settings labels -->
++ <string id="30000">MythTV Backend Hostname or IP</string>
++ <string id="30001">MythXML Port</string>
++</strings>
++
+diff --git a/addons/pvr.mythtv/resources/language/Finnish/strings.xml b/addons/pvr.mythtv/resources/language/Finnish/strings.xml
+new file mode 100644
+index 0000000..02d2863
+--- /dev/null
++++ b/addons/pvr.mythtv/resources/language/Finnish/strings.xml
+@@ -0,0 +1,6 @@
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<strings>
++ <!-- settings labels -->
++ <string id="30000">MythTV-palvelimen nimi tai IP-osoite</string>
++ <string id="30001">MythXML-portti</string>
++</strings>
+diff --git a/addons/pvr.mythtv/resources/language/German/strings.xml b/addons/pvr.mythtv/resources/language/German/strings.xml
+new file mode 100644
+index 0000000..9de6f92
+--- /dev/null
++++ b/addons/pvr.mythtv/resources/language/German/strings.xml
+@@ -0,0 +1,7 @@
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<strings>
++ <!-- settings labels -->
++ <string id="30000">MythTV Backend Hostname oder IP</string>
++ <string id="30001">MythXML Port</string>
++</strings>
++
+diff --git a/addons/pvr.mythtv/resources/settings.xml b/addons/pvr.mythtv/resources/settings.xml
+new file mode 100644
+index 0000000..1620968
+--- /dev/null
++++ b/addons/pvr.mythtv/resources/settings.xml
+@@ -0,0 +1,6 @@
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<settings>
++ <setting id="host" type="text" label="30000" option="urlencoded" default="127.0.0.1" />
++ <setting id="mythXMLPort" type="number" label="30001" default="6544" />
++</settings>
++
+diff --git a/addons/pvr.team-mediaportal.tvserver/LICENSE.txt b/addons/pvr.team-mediaportal.tvserver/LICENSE.txt
+new file mode 100644
+index 0000000..4f8e8eb
+--- /dev/null
++++ b/addons/pvr.team-mediaportal.tvserver/LICENSE.txt
+@@ -0,0 +1,282 @@
++
++ GNU GENERAL PUBLIC LICENSE
++ Version 2, June 1991
++
++ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
++ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ Everyone is permitted to copy and distribute verbatim copies
++ of this license document, but changing it is not allowed.
++
++ Preamble
++
++ The licenses for most software are designed to take away your
++freedom to share and change it. By contrast, the GNU General Public
++License is intended to guarantee your freedom to share and change free
++software--to make sure the software is free for all its users. This
++General Public License applies to most of the Free Software
++Foundation's software and to any other program whose authors commit to
++using it. (Some other Free Software Foundation software is covered by
++the GNU Library General Public License instead.) You can apply it to
++your programs, too.
++
++ When we speak of free software, we are referring to freedom, not
++price. Our General Public Licenses are designed to make sure that you
++have the freedom to distribute copies of free software (and charge for
++this service if you wish), that you receive source code or can get it
++if you want it, that you can change the software or use pieces of it
++in new free programs; and that you know you can do these things.
++
++ To protect your rights, we need to make restrictions that forbid
++anyone to deny you these rights or to ask you to surrender the rights.
++These restrictions translate to certain responsibilities for you if you
++distribute copies of the software, or if you modify it.
++
++ For example, if you distribute copies of such a program, whether
++gratis or for a fee, you must give the recipients all the rights that
++you have. You must make sure that they, too, receive or can get the
++source code. And you must show them these terms so they know their
++rights.
++
++ We protect your rights with two steps: (1) copyright the software, and
++(2) offer you this license which gives you legal permission to copy,
++distribute and/or modify the software.
++
++ Also, for each author's protection and ours, we want to make certain
++that everyone understands that there is no warranty for this free
++software. If the software is modified by someone else and passed on, we
++want its recipients to know that what they have is not the original, so
++that any problems introduced by others will not reflect on the original
++authors' reputations.
++
++ Finally, any free program is threatened constantly by software
++patents. We wish to avoid the danger that redistributors of a free
++program will individually obtain patent licenses, in effect making the
++program proprietary. To prevent this, we have made it clear that any
++patent must be licensed for everyone's free use or not licensed at all.
++
++ The precise terms and conditions for copying, distribution and
++modification follow.
++
++ GNU GENERAL PUBLIC LICENSE
++ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
++
++ 0. This License applies to any program or other work which contains
++a notice placed by the copyright holder saying it may be distributed
++under the terms of this General Public License. The "Program", below,
++refers to any such program or work, and a "work based on the Program"
++means either the Program or any derivative work under copyright law:
++that is to say, a work containing the Program or a portion of it,
++either verbatim or with modifications and/or translated into another
++language. (Hereinafter, translation is included without limitation in
++the term "modification".) Each licensee is addressed as "you".
++
++Activities other than copying, distribution and modification are not
++covered by this License; they are outside its scope. The act of
++running the Program is not restricted, and the output from the Program
++is covered only if its contents constitute a work based on the
++Program (independent of having been made by running the Program).
++Whether that is true depends on what the Program does.
++
++ 1. You may copy and distribute verbatim copies of the Program's
++source code as you receive it, in any medium, provided that you
++conspicuously and appropriately publish on each copy an appropriate
++copyright notice and disclaimer of warranty; keep intact all the
++notices that refer to this License and to the absence of any warranty;
++and give any other recipients of the Program a copy of this License
++along with the Program.
++
++You may charge a fee for the physical act of transferring a copy, and
++you may at your option offer warranty protection in exchange for a fee.
++
++ 2. You may modify your copy or copies of the Program or any portion
++of it, thus forming a work based on the Program, and copy and
++distribute such modifications or work under the terms of Section 1
++above, provided that you also meet all of these conditions:
++
++ a) You must cause the modified files to carry prominent notices
++ stating that you changed the files and the date of any change.
++
++ b) You must cause any work that you distribute or publish, that in
++ whole or in part contains or is derived from the Program or any
++ part thereof, to be licensed as a whole at no charge to all third
++ parties under the terms of this License.
++
++ c) If the modified program normally reads commands interactively
++ when run, you must cause it, when started running for such
++ interactive use in the most ordinary way, to print or display an
++ announcement including an appropriate copyright notice and a
++ notice that there is no warranty (or else, saying that you provide
++ a warranty) and that users may redistribute the program under
++ these conditions, and telling the user how to view a copy of this
++ License. (Exception: if the Program itself is interactive but
++ does not normally print such an announcement, your work based on
++ the Program is not required to print an announcement.)
++
++These requirements apply to the modified work as a whole. If
++identifiable sections of that work are not derived from the Program,
++and can be reasonably considered independent and separate works in
++themselves, then this License, and its terms, do not apply to those
++sections when you distribute them as separate works. But when you
++distribute the same sections as part of a whole which is a work based
++on the Program, the distribution of the whole must be on the terms of
++this License, whose permissions for other licensees extend to the
++entire whole, and thus to each and every part regardless of who wrote it.
++
++Thus, it is not the intent of this section to claim rights or contest
++your rights to work written entirely by you; rather, the intent is to
++exercise the right to control the distribution of derivative or
++collective works based on the Program.
++
++In addition, mere aggregation of another work not based on the Program
++with the Program (or with a work based on the Program) on a volume of
++a storage or distribution medium does not bring the other work under
++the scope of this License.
++
++ 3. You may copy and distribute the Program (or a work based on it,
++under Section 2) in object code or executable form under the terms of
++Sections 1 and 2 above provided that you also do one of the following:
++
++ a) Accompany it with the complete corresponding machine-readable
++ source code, which must be distributed under the terms of Sections
++ 1 and 2 above on a medium customarily used for software interchange; or,
++
++ b) Accompany it with a written offer, valid for at least three
++ years, to give any third party, for a charge no more than your
++ cost of physically performing source distribution, a complete
++ machine-readable copy of the corresponding source code, to be
++ distributed under the terms of Sections 1 and 2 above on a medium
++ customarily used for software interchange; or,
++
++ c) Accompany it with the information you received as to the offer
++ to distribute corresponding source code. (This alternative is
++ allowed only for noncommercial distribution and only if you
++ received the program in object code or executable form with such
++ an offer, in accord with Subsection b above.)
++
++The source code for a work means the preferred form of the work for
++making modifications to it. For an executable work, complete source
++code means all the source code for all modules it contains, plus any
++associated interface definition files, plus the scripts used to
++control compilation and installation of the executable. However, as a
++special exception, the source code distributed need not include
++anything that is normally distributed (in either source or binary
++form) with the major components (compiler, kernel, and so on) of the
++operating system on which the executable runs, unless that component
++itself accompanies the executable.
++
++If distribution of executable or object code is made by offering
++access to copy from a designated place, then offering equivalent
++access to copy the source code from the same place counts as
++distribution of the source code, even though third parties are not
++compelled to copy the source along with the object code.
++
++ 4. You may not copy, modify, sublicense, or distribute the Program
++except as expressly provided under this License. Any attempt
++otherwise to copy, modify, sublicense or distribute the Program is
++void, and will automatically terminate your rights under this License.
++However, parties who have received copies, or rights, from you under
++this License will not have their licenses terminated so long as such
++parties remain in full compliance.
++
++ 5. You are not required to accept this License, since you have not
++signed it. However, nothing else grants you permission to modify or
++distribute the Program or its derivative works. These actions are
++prohibited by law if you do not accept this License. Therefore, by
++modifying or distributing the Program (or any work based on the
++Program), you indicate your acceptance of this License to do so, and
++all its terms and conditions for copying, distributing or modifying
++the Program or works based on it.
++
++ 6. Each time you redistribute the Program (or any work based on the
++Program), the recipient automatically receives a license from the
++original licensor to copy, distribute or modify the Program subject to
++these terms and conditions. You may not impose any further
++restrictions on the recipients' exercise of the rights granted herein.
++You are not responsible for enforcing compliance by third parties to
++this License.
++
++ 7. If, as a consequence of a court judgment or allegation of patent
++infringement or for any other reason (not limited to patent issues),
++conditions are imposed on you (whether by court order, agreement or
++otherwise) that contradict the conditions of this License, they do not
++excuse you from the conditions of this License. If you cannot
++distribute so as to satisfy simultaneously your obligations under this
++License and any other pertinent obligations, then as a consequence you
++may not distribute the Program at all. For example, if a patent
++license would not permit royalty-free redistribution of the Program by
++all those who receive copies directly or indirectly through you, then
++the only way you could satisfy both it and this License would be to
++refrain entirely from distribution of the Program.
++
++If any portion of this section is held invalid or unenforceable under
++any particular circumstance, the balance of the section is intended to
++apply and the section as a whole is intended to apply in other
++circumstances.
++
++It is not the purpose of this section to induce you to infringe any
++patents or other property right claims or to contest validity of any
++such claims; this section has the sole purpose of protecting the
++integrity of the free software distribution system, which is
++implemented by public license practices. Many people have made
++generous contributions to the wide range of software distributed
++through that system in reliance on consistent application of that
++system; it is up to the author/donor to decide if he or she is willing
++to distribute software through any other system and a licensee cannot
++impose that choice.
++
++This section is intended to make thoroughly clear what is believed to
++be a consequence of the rest of this License.
++
++ 8. If the distribution and/or use of the Program is restricted in
++certain countries either by patents or by copyrighted interfaces, the
++original copyright holder who places the Program under this License
++may add an explicit geographical distribution limitation excluding
++those countries, so that distribution is permitted only in or among
++countries not thus excluded. In such case, this License incorporates
++the limitation as if written in the body of this License.
++
++ 9. The Free Software Foundation may publish revised and/or new versions
++of the General Public License from time to time. Such new versions will
++be similar in spirit to the present version, but may differ in detail to
++address new problems or concerns.
++
++Each version is given a distinguishing version number. If the Program
++specifies a version number of this License which applies to it and "any
++later version", you have the option of following the terms and conditions
++either of that version or of any later version published by the Free
++Software Foundation. If the Program does not specify a version number of
++this License, you may choose any version ever published by the Free Software
++Foundation.
++
++ 10. If you wish to incorporate parts of the Program into other free
++programs whose distribution conditions are different, write to the author
++to ask for permission. For software which is copyrighted by the Free
++Software Foundation, write to the Free Software Foundation; we sometimes
++make exceptions for this. Our decision will be guided by the two goals
++of preserving the free status of all derivatives of our free software and
++of promoting the sharing and reuse of software generally.
++
++ NO WARRANTY
++
++ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
++FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
++OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
++PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
++OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
++MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
++TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
++PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
++REPAIR OR CORRECTION.
++
++ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
++WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
++REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
++INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
++OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
++TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
++YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
++PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
++POSSIBILITY OF SUCH DAMAGES.
++
++ END OF TERMS AND CONDITIONS
++-------------------------------------------------------------------------
+diff --git a/addons/pvr.team-mediaportal.tvserver/addon.xml b/addons/pvr.team-mediaportal.tvserver/addon.xml
+new file mode 100644
+index 0000000..3b845b1
+--- /dev/null
++++ b/addons/pvr.team-mediaportal.tvserver/addon.xml
+@@ -0,0 +1,23 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<addon
++ id="pvr.team-mediaportal.tvserver"
++ version="1.2.2.111"
++ name="MediaPortal PVR Client (ffmpeg)"
++ provider-name="Marcel Groothuis">
++ <requires>
++ <c-pluff version="0.1"/>
++ </requires>
++ <extension
++ point="xbmc.pvrclient"
++ library_linux="XBMC_MPTV.pvr"
++ library_osx="XBMC_MPTV.pvr"
++ library_wingl="XBMC_MPTV_win32.pvr"
++ library_windx="XBMC_MPTV_win32.pvr"/>
++ <extension point="xbmc.addon.metadata">
++ <summary>XBMC frontend for the MediaPortal TV Server (ffmpeg/rtsp only version)</summary>
++ <description>MediaPortal TV Server frontend. Supports streaming of Live TV &amp; Recordings, listening to Radio channels, EPG and Timers.</description>
++ <description lang="nl">MediaPortal TV Server frontend. Ondersteunt het bekijken van Live TV en opnames, het luisteren van radio zenders, het tonen van de EPG en het inplannen/beheren van nieuwe opnames (Timers).</description>
++ <disclaimer>This is unstable software! The authors are in no way responsible for failed recordings, incorrect timers, wasted hours, or any other undesirable effects..</disclaimer>
++ <platform>all</platform>
++ </extension>
++</addon>
+diff --git a/addons/pvr.team-mediaportal.tvserver/changelog.txt b/addons/pvr.team-mediaportal.tvserver/changelog.txt
+new file mode 100644
+index 0000000..6074a08
+--- /dev/null
++++ b/addons/pvr.team-mediaportal.tvserver/changelog.txt
+@@ -0,0 +1,89 @@
++v1.2.2.11:
++- Fixed: Day of the week mismatch for timers between MePo and XBMC. Credits: jajoflo
++- Added: Trigger recording update on timer retrieval so the recording list is kept up to date when there is a new recording. Credits: jajoflo
++- Fixed: tv group retrieval requested radio groups
++- Added: add support for recording genre field
++- Fixed: fixes for recording filepath split-up
++- Added: show an error message when the recording filename or playback URL is empty
++
++v1.2.2.110:
++- Added: use channel icons from MediaPortal (Windows, localhost only)
++- Changed: switch from libPlatform + libTcpSocket to platform library
++
++v1.2.2.109:
++- Fixed: fix mimetype for tv channels
++- Fixed: make sure that OpenLiveStream does not return true due to m_iCurrentChannel when the TSReader fails in a later stage
++- Fixed: compilation IOS/OSX/Linux
++- Fixed: compile fixes for API move into ADDON namespace
++- Added: show localized error messages when OpenLiveStream fails
++- Added: signal status support
++- Fixed: Don't return encrypted channels as group members when FTA only option is turned on
++- Added: add more debug log information about the addon settings and status
++- Added: add support for direct recording playback from network shares (smb, Windows only)
++- Added: implement support for recording subdirectories
++- Fixed: don't return radio channels/channelgroup/channelgroup mappings when radio support is disabled
++
++v1.1.3.107:
++- Added: add additional checks for communication errors
++- Fixed: trigger also a recording list update on timer changes
++- Fixed: add a mutex to prevent mixing up backend communication on concurrent access
++- Fixed: stack overflow while closing a live stream
++- Fixed: tv/radio playback after recording playback
++- Added: EPG genre string-to-id translation table (addons/pvr.team-mediaportal.tvserver/resources/genre_translation.xml)
++- Added: EPG genre string support
++- Added: Retrieve TV/Radio card settings from the backend
++- Added: support for TVServerXBMC v1.1.x.105-107
++- Fixed: several memory leaks
++- Changed: sources adapted for PVR API changes
++
++v1.1.3.103:
++- Fixed: trigger timer and recording list update on changes from XBMC side
++- Fixed: check for empty recordings list
++- Fixed: check for empty timer list
++- Fixed: limit EPG request to the requested period
++- Fixed: use tuning details to retrieve the channel number from the backend
++- Added: support for TVServerXBMC v1.1.x.104
++- Changed: sources adapted for PVR API changes
++
++v1.1.2.102:
++- Added: Channel group support
++- Fixed: live stream playback
++- Fixed: recording retrieval
++- Changed: sources adapted for PVR API changes
++
++v1.1.2.101:
++- Fixed: "Include radio" setting
++- Changed: cleanup unused settings
++
++v1.1.0.100
++- Rewrite: timer code
++- Changed: BackendName
++- Fixed: Retrieve more details for timers (repeat, lifetime, ...)
++- Fixed: Playback of radio channels with a webstream URL (added via the MediaPortal TV-server)
++- Fixed: "Free-to-air only" setting
++- Support for TVServerXBMC v1.1.0.100
++
++v1.1.0.98
++- Log more information on socket related problems
++
++v1.1.0.97
++- Fixed: Use uri decode for ip-address in hostname field
++
++v1.1.0.96
++- Fixed: allow spaces in groupnames, recording names and timer titles
++- Fixed: GetTimerInfo
++- Add debug messages for empty channel list
++
++v1.1.0.95
++- Fix time mismatch between XBMC and MediaPortal TV Server for new timers (scheduled recordings)
++- Send genre strings to XBMC for unrecognised genres. No colors in EPG, but at least the text is now ok.
++
++v1.1.0.90
++- Faster channel switching (around 2 sec faster)
++- Decrease CPU uage when a buffer underrun occurs
++
++v1.1.0.75
++- PVR client should abort connection when the TVServerXBMC version is too old
++
++v1.1.0.60
++- Fix PVR client destroy
+\ No newline at end of file
+diff --git a/addons/pvr.team-mediaportal.tvserver/icon.png b/addons/pvr.team-mediaportal.tvserver/icon.png
+new file mode 100644
+index 0000000..42f6e37
+Binary files /dev/null and b/addons/pvr.team-mediaportal.tvserver/icon.png differ
+diff --git a/addons/pvr.team-mediaportal.tvserver/resources/genre_translation.xml b/addons/pvr.team-mediaportal.tvserver/resources/genre_translation.xml
+new file mode 100644
+index 0000000..ca6cedd
+--- /dev/null
++++ b/addons/pvr.team-mediaportal.tvserver/resources/genre_translation.xml
+@@ -0,0 +1,128 @@
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<!-- Use only lower case strings and hexadecimal id's //-->
++<!-- See below for the type/subtype number definitions //-->
++<genrestrings>
++ <!-- English strings //-->
++ <genre lang="en" type="0x10" subtype="0x00">movie/drama (general)</genre>
++ <genre lang="en" type="0x10" subtype="0x01">detective/thriller</genre>
++ <genre lang="en" type="0x10" subtype="0x04">comedy</genre>
++ <genre lang="en" type="0x10" subtype="0x05">soap/melodram/folkloric</genre>
++ <genre lang="en" type="0x10" subtype="0x08">adult movie/drama</genre>
++ <genre lang="en" type="0x20" subtype="0x00">news/current affairs (general)</genre>
++ <genre lang="en" type="0x20" subtype="0x03">documentary</genre>
++ <genre lang="en" type="0x30" subtype="0x00">show/game show (general)</genre>
++ <genre lang="en" type="0x40" subtype="0x00">sports (general)</genre>
++ <genre lang="en" type="0x50" subtype="0x00">childrens's/youth program (general)</genre>
++ <genre lang="en" type="0x50" subtype="0x05">cartoon/puppets</genre>
++ <genre lang="en" type="0x60" subtype="0x00">music/ballet/dance (general)</genre>
++ <genre lang="en" type="0x70" subtype="0x00">arts/culture (without music, general)</genre>
++ <genre lang="en" type="0x70" subtype="0x03">religion</genre>
++ <genre lang="en" type="0x80" subtype="0x01">magazines/reports/documentary</genre>
++ <genre lang="en" type="0x90" subtype="0x00">education/science/factual topics (general)</genre>
++ <genre lang="en" type="0x90" subtype="0x01">nature/animals/environment</genre>
++
++</genrestrings>
++
++<!-- TODO: Finish me... This list is probably not complete yet
++CONTENTMASK_MOVIEDRAMA 0x10
++Subtypes:
++ DETECTIVE_THRILLER 0x01
++ ADVENTURE_WESTERN_WAR 0x02
++ SF_FANTASY_HORROR 0x03
++ COMEDY 0x04
++ SOAP_MELODRAMA_FOLKLORIC 0x05
++ ROMANCE 0x06
++ SERIOUS_CLASSICAL_RELIGIOUS_HISTORICAL_DRAMA 0x07
++ ADULTMOVIE_DRAMA 0x08
++
++NEWSCURRENTAFFAIRS 0x20
++Subtypes:
++ NEWS_WEATHER_REPORT 0x01
++ NEWS_MAGAZINE 0x02
++ DOCUMENTARY 0x03
++ DISCUSSION_INTERVIEW_DEBATE 0x04
++
++CONTENTMASK_SHOW 0x30
++Subtypes:
++ GAMESHOW_QUIZ_CONTEST 0x01
++ VARIETY_SHOW 0x02
++ TALK_SHOW 0x03
++
++SPORTS 0x40
++Subtypes:
++ SPECIAL_EVENTS 0x01
++ SPORTS_MAGAZINES 0x02
++ FOOTBALL_SOCCER 0x03
++ TENNIS_SQUASH 0x04
++ TEAM_SPORTS 0x05
++ ATHLETICS 0x06
++ MOTOR_SPORT 0x07
++ WATER_SPORT 0x08
++ WINTER_SPORT 0x09
++ EQUESTRIAN 0x0A
++ MARTIAL_SPORTS 0x0B
++
++CHILDRENYOUTH 0x50
++Subtypes:
++ PRESCHOOL_CHILD_PROGRAM 0x01
++ ENTERTAINMENT_6TO14 0x02
++ ENTERTAINMENT_10TO16 0x03
++ INFO_EDUC_SCHOOL_PROGRAM 0x04
++ CARTOONS_PUPPETS 0x05
++
++MUSICBALLETDANCE 0x60
++Subtypes:
++ ROCK_POP 0x01
++ SERIOUS_CLASSICAL_MUSIC 0x02
++ FOLK_TRADITIONAL_MUSIC 0x03
++ JAZZ 0x04
++ MUSICAL_OPERA 0x05
++ BALLET 0x06
++
++ARTSCULTURE 0x70 //without music
++Subtypes:
++ PERFORMING_ARTS 0x01
++ FINE_ARTS 0x02
++ RELIGION 0x03
++ POP_CULTURE_TRAD_ARTS 0x04
++ LITERATURE 0x05
++ FILM_CINEMA 0x06
++ EXPERIMENTAL_FILM_VIDEO 0x07
++ BROADCASTING_PRESS 0x08
++ NEW_MEDIA 0x09
++ ARTS_CULTURE_MAGAZINES 0x0A
++ FASHION 0x0B
++
++SOCIALPOLITICALECONOMICS 0x80
++Subtypes:
++ MAGAZINES_REPORTS_DOCUMENTARY 0x01
++ ECONOMICS_SOCIAL_ADVISORY 0x02
++ REMARKABLE_PEOPLE 0x03
++
++EDUCATIONALSCIENCE 0x90
++Subtypes:
++ NATURE_ANIMALS_ENVIRONMENT 0x01
++ TECHNOLOGY_NATURAL_SCIENCES 0x02
++ MEDICINE_PHYSIOLOGY_PSYCHOLOGY 0x03
++ FOREIGN_COUNTRIES_EXPEDITIONS 0x04
++ SOCIAL_SPIRITUAL_SCIENCES 0x05
++ FURTHER_EDUCATION 0x06
++ LANGUAGES 0x07
++
++LEISUREHOBBIES 0xA0
++Subtypes:
++ TOURISM_TRAVEL 0x01
++ HANDICRAFT 0x02
++ MOTORING 0x03
++ FITNESS_HEALTH 0x04
++ COOKING 0x05
++ ADVERTISEMENT_SHOPPING 0x06
++ GARDENING 0x07
++
++SPECIAL 0xB0
++ BLACK_WHITE 0x01
++ UNPUBLISHED 0x02
++ LIVE_BROADCAST 0x03
++
++USERDEFINED 0xF0
++//-->
+diff --git a/addons/pvr.team-mediaportal.tvserver/resources/language/Dutch/strings.xml b/addons/pvr.team-mediaportal.tvserver/resources/language/Dutch/strings.xml
+new file mode 100644
+index 0000000..951c6f5
+--- /dev/null
++++ b/addons/pvr.team-mediaportal.tvserver/resources/language/Dutch/strings.xml
+@@ -0,0 +1,44 @@
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<strings>
++ <!-- settings labels -->
++ <string id="30000">Mediaportal hostnaam</string>
++ <string id="30001">Mediaportal XBMC plugin poort</string>
++ <string id="30002">Toon alleen vrij te ontvangen kanalen</string>
++ <string id="30003">Toon radio</string>
++
++ <string id="30005">Verbindingstimeout (s)</string>
++ <string id="30006">Importeer alleen TV kanalen uit groep</string>
++ <string id="30007">Importeer alleen radio kanalen uit groep</string>
++ <string id="30008">Converteer hostnaam naar IP adres</string>
++ <string id="30009">EPG: Genre tekst inlezen (traag)</string>
++ <string id="30010">Wachttijd na kanaal tunen(ms)</string>
++ <string id="30011">Opnames direct afspelen (niet streamen)</string>
++ <string id="30012">Mediaportal directory met opnames</string>
++
++ <!-- category labels -->
++ <string id="30040">Verbinding</string>
++ <string id="30041">MediaPortal</string>
++
++ <!-- on-screen error messages -->
++ <string id="30050">Uw TVServerXBMC plugin '%s' is te oud. U heeft minimaal versie '%s' of nieuwer nodig!</string>
++ <string id="30051">Uw TVServerXBMC plugin is te oud. U heeft minimaal versie '%s' of nieuwer nodig!</string>
++ <string id="30052">Afspelen opname mislukt. Lege URL of bestandsnaam.</string>
++
++ <!-- Status message strings corresponding to the MediaPortal TvResult enum -->
++ <string id="30060">Alle kaarten zijn bezet</string>
++ <string id="30061">Kanaal is gecodeerd</string>
++ <string id="30062">Geen video of audio gedetecteerd</string>
++ <string id="30063">Geen signaal</string>
++ <string id="30064">Onbekende fout</string>
++ <string id="30065">Graph starten mislukt</string>
++ <string id="30066">Onbekend kanaal</string>
++ <string id="30067">Geen tuning details</string>
++ <string id="30068">Kanaal is niet gekoppeld aan een kaart</string>
++ <string id="30069">Kaart is uitgeschakeld</string>
++ <string id="30070">Verbinding met slave verbroken</string>
++ <string id="30071">Niet de eigenaar</string>
++ <string id="30072">Aanmaken Graph mislukt</string>
++ <string id="30073">SW Encoder ontbreekt</string>
++ <string id="30074">Geen vrije diskruimte</string>
++ <string id="30075">Geen PMT gevonden</string>
++</strings>
+diff --git a/addons/pvr.team-mediaportal.tvserver/resources/language/English/strings.xml b/addons/pvr.team-mediaportal.tvserver/resources/language/English/strings.xml
+new file mode 100644
+index 0000000..2fb3a1a
+--- /dev/null
++++ b/addons/pvr.team-mediaportal.tvserver/resources/language/English/strings.xml
+@@ -0,0 +1,44 @@
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<strings>
++ <!-- settings labels -->
++ <string id="30000">Mediaportal Hostname</string>
++ <string id="30001">Mediaportal XBMC plugin Port</string>
++ <string id="30002">Free-to-air only</string>
++ <string id="30003">Include Radio</string>
++
++ <string id="30005">Connect timeout (s)</string>
++ <string id="30006">Import only TV Channels from group</string>
++ <string id="30007">Import only Radio Channels from group</string>
++ <string id="30008">Convert hostname to IP-adress</string>
++ <string id="30009">EPG: Read genre strings (slow)</string>
++ <string id="30010">Wait time after tuning a channel (ms)</string>
++ <string id="30011">Play recordings directly (no streaming)</string>
++ <string id="30012">Mediaportal recordings directory</string>
++
++ <!-- category labels -->
++ <string id="30040">Connection</string>
++ <string id="30041">MediaPortal</string>
++
++ <!-- on-screen error messages -->
++ <string id="30050">Your TVServerXBMC version '%s' is too old. Please upgrade to '%s' or higher!</string>
++ <string id="30051">Your TVServerXBMC version is too old. Please upgrade to '%s' or higher!</string>
++ <string id="30052">Recording playback failed. Empty URL of filename.</string>
++
++ <!-- Status message strings corresponding to the MediaPortal TvResult enum -->
++ <string id="30060">All cards are busy</string>
++ <string id="30061">Channel is scrambled</string>
++ <string id="30062">No video or audio detected</string>
++ <string id="30063">No signal detected</string>
++ <string id="30064">Unknown error</string>
++ <string id="30065">Unable to start graph</string>
++ <string id="30066">Unknown channel</string>
++ <string id="30067">No tuning details</string>
++ <string id="30068">Channel is not mapped to any card</string>
++ <string id="30069">Card is disabled</string>
++ <string id="30070">Connection to slave failed</string>
++ <string id="30071">Not the owner</string>
++ <string id="30072">Graph building failed</string>
++ <string id="30073">SW Encoder missing</string>
++ <string id="30074">No free disk space</string>
++ <string id="30075">No PMT found</string>
++</strings>
+diff --git a/addons/pvr.team-mediaportal.tvserver/resources/language/Finnish/strings.xml b/addons/pvr.team-mediaportal.tvserver/resources/language/Finnish/strings.xml
+new file mode 100644
+index 0000000..c151b01
+--- /dev/null
++++ b/addons/pvr.team-mediaportal.tvserver/resources/language/Finnish/strings.xml
+@@ -0,0 +1,25 @@
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<strings>
++ <!-- settings labels -->
++ <string id="30000">Mediaportal-palvelimen nimi</string>
++ <string id="30001">Mediaportal XBMC-lisäosan portti</string>
++ <string id="30002">Vain ilmaiskanavat</string>
++ <string id="30003">Sisällytä radiokanavat</string>
++
++ <string id="30005">Yhteyden aikakatkaisu (s)</string>
++ <string id="30006">Tuo ainoastaan TV-kanavat ryhmästä</string>
++ <string id="30007">Tuo ainoastaan radiokanavat ryhmästä</string>
++ <string id="30008">Muunna palvelimen nimi IP-osoitteeksi</string>
++ <string id="30009">Ohjelmaopas: Lue lajityyppi merkkijonoista (hidas)</string>
++ <string id="30010">Odotusaika kanavan virittämisen jälkeen (ms)</string>
++ <string id="30011">Toista nauhoitukset suoraan (ei streamausta)</string>
++ <string id="30012">Mediaportal-nauhoitusten kansio</string>
++
++ <!-- category labels -->
++ <string id="30040">Yhteys</string>
++ <string id="30041">MediaPortal</string>
++
++ <!-- on-screen error messages -->
++ <string id="30050">TVServerXBMC:n versio v%s on liian vanha. Päivitä vähintään versioon v%s tai uudempaan!</string>
++ <string id="30051">TVServerXBMC:n versio on liian vanha. Päivitä vähintään versioon v%s tai uudempaan!</string>
++</strings>
+diff --git a/addons/pvr.team-mediaportal.tvserver/resources/language/German/strings.xml b/addons/pvr.team-mediaportal.tvserver/resources/language/German/strings.xml
+new file mode 100644
+index 0000000..7121c24
+--- /dev/null
++++ b/addons/pvr.team-mediaportal.tvserver/resources/language/German/strings.xml
+@@ -0,0 +1,21 @@
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<strings>
++ <!-- settings labels -->
++ <string id="30000">Mediaportal Hostname oder IP</string>
++ <string id="30001">Mediaportal Port</string>
++ <string id="30002">Nur frei empfangbare Kanäle</string>
++ <string id="30003">Zeige Radiokanäle</string>
++
++ <string id="30005">Verbindungszeitüberlauf (s)</string>
++ <string id="30006">Importiere nur TV Kanäle aus Gruppe</string>
++ <string id="30007">Importiere nur Radiokanäle aus Gruppe</string>
++ <string id="30008">Konvertiere Hostname nach IP-Adresse</string>
++ <string id="30009">EPG: Genre Texte hochladen (langsam)</string>
++ <string id="30010">Wartezeit, nachdem ein Kanal abgestimmt worden ist (ms)</string>
++ <string id="30011">Aufnahmen aus Ordner lesen</string>
++ <string id="30012">Mediaportal Aufnahmeordner</string>
++
++ <!-- on-screen error messages -->
++ <string id="30050">Ihre TVServerXBMC plugin v%s ist zu alt. Sie brauchen mindestens Version v%s!</string>
++ <string id="30051">Ihre TVServerXBMC plugin Version ist zu alt. Sie brauchen mindestens Version v%s!</string>
++</strings>
+diff --git a/addons/pvr.team-mediaportal.tvserver/resources/language/Italian/strings.xml b/addons/pvr.team-mediaportal.tvserver/resources/language/Italian/strings.xml
+new file mode 100644
+index 0000000..a1880c6
+--- /dev/null
++++ b/addons/pvr.team-mediaportal.tvserver/resources/language/Italian/strings.xml
+@@ -0,0 +1,25 @@
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<strings>
++ <!-- settings labels -->
++ <string id="30000">Nome del server Mediaportal</string>
++ <string id="30001">Porta del plugin Mediaportal di XBMC</string>
++ <string id="30002">Solo Free-to-air</string>
++ <string id="30003">Includi Radio</string>
++
++ <string id="30005">Timeout per la connessione (s)</string>
++ <string id="30006">Importa solo canali TV dal gruppo</string>
++ <string id="30007">Importa solo canali radio dal gruppo</string>
++ <string id="30008">Converti il nome del server nell'indirizzo IP</string>
++ <string id="30009">EPG: Leggi le stringhe dei generi (lento)</string>
++ <string id="30010">Tempo di attesa dopo il tuning di un canale (ms)</string>
++ <string id="30011">Registra direttamente (nessuno streaming)</string>
++ <string id="30012">Cartella di salvataggio per Mediaportal</string>
++
++ <!-- category labels -->
++ <string id="30040">Connessione</string>
++ <string id="30041">MediaPortal</string>
++
++ <!-- on-screen error messages -->
++ <string id="30050">La tua versione di TVServerXBMC v%s è troppo vecchia. Per favore, aggiornala a v%s o superiore!</string>
++ <string id="30051">La tua versione di TVServerXBMC è troppo vecchia. Per favore, aggiornala a v%s o superiore!</string>
++</strings>
+diff --git a/addons/pvr.team-mediaportal.tvserver/resources/settings.xml b/addons/pvr.team-mediaportal.tvserver/resources/settings.xml
+new file mode 100644
+index 0000000..419c61b
+--- /dev/null
++++ b/addons/pvr.team-mediaportal.tvserver/resources/settings.xml
+@@ -0,0 +1,22 @@
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<settings>
++ <!-- Connection -->
++ <category label="30040">
++ <setting id="host" type="text" label="30000" option="urlencoded" default="127.0.0.1" />
++ <setting id="port" type="number" label="30001" default="9596" />
++ <setting id="timeout" type="enum" label="30005" values="0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20" default="10"/>
++ </category>
++
++ <!-- Mediaportal -->
++ <category label="30041">
++ <setting id="ftaonly" type="bool" label="30002" default="false" />
++ <setting id="useradio" type="bool" label="30003" default="true" />
++ <setting id="tvgroup" type="text" label="30006" default="" />
++ <setting id="radiogroup" type="text" label="30007" default="" />
++ <setting id="resolvertsphostname" type="bool" label="30008" default="true" />
++ <setting id="readgenre" type="bool" label="30009" default="false" />
++ <setting id="sleeponrtspurl" type="enum" label="30010" values="0|100|200|300|400|500|600|700|800|900|1000|1200|1500|2000|2500|3000" default="0"/>
++ <setting id="userecordingsdir" type="bool" label="30011" default="false" />
++ <setting id="recordingsdir" type="folder" option="smb" label="30012" default="" />
++ </category>
++</settings>
+diff --git a/addons/pvr.vdr.vnsi/addon.xml b/addons/pvr.vdr.vnsi/addon.xml
+new file mode 100644
+index 0000000..a0b1e03
+--- /dev/null
++++ b/addons/pvr.vdr.vnsi/addon.xml
+@@ -0,0 +1,23 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<addon
++ id="pvr.vdr.vnsi"
++ version="1.0.0"
++ name="VDR VNSI Client"
++ provider-name="Alwin Esch, Team XBMC">
++ <requires>
++ <c-pluff version="0.1"/>
++ </requires>
++ <extension
++ point="xbmc.pvrclient"
++ library_linux="XBMC_VDR_vnsi.pvr"
++ library_osx="XBMC_VDR_vnsi.pvr"
++ library_wingl="XBMC_VDR_vnsi_WIN32.pvr"
++ library_windx="XBMC_VDR_vnsi_WIN32.pvr"/>
++ <extension point="xbmc.addon.metadata">
++ <summary>PVR client to connect VDR to XBMC over the VNSI interface</summary>
++ <description>VDR frontend; supporting streaming of Live TV &amp; Recordings, EPG, Timers over the VNSI plugin</description>
++ <description lang="de">Erlaubt das wiedergeben von Live TV und Aufnahmen mittels VDR auf XBMC. Des weiteren werden EPG, Kanalsuche und Timer unterstützt.</description>
++ <disclaimer>This is unstable software! The authors are in no way responsible for failed recordings, incorrect timers, wasted hours, or any other undesirable effects..</disclaimer>
++ <platform>all</platform>
++ </extension>
++</addon>
+diff --git a/addons/pvr.vdr.vnsi/icon.png b/addons/pvr.vdr.vnsi/icon.png
+new file mode 100644
+index 0000000..d310dde
+Binary files /dev/null and b/addons/pvr.vdr.vnsi/icon.png differ
+diff --git a/addons/pvr.vdr.vnsi/pthreadVC2.dll b/addons/pvr.vdr.vnsi/pthreadVC2.dll
+new file mode 100644
+index 0000000..fdea676
+Binary files /dev/null and b/addons/pvr.vdr.vnsi/pthreadVC2.dll differ
+diff --git a/addons/pvr.vdr.vnsi/pthreadVC2d.dll b/addons/pvr.vdr.vnsi/pthreadVC2d.dll
+new file mode 100644
+index 0000000..6fffdc4
+Binary files /dev/null and b/addons/pvr.vdr.vnsi/pthreadVC2d.dll differ
+diff --git a/addons/pvr.vdr.vnsi/resources/language/Dutch/strings.xml b/addons/pvr.vdr.vnsi/resources/language/Dutch/strings.xml
+new file mode 100644
+index 0000000..7a88831
+--- /dev/null
++++ b/addons/pvr.vdr.vnsi/resources/language/Dutch/strings.xml
+@@ -0,0 +1,48 @@
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<strings>
++ <!-- settings labels -->
++ <string id="30000">VDR hostnaam of IP adres</string>
++ <string id="30001">VNSI Poort</string>
++ <string id="30002">Prioriteit</string>
++ <string id="30003">Character Set Conversie</string>
++ <string id="30004">Connectie timeout (s)</string>
++ <string id="30005">Berichten vanuit VDR toestaan</string>
++ <string id="30006">Lees opnames van directory</string>^M
++ <string id="30007">VDR opname directory</string>^M
++ <string id="30008">Kanalen scannen</string>
++ <string id="30009">Kanalen scan - opties</string>
++ <string id="30010">kanalen scan starten</string>
++ <string id="30011">Bron Type</string>
++ <string id="30012">TV kanalen</string>
++ <string id="30013">Radio kanalen</string>
++ <string id="30014">FTA kanalen</string>
++ <string id="30015">Gecodeerde kanalen</string>
++ <string id="30016">HD kanalen</string>
++ <string id="30017">Land</string>
++ <string id="30018">Kabel Inversion</string>
++ <string id="30019">Kabel Symbolrate</string>
++ <string id="30020">Kabel modulation</string>
++ <string id="30021">Antenne Inversion</string>
++ <string id="30022">Sateliet</string>
++ <string id="30023">ATSC type</string>
++ <string id="30024">Terug</string>
++ <string id="30025">Kanalen zoeken - bezig... %i %%</string>
++ <string id="30026">Type:</string>
++ <string id="30027">Device:</string>
++ <string id="30028">Scan: %i</string>
++ <string id="30029">Signaal: %i %%</string>
++ <string id="30030">Nieuwe kanalen: %i</string>
++ <string id="30031">Alle kanalen: %i</string>
++ <string id="30032">Analoge TV</string>
++ <string id="30033">Analoge Radio</string>
++ <string id="30034">Transponder:</string>
++ <string id="30035">Nieuwe kanalen</string>
++ <string id="30036">Kanalen zoeken - Klaar</string>
++ <string id="30037">Geen device beschikbaar - exiting</string>
++ <string id="30038">Geen DVB-S2 apparaat beschikbaar - We vallen terug op DVB-S</string>
++ <string id="30039">Bezig</string>
++ <string id="30040">Gestopt</string>
++ <string id="30041">Klaar</string>
++ <string id="30042">Kanalen scan - Geanuleerd</string>
++ <string id="30043">Kanalen scan - Fout</string>
++</strings>
+diff --git a/addons/pvr.vdr.vnsi/resources/language/English/strings.xml b/addons/pvr.vdr.vnsi/resources/language/English/strings.xml
+new file mode 100644
+index 0000000..1553912
+--- /dev/null
++++ b/addons/pvr.vdr.vnsi/resources/language/English/strings.xml
+@@ -0,0 +1,51 @@
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<strings>
++ <!-- settings labels -->
++ <string id="30000">VDR Hostname or IP</string>
++ <string id="30001">VNSI Port</string>
++ <string id="30002">Priority</string>
++ <string id="30003">Character Set Conversion</string>
++ <string id="30004">Connect timeout (s)</string>
++ <string id="30005">Allow VDR Messages</string>
++ <string id="30006">Read recordings from directory</string>
++ <string id="30007">VDR recordings directory</string>
++ <string id="30008">Channel search</string>
++ <string id="30009">Channel search - Settings</string>
++ <string id="30010">Start Channel search</string>
++ <string id="30011">Source Type</string>
++ <string id="30012">TV channels</string>
++ <string id="30013">Radio channels</string>
++ <string id="30014">FTA channels</string>
++ <string id="30015">Scrambled channels</string>
++ <string id="30016">HD channels</string>
++ <string id="30017">Country</string>
++ <string id="30018">Cable Inversion</string>
++ <string id="30019">Cable Symbolrate</string>
++ <string id="30020">Cable modulation</string>
++ <string id="30021">Terr Inversion</string>
++ <string id="30022">Satellite</string>
++ <string id="30023">ATSC Type</string>
++ <string id="30024">Back</string>
++ <string id="30025">Channel search - running... %i %%</string>
++ <string id="30026">Type:</string>
++ <string id="30027">Device:</string>
++ <string id="30028">Scan: %i</string>
++ <string id="30029">Signal: %i %%</string>
++ <string id="30030">New channels: %i</string>
++ <string id="30031">All channels: %i</string>
++ <string id="30032">Analog TV</string>
++ <string id="30033">Analog Radio</string>
++ <string id="30034">Transponder:</string>
++ <string id="30035">New channels</string>
++ <string id="30036">Channel search - Finished</string>
++ <string id="30037">No device available - exiting</string>
++ <string id="30038">No DVB-S2 device available - trying fallback to DVB-S</string>
++ <string id="30039">Running</string>
++ <string id="30040">Stopped</string>
++ <string id="30041">Finished</string>
++ <string id="30042">Channel search - Canceled</string>
++ <string id="30043">Channel search - Error</string>
++ <string id="30044">Lost connection to VDR Server</string>
++ <string id="30045">Connection to VDR Server restored</string>
++ <string id="30046">Create channel groups automatically on the server</string>
++</strings>
+diff --git a/addons/pvr.vdr.vnsi/resources/language/Finnish/strings.xml b/addons/pvr.vdr.vnsi/resources/language/Finnish/strings.xml
+new file mode 100644
+index 0000000..c21e4c0
+--- /dev/null
++++ b/addons/pvr.vdr.vnsi/resources/language/Finnish/strings.xml
+@@ -0,0 +1,51 @@
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<strings>
++ <!-- settings labels -->
++ <string id="30000">VDR-palvelimen nimi tai IP-osoite</string>
++ <string id="30001">VNSI-portti</string>
++ <string id="30002">Tärkeysaste</string>
++ <string id="30003">Kirjainmerkistön muunnos</string>
++ <string id="30004">Yhteyden aikakatkaisu (s)</string>
++ <string id="30005">Salli VDR-palvelimen viestit</string>
++ <string id="30006">Lue nauhoitukset kansiosta</string>
++ <string id="30007">VDR-nauhoitusten kansio</string>
++ <string id="30008">Kanavahaku</string>
++ <string id="30009">Kanavahaun asetukset</string>
++ <string id="30010">Aloita kanavahaku</string>
++ <string id="30011">Lähteen tyyppi</string>
++ <string id="30012">TV-kanavat</string>
++ <string id="30013">Radiokanavat</string>
++ <string id="30014">Ilmaiskanavat</string>
++ <string id="30015">Salatut kanavat</string>
++ <string id="30016">HD-kanavat</string>
++ <string id="30017">Maa</string>
++ <string id="30018">Kaapeliverkon inversio</string>
++ <string id="30019">Kaapeliverkon symbolinopeus</string>
++ <string id="30020">Kaapeliverkon modulointi</string>
++ <string id="30021">Antenniverkon inversio</string>
++ <string id="30022">Satelliitti</string>
++ <string id="30023">ATSC tyyppi</string>
++ <string id="30024">Takaisin</string>
++ <string id="30025">Kanavahaku käynnissä... %i %%</string>
++ <string id="30026">Tyyppi:</string>
++ <string id="30027">Laite:</string>
++ <string id="30028">Haku: %i</string>
++ <string id="30029">Signaali: %i %%</string>
++ <string id="30030">Uudet kanavat: %i</string>
++ <string id="30031">Kaikki kanavat: %i</string>
++ <string id="30032">Analoginen TV</string>
++ <string id="30033">Analoginen radio</string>
++ <string id="30034">Transponder:</string>
++ <string id="30035">Uudet kanavat</string>
++ <string id="30036">Kanavahaku valmis</string>
++ <string id="30037">Ei laitetta saatavilla - lopetetaan</string>
++ <string id="30038">Ei DVB-S2 laitetta saatavilla - kokeillaan DVB-S</string>
++ <string id="30039">Käynnissä</string>
++ <string id="30040">Pysäytetty</string>
++ <string id="30041">Valmis</string>
++ <string id="30042">Kanavahaku peruutettu</string>
++ <string id="30043">Kanavahaku virhe</string>
++ <string id="30044">Yhteys VDR-palvelimeen menetetty</string>
++ <string id="30045">Yhteys VDR-palvelimeen palautettu</string>
++ <string id="30046">Luo kanavaryhmät automaattisesti palvelimella</string>
++</strings>
+diff --git a/addons/pvr.vdr.vnsi/resources/language/German/strings.xml b/addons/pvr.vdr.vnsi/resources/language/German/strings.xml
+new file mode 100644
+index 0000000..48f6834
+--- /dev/null
++++ b/addons/pvr.vdr.vnsi/resources/language/German/strings.xml
+@@ -0,0 +1,51 @@
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<strings>
++ <!-- settings labels -->
++ <string id="30000">VDR Hostname oder IP</string>
++ <string id="30001">VNSI Port</string>
++ <string id="30002">Priorität</string>
++ <string id="30003">Textkonvertierung (UTF-8)</string>
++ <string id="30004">Verbindungszeitüberlauf (s)</string>
++ <string id="30005">VDR Nachrichten erlauben</string>
++ <string id="30006">Aufnahmen aus Ordner lesen</string>
++ <string id="30007">VDR Aufnahmeordner</string>
++ <string id="30008">Kanalsuche</string>
++ <string id="30009">Kanalsuche - Einstellungen</string>
++ <string id="30010">Kanalsuche starten</string>
++ <string id="30011">Empfangsart</string>
++ <string id="30012">TV Kanäle</string>
++ <string id="30013">Radio Kanäle</string>
++ <string id="30014">Frei empfangbare Kanäle</string>
++ <string id="30015">Verschlüsselte Kanäle</string>
++ <string id="30016">HD Kanäle</string>
++ <string id="30017">Land</string>
++ <string id="30018">Kabel Inversion</string>
++ <string id="30019">Kabel Symbolrate</string>
++ <string id="30020">Kabel Modulation</string>
++ <string id="30021">Terrestrisch Inversion</string>
++ <string id="30022">Satellit</string>
++ <string id="30023">ATSC Type</string>
++ <string id="30024">Zurück</string>
++ <string id="30025">Kanalsuche - läuft... %i %%</string>
++ <string id="30026">Empfangsart:</string>
++ <string id="30027">Gerät:</string>
++ <string id="30028">Fortschritt: %i</string>
++ <string id="30029">Signal: %i %%</string>
++ <string id="30030">Neue Kanäle: %i</string>
++ <string id="30031">Alle Kanäle: %i</string>
++ <string id="30032">Analog TV</string>
++ <string id="30033">Analog Radio</string>
++ <string id="30034">Transponder:</string>
++ <string id="30035">Neue Kanäle</string>
++ <string id="30036">Kanalsuche - Abgeschlossen</string>
++ <string id="30037">Kein Empfangsgerät verfügbar</string>
++ <string id="30038">Kein DVB-S2 Empfangsgerät verfügbar - versuche DVB-S</string>
++ <string id="30039">Läuft...</string>
++ <string id="30040">Angehalten</string>
++ <string id="30041">Fertig</string>
++ <string id="30042">Kanalsuche - Abgebrochen</string>
++ <string id="30043">Kanalsuche - Fehler</string>
++ <string id="30044">Verbindung zum VDR Server unterbrochen</string>
++ <string id="30045">Verbindung zum VDR Server wiederhergestellt</string>
++ <string id="30046">Kanalgruppen automatisch am Server erzeugen</string>
++</strings>
+diff --git a/addons/pvr.vdr.vnsi/resources/settings.xml b/addons/pvr.vdr.vnsi/resources/settings.xml
+new file mode 100644
+index 0000000..557f54c
+--- /dev/null
++++ b/addons/pvr.vdr.vnsi/resources/settings.xml
+@@ -0,0 +1,10 @@
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<settings>
++ <setting id="host" type="text" label="30000" default="127.0.0.1" />
++ <setting id="port" type="number" label="30001" default="34890" />
++ <setting id="priority" type="enum" label="30002" values="-1|0|5|10|15|20|25|30|35|40|45|50|55|60|65|70|75|80|85|90|95|99|100" default="99"/>
++ <setting id="convertchar" type="bool" label="30003" default="true" />
++ <setting id="timeout" type="enum" label="30004" values="0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15" default="3"/>
++ <setting id="handlemessages" type="bool" label="30005" default="true" />
++ <setting id="autochannelgroups" type="bool" label="30046" default="false" />
++</settings>
+diff --git a/addons/pvr.vdr.vnsi/resources/skins/Confluence/720p/ChannelScan.xml b/addons/pvr.vdr.vnsi/resources/skins/Confluence/720p/ChannelScan.xml
+new file mode 100644
+index 0000000..e4f6338
+--- /dev/null
++++ b/addons/pvr.vdr.vnsi/resources/skins/Confluence/720p/ChannelScan.xml
+@@ -0,0 +1,733 @@
++<window>
++ <defaultcontrol always="true">5</defaultcontrol>
++ <allowoverlay>no</allowoverlay>
++ <views>2</views>
++ <controls>
++ <include>CommonSettingsBackground</include>
++ <include>CommonMediaPlayingBackground</include>
++ <control type="group">
++ <posx>90</posx>
++ <posy>50</posy>
++ <animation type="WindowOpen" reversible="false">
++ <effect type="zoom" start="80" end="100" center="640,360" easing="out" tween="back" time="300" />
++ <effect type="fade" start="0" end="100" time="300" />
++ </animation>
++ <animation type="WindowClose" reversible="false">
++ <effect type="zoom" start="100" end="80" center="640,360" easing="in" tween="back" time="300" />
++ <effect type="fade" start="100" end="0" time="300" />
++ </animation>
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>1100</width>
++ <height>640</height>
++ <texture border="20">DialogBack.png</texture>
++ </control>
++ <control type="image">
++ <description>LOGO</description>
++ <posx>30</posx>
++ <posy>15</posy>
++ <width>220</width>
++ <height>80</height>
++ <aspectratio>keep</aspectratio>
++ <texture>Confluence_Logo.png</texture>
++ </control>
++ <control type="image">
++ <posx>268</posx>
++ <posy>10</posy>
++ <width>790</width>
++ <height>618</height>
++ <texture border="5">black-back2.png</texture>
++ </control>
++ <control type="image">
++ <posx>268</posx>
++ <posy>10</posy>
++ <width>804</width>
++ <height>70</height>
++ <aspectratio>stretch</aspectratio>
++ <texture>GlassTitleBar.png</texture>
++ </control>
++ <control type="label" id ="8">
++ <description>header label</description>
++ <posx>300</posx>
++ <posy>20</posy>
++ <width>740</width>
++ <height>30</height>
++ <font>font16caps</font>
++ <label>$ADDON[pvr.vdr.vnsi 30009]</label>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ </control>
++ <control type="button" id ="5">
++ <description>Start/Stop Channel search</description>
++ <posx>10</posx>
++ <posy>90</posy>
++ <width>260</width>
++ <height>60</height>
++ <textoffsety>13</textoffsety>
++ <label>$ADDON[pvr.vdr.vnsi 30010]</label>
++ <font>font13_title</font>
++ <align>right</align>
++ <aligny>center</aligny>
++ <texturenofocus border="5">MenuItemNF.png</texturenofocus>
++ <texturefocus border="5">MenuItemFO.png</texturefocus>
++ <onleft>2</onleft>
++ <onright>10</onright>
++ <onup>6</onup>
++ <ondown>6</ondown>
++ </control>
++ <control type="button" id ="6">
++ <description>Cancel</description>
++ <posx>10</posx>
++ <posy>150</posy>
++ <width>260</width>
++ <height>60</height>
++ <textoffsety>13</textoffsety>
++ <label>$ADDON[pvr.vdr.vnsi 30024]</label>
++ <font>font13_title</font>
++ <align>right</align>
++ <aligny>center</aligny>
++ <texturenofocus border="5">MenuItemNF.png</texturenofocus>
++ <texturefocus border="5">MenuItemFO.png</texturefocus>
++ <onleft>10</onleft>
++ <onright>10</onright>
++ <onup>5</onup>
++ <ondown>5</ondown>
++ <visible>IsEmpty(Window.Property(Scanning))</visible>
++ </control>
++ <control type="group">
++ <visible>IsEmpty(Window.Property(Scanning))</visible>
++ <control type="spincontrolex" id="10">
++ <description>Source Type</description>
++ <posx>268</posx>
++ <posy>80</posy>
++ <width>790</width>
++ <height>40</height>
++ <font>font13</font>
++ <textcolor>grey2</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
++ <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
++ <aligny>center</aligny>
++ <label>$ADDON[pvr.vdr.vnsi 30011]</label>
++ <onright>5</onright>
++ <onleft>5</onleft>
++ <onup>22</onup>
++ <ondown>11</ondown>
++ </control>
++ <control type="radiobutton" id="11">
++ <description>Default RadioButton</description>
++ <posx>268</posx>
++ <posy>120</posy>
++ <width>790</width>
++ <height>40</height>
++ <font>font13</font>
++ <textcolor>grey2</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
++ <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
++ <label>$ADDON[pvr.vdr.vnsi 30012]</label>
++ <onright>5</onright>
++ <onleft>5</onleft>
++ <onup>10</onup>
++ <ondown>12</ondown>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(10)">Conditional</animation>
++ </control>
++ <control type="radiobutton" id="12">
++ <description>Default RadioButton</description>
++ <posx>268</posx>
++ <posy>160</posy>
++ <width>790</width>
++ <height>40</height>
++ <font>font13</font>
++ <textcolor>grey2</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
++ <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
++ <label>$ADDON[pvr.vdr.vnsi 30013]</label>
++ <onright>5</onright>
++ <onleft>5</onleft>
++ <onup>11</onup>
++ <ondown>13</ondown>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(10)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(11)">Conditional</animation>
++ </control>
++ <control type="radiobutton" id="13">
++ <description>Default RadioButton</description>
++ <posx>268</posx>
++ <posy>200</posy>
++ <width>790</width>
++ <height>40</height>
++ <font>font13</font>
++ <textcolor>grey2</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
++ <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
++ <label>$ADDON[pvr.vdr.vnsi 30014]</label>
++ <onright>5</onright>
++ <onleft>5</onleft>
++ <onup>12</onup>
++ <ondown>14</ondown>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(10)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(11)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(12)">Conditional</animation>
++ </control>
++ <control type="radiobutton" id="14">
++ <description>Default RadioButton</description>
++ <posx>268</posx>
++ <posy>240</posy>
++ <width>790</width>
++ <height>40</height>
++ <font>font13</font>
++ <textcolor>grey2</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
++ <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
++ <label>$ADDON[pvr.vdr.vnsi 30015]</label>
++ <onright>5</onright>
++ <onleft>5</onleft>
++ <onup>13</onup>
++ <ondown>15</ondown>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(10)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(11)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(12)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(13)">Conditional</animation>
++ </control>
++ <control type="radiobutton" id="15">
++ <description>Default RadioButton</description>
++ <posx>268</posx>
++ <posy>280</posy>
++ <width>790</width>
++ <height>40</height>
++ <font>font13</font>
++ <textcolor>grey2</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
++ <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
++ <label>$ADDON[pvr.vdr.vnsi 30016]</label>
++ <onright>5</onright>
++ <onleft>5</onleft>
++ <onup>14</onup>
++ <ondown>16</ondown>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(10)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(11)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(12)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(13)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(14)">Conditional</animation>
++ </control>
++ <control type="spincontrolex" id="16">
++ <description>Country selection</description>
++ <posx>268</posx>
++ <posy>320</posy>
++ <width>790</width>
++ <height>40</height>
++ <font>font13</font>
++ <textcolor>grey2</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
++ <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
++ <aligny>center</aligny>
++ <label>$ADDON[pvr.vdr.vnsi 30017]</label>
++ <onright>5</onright>
++ <onleft>5</onleft>
++ <onup>15</onup>
++ <ondown>17</ondown>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(10)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(11)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(12)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(13)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(14)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(15)">Conditional</animation>
++ </control>
++ <control type="spincontrolex" id="17">
++ <description>Satellite selection</description>
++ <posx>268</posx>
++ <posy>360</posy>
++ <width>790</width>
++ <height>40</height>
++ <font>font13</font>
++ <textcolor>grey2</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
++ <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
++ <aligny>center</aligny>
++ <label>$ADDON[pvr.vdr.vnsi 30022]</label>
++ <onright>5</onright>
++ <onleft>5</onleft>
++ <onup>16</onup>
++ <ondown>18</ondown>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(10)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(11)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(12)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(13)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(14)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(15)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(16)">Conditional</animation>
++ </control>
++ <control type="spincontrolex" id="18">
++ <description>DVB-C Inversion</description>
++ <posx>268</posx>
++ <posy>400</posy>
++ <width>790</width>
++ <height>40</height>
++ <font>font13</font>
++ <textcolor>grey2</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
++ <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
++ <aligny>center</aligny>
++ <label>$ADDON[pvr.vdr.vnsi 30018]</label>
++ <onright>5</onright>
++ <onleft>5</onleft>
++ <onup>17</onup>
++ <ondown>29</ondown>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(10)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(11)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(12)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(13)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(14)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(15)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(16)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(17)">Conditional</animation>
++ </control>
++ <control type="spincontrolex" id="29">
++ <description>DVB-C Symbolrate</description>
++ <posx>268</posx>
++ <posy>440</posy>
++ <width>790</width>
++ <height>40</height>
++ <font>font13</font>
++ <textcolor>grey2</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
++ <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
++ <aligny>center</aligny>
++ <label>$ADDON[pvr.vdr.vnsi 30019]</label>
++ <onright>5</onright>
++ <onleft>5</onleft>
++ <onup>18</onup>
++ <ondown>20</ondown>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(10)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(11)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(12)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(13)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(14)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(15)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(16)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(17)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(18)">Conditional</animation>
++ </control>
++ <control type="spincontrolex" id="20">
++ <description>DVB-C QAM</description>
++ <posx>268</posx>
++ <posy>480</posy>
++ <width>790</width>
++ <height>40</height>
++ <font>font13</font>
++ <textcolor>grey2</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
++ <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
++ <aligny>center</aligny>
++ <label>$ADDON[pvr.vdr.vnsi 30020]</label>
++ <onright>5</onright>
++ <onleft>5</onleft>
++ <onup>29</onup>
++ <ondown>21</ondown>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(10)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(11)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(12)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(13)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(14)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(15)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(16)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(17)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(18)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(29)">Conditional</animation>
++ </control>
++ <control type="spincontrolex" id="21">
++ <description>DVB-T Inversion</description>
++ <posx>268</posx>
++ <posy>520</posy>
++ <width>790</width>
++ <height>40</height>
++ <font>font13</font>
++ <textcolor>grey2</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
++ <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
++ <aligny>center</aligny>
++ <label>$ADDON[pvr.vdr.vnsi 30021]</label>
++ <onright>5</onright>
++ <onleft>5</onleft>
++ <onup>20</onup>
++ <ondown>22</ondown>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(10)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(11)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(12)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(13)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(14)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(15)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(16)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(17)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(18)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(29)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(20)">Conditional</animation>
++ </control>
++ <control type="spincontrolex" id="22">
++ <description>ATSC Type</description>
++ <posx>268</posx>
++ <posy>560</posy>
++ <width>790</width>
++ <height>40</height>
++ <font>font13</font>
++ <textcolor>grey2</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
++ <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
++ <aligny>center</aligny>
++ <label>$ADDON[pvr.vdr.vnsi 30023]</label>
++ <onright>5</onright>
++ <onleft>5</onleft>
++ <onup>21</onup>
++ <ondown>10</ondown>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(10)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(11)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(12)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(13)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(14)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(15)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(16)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(17)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(18)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(29)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(20)">Conditional</animation>
++ <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(21)">Conditional</animation>
++ </control>
++ </control>
++ <control type="group">
++ <visible>!IsEmpty(Window.Property(Scanning))</visible>
++ <control type="progress" id ="32">
++ <description>Progressbar</description>
++ <posx>275</posx>
++ <posy>60</posy>
++ <width>780</width>
++ <height>14</height>
++ </control>
++ <control type="label">
++ <description>type label</description>
++ <posx>275</posx>
++ <posy>85</posy>
++ <width>250</width>
++ <height>30</height>
++ <font>font13</font>
++ <label>$ADDON[pvr.vdr.vnsi 30026]</label>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey</textcolor>
++ <shadowcolor>black</shadowcolor>
++ </control>
++ <control type="label" id ="30">
++ <description>type value</description>
++ <posx>1040</posx>
++ <posy>85</posy>
++ <width>500</width>
++ <height>30</height>
++ <font>font13</font>
++ <label>-</label>
++ <align>right</align>
++ <aligny>center</aligny>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ </control>
++ <control type="label">
++ <description>device label</description>
++ <posx>275</posx>
++ <posy>115</posy>
++ <width>250</width>
++ <height>30</height>
++ <font>font13</font>
++ <label>$ADDON[pvr.vdr.vnsi 30027]</label>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ </control>
++ <control type="label" id ="31">
++ <description>device value</description>
++ <posx>1040</posx>
++ <posy>115</posy>
++ <width>500</width>
++ <height>30</height>
++ <font>font13</font>
++ <label>-</label>
++ <align>right</align>
++ <aligny>center</aligny>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ </control>
++ <control type="label">
++ <description>transponder label</description>
++ <posx>275</posx>
++ <posy>145</posy>
++ <width>250</width>
++ <height>30</height>
++ <font>font13</font>
++ <label>$ADDON[pvr.vdr.vnsi 30034]</label>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ </control>
++ <control type="label" id ="33">
++ <description>transponder value</description>
++ <posx>1040</posx>
++ <posy>145</posy>
++ <width>500</width>
++ <height>30</height>
++ <font>font13</font>
++ <label>-</label>
++ <align>right</align>
++ <aligny>center</aligny>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ </control>
++ <control type="progress" id ="35">
++ <description>Progressbar</description>
++ <posx>30</posx>
++ <posy>160</posy>
++ <width>220</width>
++ <height>50</height>
++ </control>
++ <control type="label" id ="34">
++ <description>Signal label</description>
++ <posx>40</posx>
++ <posy>168</posy>
++ <width>250</width>
++ <height>30</height>
++ <font>font13</font>
++ <label>-</label>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ </control>
++ <control type="image">
++ <posx>215</posx>
++ <posy>170</posy>
++ <width>30</width>
++ <height>30</height>
++ <aspectratio>stretch</aspectratio>
++ <texture>amt-overlay-watched.png</texture>
++ <visible>!IsEmpty(Window.Property(Locked))</visible>
++ </control>
++ <control type="list" id="2">
++ <posx>290</posx>
++ <posy>180</posy>
++ <width>750</width>
++ <height>400</height>
++ <onup>2</onup>
++ <ondown>2</ondown>
++ <onleft>10</onleft>
++ <onright>60</onright>
++ <pagecontrol>60</pagecontrol>
++ <scrolltime>200</scrolltime>
++ <itemlayout height="40" width="750">
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>750</width>
++ <height>40</height>
++ <aspectratio>stretch</aspectratio>
++ <texture border="5">MenuItemNF.png</texture>
++ </control>
++ <control type="image">
++ <posx>710</posx>
++ <posy>5</posy>
++ <width>40</width>
++ <height>30</height>
++ <aspectratio>stretch</aspectratio>
++ <texture>OverlayLocked.png</texture>
++ <visible>!IsEmpty(ListItem.Property(IsEncrypted))</visible>
++ </control>
++ <control type="image">
++ <posx>690</posx>
++ <posy>7</posy>
++ <width>60</width>
++ <height>25</height>
++ <aspectratio>stretch</aspectratio>
++ <texture>OverlayHD.png</texture>
++ <animation effect="slide" start="0,0" end="-45,0" time="100" condition="!IsEmpty(ListItem.Property(IsEncrypted))">Conditional</animation>
++ <visible>!IsEmpty(ListItem.Property(IsHD))</visible>
++ </control>
++ <control type="image">
++ <posx>0</posx>
++ <posy>2</posy>
++ <width>36</width>
++ <height>36</height>
++ <aspectratio>stretch</aspectratio>
++ <texture>DefaultVideoCover.png</texture>
++ <visible>IsEmpty(ListItem.Property(IsRadio))</visible>
++ </control>
++ <control type="image">
++ <posx>0</posx>
++ <posy>2</posy>
++ <width>36</width>
++ <height>36</height>
++ <aspectratio>stretch</aspectratio>
++ <texture>DefaultAlbumCover.png</texture>
++ <visible>!IsEmpty(ListItem.Property(IsRadio))</visible>
++ </control>
++ <control type="label">
++ <posx>45</posx>
++ <posy>0</posy>
++ <width>500</width>
++ <height>40</height>
++ <font>font14</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.Label</info>
++ </control>
++ </itemlayout>
++ <focusedlayout height="40" width="750">
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>750</width>
++ <height>40</height>
++ <aspectratio>stretch</aspectratio>
++ <texture border="5">MenuItemNF.png</texture>
++ <visible>!Control.HasFocus(2)</visible>
++ <include>VisibleFadeEffect</include>
++ </control>
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>750</width>
++ <height>40</height>
++ <aspectratio>stretch</aspectratio>
++ <texture border="5">MenuItemFO.png</texture>
++ <visible>Control.HasFocus(2)</visible>
++ <include>VisibleFadeEffect</include>
++ </control>
++ <control type="image">
++ <posx>710</posx>
++ <posy>5</posy>
++ <width>40</width>
++ <height>30</height>
++ <aspectratio>stretch</aspectratio>
++ <texture>OverlayLocked.png</texture>
++ <visible>!IsEmpty(ListItem.Property(IsEncrypted))</visible>
++ </control>
++ <control type="image">
++ <posx>690</posx>
++ <posy>7</posy>
++ <width>60</width>
++ <height>25</height>
++ <aspectratio>stretch</aspectratio>
++ <texture>OverlayHD.png</texture>
++ <animation effect="slide" start="0,0" end="-45,0" time="100" condition="!IsEmpty(ListItem.Property(IsEncrypted))">Conditional</animation>
++ <visible>!IsEmpty(ListItem.Property(IsHD))</visible>
++ </control>
++ <control type="image">
++ <posx>0</posx>
++ <posy>2</posy>
++ <width>36</width>
++ <height>36</height>
++ <aspectratio>stretch</aspectratio>
++ <texture>DefaultVideoCover.png</texture>
++ <visible>IsEmpty(ListItem.Property(IsRadio))</visible>
++ </control>
++ <control type="image">
++ <posx>0</posx>
++ <posy>2</posy>
++ <width>36</width>
++ <height>36</height>
++ <aspectratio>stretch</aspectratio>
++ <texture>DefaultAlbumCover.png</texture>
++ <visible>!IsEmpty(ListItem.Property(IsRadio))</visible>
++ </control>
++ <control type="label">
++ <posx>45</posx>
++ <posy>0</posy>
++ <width>500</width>
++ <height>40</height>
++ <font>font14</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.Label</info>
++ </control>
++ </focusedlayout>
++ </control>
++ <control type="scrollbar" id="60">
++ <posx>1060</posx>
++ <posy>180</posy>
++ <width>25</width>
++ <height>410</height>
++ <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
++ <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
++ <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
++ <textureslidernib>ScrollBarNib.png</textureslidernib>
++ <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
++ <onleft>2</onleft>
++ <onright>10</onright>
++ <showonepage>true</showonepage>
++ <orientation>vertical</orientation>
++ </control>
++ <control type="label">
++ <description>Page Count Label</description>
++ <posx>1040</posx>
++ <posy>600</posy>
++ <width>500</width>
++ <height>20</height>
++ <font>font12</font>
++ <textcolor>grey</textcolor>
++ <scroll>false</scroll>
++ <align>right</align>
++ <aligny>center</aligny>
++ <label>([COLOR=blue]$INFO[Container(2).NumItems][/COLOR]) $LOCALIZE[19019] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(2).CurrentPage]/$INFO[Container(2).NumPages][/COLOR])</label>
++ </control>
++ <control type="label" id="36">
++ <description>Status Label</description>
++ <posx>275</posx>
++ <posy>590</posy>
++ <width>500</width>
++ <height>20</height>
++ <font>font14</font>
++ <textcolor>yellow</textcolor>
++ <scroll>false</scroll>
++ <align>left</align>
++ <aligny>center</aligny>
++ <label>-</label>
++ </control>
++ </control>
++ </control>
++ <include>BehindDialogFadeOut</include>
++ <control type="group">
++ <posx>60</posx>
++ <posy>0</posy>
++ <animation effect="slide" end="-310,0" time="400" tween="quadratic" easing="out">WindowClose</animation>
++ <animation effect="slide" start="-310,0" time="400" tween="quadratic" easing="out">WindowOpen</animation>
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>250</width>
++ <height>35</height>
++ <texture border="0,0,32,0">header.png</texture>
++ </control>
++ <control type="label">
++ <include>WindowTitleCommons</include>
++ <posx>220</posx>
++ <label>$ADDON[pvr.vdr.vnsi 30008]</label>
++ </control>
++ </control>
++ <include>WindowTitleHomeButton</include>
++ <include>Clock</include>
++ </controls>
++</window>
+diff --git a/addons/skin.confluence/720p/DialogButtonMenu.xml b/addons/skin.confluence/720p/DialogButtonMenu.xml
+index 0884883..0ae22e8 100644
+--- a/addons/skin.confluence/720p/DialogButtonMenu.xml
++++ b/addons/skin.confluence/720p/DialogButtonMenu.xml
+@@ -63,9 +63,9 @@
+ <onclick>PreviousMenu</onclick>
+ <texturefocus>DialogCloseButton-focus.png</texturefocus>
+ <texturenofocus>DialogCloseButton.png</texturenofocus>
+- <onleft>13</onleft>
++ <onleft>2</onleft>
+ <onright>13</onright>
+- <onup>9</onup>
++ <onup>13</onup>
+ <ondown>2</ondown>
+ <visible>system.getbool(input.enablemouse)</visible>
+ </control>
+@@ -222,7 +222,7 @@
+ <font>font13</font>
+ <visible>System.HasLocks</visible>
+ </control>
+- <control type="group" id="11">
++ <control type="group" id="10">
+ <width>340</width>
+ <height>70</height>
+ <visible>System.HasAlarm(shutdowntimer)</visible>
+@@ -246,7 +246,39 @@
+ <label>$LOCALIZE[31329] [B]$INFO[System.Alarmpos][/B]</label>
+ </control>
+ </control>
+- <control type="image" id="12">
++ <control type="button" id="11">
++ <description>Inhibit idle shutdown</description>
++ <width>340</width>
++ <height>40</height>
++ <textcolor>grey2</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <align>center</align>
++ <textwidth>290</textwidth>
++ <texturefocus border="25,5,25,5">ShutdownButtonFocus.png</texturefocus>
++ <texturenofocus border="25,5,25,5">ShutdownButtonNoFocus.png</texturenofocus>
++ <onclick>XBMC.InhibitIdleShutdown(true)</onclick>
++ <pulseonselect>no</pulseonselect>
++ <font>font13</font>
++ <label>13017</label>
++ <visible>System.HasShutdown +!System.IsInhibit</visible>
++ </control>
++ <control type="button" id="12">
++ <description>Allow idle shutdown</description>
++ <width>340</width>
++ <height>40</height>
++ <textcolor>grey2</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <align>center</align>
++ <textwidth>290</textwidth>
++ <texturefocus border="25,5,25,5">ShutdownButtonFocus.png</texturefocus>
++ <texturenofocus border="25,5,25,5">ShutdownButtonNoFocus.png</texturenofocus>
++ <onclick>XBMC.InhibitIdleShutdown(false)</onclick>
++ <pulseonselect>no</pulseonselect>
++ <font>font13</font>
++ <label>13018</label>
++ <visible>System.HasShutdown + System.IsInhibit</visible>
++ </control>
++ <control type="image" id="13">
+ <description>background bottom image</description>
+ <posx>0</posx>
+ <width>340</width>
+diff --git a/addons/skin.confluence/720p/DialogExtendedProgressBar.xml b/addons/skin.confluence/720p/DialogExtendedProgressBar.xml
+new file mode 100644
+index 0000000..412d92c
+--- /dev/null
++++ b/addons/skin.confluence/720p/DialogExtendedProgressBar.xml
+@@ -0,0 +1,48 @@
++<window id="614">
++ <defaultcontrol></defaultcontrol>
++ <animation effect="slide" start="0,-70" end="0,0" time="100">WindowOpen</animation>
++ <animation effect="slide" start="0,0" end="0,-70" delay="400" time="100">WindowClose</animation>
++ <controls>
++ <control type="group">
++ <posx>720</posx>
++ <posy>0</posy>
++ <animation effect="slide" end="-400,0" time="200" condition="Window.IsVisible(133)">conditional</animation>
++ <animation effect="slide" end="0,-80" time="200" condition="Window.IsVisible(FullscreenVideo) | Window.IsVisible(Visualisation)">conditional</animation>
++ <control type="image">
++ <posx>0</posx>
++ <posy>-10</posy>
++ <width>400</width>
++ <height>70</height>
++ <texture flipy="true" border="20,20,20,2">InfoMessagePanel.png</texture>
++ </control>
++ <control type="label" id="30">
++ <description>Header Label</description>
++ <posx>15</posx>
++ <posy>4</posy>
++ <width>370</width>
++ <height>18</height>
++ <font>font10_title</font>
++ <textcolor>selected</textcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ </control>
++ <control type="label" id="31">
++ <description>Title Label</description>
++ <posx>15</posx>
++ <posy>20</posy>
++ <width>370</width>
++ <height>20</height>
++ <font>font10</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ </control>
++ <control type="progress" id="32">
++ <description>progress control</description>
++ <posx>15</posx>
++ <posy>42</posy>
++ <width>370</width>
++ <height>8</height>
++ </control>
++ </control>
++ </controls>
++</window>
+\ No newline at end of file
+diff --git a/addons/skin.confluence/720p/DialogPVRChannelManager.xml b/addons/skin.confluence/720p/DialogPVRChannelManager.xml
+new file mode 100644
+index 0000000..d1ffa67
+--- /dev/null
++++ b/addons/skin.confluence/720p/DialogPVRChannelManager.xml
+@@ -0,0 +1,539 @@
++<window id="605">
++ <defaultcontrol always="true">20</defaultcontrol>
++ <allowoverlay>no</allowoverlay>
++ <coordinates>
++ <system>1</system>
++ <posx>190</posx>
++ <posy>30</posy>
++ </coordinates>
++ <include>dialogeffect</include>
++
++ <controls>
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>900</width>
++ <height>660</height>
++ <texture border="40">DialogBack.png</texture>
++ </control>
++ <control type="image">
++ <description>Dialog Header image</description>
++ <posx>40</posx>
++ <posy>16</posy>
++ <width>820</width>
++ <height>40</height>
++ <texture>dialogheader.png</texture>
++ </control>
++ <control type="label">
++ <description>header label</description>
++ <posx>40</posx>
++ <posy>20</posy>
++ <width>820</width>
++ <height>30</height>
++ <font>font13_title</font>
++ <label>$LOCALIZE[19199] - $LOCALIZE[19023]</label>
++ <align>center</align>
++ <aligny>center</aligny>
++ <textcolor>selected</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <visible>IsEmpty(Window.Property(IsRadio))</visible>
++ </control>
++ <control type="label">
++ <description>header label</description>
++ <posx>40</posx>
++ <posy>20</posy>
++ <width>820</width>
++ <height>30</height>
++ <font>font13_title</font>
++ <label>$LOCALIZE[19199] - $LOCALIZE[19024]</label>
++ <align>center</align>
++ <aligny>center</aligny>
++ <textcolor>selected</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <visible>!IsEmpty(Window.Property(IsRadio))</visible>
++ </control>
++ <control type="button">
++ <description>Close Window button</description>
++ <posx>810</posx>
++ <posy>15</posy>
++ <width>64</width>
++ <height>32</height>
++ <label>-</label>
++ <font>-</font>
++ <onclick>PreviousMenu</onclick>
++ <texturefocus>DialogCloseButton-focus.png</texturefocus>
++ <texturenofocus>DialogCloseButton.png</texturenofocus>
++ <onleft>10</onleft>
++ <onright>10</onright>
++ <onup>10</onup>
++ <ondown>10</ondown>
++ <visible>system.getbool(input.enablemouse)</visible>
++ </control>
++ <control type="group">
++ <posx>20</posx>
++ <posy>70</posy>
++ <control type="scrollbar" id="60">
++ <posx>0</posx>
++ <posy>5</posy>
++ <width>25</width>
++ <height>470</height>
++ <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
++ <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
++ <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
++ <textureslidernib>ScrollBarNib.png</textureslidernib>
++ <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
++ <onleft>9002</onleft>
++ <onright>20</onright>
++ <showonepage>false</showonepage>
++ <orientation>vertical</orientation>
++ </control>
++ <control type="image">
++ <posx>25</posx>
++ <posy>0</posy>
++ <width>430</width>
++ <height>475</height>
++ <texture border="5">button-nofocus.png</texture>
++ </control>
++ <control type="list" id="20">
++ <posx>30</posx>
++ <posy>5</posy>
++ <width>420</width>
++ <height>470</height>
++ <onup>20</onup>
++ <ondown>20</ondown>
++ <onleft>60</onleft>
++ <onright>9002</onright>
++ <pagecontrol>60</pagecontrol>
++ <scrolltime>200</scrolltime>
++ <itemlayout height="45" width="420">
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>420</width>
++ <height>40</height>
++ <texture border="5">button-nofocus.png</texture>
++ </control>
++ <control type="image">
++ <posx>5</posx>
++ <posy>5</posy>
++ <width>30</width>
++ <height>30</height>
++ <texture>$INFO[ListItem.Property(Icon)]</texture>
++ <visible>ListItem.Property(ActiveChannel)</visible>
++ </control>
++ <control type="image">
++ <posx>5</posx>
++ <posy>5</posy>
++ <width>30</width>
++ <height>30</height>
++ <colordiffuse>77FFFFFF</colordiffuse>
++ <texture>$INFO[ListItem.Property(Icon)]</texture>
++ <visible>!ListItem.Property(ActiveChannel)</visible>
++ </control>
++ <control type="label">
++ <posx>45</posx>
++ <posy>0</posy>
++ <width>335</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>white</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <label>$INFO[ListItem.Property(Number),(,) - ]$INFO[ListItem.Property(Name)]</label>
++ <visible>ListItem.Property(ActiveChannel)</visible>
++ </control>
++ <control type="label">
++ <posx>45</posx>
++ <posy>0</posy>
++ <width>335</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey3</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <label>$INFO[ListItem.Property(Number),(,) - ]$INFO[ListItem.Property(Name)]</label>
++ <visible>!ListItem.Property(ActiveChannel)</visible>
++ </control>
++ <control type="image">
++ <posx>390</posx>
++ <posy>10</posy>
++ <width>20</width>
++ <height>20</height>
++ <texture>OverlayWatched.png</texture>
++ <visible>ListItem.Property(Changed)</visible>
++ </control>
++ </itemlayout>
++ <focusedlayout height="65" width="420">
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>420</width>
++ <height>60</height>
++ <texture border="5">button-focus2.png</texture>
++ <animation effect="fade" start="100" end="30" time="0" condition="!Control.HasFocus(20)">conditional</animation>
++ </control>
++ <control type="image">
++ <posx>5</posx>
++ <posy>5</posy>
++ <width>30</width>
++ <height>30</height>
++ <texture>$INFO[ListItem.Property(Icon)]</texture>
++ <visible>ListItem.Property(ActiveChannel)</visible>
++ </control>
++ <control type="image">
++ <posx>5</posx>
++ <posy>5</posy>
++ <width>30</width>
++ <height>30</height>
++ <colordiffuse>77FFFFFF</colordiffuse>
++ <texture>$INFO[ListItem.Property(Icon)]</texture>
++ <visible>!ListItem.Property(ActiveChannel)</visible>
++ </control>
++ <control type="label">
++ <posx>45</posx>
++ <posy>0</posy>
++ <width>335</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>white</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <label>$INFO[ListItem.Property(Number),(,) - ]$INFO[ListItem.Property(Name)]</label>
++ <visible>ListItem.Property(ActiveChannel)</visible>
++ </control>
++ <control type="label">
++ <posx>45</posx>
++ <posy>0</posy>
++ <width>335</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey3</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <label>$INFO[ListItem.Property(Number),(,) - ]$INFO[ListItem.Property(Name)]</label>
++ <visible>!ListItem.Property(ActiveChannel)</visible>
++ </control>
++ <control type="image">
++ <posx>390</posx>
++ <posy>10</posy>
++ <width>20</width>
++ <height>20</height>
++ <texture>OverlayWatched.png</texture>
++ <visible>ListItem.Property(Changed)</visible>
++ </control>
++ <control type="label">
++ <posx>5</posx>
++ <posy>30</posy>
++ <width>410</width>
++ <height>30</height>
++ <font>font12</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <label>$LOCALIZE[19210]: $INFO[ListItem.Property(ClientName)]</label>
++ </control>
++ </focusedlayout>
++ </control>
++ <control type="label">
++ <description>Page Count Label</description>
++ <posx>30</posx>
++ <posy>485</posy>
++ <width>420</width>
++ <height>20</height>
++ <font>font12</font>
++ <textcolor>grey</textcolor>
++ <scroll>false</scroll>
++ <align>center</align>
++ <aligny>center</aligny>
++ <label>([COLOR=blue]$INFO[Container(20).NumItems][/COLOR]) $LOCALIZE[19019] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(20).CurrentPage]/$INFO[Container(20).NumPages][/COLOR])</label>
++ </control>
++ </control>
++ <control type="group" id="9002">
++ <control type="group">
++ <posx>490</posx>
++ <posy>70</posy>
++ <control type="label">
++ <description>channel options Header</description>
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>380</width>
++ <height>20</height>
++ <font>font12</font>
++ <label>$LOCALIZE[31511]</label>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>blue</textcolor>
++ <shadowcolor>black</shadowcolor>
++ </control>
++ <control type="radiobutton" id ="7">
++ <description>Channel activated</description>
++ <posx>0</posx>
++ <posy>25</posy>
++ <width>380</width>
++ <height>35</height>
++ <font>font12</font>
++ <textcolor>white</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <shadowcolor>black</shadowcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <pulseonselect>no</pulseonselect>
++ <label>19074</label>
++ <onleft>20</onleft>
++ <onright>60</onright>
++ <onup>9000</onup>
++ <ondown>8</ondown>
++ </control>
++ <control type="edit" id ="8">
++ <description>Channel name</description>
++ <posx>0</posx>
++ <posy>65</posy>
++ <width>380</width>
++ <height>35</height>
++ <font>font12</font>
++ <textcolor>white</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <shadowcolor>black</shadowcolor>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <label>19201</label>
++ <onright>60</onright>
++ <onleft>20</onleft>
++ <onup>7</onup>
++ <ondown>9</ondown>
++ </control>
++ <control type="button" id ="9">
++ <description>Channel logo Button</description>
++ <posx>0</posx>
++ <posy>105</posy>
++ <width>380</width>
++ <height>35</height>
++ <font>font12</font>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <label>19202</label>
++ <onleft>20</onleft>
++ <onright>60</onright>
++ <onup>8</onup>
++ <ondown>12</ondown>
++ </control>
++ <control type="image" id ="10">
++ <description>Current Channel Icon</description>
++ <posx>345</posx>
++ <posy>107</posy>
++ <width>30</width>
++ <height>30</height>
++ <aspectratio>keep</aspectratio>
++ <info>ListItem.Property(Icon)</info>
++ </control>
++ <control type="radiobutton" id ="12">
++ <description>EPG activated</description>
++ <posx>0</posx>
++ <posy>145</posy>
++ <width>380</width>
++ <height>35</height>
++ <font>font12</font>
++ <textcolor>white</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <shadowcolor>black</shadowcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <pulseonselect>no</pulseonselect>
++ <label>19206</label>
++ <onleft>20</onleft>
++ <onright>60</onright>
++ <onup>9</onup>
++ <ondown>13</ondown>
++ </control>
++ <control type="spincontrolex" id ="13">
++ <description>EPG source</description>
++ <posx>0</posx>
++ <posy>185</posy>
++ <width>380</width>
++ <height>35</height>
++ <font>font12</font>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <label>19200</label>
++ <onright>60</onright>
++ <onleft>20</onleft>
++ <onup>12</onup>
++ <ondown>30</ondown>
++ </control>
++ </control>
++ <control type="group">
++ <posx>490</posx>
++ <posy>360</posy>
++ <control type="label">
++ <description>channel options Header</description>
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>380</width>
++ <height>20</height>
++ <font>font12</font>
++ <label>$LOCALIZE[31026]</label>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>blue</textcolor>
++ <shadowcolor>black</shadowcolor>
++ </control>
++ <control type="button" id ="30">
++ <description>Group Manager Button</description>
++ <posx>0</posx>
++ <posy>25</posy>
++ <width>190</width>
++ <height>35</height>
++ <font>font12</font>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <align>center</align>
++ <label>19205</label>
++ <onleft>20</onleft>
++ <onright>34</onright>
++ <onup>13</onup>
++ <ondown>31</ondown>
++ </control>
++ <control type="button" id ="34">
++ <description>TV/Radio Button</description>
++ <posx>195</posx>
++ <posy>25</posy>
++ <width>185</width>
++ <height>35</height>
++ <font>font12</font>
++ <visible>IsEmpty(Window.Property(IsRadio))</visible>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <align>center</align>
++ <label>19024</label>
++ <onleft>30</onleft>
++ <onright>60</onright>
++ <onup>13</onup>
++ <ondown>31</ondown>
++ </control>
++ <control type="button" id ="34">
++ <description>TV/Radio Button</description>
++ <posx>195</posx>
++ <posy>25</posy>
++ <width>185</width>
++ <height>35</height>
++ <font>font12</font>
++ <visible>!IsEmpty(Window.Property(IsRadio))</visible>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <align>center</align>
++ <label>19023</label>
++ <onleft>30</onleft>
++ <onright>60</onright>
++ <onup>13</onup>
++ <ondown>31</ondown>
++ </control>
++ <control type="button" id ="31">
++ <description>Edit channel Button</description>
++ <posx>0</posx>
++ <posy>65</posy>
++ <width>380</width>
++ <height>35</height>
++ <font>font12</font>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <align>center</align>
++ <label>19203</label>
++ <onleft>20</onleft>
++ <onright>60</onright>
++ <onup>30</onup>
++ <ondown>32</ondown>
++ </control>
++ <control type="button" id ="32">
++ <description>Delete channel Button</description>
++ <posx>0</posx>
++ <posy>105</posy>
++ <width>380</width>
++ <height>35</height>
++ <font>font12</font>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <align>center</align>
++ <label>19211</label>
++ <onleft>20</onleft>
++ <onright>60</onright>
++ <onup>31</onup>
++ <ondown>33</ondown>
++ </control>
++ <control type="button" id ="33">
++ <description>New channel Button</description>
++ <posx>0</posx>
++ <posy>145</posy>
++ <width>380</width>
++ <height>35</height>
++ <font>font12</font>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <align>center</align>
++ <label>19204</label>
++ <onleft>20</onleft>
++ <onright>60</onright>
++ <onup>32</onup>
++ <ondown>9000</ondown>
++ </control>
++ </control>
++ </control>
++ <control type="group" id="9000">
++ <posx>70</posx>
++ <posy>590</posy>
++ <control type="button" id ="4">
++ <description>OK Button</description>
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>250</width>
++ <height>40</height>
++ <label>186</label>
++ <font>font12_title</font>
++ <align>center</align>
++ <aligny>center</aligny>
++ <onleft>6</onleft>
++ <onright>5</onright>
++ <onup>33</onup>
++ <ondown>7</ondown>
++ </control>
++ <control type="button" id ="5">
++ <description>Apply changes Button</description>
++ <posx>260</posx>
++ <posy>0</posy>
++ <width>250</width>
++ <height>40</height>
++ <label>14070</label>
++ <font>font12_title</font>
++ <align>center</align>
++ <aligny>center</aligny>
++ <onleft>4</onleft>
++ <onright>6</onright>
++ <onup>33</onup>
++ <ondown>7</ondown>
++ </control>
++ <control type="button" id ="6">
++ <description>Cancel Button</description>
++ <posx>520</posx>
++ <posy>0</posy>
++ <width>250</width>
++ <height>40</height>
++ <label>222</label>
++ <font>font12_title</font>
++ <align>center</align>
++ <aligny>center</aligny>
++ <onleft>5</onleft>
++ <onright>4</onright>
++ <onup>33</onup>
++ <ondown>7</ondown>
++ </control>
++ </control>
++ </controls>
++</window>
+diff --git a/addons/skin.confluence/720p/DialogPVRChannelsOSD.xml b/addons/skin.confluence/720p/DialogPVRChannelsOSD.xml
+new file mode 100644
+index 0000000..4d86a0a
+--- /dev/null
++++ b/addons/skin.confluence/720p/DialogPVRChannelsOSD.xml
+@@ -0,0 +1,296 @@
++<window id="609">
++ <defaultcontrol always="true">11</defaultcontrol>
++ <coordinates>
++ <system>1</system>
++ <posx>780</posx>
++ <posy>30</posy>
++ </coordinates>
++ <include>dialogeffect</include>
++ <controls>
++ <control type="group">
++ <control type="image">
++ <description>background image</description>
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>480</width>
++ <height>660</height>
++ <texture border="40">DialogBack2.png</texture>
++ </control>
++ <control type="button">
++ <description>Close Window button</description>
++ <posx>390</posx>
++ <posy>15</posy>
++ <width>64</width>
++ <height>32</height>
++ <label>-</label>
++ <font>-</font>
++ <onclick>PreviousMenu</onclick>
++ <texturefocus>DialogCloseButton-focus.png</texturefocus>
++ <texturenofocus>DialogCloseButton.png</texturenofocus>
++ <onleft>2</onleft>
++ <onright>2</onright>
++ <onup>2</onup>
++ <ondown>2</ondown>
++ <visible>system.getbool(input.enablemouse)</visible>
++ </control>
++ <control type="image">
++ <description>Dialog Header image</description>
++ <posx>40</posx>
++ <posy>16</posy>
++ <width>400</width>
++ <height>40</height>
++ <texture>dialogheader.png</texture>
++ </control>
++ <control type="label">
++ <description>header label</description>
++ <posx>40</posx>
++ <posy>16</posy>
++ <width>430</width>
++ <height>40</height>
++ <font>font12_title</font>
++ <label>$LOCALIZE[19023] - $INFO[VideoPlayer.ChannelGroup]</label>
++ <align>center</align>
++ <aligny>center</aligny>
++ <textcolor>blue</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <visible>!pvr.IsPlayingRadio</visible>
++ </control>
++ <control type="label">
++ <description>header label</description>
++ <posx>40</posx>
++ <posy>16</posy>
++ <width>430</width>
++ <height>40</height>
++ <font>font12_title</font>
++ <label>$LOCALIZE[19024] - $INFO[MusicPlayer.ChannelGroup]</label>
++ <align>center</align>
++ <aligny>center</aligny>
++ <textcolor>blue</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <visible>pvr.IsPlayingRadio</visible>
++ </control>
++ <control type="list" id="11">
++ <posx>30</posx>
++ <posy>70</posy>
++ <width>410</width>
++ <height>520</height>
++ <onleft>60</onleft>
++ <onright>60</onright>
++ <onup>11</onup>
++ <ondown>11</ondown>
++ <viewtype label="535">list</viewtype>
++ <pagecontrol>60</pagecontrol>
++ <scrolltime>200</scrolltime>
++ <itemlayout height="65" width="410">
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>410</width>
++ <height>60</height>
++ <texture border="5">button-nofocus.png</texture>
++ <include>VisibleFadeEffect</include>
++ </control>
++ <control type="label">
++ <posx>5</posx>
++ <posy>0</posy>
++ <width>40</width>
++ <height>30</height>
++ <font>font12</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey</textcolor>
++ <selectedcolor>grey</selectedcolor>
++ <info>ListItem.ChannelNumber</info>
++ </control>
++ <control type="image">
++ <posx>5</posx>
++ <posy>35</posy>
++ <width>30</width>
++ <height>20</height>
++ <texture>PVR-IsRecording.png</texture>
++ <visible>ListItem.IsRecording</visible>
++ </control>
++ <control type="label">
++ <posx>50</posx>
++ <posy>0</posy>
++ <width>350</width>
++ <height>25</height>
++ <font>font13</font>
++ <textcolor>white</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <label>$INFO[ListItem.Label]</label>
++ </control>
++ <control type="label">
++ <posx>50</posx>
++ <posy>25</posy>
++ <width>350</width>
++ <height>20</height>
++ <font>font12</font>
++ <textcolor>grey</textcolor>
++ <selectedcolor>grey</selectedcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <label>$INFO[ListItem.Title]</label>
++ <visible>IsEmpty(Listitem.Icon)</visible>
++ </control>
++ <control type="label">
++ <posx>50</posx>
++ <posy>25</posy>
++ <width>300</width>
++ <height>20</height>
++ <font>font12</font>
++ <textcolor>grey</textcolor>
++ <selectedcolor>grey</selectedcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <label>$INFO[ListItem.Title]</label>
++ <visible>!IsEmpty(Listitem.Icon)</visible>
++ </control>
++ <control type="progress">
++ <description>Progressbar</description>
++ <posx>50</posx>
++ <posy>50</posy>
++ <width>300</width>
++ <height>6</height>
++ <colordiffuse>88FFFFFF</colordiffuse>
++ <info>ListItem.Progress</info>
++ </control>
++ <control type="image">
++ <posx>355</posx>
++ <posy>4</posy>
++ <width>50</width>
++ <height>50</height>
++ <texture>$INFO[ListItem.Icon]</texture>
++ </control>
++ </itemlayout>
++ <focusedlayout height="65" width="410">
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>410</width>
++ <height>60</height>
++ <texture border="5">button-nofocus.png</texture>
++ <visible>!Control.HasFocus(11)</visible>
++ <include>VisibleFadeEffect</include>
++ </control>
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>410</width>
++ <height>60</height>
++ <texture border="5">button-focus2.png</texture>
++ <visible>Control.HasFocus(11)</visible>
++ <include>VisibleFadeEffect</include>
++ </control>
++ <control type="label">
++ <posx>5</posx>
++ <posy>0</posy>
++ <width>40</width>
++ <height>30</height>
++ <font>font12</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey</textcolor>
++ <selectedcolor>grey</selectedcolor>
++ <info>ListItem.ChannelNumber</info>
++ </control>
++ <control type="image">
++ <posx>5</posx>
++ <posy>35</posy>
++ <width>30</width>
++ <height>20</height>
++ <texture>PVR-IsRecording.png</texture>
++ <visible>ListItem.IsRecording</visible>
++ </control>
++ <control type="label">
++ <posx>50</posx>
++ <posy>0</posy>
++ <width>350</width>
++ <height>25</height>
++ <font>font13</font>
++ <textcolor>white</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <label>$INFO[ListItem.Label]</label>
++ </control>
++ <control type="label">
++ <posx>50</posx>
++ <posy>25</posy>
++ <width>350</width>
++ <height>20</height>
++ <font>font12</font>
++ <textcolor>grey</textcolor>
++ <selectedcolor>grey</selectedcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <label>$INFO[ListItem.Title]</label>
++ <visible>IsEmpty(Listitem.Icon)</visible>
++ </control>
++ <control type="label">
++ <posx>50</posx>
++ <posy>25</posy>
++ <width>300</width>
++ <height>20</height>
++ <font>font12</font>
++ <textcolor>grey</textcolor>
++ <selectedcolor>grey</selectedcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <label>$INFO[ListItem.Title]</label>
++ <visible>!IsEmpty(Listitem.Icon)</visible>
++ </control>
++ <control type="progress">
++ <description>Progressbar</description>
++ <posx>50</posx>
++ <posy>50</posy>
++ <width>300</width>
++ <height>6</height>
++ <colordiffuse>88FFFFFF</colordiffuse>
++ <info>ListItem.Progress</info>
++ </control>
++ <control type="image">
++ <posx>355</posx>
++ <posy>4</posy>
++ <width>50</width>
++ <height>50</height>
++ <texture>$INFO[ListItem.Icon]</texture>
++ </control>
++ </focusedlayout>
++ </control>
++ <control type="scrollbar" id="60">
++ <posx>440</posx>
++ <posy>70</posy>
++ <width>25</width>
++ <height>520</height>
++ <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
++ <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
++ <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
++ <textureslidernib>ScrollBarNib.png</textureslidernib>
++ <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
++ <onleft>11</onleft>
++ <onright>11</onright>
++ <ondown>61</ondown>
++ <onup>61</onup>
++ <showonepage>false</showonepage>
++ <orientation>vertical</orientation>
++ </control>
++ <control type="label">
++ <description>Page Count Label</description>
++ <posx>450</posx>
++ <posy>610</posy>
++ <width>400</width>
++ <height>20</height>
++ <font>font12</font>
++ <textcolor>grey</textcolor>
++ <scroll>false</scroll>
++ <align>right</align>
++ <aligny>center</aligny>
++ <label>([COLOR=blue]$INFO[Container(11).NumItems][/COLOR]) $LOCALIZE[19019] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(11).CurrentPage]/$INFO[Container(11).NumPages][/COLOR])</label>
++ <include>Window_OpenClose_Animation</include>
++ </control>
++ </control>
++ </controls>
++</window>
+\ No newline at end of file
+diff --git a/addons/skin.confluence/720p/DialogPVRGroupManager.xml b/addons/skin.confluence/720p/DialogPVRGroupManager.xml
+new file mode 100644
+index 0000000..5cdaaab
+--- /dev/null
++++ b/addons/skin.confluence/720p/DialogPVRGroupManager.xml
+@@ -0,0 +1,433 @@
++<window id="604">
++ <defaultcontrol always="true">29</defaultcontrol>
++ <controls>
++ <control type="group">
++ <visible>!Window.IsVisible(FileBrowser)</visible>
++ <animation effect="slide" start="1150,0" end="0,0" time="400" tween="quadratic" easing="out">WindowOpen</animation>
++ <animation effect="slide" start="0,0" end="1150,0" time="400" tween="quadratic" easing="out">WindowClose</animation>
++ <control type="image">
++ <posx>130</posx>
++ <posy>0</posy>
++ <width>1150</width>
++ <height>720</height>
++ <texture border="15,0,0,0" flipx="true">MediaBladeSub.png</texture>
++ </control>
++ <control type="button">
++ <description>Close Window button</description>
++ <posx>180</posx>
++ <posy>0</posy>
++ <width>64</width>
++ <height>32</height>
++ <label>-</label>
++ <font>-</font>
++ <onclick>PreviousMenu</onclick>
++ <texturefocus>DialogCloseButton-focus.png</texturefocus>
++ <texturenofocus>DialogCloseButton.png</texturenofocus>
++ <onleft>9000</onleft>
++ <onright>9000</onright>
++ <onup>9000</onup>
++ <ondown>9000</ondown>
++ <visible>system.getbool(input.enablemouse)</visible>
++ </control>
++ <control type="group">
++ <animation effect="fade" delay="400" start="0" end="100" time="200">WindowOpen</animation>
++ <animation effect="fade" start="100" end="0" time="200">WindowClose</animation>
++ <control type="label">
++ <description>header label</description>
++ <posx>160</posx>
++ <posy>40</posy>
++ <width>1080</width>
++ <height>30</height>
++ <font>font24_title</font>
++ <label>19143</label>
++ <align>center</align>
++ <aligny>center</aligny>
++ <textcolor>selected</textcolor>
++ <shadowcolor>black</shadowcolor>
++ </control>
++ <control type="group">
++ <description>Group list</description>
++ <posx>160</posx>
++ <posy>80</posy>
++ <control type="label">
++ <description>name label</description>
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>340</width>
++ <height>70</height>
++ <font>font13</font>
++ <label>31506</label>
++ <align>center</align>
++ <aligny>center</aligny>
++ <textcolor>blue</textcolor>
++ </control>
++ <control type="image">
++ <posx>0</posx>
++ <posy>75</posy>
++ <width>340</width>
++ <height>460</height>
++ <texture border="5">button-nofocus.png</texture>
++ </control>
++ <control type="list" id="13">
++ <posx>5</posx>
++ <posy>80</posy>
++ <width>330</width>
++ <height>450</height>
++ <onup>13</onup>
++ <ondown>13</ondown>
++ <onleft>29</onleft>
++ <onright>73</onright>
++ <pagecontrol>73</pagecontrol>
++ <scrolltime>200</scrolltime>
++ <itemlayout height="45">
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>330</width>
++ <height>40</height>
++ <texture border="5">button-nofocus.png</texture>
++ </control>
++ <control type="label">
++ <posx>10</posx>
++ <posy>0</posy>
++ <width>310</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.Label</info>
++ </control>
++ </itemlayout>
++ <focusedlayout height="45">
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>330</width>
++ <height>40</height>
++ <texture border="5">button-nofocus.png</texture>
++ <visible>!Control.HasFocus(13)</visible>
++ </control>
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>330</width>
++ <height>40</height>
++ <texture border="5">button-focus2.png</texture>
++ <visible>Control.HasFocus(13)</visible>
++ </control>
++ <control type="label">
++ <posx>10</posx>
++ <posy>0</posy>
++ <width>310</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>white</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.Label</info>
++ </control>
++ </focusedlayout>
++ </control>
++ <control type="scrollbar" id="73">
++ <posx>340</posx>
++ <posy>75</posy>
++ <width>25</width>
++ <height>460</height>
++ <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
++ <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
++ <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
++ <textureslidernib>ScrollBarNib.png</textureslidernib>
++ <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
++ <onleft>13</onleft>
++ <onright>11</onright>
++ <ondown>73</ondown>
++ <onup>73</onup>
++ <showonepage>false</showonepage>
++ <orientation>vertical</orientation>
++ </control>
++ </control>
++ <control type="group">
++ <description>Channels list</description>
++ <posx>525</posx>
++ <posy>80</posy>
++ <control type="label" id="21">
++ <description>name label</description>
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>340</width>
++ <height>70</height>
++ <font>font13</font>
++ <align>center</align>
++ <aligny>center</aligny>
++ <textcolor>blue</textcolor>
++ </control>
++ <control type="image">
++ <posx>0</posx>
++ <posy>75</posy>
++ <width>340</width>
++ <height>460</height>
++ <texture border="5">button-nofocus.png</texture>
++ </control>
++ <control type="list" id="11">
++ <posx>5</posx>
++ <posy>85</posy>
++ <width>330</width>
++ <height>450</height>
++ <onup>11</onup>
++ <ondown>11</ondown>
++ <onleft>73</onleft>
++ <onright>71</onright>
++ <pagecontrol>71</pagecontrol>
++ <scrolltime>200</scrolltime>
++ <itemlayout height="45">
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>330</width>
++ <height>40</height>
++ <texture border="5">button-nofocus.png</texture>
++ </control>
++ <control type="image">
++ <width>32</width>
++ <height>32</height>
++ <posx>5</posx>
++ <posy>4</posy>
++ <texture>$INFO[ListItem.Icon]</texture>
++ </control>
++ <control type="label">
++ <posx>40</posx>
++ <posy>0</posy>
++ <width>280</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <label>$INFO[ListItem.ChannelNumber,(,) - ]$INFO[ListItem.ChannelName]</label>
++ </control>
++ </itemlayout>
++ <focusedlayout height="45">
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>330</width>
++ <height>40</height>
++ <texture border="5">button-nofocus.png</texture>
++ <visible>!Control.HasFocus(11)</visible>
++ </control>
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>330</width>
++ <height>40</height>
++ <texture border="5">button-focus2.png</texture>
++ <visible>Control.HasFocus(11)</visible>
++ </control>
++ <control type="image">
++ <width>32</width>
++ <height>32</height>
++ <posx>5</posx>
++ <posy>4</posy>
++ <texture>$INFO[ListItem.Icon]</texture>
++ </control>
++ <control type="label">
++ <posx>40</posx>
++ <posy>0</posy>
++ <width>280</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>white</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <label>$INFO[ListItem.ChannelNumber,(,) - ]$INFO[ListItem.ChannelName]</label>
++ </control>
++ </focusedlayout>
++ </control>
++ <control type="scrollbar" id="71">
++ <posx>340</posx>
++ <posy>75</posy>
++ <width>25</width>
++ <height>460</height>
++ <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
++ <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
++ <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
++ <textureslidernib>ScrollBarNib.png</textureslidernib>
++ <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
++ <onleft>11</onleft>
++ <onright>12</onright>
++ <ondown>71</ondown>
++ <onup>71</onup>
++ <showonepage>false</showonepage>
++ <orientation>vertical</orientation>
++ </control>
++ </control>
++ <control type="group">
++ <description>Grouped Channels list</description>
++ <posx>890</posx>
++ <posy>80</posy>
++ <control type="label" id="22">
++ <description>name label</description>
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>340</width>
++ <height>70</height>
++ <font>font13</font>
++ <align>center</align>
++ <aligny>center</aligny>
++ <textcolor>blue</textcolor>
++ </control>
++ <control type="image">
++ <posx>0</posx>
++ <posy>75</posy>
++ <width>340</width>
++ <height>460</height>
++ <texture border="5">button-nofocus.png</texture>
++ </control>
++ <control type="list" id="12">
++ <posx>5</posx>
++ <posy>85</posy>
++ <width>330</width>
++ <height>450</height>
++ <onup>12</onup>
++ <ondown>12</ondown>
++ <onleft>71</onleft>
++ <onright>72</onright>
++ <pagecontrol>72</pagecontrol>
++ <scrolltime>200</scrolltime>
++ <itemlayout height="45">
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>330</width>
++ <height>40</height>
++ <texture border="5">button-nofocus.png</texture>
++ </control>
++ <control type="image">
++ <width>32</width>
++ <height>32</height>
++ <posx>5</posx>
++ <posy>4</posy>
++ <texture>$INFO[ListItem.Icon]</texture>
++ </control>
++ <control type="label">
++ <posx>40</posx>
++ <posy>0</posy>
++ <width>280</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <label>$INFO[ListItem.ChannelNumber,(,) - ]$INFO[ListItem.ChannelName]</label>
++ </control>
++ </itemlayout>
++ <focusedlayout height="45">
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>330</width>
++ <height>40</height>
++ <texture border="5">button-nofocus.png</texture>
++ <visible>!Control.HasFocus(12)</visible>
++ </control>
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>330</width>
++ <height>40</height>
++ <texture border="5">button-focus2.png</texture>
++ <visible>Control.HasFocus(12)</visible>
++ </control>
++ <control type="image">
++ <width>32</width>
++ <height>32</height>
++ <posx>5</posx>
++ <posy>4</posy>
++ <texture>$INFO[ListItem.Icon]</texture>
++ </control>
++ <control type="label">
++ <posx>40</posx>
++ <posy>0</posy>
++ <width>280</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>white</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <label>$INFO[ListItem.ChannelNumber,(,) - ]$INFO[ListItem.ChannelName]</label>
++ </control>
++ </focusedlayout>
++ </control>
++ <control type="scrollbar" id="72">
++ <posx>340</posx>
++ <posy>75</posy>
++ <width>25</width>
++ <height>460</height>
++ <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
++ <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
++ <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
++ <textureslidernib>ScrollBarNib.png</textureslidernib>
++ <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
++ <onleft>12</onleft>
++ <onright>26</onright>
++ <ondown>72</ondown>
++ <onup>72</onup>
++ <showonepage>false</showonepage>
++ <orientation>vertical</orientation>
++ </control>
++ </control>
++ <control type="grouplist" id="9000">
++ <posx>160</posx>
++ <posy>660</posy>
++ <width>1080</width>
++ <height>40</height>
++ <itemgap>2</itemgap>
++ <align>center</align>
++ <orientation>horizontal</orientation>
++ <onleft>72</onleft>
++ <onright>13</onright>
++ <onup>9000</onup>
++ <ondown>9000</ondown>
++ <control type="button" id="26">
++ <description>Add Group</description>
++ <width>230</width>
++ <include>ButtonInfoDialogsCommonValues</include>
++ <label>31503</label>
++ </control>
++ <control type="button" id="27">
++ <description>Rename Group</description>
++ <width>230</width>
++ <include>ButtonInfoDialogsCommonValues</include>
++ <label>31504</label>
++ </control>
++ <control type="button" id="28">
++ <description>Delete Group</description>
++ <width>230</width>
++ <include>ButtonInfoDialogsCommonValues</include>
++ <label>31505</label>
++ </control>
++ <control type="button" id="29">
++ <description>OK</description>
++ <width>230</width>
++ <include>ButtonInfoDialogsCommonValues</include>
++ <label>186</label>
++ </control>
++ </control>
++ </control>
++ </control>
++ <include>SideBladeRight</include>
++ <include>Clock</include>
++
++ <control type="label" id="20">
++ <description>Fake Label used to pass on name label</description>
++ <visible>false</visible>
++ </control>
++ </controls>
++</window>
+\ No newline at end of file
+diff --git a/addons/skin.confluence/720p/DialogPVRGuideInfo.xml b/addons/skin.confluence/720p/DialogPVRGuideInfo.xml
+new file mode 100644
+index 0000000..24cc03c
+--- /dev/null
++++ b/addons/skin.confluence/720p/DialogPVRGuideInfo.xml
+@@ -0,0 +1,226 @@
++<window id="601">
++ <defaultcontrol always="true">7</defaultcontrol>
++ <coordinates>
++ <system>1</system>
++ <posx>20</posx>
++ <posy>30</posy>
++ <origin x="275" y="30">![Window.IsVisible(FullscreenVideo) | Window.IsVisible(Visualisation)]</origin>
++ </coordinates>
++ <include>dialogeffect</include>
++ <controls>
++ <control type="group">
++ <control type="image">
++ <description>background image</description>
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>730</width>
++ <height>660</height>
++ <texture border="40">DialogBack2.png</texture>
++ <visible>Window.IsVisible(FullscreenVideo) | Window.IsVisible(Visualisation)</visible>
++ </control>
++ <control type="image">
++ <description>background image</description>
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>730</width>
++ <height>660</height>
++ <texture border="40">DialogBack.png</texture>
++ <visible>![Window.IsVisible(FullscreenVideo) | Window.IsVisible(Visualisation)]</visible>
++ </control>
++ <control type="image">
++ <description>Dialog Header image</description>
++ <posx>40</posx>
++ <posy>16</posy>
++ <width>650</width>
++ <height>40</height>
++ <texture>dialogheader.png</texture>
++ </control>
++ <control type="label">
++ <description>header label</description>
++ <posx>40</posx>
++ <posy>20</posy>
++ <width>650</width>
++ <height>30</height>
++ <font>font13_title</font>
++ <label>$LOCALIZE[19047]</label>
++ <align>center</align>
++ <aligny>center</aligny>
++ <textcolor>selected</textcolor>
++ <shadowcolor>black</shadowcolor>
++ </control>
++ <control type="button">
++ <description>Close Window button</description>
++ <posx>640</posx>
++ <posy>15</posy>
++ <width>64</width>
++ <height>32</height>
++ <label>-</label>
++ <font>-</font>
++ <onclick>PreviousMenu</onclick>
++ <texturefocus>DialogCloseButton-focus.png</texturefocus>
++ <texturenofocus>DialogCloseButton.png</texturenofocus>
++ <onleft>10</onleft>
++ <onright>10</onright>
++ <onup>10</onup>
++ <ondown>10</ondown>
++ <visible>system.getbool(input.enablemouse)</visible>
++ </control>
++ <control type="label">
++ <description>Title label</description>
++ <posx>40</posx>
++ <posy>70</posy>
++ <width>650</width>
++ <height>30</height>
++ <font>font13_title</font>
++ <label>$INFO[ListItem.Title]</label>
++ <align>center</align>
++ <aligny>center</aligny>
++ <textcolor>blue</textcolor>
++ <shadowcolor>black</shadowcolor>
++ </control>
++ <control type="group">
++ <control type="group">
++ <posx>40</posx>
++ <posy>120</posy>
++ <control type="label">
++ <description>Time description</description>
++ <posx>170</posx>
++ <posy>0</posy>
++ <width>170</width>
++ <height>25</height>
++ <align>right</align>
++ <aligny>center</aligny>
++ <font>font13</font>
++ <textcolor>blue</textcolor>
++ <label>$LOCALIZE[142]</label>
++ </control>
++ <control type="label">
++ <description>Time value</description>
++ <posx>180</posx>
++ <posy>0</posy>
++ <width>470</width>
++ <height>25</height>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font13</font>
++ <textcolor>white</textcolor>
++ <label>$INFO[ListItem.StartTime] - $INFO[ListItem.EndTime] ($INFO[ListItem.StartDate])</label>
++ </control>
++ <control type="label">
++ <description>Duration</description>
++ <posx>170</posx>
++ <posy>35</posy>
++ <width>170</width>
++ <height>25</height>
++ <align>right</align>
++ <aligny>center</aligny>
++ <font>font13</font>
++ <textcolor>blue</textcolor>
++ <label>$LOCALIZE[180]:</label>
++ </control>
++ <control type="label">
++ <description>Duration value</description>
++ <posx>180</posx>
++ <posy>35</posy>
++ <width>470</width>
++ <height>25</height>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font13</font>
++ <textcolor>white</textcolor>
++ <label>$INFO[ListItem.Duration]</label>
++ </control>
++ <control type="label">
++ <description>Channel Name</description>
++ <posx>170</posx>
++ <posy>70</posy>
++ <width>170</width>
++ <height>25</height>
++ <align>right</align>
++ <aligny>center</aligny>
++ <font>font13</font>
++ <textcolor>blue</textcolor>
++ <label>$LOCALIZE[19148]:</label>
++ </control>
++ <control type="fadelabel">
++ <description>Channel Value</description>
++ <posx>180</posx>
++ <posy>70</posy>
++ <width>470</width>
++ <height>25</height>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font13</font>
++ <textcolor>white</textcolor>
++ <label>$INFO[ListItem.ChannelName]</label>
++ </control>
++ <control type="label">
++ <description>Genre</description>
++ <posx>170</posx>
++ <posy>105</posy>
++ <width>170</width>
++ <height>25</height>
++ <align>right</align>
++ <aligny>center</aligny>
++ <font>font13</font>
++ <textcolor>blue</textcolor>
++ <label>$LOCALIZE[135]:</label>
++ </control>
++ <control type="label">
++ <description>Genre value</description>
++ <posx>180</posx>
++ <posy>105</posy>
++ <width>470</width>
++ <label fallback="161">$INFO[ListItem.Genre]</label>
++ <align>left</align>
++ <font>font13</font>
++ <scroll>true</scroll>
++ </control>
++ </control>
++ <control type="textbox" id="400">
++ <description>Plot value</description>
++ <posx>40</posx>
++ <posy>275</posy>
++ <width>650</width>
++ <height>295</height>
++ <font>font12</font>
++ <align>justify</align>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <pagecontrol>-</pagecontrol>
++ <autoscroll time="2000" delay="3000" repeat="5000">true</autoscroll>
++ <label fallback="161">$INFO[ListItem.Plot]</label>
++ </control>
++ <control type="grouplist" id="9000">
++ <posx>40</posx>
++ <posy>590</posy>
++ <width>640</width>
++ <height>40</height>
++ <itemgap>5</itemgap>
++ <align>center</align>
++ <orientation>horizontal</orientation>
++ <onleft>9000</onleft>
++ <onright>9000</onright>
++ <onup>60</onup>
++ <ondown>60</ondown>
++ <control type="button" id="5">
++ <description>Switch to Channel</description>
++ <include>ButtonInfoDialogsCommonValues</include>
++ <label>19165</label>
++ </control>
++ <control type="button" id="6">
++ <description>Record</description>
++ <include>ButtonInfoDialogsCommonValues</include>
++ <label>-</label>
++ </control>
++ <control type="button" id="7">
++ <description>OK</description>
++ <include>ButtonInfoDialogsCommonValues</include>
++ <label>186</label>
++ </control>
++ </control>
++ </control>
++ </control>
++ <include>SideBladeRight</include>
++ </controls>
++</window>
+\ No newline at end of file
+diff --git a/addons/skin.confluence/720p/DialogPVRGuideOSD.xml b/addons/skin.confluence/720p/DialogPVRGuideOSD.xml
+new file mode 100644
+index 0000000..8cd50ea
+--- /dev/null
++++ b/addons/skin.confluence/720p/DialogPVRGuideOSD.xml
+@@ -0,0 +1,253 @@
++<window id="610">
++ <defaultcontrol always="true">11</defaultcontrol>
++ <coordinates>
++ <system>1</system>
++ <posx>780</posx>
++ <posy>30</posy>
++ </coordinates>
++ <include>dialogeffect</include>
++ <controls>
++ <control type="group">
++ <control type="image">
++ <description>background image</description>
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>480</width>
++ <height>660</height>
++ <texture border="40">DialogBack2.png</texture>
++ </control>
++ <control type="button">
++ <description>Close Window button</description>
++ <posx>390</posx>
++ <posy>15</posy>
++ <width>64</width>
++ <height>32</height>
++ <label>-</label>
++ <font>-</font>
++ <onclick>PreviousMenu</onclick>
++ <texturefocus>DialogCloseButton-focus.png</texturefocus>
++ <texturenofocus>DialogCloseButton.png</texturenofocus>
++ <onleft>2</onleft>
++ <onright>2</onright>
++ <onup>2</onup>
++ <ondown>2</ondown>
++ <visible>system.getbool(input.enablemouse)</visible>
++ </control>
++ <control type="image">
++ <description>Dialog Header image</description>
++ <posx>40</posx>
++ <posy>16</posy>
++ <width>400</width>
++ <height>40</height>
++ <texture>dialogheader.png</texture>
++ </control>
++ <control type="label">
++ <description>header label</description>
++ <posx>40</posx>
++ <posy>16</posy>
++ <width>430</width>
++ <height>40</height>
++ <font>font12_title</font>
++ <label>$LOCALIZE[19222] - $INFO[VideoPlayer.ChannelName]</label>
++ <align>center</align>
++ <aligny>center</aligny>
++ <textcolor>blue</textcolor>
++ <shadowcolor>black</shadowcolor>
++ </control>
++ <control type="label">
++ <description>Selected item's date</description>
++ <posx>40</posx>
++ <posy>60</posy>
++ <width>430</width>
++ <height>30</height>
++ <font>font11</font>
++ <textcolor>grey2</textcolor>
++ <label>$INFO[Container(11).ListItem.StartDate]</label>
++ <align>center</align>
++ <aligny>center</aligny>
++ </control>
++ <control type="list" id="11">
++ <posx>30</posx>
++ <posy>100</posy>
++ <width>410</width>
++ <height>490</height>
++ <onleft>60</onleft>
++ <onright>60</onright>
++ <onup>11</onup>
++ <ondown>11</ondown>
++ <viewtype label="535">list</viewtype>
++ <pagecontrol>60</pagecontrol>
++ <scrolltime>200</scrolltime>
++ <itemlayout height="35" width="410">
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>410</width>
++ <height>30</height>
++ <texture border="5">button-nofocus.png</texture>
++ <include>VisibleFadeEffect</include>
++ </control>
++ <control type="label">
++ <posx>10</posx>
++ <posy>0</posy>
++ <width>100</width>
++ <height>30</height>
++ <font>font13</font>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <label>$INFO[ListItem.StartTime]</label>
++ </control>
++ <control type="image">
++ <posx>120</posx>
++ <posy>5</posy>
++ <width>30</width>
++ <height>20</height>
++ <texture>PVR-IsRecording.png</texture>
++ <visible>ListItem.IsRecording</visible>
++ </control>
++ <control type="image">
++ <posx>120</posx>
++ <posy>5</posy>
++ <width>20</width>
++ <height>20</height>
++ <texture>PVR-HasTimer.png</texture>
++ <visible>ListItem.HasTimer + !ListItem.IsRecording</visible>
++ </control>
++ <control type="label">
++ <posx>400</posx>
++ <posy>0</posy>
++ <width>300</width>
++ <height>30</height>
++ <font>font12</font>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <align>right</align>
++ <aligny>center</aligny>
++ <label>$INFO[ListItem.Label]</label>
++ <visible>![ListItem.IsRecording | ListItem.HasTimer]</visible>
++ </control>
++ <control type="label">
++ <posx>400</posx>
++ <posy>0</posy>
++ <width>250</width>
++ <height>30</height>
++ <font>font12</font>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <align>right</align>
++ <aligny>center</aligny>
++ <label>$INFO[ListItem.Label]</label>
++ <visible>ListItem.IsRecording | ListItem.HasTimer</visible>
++ </control>
++ </itemlayout>
++ <focusedlayout height="35" width="410">
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>410</width>
++ <height>30</height>
++ <texture border="5">button-nofocus.png</texture>
++ <visible>!Control.HasFocus(11)</visible>
++ <include>VisibleFadeEffect</include>
++ </control>
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>410</width>
++ <height>30</height>
++ <texture border="5">button-focus2.png</texture>
++ <visible>Control.HasFocus(11)</visible>
++ <include>VisibleFadeEffect</include>
++ </control>
++ <control type="label">
++ <posx>10</posx>
++ <posy>0</posy>
++ <width>100</width>
++ <height>30</height>
++ <font>font13</font>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <label>$INFO[ListItem.StartTime]</label>
++ </control>
++ <control type="image">
++ <posx>120</posx>
++ <posy>5</posy>
++ <width>30</width>
++ <height>20</height>
++ <texture>PVR-IsRecording.png</texture>
++ <visible>ListItem.IsRecording</visible>
++ </control>
++ <control type="image">
++ <posx>120</posx>
++ <posy>5</posy>
++ <width>20</width>
++ <height>20</height>
++ <texture>PVR-HasTimer.png</texture>
++ <visible>ListItem.HasTimer + !ListItem.IsRecording</visible>
++ </control>
++ <control type="label">
++ <posx>400</posx>
++ <posy>0</posy>
++ <width>300</width>
++ <height>30</height>
++ <font>font12</font>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <align>right</align>
++ <aligny>center</aligny>
++ <label>$INFO[ListItem.Label]</label>
++ <visible>![ListItem.IsRecording | ListItem.HasTimer]</visible>
++ </control>
++ <control type="label">
++ <posx>400</posx>
++ <posy>0</posy>
++ <width>250</width>
++ <height>30</height>
++ <font>font12</font>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <align>right</align>
++ <aligny>center</aligny>
++ <label>$INFO[ListItem.Label]</label>
++ <visible>ListItem.IsRecording | ListItem.HasTimer</visible>
++ </control>
++ </focusedlayout>
++ </control>
++ <control type="scrollbar" id="60">
++ <posx>440</posx>
++ <posy>100</posy>
++ <width>25</width>
++ <height>490</height>
++ <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
++ <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
++ <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
++ <textureslidernib>ScrollBarNib.png</textureslidernib>
++ <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
++ <onleft>11</onleft>
++ <onright>11</onright>
++ <ondown>61</ondown>
++ <onup>61</onup>
++ <showonepage>false</showonepage>
++ <orientation>vertical</orientation>
++ </control>
++ <control type="label">
++ <description>Page Count Label</description>
++ <posx>450</posx>
++ <posy>610</posy>
++ <width>400</width>
++ <height>20</height>
++ <font>font12</font>
++ <textcolor>grey</textcolor>
++ <scroll>false</scroll>
++ <align>right</align>
++ <aligny>center</aligny>
++ <label>([COLOR=blue]$INFO[Container(11).NumItems][/COLOR]) $LOCALIZE[31025] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(11).CurrentPage]/$INFO[Container(11).NumPages][/COLOR])</label>
++ <include>Window_OpenClose_Animation</include>
++ </control>
++ </control>
++ </controls>
++</window>
+diff --git a/addons/skin.confluence/720p/DialogPVRGuideSearch.xml b/addons/skin.confluence/720p/DialogPVRGuideSearch.xml
+new file mode 100644
+index 0000000..9eda477
+--- /dev/null
++++ b/addons/skin.confluence/720p/DialogPVRGuideSearch.xml
+@@ -0,0 +1,456 @@
++<window id="606">
++ <defaultcontrol always="true">9</defaultcontrol>
++ <coordinates>
++ <system>1</system>
++ <posx>210</posx>
++ <posy>65</posy>
++ </coordinates>
++ <include>dialogeffect</include>
++ <controls>
++ <control type="image">
++ <description>background image</description>
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>865</width>
++ <height>605</height>
++ <texture border="40">DialogBack.png</texture>
++ </control>
++ <control type="image">
++ <description>Dialog Header image</description>
++ <posx>40</posx>
++ <posy>16</posy>
++ <width>785</width>
++ <height>40</height>
++ <texture>dialogheader.png</texture>
++ </control>
++ <control type="label">
++ <description>header label</description>
++ <posx>40</posx>
++ <posy>20</posy>
++ <width>785</width>
++ <height>30</height>
++ <font>font13_title</font>
++ <label>$LOCALIZE[19142]</label>
++ <align>center</align>
++ <aligny>center</aligny>
++ <textcolor>selected</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <visible>!pvr.IsPlayingRadio</visible>
++ </control>
++ <control type="button">
++ <description>Close Window button</description>
++ <posx>775</posx>
++ <posy>15</posy>
++ <width>64</width>
++ <height>32</height>
++ <label>-</label>
++ <font>-</font>
++ <onclick>PreviousMenu</onclick>
++ <texturefocus>DialogCloseButton-focus.png</texturefocus>
++ <texturenofocus>DialogCloseButton.png</texturenofocus>
++ <onleft>3</onleft>
++ <onright>3</onright>
++ <onup>3</onup>
++ <ondown>3</ondown>
++ <visible>system.getbool(input.enablemouse)</visible>
++ </control>
++ <control type="label">
++ <description>Search string</description>
++ <posx>30</posx>
++ <posy>60</posy>
++ <width>320</width>
++ <height>40</height>
++ <font>font12caps</font>
++ <label>$LOCALIZE[19133]</label>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>blue</textcolor>
++ <shadowcolor>black</shadowcolor>
++ </control>
++ <control type="image">
++ <posx>30</posx>
++ <posy>100</posy>
++ <width>800</width>
++ <height>50</height>
++ <aspectratio>stretch</aspectratio>
++ <texture border="20">KeyboardEditArea.png</texture>
++ </control>
++ <control type="edit" id="9">
++ <description>Search string</description>
++ <posx>35</posx>
++ <posy>105</posy>
++ <width>790</width>
++ <height>40</height>
++ <font>font13</font>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <texturefocus>-</texturefocus>
++ <texturenofocus>-</texturenofocus>
++ <align>left</align>
++ <onleft>9</onleft>
++ <onright>9</onright>
++ <onup>26</onup>
++ <ondown>10</ondown>
++ </control>
++ <control type="textbox">
++ <description>Search help</description>
++ <posx>30</posx>
++ <posy>155</posy>
++ <width>800</width>
++ <height>70</height>
++ <align>left</align>
++ <font>font12</font>
++ <textcolor>grey2</textcolor>
++ <label>$LOCALIZE[19001] $LOCALIZE[19002]</label>
++ </control>
++ <control type="group">
++ <posx>30</posx>
++ <posy>230</posy>
++ <control type="radiobutton" id="10">
++ <description>Include Description</description>
++ <posx>0</posx>
++ <posy>0</posy>
++ <height>35</height>
++ <width>400</width>
++ <font>font12</font>
++ <textcolor>white</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <shadowcolor>black</shadowcolor>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <align>left</align>
++ <aligny>center</aligny>
++ <label>19134</label>
++ <onleft>12</onleft>
++ <onright>12</onright>
++ <onup>9</onup>
++ <ondown>11</ondown>
++ </control>
++ <control type="radiobutton" id="11">
++ <description>Case Sensitive</description>
++ <posx>0</posx>
++ <posy>35</posy>
++ <height>35</height>
++ <width>400</width>
++ <font>font12</font>
++ <textcolor>white</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <shadowcolor>black</shadowcolor>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <align>left</align>
++ <aligny>center</aligny>
++ <label>19135</label>
++ <onleft>13</onleft>
++ <onright>13</onright>
++ <onup>10</onup>
++ <ondown>14</ondown>
++ </control>
++ <control type="edit" id="14">
++ <description>Start Date</description>
++ <posx>0</posx>
++ <posy>70</posy>
++ <width>400</width>
++ <height>35</height>
++ <font>font12</font>
++ <textcolor>white</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <shadowcolor>black</shadowcolor>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <label>19128</label>
++ <onright>16</onright>
++ <onleft>16</onleft>
++ <onup>11</onup>
++ <ondown>15</ondown>
++ </control>
++ <control type="edit" id="15">
++ <description>Stop Date</description>
++ <posx>0</posx>
++ <posy>105</posy>
++ <width>400</width>
++ <height>35</height>
++ <font>font12</font>
++ <textcolor>white</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <shadowcolor>black</shadowcolor>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <label>19129</label>
++ <onright>17</onright>
++ <onleft>17</onleft>
++ <onup>14</onup>
++ <ondown>18</ondown>
++ </control>
++ <control type="spincontrolex" id="18">
++ <description>Genre</description>
++ <posx>0</posx>
++ <posy>140</posy>
++ <width>400</width>
++ <height>35</height>
++ <font>font12</font>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <label>515</label>
++ <onright>19</onright>
++ <onleft>19</onleft>
++ <onup>15</onup>
++ <ondown>20</ondown>
++ </control>
++ <control type="radiobutton" id="20">
++ <description>Include unknown Genres</description>
++ <posx>0</posx>
++ <posy>175</posy>
++ <height>35</height>
++ <width>400</width>
++ <font>font12</font>
++ <textcolor>white</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <shadowcolor>black</shadowcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <pulseonselect>no</pulseonselect>
++ <label>19132</label>
++ <onleft>21</onleft>
++ <onright>21</onright>
++ <onup>18</onup>
++ <ondown>22</ondown>
++ </control>
++ <control type="radiobutton" id="22">
++ <description>FTA only</description>
++ <posx>0</posx>
++ <posy>210</posy>
++ <height>35</height>
++ <width>400</width>
++ <font>font12</font>
++ <textcolor>white</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <shadowcolor>black</shadowcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <pulseonselect>no</pulseonselect>
++ <label>19123</label>
++ <onleft>23</onleft>
++ <onright>23</onright>
++ <onup>20</onup>
++ <ondown>24</ondown>
++ </control>
++ <control type="radiobutton" id="24">
++ <description>Ignore Timers</description>
++ <posx>0</posx>
++ <posy>245</posy>
++ <height>35</height>
++ <width>400</width>
++ <font>font12</font>
++ <textcolor>white</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <shadowcolor>black</shadowcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <pulseonselect>no</pulseonselect>
++ <label>19124</label>
++ <onleft>27</onleft>
++ <onright>27</onright>
++ <onup>22</onup>
++ <ondown>26</ondown>
++ </control>
++ </control>
++ <control type="group">
++ <posx>440</posx>
++ <posy>230</posy>
++ <control type="spincontrolex" id="12">
++ <description>Min Duration</description>
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>400</width>
++ <height>35</height>
++ <font>font12</font>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <label>19130</label>
++ <onright>10</onright>
++ <onleft>10</onleft>
++ <onup>9</onup>
++ <ondown>13</ondown>
++ </control>
++ <control type="spincontrolex" id="13">
++ <description>Max Duration</description>
++ <posx>0</posx>
++ <posy>35</posy>
++ <width>400</width>
++ <height>35</height>
++ <font>font12</font>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <label>19131</label>
++ <onright>11</onright>
++ <onleft>11</onleft>
++ <onup>12</onup>
++ <ondown>16</ondown>
++ </control>
++ <control type="edit" id="16">
++ <description>Start time</description>
++ <posx>0</posx>
++ <posy>70</posy>
++ <width>400</width>
++ <height>35</height>
++ <font>font12</font>
++ <textcolor>white</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <shadowcolor>black</shadowcolor>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <label>19126</label>
++ <onright>14</onright>
++ <onleft>14</onleft>
++ <onup>13</onup>
++ <ondown>17</ondown>
++ </control>
++ <control type="edit" id="17">
++ <description>Stop time</description>
++ <posx>0</posx>
++ <posy>105</posy>
++ <width>400</width>
++ <height>35</height>
++ <font>font12</font>
++ <textcolor>white</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <shadowcolor>black</shadowcolor>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <label>19127</label>
++ <onright>15</onright>
++ <onleft>15</onleft>
++ <onup>16</onup>
++ <ondown>19</ondown>
++ </control>
++ <control type="radiobutton" id="19">
++ <description>avoid repeats</description>
++ <posx>0</posx>
++ <posy>140</posy>
++ <width>400</width>
++ <height>35</height>
++ <font>font12</font>
++ <textcolor>white</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <shadowcolor>black</shadowcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <pulseonselect>no</pulseonselect>
++ <label>19121</label>
++ <onright>18</onright>
++ <onleft>18</onleft>
++ <onup>17</onup>
++ <ondown>21</ondown>
++ </control>
++ <control type="spincontrolex" id="21">
++ <description>Groups</description>
++ <posx>0</posx>
++ <posy>175</posy>
++ <width>400</width>
++ <height>35</height>
++ <font>font12</font>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <label>19141</label>
++ <onright>20</onright>
++ <onleft>20</onleft>
++ <onup>19</onup>
++ <ondown>23</ondown>
++ </control>
++ <control type="spincontrolex" id="23">
++ <description>Channels</description>
++ <posx>0</posx>
++ <posy>210</posy>
++ <width>400</width>
++ <height>35</height>
++ <font>font12</font>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <label>19148</label>
++ <onleft>22</onleft>
++ <onright>22</onright>
++ <onup>21</onup>
++ <ondown>27</ondown>
++ </control>
++ <control type="radiobutton" id="27">
++ <description>Ignore Recordings</description>
++ <posx>0</posx>
++ <posy>245</posy>
++ <height>35</height>
++ <width>400</width>
++ <font>font12</font>
++ <textcolor>white</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <shadowcolor>black</shadowcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <pulseonselect>no</pulseonselect>
++ <label>19125</label>
++ <onleft>24</onleft>
++ <onright>24</onright>
++ <onup>23</onup>
++ <ondown>26</ondown>
++ </control>
++ </control>
++ <control type="group" id="9000">
++ <posy>540</posy>
++ <posx>125</posx>
++ <control type="button" id="28">
++ <description>Defaults Button</description>
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>200</width>
++ <height>40</height>
++ <align>center</align>
++ <aligny>center</aligny>
++ <font>font12_title</font>
++ <label>409</label>
++ <onleft>26</onleft>
++ <onright>25</onright>
++ <onup>24</onup>
++ <ondown>9</ondown>
++ </control>
++ <control type="button" id="25">
++ <description>Cancel Button</description>
++ <posx>210</posx>
++ <posy>0</posy>
++ <width>200</width>
++ <height>40</height>
++ <align>center</align>
++ <aligny>center</aligny>
++ <font>font12_title</font>
++ <label>222</label>
++ <onleft>28</onleft>
++ <onright>26</onright>
++ <onup>27</onup>
++ <ondown>9</ondown>
++ </control>
++ <control type="button" id="26">
++ <description>Search Button</description>
++ <posx>420</posx>
++ <posy>0</posy>
++ <width>200</width>
++ <height>40</height>
++ <align>center</align>
++ <aligny>center</aligny>
++ <font>font12_title</font>
++ <label>137</label>
++ <onleft>25</onleft>
++ <onright>28</onright>
++ <onup>27</onup>
++ <ondown>9</ondown>
++ </control>
++ </control>
++ </controls>
++</window>
+\ No newline at end of file
+diff --git a/addons/skin.confluence/720p/DialogPVRRecordingInfo.xml b/addons/skin.confluence/720p/DialogPVRRecordingInfo.xml
+new file mode 100644
+index 0000000..83c3d6b
+--- /dev/null
++++ b/addons/skin.confluence/720p/DialogPVRRecordingInfo.xml
+@@ -0,0 +1,267 @@
++<window id="602">
++ <defaultcontrol always="true">10</defaultcontrol>
++ <coordinates>
++ <system>1</system>
++ <posx>275</posx>
++ <posy>30</posy>
++ </coordinates>
++ <include>dialogeffect</include>
++ <controls>
++ <control type="image">
++ <description>background image</description>
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>730</width>
++ <height>660</height>
++ <texture border="40">DialogBack2.png</texture>
++ <visible>Window.IsVisible(FullscreenVideo) | Window.IsVisible(Visualisation)</visible>
++ </control>
++ <control type="image">
++ <description>background image</description>
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>730</width>
++ <height>660</height>
++ <texture border="40">DialogBack.png</texture>
++ <visible>![Window.IsVisible(FullscreenVideo) | Window.IsVisible(Visualisation)]</visible>
++ </control>
++ <control type="image">
++ <description>Dialog Header image</description>
++ <posx>40</posx>
++ <posy>16</posy>
++ <width>650</width>
++ <height>40</height>
++ <texture>dialogheader.png</texture>
++ </control>
++ <control type="label">
++ <description>header label</description>
++ <posx>40</posx>
++ <posy>20</posy>
++ <width>650</width>
++ <height>30</height>
++ <font>font13_title</font>
++ <label>$LOCALIZE[19053]</label>
++ <align>center</align>
++ <aligny>center</aligny>
++ <textcolor>selected</textcolor>
++ <shadowcolor>black</shadowcolor>
++ </control>
++ <control type="button">
++ <description>Close Window button</description>
++ <posx>640</posx>
++ <posy>15</posy>
++ <width>64</width>
++ <height>32</height>
++ <label>-</label>
++ <font>-</font>
++ <onclick>PreviousMenu</onclick>
++ <texturefocus>DialogCloseButton-focus.png</texturefocus>
++ <texturenofocus>DialogCloseButton.png</texturenofocus>
++ <onleft>10</onleft>
++ <onright>10</onright>
++ <onup>10</onup>
++ <ondown>10</ondown>
++ <visible>system.getbool(input.enablemouse)</visible>
++ </control>
++ <control type="label">
++ <description>Title label</description>
++ <posx>40</posx>
++ <posy>70</posy>
++ <width>650</width>
++ <height>30</height>
++ <font>font13_title</font>
++ <label>$INFO[ListItem.Title]</label>
++ <align>center</align>
++ <aligny>center</aligny>
++ <textcolor>blue</textcolor>
++ <shadowcolor>black</shadowcolor>
++ </control>
++ <control type="group">
++ <posx>40</posx>
++ <posy>140</posy>
++ <control type="label">
++ <description>Start Date</description>
++ <posx>170</posx>
++ <posy>0</posy>
++ <width>160</width>
++ <height>25</height>
++ <align>right</align>
++ <aligny>center</aligny>
++ <font>font13</font>
++ <textcolor>blue</textcolor>
++ <label>$LOCALIZE[552]:</label>
++ </control>
++ <control type="label">
++ <description>Start date value</description>
++ <posx>180</posx>
++ <posy>0</posy>
++ <width>470</width>
++ <height>25</height>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font13</font>
++ <textcolor>white</textcolor>
++ <label>$INFO[ListItem.StartDate]</label>
++ </control>
++ <control type="label">
++ <description>Start time</description>
++ <posx>170</posx>
++ <posy>35</posy>
++ <width>160</width>
++ <height>25</height>
++ <align>right</align>
++ <aligny>center</aligny>
++ <font>font13</font>
++ <textcolor>blue</textcolor>
++ <label>$LOCALIZE[142]</label>
++ </control>
++ <control type="label">
++ <description>Start Time value</description>
++ <posx>180</posx>
++ <posy>35</posy>
++ <width>470</width>
++ <height>25</height>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font13</font>
++ <textcolor>white</textcolor>
++ <label>$INFO[ListItem.StartTime]</label>
++ </control>
++ <control type="label">
++ <description>Channel Name</description>
++ <posx>170</posx>
++ <posy>70</posy>
++ <width>160</width>
++ <height>25</height>
++ <align>right</align>
++ <aligny>center</aligny>
++ <font>font13</font>
++ <textcolor>blue</textcolor>
++ <label>$LOCALIZE[19148]:</label>
++ </control>
++ <control type="fadelabel">
++ <description>Channel Value</description>
++ <posx>180</posx>
++ <posy>70</posy>
++ <width>470</width>
++ <height>25</height>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font13</font>
++ <textcolor>white</textcolor>
++ <label>$INFO[ListItem.ChannelName]</label>
++ </control>
++ <control type="label">
++ <description>Duration</description>
++ <posx>170</posx>
++ <posy>105</posy>
++ <width>160</width>
++ <height>25</height>
++ <align>right</align>
++ <aligny>center</aligny>
++ <font>font13</font>
++ <textcolor>blue</textcolor>
++ <label>$LOCALIZE[180]:</label>
++ </control>
++ <control type="label">
++ <description>Duration value</description>
++ <posx>180</posx>
++ <posy>105</posy>
++ <width>470</width>
++ <label>$INFO[ListItem.Duration]</label>
++ <align>left</align>
++ <font>font13</font>
++ <scroll>true</scroll>
++ </control>
++ <control type="label">
++ <description>Genre</description>
++ <posx>170</posx>
++ <posy>140</posy>
++ <width>160</width>
++ <height>25</height>
++ <align>right</align>
++ <aligny>center</aligny>
++ <font>font13</font>
++ <textcolor>blue</textcolor>
++ <label>$LOCALIZE[135]:</label>
++ </control>
++ <control type="label">
++ <description>Genre value</description>
++ <posx>180</posx>
++ <posy>140</posy>
++ <width>470</width>
++ <label fallback="161">$INFO[ListItem.Genre]</label>
++ <align>left</align>
++ <font>font13</font>
++ <scroll>true</scroll>
++ </control>
++ <control type="label">
++ <description>Subtitle value</description>
++ <posx>40</posx>
++ <posy>185</posy>
++ <width>650</width>
++ <label>$INFO[ListItem.PlotOutline]</label>
++ <align>center</align>
++ <font>font13</font>
++ <textcolor>blue</textcolor>
++ <scroll>true</scroll>
++ <visible>!IsEmpty(ListItem.PlotOutline)</visible>
++ </control>
++ </control>
++ <control type="label">
++ <posx>610</posx>
++ <posy>370</posy>
++ <width>400</width>
++ <height>30</height>
++ <font>font12</font>
++ <textcolor>grey</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <scroll>true</scroll>
++ <align>right</align>
++ <aligny>center</aligny>
++ <label>[COLOR=blue]$LOCALIZE[207][/COLOR]$INFO[Container(400).CurrentPage, ( $LOCALIZE[31024] ]$INFO[Container(400).NumPages,/, )]</label>
++ </control>
++ <control type="spincontrol" id="60">
++ <description>Next page button</description>
++ <posx>620</posx>
++ <posy>375</posy>
++ <subtype>page</subtype>
++ <font>-</font>
++ <onleft>60</onleft>
++ <onright>60</onright>
++ <ondown>9000</ondown>
++ <onup>9000</onup>
++ <textcolor>-</textcolor>
++ <showonepage>true</showonepage>
++ </control>
++ <control type="textbox" id="400">
++ <description>PLOT</description>
++ <posx>40</posx>
++ <posy>400</posy>
++ <width>650</width>
++ <height>180</height>
++ <font>font12</font>
++ <align>justify</align>
++ <pagecontrol>60</pagecontrol>
++ <label fallback="161">$INFO[ListItem.Plot]</label>
++ </control>
++ <control type="grouplist" id="9000">
++ <posx>40</posx>
++ <posy>590</posy>
++ <width>640</width>
++ <height>40</height>
++ <itemgap>5</itemgap>
++ <align>center</align>
++ <orientation>horizontal</orientation>
++ <onleft>9000</onleft>
++ <onright>9000</onright>
++ <onup>60</onup>
++ <ondown>60</ondown>
++ <control type="button" id="10">
++ <description>OK</description>
++ <include>ButtonInfoDialogsCommonValues</include>
++ <label>186</label>
++ </control>
++ </control>
++ </controls>
++</window>
+\ No newline at end of file
+diff --git a/addons/skin.confluence/720p/DialogPVRTimerSettings.xml b/addons/skin.confluence/720p/DialogPVRTimerSettings.xml
+new file mode 100644
+index 0000000..219ac5c
+--- /dev/null
++++ b/addons/skin.confluence/720p/DialogPVRTimerSettings.xml
+@@ -0,0 +1,155 @@
++<window id="603">
++ <defaultcontrol>29</defaultcontrol>
++ <coordinates>
++ <system>1</system>
++ <posx>275</posx>
++ <posy>30</posy>
++ </coordinates>
++ <include>dialogeffect</include>
++ <controls>
++ <control type="image">
++ <description>background image</description>
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>730</width>
++ <height>660</height>
++ <texture border="40">DialogBack.png</texture>
++ </control>
++ <control type="image">
++ <description>Dialog Header image</description>
++ <posx>40</posx>
++ <posy>16</posy>
++ <width>650</width>
++ <height>40</height>
++ <texture>dialogheader.png</texture>
++ </control>
++ <control type="label" id="2">
++ <description>header label</description>
++ <posx>40</posx>
++ <posy>20</posy>
++ <width>650</width>
++ <height>30</height>
++ <font>font13_title</font>
++ <label>-</label>
++ <align>center</align>
++ <aligny>center</aligny>
++ <textcolor>selected</textcolor>
++ <shadowcolor>black</shadowcolor>
++ </control>
++ <control type="button">
++ <description>Close Window button</description>
++ <posx>640</posx>
++ <posy>15</posy>
++ <width>64</width>
++ <height>32</height>
++ <label>-</label>
++ <font>-</font>
++ <onclick>PreviousMenu</onclick>
++ <texturefocus>DialogCloseButton-focus.png</texturefocus>
++ <texturenofocus>DialogCloseButton.png</texturenofocus>
++ <onleft>10</onleft>
++ <onright>10</onright>
++ <onup>10</onup>
++ <ondown>10</ondown>
++ <visible>system.getbool(input.enablemouse)</visible>
++ </control>
++ <control type="grouplist" id="5">
++ <description>control area</description>
++ <posx>30</posx>
++ <posy>70</posy>
++ <width>670</width>
++ <height>510</height>
++ <itemgap>5</itemgap>
++ <onup>9001</onup>
++ <ondown>9001</ondown>
++ <onleft>9001</onleft>
++ <onright>9001</onright>
++ </control>
++ <control type="group" id="9001">
++ <posx>160</posx>
++ <posy>590</posy>
++ <control type="button" id="28">
++ <description>OK Button</description>
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>200</width>
++ <height>40</height>
++ <align>center</align>
++ <aligny>center</aligny>
++ <font>font12_title</font>
++ <label>186</label>
++ <onleft>29</onleft>
++ <onright>29</onright>
++ <onup>5</onup>
++ <ondown>5</ondown>
++ </control>
++ <control type="button" id="29">
++ <description>Cancel Button</description>
++ <posx>210</posx>
++ <posy>0</posy>
++ <width>200</width>
++ <height>40</height>
++ <align>center</align>
++ <aligny>center</aligny>
++ <font>font12_title</font>
++ <label>222</label>
++ <onleft>28</onleft>
++ <onright>28</onright>
++ <onup>5</onup>
++ <ondown>5</ondown>
++ </control>
++ </control>
++ <control type="button" id="7">
++ <description>Default Button</description>
++ <height>40</height>
++ <font>font13</font>
++ <textcolor>grey2</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ </control>
++ <control type="radiobutton" id="8">
++ <description>Default RadioButton</description>
++ <height>40</height>
++ <font>font13</font>
++ <textcolor>grey2</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ </control>
++ <control type="spincontrolex" id="9">
++ <description>Default spincontrolex</description>
++ <height>40</height>
++ <textcolor>grey2</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <font>font13</font>
++ <aligny>center</aligny>
++ <reverse>yes</reverse>
++ </control>
++ <control type="sliderex" id="10">
++ <description>Default Slider</description>
++ <height>40</height>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ <font>font13</font>
++ <textcolor>grey2</textcolor>
++ <focusedcolor>white</focusedcolor>
++ </control>
++ <control type="image" id="11">
++ <description>Default Seperator</description>
++ <height>2</height>
++ <texture>separator2.png</texture>
++ </control>
++ <control type="edit" id="12">
++ <description>Default Edit</description>
++ <height>40</height>
++ <font>font13</font>
++ <textcolor>grey2</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <texturefocus border="5">button-focus2.png</texturefocus>
++ <texturenofocus border="5">button-nofocus.png</texturenofocus>
++ </control>
++ </controls>
++</window>
+\ No newline at end of file
+diff --git a/addons/skin.confluence/720p/Home.xml b/addons/skin.confluence/720p/Home.xml
+index ee9f44e..72267d3 100644
+--- a/addons/skin.confluence/720p/Home.xml
++++ b/addons/skin.confluence/720p/Home.xml
+@@ -111,6 +111,150 @@
+ <shadowcolor>black</shadowcolor>
+ </control>
+ </control>
++ <!-- LiveTV Info -->
++ <control type="group">
++ <posx>490r</posx>
++ <posy>40</posy>
++ <visible>Container(9000).HasFocus(12) + [PVR.IsRecording | PVR.HasNonRecordingTimer]</visible>
++ <include>VisibleFadeEffect</include>
++ <include>Window_OpenClose_Animation</include>
++ <animation effect="fade" start="100" end="0" time="200" condition="Window.IsActive(Favourites)">conditional</animation>
++ <control type="group">
++ <animation effect="slide" start="0,0" end="0,100" time="0" condition="PVR.IsRecording">conditional</animation>
++ <visible>PVR.HasNonRecordingTimer</visible>
++ <control type="image">
++ <posx>0</posx>
++ <posy>-5</posy>
++ <width>490</width>
++ <height>90</height>
++ <texture>gradient.png</texture>
++ </control>
++ <control type="image">
++ <posx>400</posx>
++ <posy>0</posy>
++ <width>80</width>
++ <height>80</height>
++ <aspectratio>keep</aspectratio>
++ <texture background="true" fallback="DefaultVideoCover.png">$INFO[PVR.NextRecordingChannelIcon]</texture>
++ <bordertexture border="8">ThumbBorder.png</bordertexture>
++ <bordersize>4</bordersize>
++ </control>
++ <control type="image">
++ <posx>365</posx>
++ <posy>5</posy>
++ <width>25</width>
++ <height>25</height>
++ <aspectratio>keep</aspectratio>
++ <texture>PVR-HasTimer.png</texture>
++ </control>
++ <control type="label">
++ <description>Next Timer Header label</description>
++ <posx>355</posx>
++ <posy>5</posy>
++ <height>25</height>
++ <width>400</width>
++ <label>$LOCALIZE[19157]</label>
++ <align>right</align>
++ <aligny>center</aligny>
++ <font>font12_title</font>
++ <textcolor>blue</textcolor>
++ <shadowcolor>black</shadowcolor>
++ </control>
++ <control type="label">
++ <description>NextRecordingDateTime</description>
++ <posx>390</posx>
++ <posy>30</posy>
++ <height>25</height>
++ <width>400</width>
++ <label>$INFO[PVR.NextRecordingDateTime,$LOCALIZE[19126] - ]</label>
++ <align>right</align>
++ <aligny>center</aligny>
++ <font>font12</font>
++ <scroll>true</scroll>
++ <shadowcolor>black</shadowcolor>
++ </control>
++ <control type="label">
++ <description>NextRecordingTitle Channel</description>
++ <posx>390</posx>
++ <posy>50</posy>
++ <height>25</height>
++ <width>800</width>
++ <label>$INFO[PVR.NextRecordingTitle][COLOR=grey]$INFO[PVR.NextRecordingChannel, - [COLOR=blue]([/COLOR],[COLOR=blue])[/COLOR]][/COLOR]</label>
++ <align>right</align>
++ <aligny>center</aligny>
++ <font>font12_title</font>
++ <shadowcolor>black</shadowcolor>
++ <scroll>true</scroll>
++ </control>
++ </control>
++ <control type="group">
++ <visible>PVR.IsRecording</visible>
++ <control type="image">
++ <posx>0</posx>
++ <posy>-5</posy>
++ <width>490</width>
++ <height>90</height>
++ <texture>gradient.png</texture>
++ </control>
++ <control type="image">
++ <posx>400</posx>
++ <posy>0</posy>
++ <width>80</width>
++ <height>80</height>
++ <aspectratio>keep</aspectratio>
++ <texture background="true" fallback="DefaultVideoCover.png">$INFO[PVR.NowRecordingChannelIcon]</texture>
++ <bordertexture border="8">ThumbBorder.png</bordertexture>
++ <bordersize>4</bordersize>
++ </control>
++ <control type="image">
++ <posx>360</posx>
++ <posy>5</posy>
++ <width>30</width>
++ <height>25</height>
++ <aspectratio>keep</aspectratio>
++ <texture>PVR-IsRecording.png</texture>
++ </control>
++ <control type="label">
++ <description>Is Recording Header label</description>
++ <posx>350</posx>
++ <posy>5</posy>
++ <height>25</height>
++ <width>400</width>
++ <label>$LOCALIZE[19158]</label>
++ <align>right</align>
++ <aligny>center</aligny>
++ <font>font12_title</font>
++ <textcolor>blue</textcolor>
++ <shadowcolor>black</shadowcolor>
++ </control>
++ <control type="label">
++ <description>NextRecordingDateTime</description>
++ <posx>390</posx>
++ <posy>30</posy>
++ <height>25</height>
++ <width>400</width>
++ <label>$INFO[PVR.NowRecordingDateTime,$LOCALIZE[19126] - ]</label>
++ <align>right</align>
++ <aligny>center</aligny>
++ <font>font12</font>
++ <scroll>true</scroll>
++ <shadowcolor>black</shadowcolor>
++ </control>
++ <control type="label">
++ <description>NextRecordingTitle Channel</description>
++ <posx>390</posx>
++ <posy>50</posy>
++ <height>30</height>
++ <width>800</width>
++ <label>$INFO[PVR.NowRecordingTitle][COLOR=grey]$INFO[PVR.NowRecordingChannel, - [COLOR=blue]([/COLOR],[COLOR=blue])[/COLOR]][/COLOR]</label>
++ <align>right</align>
++ <aligny>center</aligny>
++ <font>font12_title</font>
++ <shadowcolor>black</shadowcolor>
++ <scroll>true</scroll>
++ </control>
++ </control>
++ </control>
+ <!-- Video Info -->
+ <control type="group">
+ <posx>0</posx>
+@@ -119,7 +263,47 @@
+ <include>VisibleFadeEffect</include>
+ <include>Window_OpenClose_Animation</include>
+ <control type="group">
+- <visible>!VideoPlayer.Content(Movies) + !VideoPlayer.Content(Episodes)</visible>
++ <visible>!VideoPlayer.Content(Movies) + !VideoPlayer.Content(Episodes) + !VideoPlayer.Content(LiveTV)</visible>
++ <control type="image">
++ <description>Cover image</description>
++ <posx>20</posx>
++ <posy>45</posy>
++ <width>150</width>
++ <height>300</height>
++ <aspectratio aligny="bottom">keep</aspectratio>
++ <texture>$INFO[VideoPlayer.Cover]</texture>
++ <bordertexture border="8">ThumbBorder.png</bordertexture>
++ <bordersize>5</bordersize>
++ </control>
++ <control type="label">
++ <description>Title label</description>
++ <posx>190</posx>
++ <posy>285</posy>
++ <height>30</height>
++ <width>1000</width>
++ <label>$INFO[VideoPlayer.Title]</label>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font13_title</font>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ </control>
++ <control type="label">
++ <description>Time Label</description>
++ <posx>190</posx>
++ <posy>310</posy>
++ <height>30</height>
++ <width>300</width>
++ <label>$INFO[Player.Time]$INFO[Player.Duration,[COLOR=blue] / [/COLOR]]</label>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font12</font>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ </control>
++ </control>
++ <control type="group">
++ <visible>VideoPlayer.Content(LiveTV)</visible>
+ <control type="image">
+ <description>Cover image</description>
+ <posx>20</posx>
+@@ -132,6 +316,19 @@
+ <bordersize>5</bordersize>
+ </control>
+ <control type="label">
++ <description>Channel label</description>
++ <posx>190</posx>
++ <posy>265</posy>
++ <height>25</height>
++ <width>660</width>
++ <label>$INFO[VideoPlayer.ChannelName]</label>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font12</font>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ </control>
++ <control type="label">
+ <description>Title label</description>
+ <posx>190</posx>
+ <posy>285</posy>
+@@ -304,8 +501,10 @@
+ <height>35</height>
+ <colordiffuse>CCFFFFFF</colordiffuse>
+ <texture flipy="true" border="0,5,0,0" flipx="true">HomeSubEnd.png</texture>
++ <animation effect="slide" start="0,0" end="30,0" time="0" condition="VideoPlayer.Content(LiveTV)">Conditional</animation>
+ </control>
+ <control type="group" id="600">
++ <visible>!VideoPlayer.Content(LiveTV)</visible>
+ <posx>35</posx>
+ <onup>9003</onup>
+ <ondown>9000</ondown>
+@@ -408,6 +607,129 @@
+ <onclick>XBMC.PlayerControl(Next)</onclick>
+ </control>
+ </control>
++ <control type="group" id="600">
++ <visible>VideoPlayer.Content(LiveTV)</visible>
++ <posx>0</posx>
++ <onup>9003</onup>
++ <ondown>9000</ondown>
++ <defaultcontrol>603</defaultcontrol>
++ <control type="image">
++ <description>Background image</description>
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>245</width>
++ <height>35</height>
++ <texture flipy="true" border="0,5,0,0">HomeSubNF.png</texture>
++ <colordiffuse>CCFFFFFF</colordiffuse>
++ </control>
++ <control type="button" id="601">
++ <posx>10</posx>
++ <posy>2</posy>
++ <width>30</width>
++ <height>30</height>
++ <label>-</label>
++ <texturefocus>OSDChannelUPFO.png</texturefocus>
++ <texturenofocus>OSDChannelUPNF.png</texturenofocus>
++ <onleft>607</onleft>
++ <onright>602</onright>
++ <onup>9003</onup>
++ <ondown>9000</ondown>
++ <onclick>XBMC.PlayerControl(Previous)</onclick>
++ </control>
++ <control type="button" id="602">
++ <posx>40</posx>
++ <posy>2</posy>
++ <width>30</width>
++ <height>30</height>
++ <label>-</label>
++ <texturefocus>OSDChannelDownFO.png</texturefocus>
++ <texturenofocus>OSDChannelDownNF.png</texturenofocus>
++ <onleft>601</onleft>
++ <onright>603</onright>
++ <onup>9003</onup>
++ <ondown>9000</ondown>
++ <onclick>XBMC.PlayerControl(Next)</onclick>
++ </control>
++ <control type="button" id="603">
++ <posx>70</posx>
++ <posy>2</posy>
++ <width>30</width>
++ <height>30</height>
++ <label>-</label>
++ <texturefocus>OSDStopFO.png</texturefocus>
++ <texturenofocus>OSDStopNF.png</texturenofocus>
++ <onleft>602</onleft>
++ <onright>604</onright>
++ <onup>9003</onup>
++ <ondown>9000</ondown>
++ <onclick>down</onclick>
++ <onclick>XBMC.PlayerControl(Stop)</onclick>
++ </control>
++ <control type="button" id="604">
++ <posx>100</posx>
++ <posy>2</posy>
++ <width>30</width>
++ <height>30</height>
++ <label>-</label>
++ <texturefocus>OSDRecordOffFO.png</texturefocus>
++ <texturenofocus>OSDRecordOffNF.png</texturenofocus>
++ <onleft>603</onleft>
++ <onright>605</onright>
++ <onup>9003</onup>
++ <ondown>9000</ondown>
++ <onclick>XBMC.PlayerControl(record)</onclick>
++ </control>
++
++ <control type="button" id="605">
++ <posx>130</posx>
++ <posy>2</posy>
++ <width>30</width>
++ <height>30</height>
++ <label>-</label>
++ <texturefocus>OSDRewindFO.png</texturefocus>
++ <texturenofocus>OSDRewindNF.png</texturenofocus>
++ <onleft>604</onleft>
++ <onright>608</onright>
++ <onup>9003</onup>
++ <ondown>9000</ondown>
++ <onback>50</onback>
++ <onclick>XBMC.PlayerControl(Rewind)</onclick>
++ </control>
++ <control type="button" id="606">
++ <posx>190</posx>
++ <posy>2</posy>
++ <width>30</width>
++ <height>30</height>
++ <label>-</label>
++ <texturefocus>OSDForwardFO.png</texturefocus>
++ <texturenofocus>OSDForwardNF.png</texturenofocus>
++ <onleft>608</onleft>
++ <onright>600</onright>
++ <onup>9003</onup>
++ <ondown>9000</ondown>
++ <onback>50</onback>
++ <onclick>XBMC.PlayerControl(Forward)</onclick>
++ </control>
++ <control type="togglebutton" id="608">
++ <posx>160</posx>
++ <posy>2</posy>
++ <width>30</width>
++ <height>30</height>
++ <label>-</label>
++ <texturefocus>OSDPauseFO.png</texturefocus>
++ <texturenofocus>OSDPauseNF.png</texturenofocus>
++ <usealttexture>Player.Paused | Player.Forwarding | Player.Rewinding</usealttexture>
++ <alttexturefocus>OSDPlayFO.png</alttexturefocus>
++ <alttexturenofocus>OSDPlayNF.png</alttexturenofocus>
++ <onleft>605</onleft>
++ <onright>606</onright>
++ <onup>9003</onup>
++ <ondown>9000</ondown>
++ <onback>50</onback>
++ <onclick>XBMC.PlayerControl(Play)</onclick>
++ </control>
++
++ </control>
+ <control type="radiobutton" id="607">
+ <colordiffuse>CCFFFFFF</colordiffuse>
+ <description>Go to fullscreen Playback</description>
+@@ -494,6 +816,13 @@
+ </control>
+ <control type="grouplist" id="9014">
+ <include>HomeSubMenuCommonValues</include>
++ <onleft>9014</onleft>
++ <onright>9014</onright>
++ <visible>Container(9000).HasFocus(12)</visible>
++ <include>HomeSubMenuLiveTV</include> <!-- Buttons for the grouplist -->
++ </control>
++ <control type="grouplist" id="9015">
++ <include>HomeSubMenuCommonValues</include>
+ <onleft>9013</onleft>
+ <onright>9013</onright>
+ <visible>Container(9000).HasFocus(4)</visible>
+@@ -598,6 +927,13 @@
+ <thumb>$INFO[Skin.String(Home_Custom_Back_Pictures_Folder)]</thumb>
+ <visible>!Skin.HasSetting(HomeMenuNoPicturesButton)</visible>
+ </item>
++ <item id="12">
++ <label>31502</label>
++ <onclick>ActivateWindow(PVR)</onclick>
++ <icon>special://skin/backgrounds/tv.jpg</icon>
++ <thumb>$INFO[Skin.String(Home_Custom_Back_TV_Folder)]</thumb>
++ <visible>System.GetBool(pvrmanager.enabled)</visible>
++ </item>
+ <item id="2">
+ <label>3</label>
+ <onclick condition="!Skin.HasSetting(HomeVideostoFiles)">ActivateWindow(Videos)</onclick>
+@@ -962,12 +1298,31 @@
+ <onclick>ActivateWindow(ShutdownMenu)</onclick>
+ <texturefocus>home-power-FO.png</texturefocus>
+ <texturenofocus>home-power.png</texturenofocus>
+- <onleft>21</onleft>
++ <onleft>22</onleft>
+ <onright>21</onright>
++ <onup>21</onup>
++ <ondown>21</ondown>
++ <visible>!System.IsInhibit</visible>
++ </control>
++ <control type="button" id="21">
++ <description>Power push button</description>
++ <posx>55</posx>
++ <posy>15</posy>
++ <width>43</width>
++ <height>43</height>
++ <label>31003</label>
++ <font>-</font>
++ <aligny>-</aligny>
++ <onclick>ActivateWindow(ShutdownMenu)</onclick>
++ <texturefocus>home-power-inhibit-FO.png</texturefocus>
++ <texturenofocus>home-power-inhibit.png</texturenofocus>
++ <onleft>20</onleft>
++ <onright>22</onright>
+ <onup>9002</onup>
+ <ondown>9003</ondown>
++ <visible>System.IsInhibit</visible>
+ </control>
+- <control type="button" id="21">
++ <control type="button" id="22">
+ <description>Favourites push button</description>
+ <posx>10</posx>
+ <posy>15</posy>
+@@ -979,7 +1334,7 @@
+ <onclick>ActivateWindow(Favourites)</onclick>
+ <texturefocus>home-favourites-FO.png</texturefocus>
+ <texturenofocus>home-favourites.png</texturenofocus>
+- <onleft>20</onleft>
++ <onleft>21</onleft>
+ <onright>20</onright>
+ <onup>9002</onup>
+ <ondown>9003</ondown>
+diff --git a/addons/skin.confluence/720p/IncludesBackgroundBuilding.xml b/addons/skin.confluence/720p/IncludesBackgroundBuilding.xml
+index a1122d0..c0d9e22 100644
+--- a/addons/skin.confluence/720p/IncludesBackgroundBuilding.xml
++++ b/addons/skin.confluence/720p/IncludesBackgroundBuilding.xml
+@@ -47,6 +47,29 @@
+ <include>Window_OpenClose_Animation</include>
+ </control>
+ </include>
++ <include name="CommonTVBackground">
++ <control type="multiimage">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>1280</width>
++ <height>720</height>
++ <imagepath fallback="special://skin/backgrounds/tv.jpg" background="true">$INFO[Skin.String(Home_Custom_Back_TV_Folder)]</imagepath>
++ <timeperimage>10000</timeperimage>
++ <randomize>true</randomize>
++ <fadetime>1000</fadetime>
++ </control>
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>1280</width>
++ <height>720</height>
++ <texture background="true">$INFO[ListItem.Property(Fanart_Image)]</texture>
++ <include>backgroundfade</include>
++ <fadetime>FanartCrossfadeTime</fadetime>
++ <visible>!Skin.HasSetting(HideBackGroundFanart) + !IsEmpty(ListItem.Property(Fanart_Image))</visible>
++ <include>Window_OpenClose_Animation</include>
++ </control>
++ </include>
+ <include name="CommonMusicBackground">
+ <control type="multiimage">
+ <posx>0</posx>
+@@ -360,4 +383,101 @@
+ </control>
+ </control>
+ </include>
++ <include name="ContentPanelBackgroundsPVR">
++ <control type="image">
++ <posx>0</posx>
++ <posy>100r</posy>
++ <width>1280</width>
++ <height>100</height>
++ <texture>floor.png</texture>
++ <animation effect="fade" time="250" condition="Window.Previous(Home)">WindowOpen</animation>
++ <animation effect="fade" time="250" condition="Window.Next(Home)">WindowClose</animation>
++ </control>
++ <control type="group">
++ <include>Window_OpenClose_Animation</include>
++ <control type="group">
++ <include>VisibleFadeEffect</include>
++ <visible>Control.IsVisible(10) | Control.IsVisible(14) | Control.IsVisible(15) | Control.IsVisible(16) | Control.IsVisible(17)</visible>
++ <control type="image">
++ <posx>55</posx>
++ <posy>60</posy>
++ <width>1170</width>
++ <height>600</height>
++ <texture border="15">ContentPanel.png</texture>
++ </control>
++ <control type="image">
++ <posx>55</posx>
++ <posy>652</posy>
++ <width>1170</width>
++ <height>64</height>
++ <texture border="15">ContentPanelMirror.png</texture>
++ </control>
++ </control>
++ <control type="group">
++ <include>VisibleFadeEffect</include>
++ <visible>Control.IsVisible(11) | Control.IsVisible(12)</visible>
++ <control type="image">
++ <posx>50</posx>
++ <posy>60</posy>
++ <width>450</width>
++ <height>600</height>
++ <texture border="15">ContentPanel.png</texture>
++ </control>
++ <control type="image">
++ <posx>50</posx>
++ <posy>652</posy>
++ <width>450</width>
++ <height>64</height>
++ <texture border="15">ContentPanelMirror.png</texture>
++ </control>
++ <control type="image">
++ <posx>510</posx>
++ <posy>60</posy>
++ <width>730</width>
++ <height>600</height>
++ <texture border="15">ContentPanel.png</texture>
++ </control>
++ <control type="image">
++ <posx>510</posx>
++ <posy>652</posy>
++ <width>730</width>
++ <height>64</height>
++ <texture border="15">ContentPanelMirror.png</texture>
++ </control>
++ </control>
++ <control type="group">
++ <include>VisibleFadeEffect</include>
++ <visible>Control.IsVisible(13)</visible>
++ <control type="image">
++ <posx>50</posx>
++ <posy>60</posy>
++ <width>840</width>
++ <height>600</height>
++ <texture border="15">ContentPanel.png</texture>
++ </control>
++ <control type="image">
++ <posx>50</posx>
++ <posy>652</posy>
++ <width>840</width>
++ <height>64</height>
++ <texture border="15">ContentPanelMirror.png</texture>
++ </control>
++ <control type="image">
++ <posx>900</posx>
++ <posy>60</posy>
++ <width>330</width>
++ <height>600</height>
++ <texture border="15">ContentPanel.png</texture>
++ </control>
++ <control type="image">
++ <posx>900</posx>
++ <posy>652</posy>
++ <width>330</width>
++ <height>64</height>
++ <texture border="15">ContentPanelMirror.png</texture>
++ </control>
++ </control>
++ </control>
++ </include>
++
+ </includes>
+\ No newline at end of file
+diff --git a/addons/skin.confluence/720p/IncludesHomeMenuItems.xml b/addons/skin.confluence/720p/IncludesHomeMenuItems.xml
+index 1012367..ed15e17 100644
+--- a/addons/skin.confluence/720p/IncludesHomeMenuItems.xml
++++ b/addons/skin.confluence/720p/IncludesHomeMenuItems.xml
+@@ -223,16 +223,56 @@
+ </control>
+ </include>
+ <include name="HomeSubMenuPictures">
+- <control type="image" id="90141">
++ <control type="image" id="90147">
+ <width>35</width>
+ <height>35</height>
+ <texture border="0,0,0,3" flipx="true">HomeSubEnd.png</texture>
+ </control>
+- <control type="button" id="90142">
++ <control type="button" id="90148">
+ <include>ButtonHomeSubCommonValues</include>
+ <label>24001</label>
+ <onclick>ActivateWindow(Pictures,Addons,return)</onclick>
+ </control>
++ <control type="image" id="90149">
++ <width>35</width>
++ <height>35</height>
++ <texture border="0,0,0,3">HomeSubEnd.png</texture>
++ </control>
++ </include>
++ <include name="HomeSubMenuLiveTV">
++ <control type="image" id="90141">
++ <width>35</width>
++ <height>35</height>
++ <texture border="0,0,0,3" flipx="true">HomeSubEnd.png</texture>
++ </control>
++ <control type="button" id="90142">
++ <include>ButtonHomeSubCommonValues</include>
++ <label>19023</label>
++ <onclick>ActivateWindow(PVR)</onclick>
++ <onclick>Setfocus(32)</onclick>
++ <onclick>Setfocus(11)</onclick>
++ </control>
++ <control type="button" id="90143">
++ <include>ButtonHomeSubCommonValues</include>
++ <label>19024</label>
++ <onclick>ActivateWindow(PVR)</onclick>
++ <onclick>Setfocus(33)</onclick>
++ <onclick>Setfocus(12)</onclick>
++ </control>
++ <control type="button" id="90144">
++ <include>ButtonHomeSubCommonValues</include>
++ <label>19163</label>
++ <onclick>ActivateWindow(PVR)</onclick>
++ <onclick>Setfocus(34)</onclick>
++ <onclick>Setfocus(13)</onclick>
++ </control>
++ <control type="button" id="90145">
++ <include>ButtonHomeSubCommonValues</include>
++ <label>19040</label>
++ <onclick>ActivateWindow(PVR)</onclick>
++ <onclick>Setfocus(35)</onclick>
++ <onclick>Setfocus(14)</onclick>
++ </control>
+ <control type="image" id="90146">
+ <width>35</width>
+ <height>35</height>
+diff --git a/addons/skin.confluence/720p/MusicOSD.xml b/addons/skin.confluence/720p/MusicOSD.xml
+index 9171ff7..49fc84b 100644
+--- a/addons/skin.confluence/720p/MusicOSD.xml
++++ b/addons/skin.confluence/720p/MusicOSD.xml
+@@ -306,8 +306,8 @@
+ <height>45</height>
+ <label>264</label>
+ <font>-</font>
+- <texturefocus>OSDRecordFO.png</texturefocus>
+- <texturenofocus>OSDRecordNF.png</texturenofocus>
++ <texturefocus>OSDRecordOffFO.png</texturefocus>
++ <texturenofocus>OSDRecordOffNF.png</texturenofocus>
+ <onleft>703</onleft>
+ <onright>600</onright>
+ <onup>1000</onup>
+@@ -318,4 +318,4 @@
+ </control>
+ </control>
+ </controls>
+- </window>
+\ No newline at end of file
++ </window>
+diff --git a/addons/skin.confluence/720p/MyPVR.xml b/addons/skin.confluence/720p/MyPVR.xml
+new file mode 100644
+index 0000000..32200a7
+--- /dev/null
++++ b/addons/skin.confluence/720p/MyPVR.xml
+@@ -0,0 +1,256 @@
++<window id="600">
++ <defaultcontrol>32</defaultcontrol>
++ <allowoverlay>no</allowoverlay>
++ <controls>
++ <include>CommonTVBackground</include>
++ <control type="group">
++ <visible>!Control.IsVisible(11) + !Control.IsVisible(12)</visible>
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>1280</width>
++ <height>720</height>
++ <texture>special://skin/backgrounds/media-overlay.png</texture>
++ <visible>Player.HasVideo + !Skin.HasSetting(ShowBackgroundVideo)</visible>
++ </control>
++ <control type="visualisation">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>1280</width>
++ <height>720</height>
++ <visible>Player.HasAudio + !Skin.HasSetting(ShowBackgroundVis)</visible>
++ </control>
++ <control type="videowindow">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>1280</width>
++ <height>720</height>
++ <visible>Player.HasVideo + !Skin.HasSetting(ShowBackgroundVideo)</visible>
++ </control>
++ </control>
++ <include>ContentPanelBackgroundsPVR</include>
++ <control type="group">
++ <description>Small Media Window</description>
++ <posx>530</posx>
++ <posy>80</posy>
++ <visible>Control.IsVisible(11) | Control.IsVisible(12)</visible>
++ <include>VisibleFadeEffect</include>
++ <include>Window_OpenClose_Animation</include>
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>690</width>
++ <height>400</height>
++ <texture border="5">button-nofocus.png</texture>
++ </control>
++ <control type="image">
++ <posx>5</posx>
++ <posy>5</posy>
++ <width>680</width>
++ <height>390</height>
++ <texture fallback="special://skin/backgrounds/tv.jpg">$INFO[Skin.String(Home_Custom_Back_TV_Folder)]</texture>
++ <include>VisibleFadeEffect</include>
++ <visible>!Player.HasVideo</visible>
++ </control>
++ <control type="videowindow">
++ <posx>5</posx>
++ <posy>5</posy>
++ <width>680</width>
++ <height>390</height>
++ <visible>Player.HasVideo</visible>
++ <animation effect="slide" start="0,0" end="-2000,0" time="0">WindowClose</animation>
++ </control>
++ <control type="image">
++ <posx>1</posx>
++ <posy>1</posy>
++ <width>688</width>
++ <height>35</height>
++ <texture>black-back.png</texture>
++ <colordiffuse>DDFFFFFF</colordiffuse>
++ <visible>Player.HasVideo</visible>
++ </control>
++ <control type="label">
++ <description>Current Video label</description>
++ <posx>30</posx>
++ <posy>1</posy>
++ <width>650</width>
++ <height>35</height>
++ <font>font12</font>
++ <textcolor>grey2</textcolor>
++ <align>center</align>
++ <aligny>center</aligny>
++ <label>$INFO[VideoPlayer.Title]</label>
++ <visible>Player.HasVideo</visible>
++ </control>
++ <!-- control type="visualisation">
++ <posx>85</posx>
++ <posy>85</posy>
++ <width>700</width>
++ <height>390</height>
++ <visible>Player.HasAudio</visible>
++ </control -->
++ </control>
++
++ <control type="group" id="50">
++ <include>Window_OpenClose_Animation</include>
++ <include>EPGTimelineView</include>--> <!-- view id = 10 -->
++ <include>LiveTVChannelView</include> <!-- view id = 11 -->
++ <include>LiveRadioChannelView</include> <!-- view id = 12 -->
++ <include>LiveTVRecordingsView</include> <!-- view id = 13 -->
++ <include>LiveTVTimersView</include> <!-- view id = 14 -->
++ <include>LiveTVGuideChannelView</include> <!-- view id = 15 -->
++ <include>LiveTVGuideNowNextView</include> <!-- view id = 16 -->
++ <include>LiveTVSearchView</include> <!-- view id = 17 -->
++ </control>
++ <include>CommonNowPlaying</include>
++ <include>BehindDialogFadeOut</include>
++ <include>ScrollOffsetLabel</include>
++
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>1280</width>
++ <height>720</height>
++ <texture>black-back.png</texture>
++ <animation effect="fade" time="400">Visible</animation>
++ <animation effect="fade" time="200">Hidden</animation>
++ <visible>Window.IsActive(FileBrowser) | Window.IsActive(PVRGuideInfo) | Window.IsActive(PVRRecordingInfo) | Window.IsActive(PVRTimerSetting) | Window.IsActive(PVRGroupManager) | Window.IsActive(PVRGuideSearch)</visible>
++ </control>
++
++ <control type="group">
++ <posx>-250</posx>
++ <include>SideBladeLeft</include>
++ <control type="grouplist" id="9000">
++ <posx>0</posx>
++ <posy>110</posy>
++ <width>250</width>
++ <height>600</height>
++ <onleft>9000</onleft>
++ <onright>50</onright>
++ <onup>9000</onup>
++ <ondown>9000</ondown>
++ <itemgap>0</itemgap>
++ <control type="label" id="200">
++ <width>250</width>
++ <height>35</height>
++ <font>font12caps</font>
++ <label>31006</label>
++ <textcolor>blue</textcolor>
++ <align>center</align>
++ <aligny>center</aligny>
++ </control>
++ <control type="button" id="32">
++ <description>TV Channels</description>
++ <posx>0</posx>
++ <posy>0</posy>
++ <textwidth>235</textwidth>
++ <include>ButtonCommonValues</include>
++ <label>19023</label>
++ </control>
++ <control type="button" id="33">
++ <description>Radio Channels</description>
++ <posx>0</posx>
++ <posy>40</posy>
++ <textwidth>235</textwidth>
++ <include>ButtonCommonValues</include>
++ <label>19024</label>
++ <onleft>12</onleft>
++ <onright>12</onright>
++ </control>
++ <control type="button" id="31">
++ <description>TV Guide</description>
++ <posx>0</posx>
++ <posy>80</posy>
++ <textwidth>235</textwidth>
++ <include>ButtonCommonValues</include>
++ <label>$LOCALIZE[19222]: $LOCALIZE[19030]</label>
++ </control>
++ <control type="button" id="34">
++ <description>Recordings</description>
++ <posx>0</posx>
++ <posy>120</posy>
++ <textwidth>235</textwidth>
++ <include>ButtonCommonValues</include>
++ <label>19163</label>
++ </control>
++ <control type="button" id="35">
++ <description>Timers</description>
++ <posx>0</posx>
++ <posy>160</posy>
++ <textwidth>235</textwidth>
++ <include>ButtonCommonValues</include>
++ <label>19040</label>
++ </control>
++ <control type="button" id="36">
++ <description>Search</description>
++ <posx>0</posx>
++ <posy>200</posy>
++ <textwidth>235</textwidth>
++ <include>ButtonCommonValues</include>
++ <label>137</label>
++ </control>
++ <include>CommonNowPlaying_Controls</include>
++ </control>
++ </control>
++
++ <control type="group">
++ <posx>500</posx>
++ <posy>0</posy>
++ <visible>!IsEmpty(Control.GetLabel(30))</visible>
++ <include>VisibleFadeEffect</include>
++ <animation effect="slide" end="-750,0" time="400" tween="quadratic" easing="out">WindowClose</animation>
++ <animation effect="slide" start="-750,0" time="400" tween="quadratic" easing="out">WindowOpen</animation>
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>250</width>
++ <height>35</height>
++ <texture border="0,0,32,0">header.png</texture>
++ </control>
++ <control type="label" id="30">
++ <include>WindowTitleCommons</include>
++ <posx>220</posx>
++ </control>
++ </control>
++
++ <control type="group">
++ <posx>240</posx>
++ <posy>0</posy>
++ <animation effect="slide" end="-540,0" time="400" tween="quadratic" easing="out">WindowClose</animation>
++ <animation effect="slide" start="-540,0" time="400" tween="quadratic" easing="out">WindowOpen</animation>
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>300</width>
++ <height>35</height>
++ <texture border="0,0,32,0">header.png</texture>
++ </control>
++ <control type="label" id="29">
++ <include>WindowTitleCommons</include>
++ <posx>270</posx>
++ </control>
++ </control>
++
++ <control type="group">
++ <posx>40</posx>
++ <posy>0</posy>
++ <animation effect="slide" end="-310,0" time="400" tween="quadratic" easing="out">WindowClose</animation>
++ <animation effect="slide" start="-310,0" time="400" tween="quadratic" easing="out">WindowOpen</animation>
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>250</width>
++ <height>35</height>
++ <texture border="0,0,32,0">header.png</texture>
++ </control>
++ <control type="label">
++ <include>WindowTitleCommons</include>
++ <posx>220</posx>
++ <label>$LOCALIZE[31502]</label>
++ </control>
++ </control>
++
++ <include>WindowTitleHomeButton</include>
++ <include>Clock</include>
++ </controls>
++</window>
+diff --git a/addons/skin.confluence/720p/PlayerControls.xml b/addons/skin.confluence/720p/PlayerControls.xml
+index 7f1899e..540872f 100644
+--- a/addons/skin.confluence/720p/PlayerControls.xml
++++ b/addons/skin.confluence/720p/PlayerControls.xml
+@@ -1,5 +1,5 @@
+ <window type="dialog" id="114">
+- <defaultcontrol always="true">603</defaultcontrol>
++ <defaultcontrol always="true">100</defaultcontrol>
+ <include>dialogeffect</include>
+ <visible>Player.HasMedia + Window.IsActive(PlayerControls) + !Window.IsActive(FullscreenVideo) + !Window.IsActive(Visualisation)</visible>
+ <coordinates>
+@@ -18,6 +18,8 @@
+ <control type="group" id="100">
+ <posx>25</posx>
+ <posy>162</posy>
++ <defaultcontrol always="true">603</defaultcontrol>
++ <visible>!VideoPlayer.Content(LiveTV)</visible>
+ <control type="button" id="600">
+ <posx>0</posx>
+ <posy>0</posy>
+@@ -31,6 +33,7 @@
+ <onup>300</onup>
+ <ondown>200</ondown>
+ <onclick>XBMC.PlayerControl(Previous)</onclick>
++ <visible>!VideoPlayer.Content(LiveTV)</visible>
+ </control>
+ <control type="button" id="601">
+ <posx>40</posx>
+@@ -45,6 +48,7 @@
+ <onup>300</onup>
+ <ondown>200</ondown>
+ <onclick>XBMC.PlayerControl(Rewind)</onclick>
++ <visible>!VideoPlayer.Content(LiveTV)</visible>
+ </control>
+ <control type="togglebutton" id="603">
+ <posx>80</posx>
+@@ -62,6 +66,7 @@
+ <onup>300</onup>
+ <ondown>200</ondown>
+ <onclick>XBMC.PlayerControl(Play)</onclick>
++ <visible>!VideoPlayer.Content(LiveTV)</visible>
+ </control>
+ <control type="button" id="602">
+ <posx>120</posx>
+@@ -77,6 +82,7 @@
+ <ondown>200</ondown>
+ <onclick>down</onclick>
+ <onclick>XBMC.PlayerControl(Stop)</onclick>
++ <visible>!VideoPlayer.Content(LiveTV)</visible>
+ </control>
+ <control type="button" id="604">
+ <posx>160</posx>
+@@ -91,6 +97,7 @@
+ <onup>300</onup>
+ <ondown>200</ondown>
+ <onclick>XBMC.PlayerControl(Forward)</onclick>
++ <visible>!VideoPlayer.Content(LiveTV)</visible>
+ </control>
+ <control type="button" id="605">
+ <posx>200</posx>
+@@ -105,6 +112,7 @@
+ <onup>300</onup>
+ <ondown>200</ondown>
+ <onclick>XBMC.PlayerControl(Next)</onclick>
++ <visible>!VideoPlayer.Content(LiveTV)</visible>
+ </control>
+ <control type="button" id="606">
+ <posx>240</posx>
+@@ -112,8 +120,8 @@
+ <width>40</width>
+ <height>40</height>
+ <label>-</label>
+- <texturefocus>OSDRecordFO.png</texturefocus>
+- <texturenofocus>OSDRecordNF.png</texturenofocus>
++ <texturefocus>OSDRecordOffFO.png</texturefocus>
++ <texturenofocus>OSDRecordOffNF.png</texturenofocus>
+ <onleft>605</onleft>
+ <onright>607</onright>
+ <onup>300</onup>
+@@ -121,6 +129,7 @@
+ <onclick>XBMC.PlayerControl(record)</onclick>
+ <enable>Player.CanRecord</enable>
+ <animation effect="fade" start="100" end="30" time="100" condition="!Player.CanRecord">Conditional</animation>
++ <visible>!VideoPlayer.Content(LiveTV)</visible>
+ </control>
+ <control type="button" id="607">
+ <posx>365</posx>
+@@ -135,6 +144,7 @@
+ <onright>608</onright>
+ <onup>100</onup>
+ <ondown>100</ondown>
++ <visible>!VideoPlayer.Content(LiveTV)</visible>
+ </control>
+ <control type="image">
+ <posx>365</posx>
+@@ -144,6 +154,7 @@
+ <texture>OSDRepeatNF.png</texture>
+ <visible>!Playlist.IsRepeat + !Playlist.IsRepeatOne</visible>
+ <visible>!Control.HasFocus(607)</visible>
++ <visible>!VideoPlayer.Content(LiveTV)</visible>
+ </control>
+ <control type="image">
+ <posx>365</posx>
+@@ -153,6 +164,7 @@
+ <texture>OSDRepeatFO.png</texture>
+ <visible>!Playlist.IsRepeat + !Playlist.IsRepeatOne</visible>
+ <visible>Control.HasFocus(607)</visible>
++ <visible>!VideoPlayer.Content(LiveTV)</visible>
+ </control>
+ <control type="image">
+ <posx>365</posx>
+@@ -162,6 +174,7 @@
+ <texture>OSDRepeatOneNF.png</texture>
+ <visible>Playlist.IsRepeatOne</visible>
+ <visible>!Control.HasFocus(607)</visible>
++ <visible>!VideoPlayer.Content(LiveTV)</visible>
+ </control>
+ <control type="image">
+ <posx>365</posx>
+@@ -171,6 +184,7 @@
+ <texture>OSDRepeatOneFO.png</texture>
+ <visible>Playlist.IsRepeatOne</visible>
+ <visible>Control.HasFocus(607)</visible>
++ <visible>!VideoPlayer.Content(LiveTV)</visible>
+ </control>
+ <control type="image">
+ <posx>365</posx>
+@@ -180,6 +194,7 @@
+ <texture>OSDRepeatAllNF.png</texture>
+ <visible>Playlist.IsRepeat</visible>
+ <visible>!Control.HasFocus(607)</visible>
++ <visible>!VideoPlayer.Content(LiveTV)</visible>
+ </control>
+ <control type="image">
+ <posx>365</posx>
+@@ -189,6 +204,7 @@
+ <texture>OSDRepeatAllFO.png</texture>
+ <visible>Playlist.IsRepeat</visible>
+ <visible>Control.HasFocus(607)</visible>
++ <visible>!VideoPlayer.Content(LiveTV)</visible>
+ </control>
+ <control type="togglebutton" id="608">
+ <posx>405</posx>
+@@ -206,6 +222,130 @@
+ <onright>600</onright>
+ <onup>100</onup>
+ <ondown>100</ondown>
++ <visible>!VideoPlayer.Content(LiveTV)</visible>
++ </control>
++ </control>
++ <control type="group" id="100">
++ <posx>25</posx>
++ <posy>162</posy>
++ <defaultcontrol always="true">700</defaultcontrol>
++ <visible>VideoPlayer.Content(LiveTV)</visible>
++ <control type="button" id="701">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>40</width>
++ <height>40</height>
++ <label>-</label>
++ <texturefocus>OSDRewindFO.png</texturefocus>
++ <texturenofocus>OSDRewindNF.png</texturenofocus>
++ <onleft>706</onleft>
++ <onright>702</onright>
++ <onup>300</onup>
++ <ondown>200</ondown>
++ <onclick>XBMC.PlayerControl(Rewind)</onclick>
++ <visible>VideoPlayer.Content(LiveTV)</visible>
++ <enable>false</enable>
++ <animation effect="fade" start="100" end="50" time="100" condition="true">Conditional</animation>
++ </control>
++ <control type="button" id="702">
++ <posx>40</posx>
++ <posy>0</posy>
++ <width>40</width>
++ <height>40</height>
++ <label>-</label>
++ <texturefocus>OSDStopFO.png</texturefocus>
++ <texturenofocus>OSDStopNF.png</texturenofocus>
++ <onleft>701</onleft>
++ <onright>703</onright>
++ <onup>300</onup>
++ <ondown>200</ondown>
++ <onclick>down</onclick>
++ <onclick>XBMC.PlayerControl(Stop)</onclick>
++ <visible>VideoPlayer.Content(LiveTV)</visible>
++ </control>
++ <control type="togglebutton" id="703">
++ <posx>80</posx>
++ <posy>0</posy>
++ <width>40</width>
++ <height>40</height>
++ <label>-</label>
++ <texturefocus>OSDPauseFO.png</texturefocus>
++ <texturenofocus>OSDPauseNF.png</texturenofocus>
++ <usealttexture>Player.Paused | Player.Forwarding | Player.Rewinding</usealttexture>
++ <alttexturefocus>OSDPlayFO.png</alttexturefocus>
++ <alttexturenofocus>OSDPlayNF.png</alttexturenofocus>
++ <onleft>702</onleft>
++ <onright>704</onright>
++ <onup>300</onup>
++ <ondown>200</ondown>
++ <onclick>XBMC.PlayerControl(Play)</onclick>
++ <visible>VideoPlayer.Content(LiveTV)</visible>
++ <enable>false</enable>
++ <animation effect="fade" start="100" end="50" time="100" condition="true">Conditional</animation>
++ </control>
++ <control type="button" id="704">
++ <posx>120</posx>
++ <posy>0</posy>
++ <width>40</width>
++ <height>40</height>
++ <label>-</label>
++ <texturefocus>OSDForwardFO.png</texturefocus>
++ <texturenofocus>OSDForwardNF.png</texturenofocus>
++ <onleft>703</onleft>
++ <onright>700</onright>
++ <onup>300</onup>
++ <ondown>200</ondown>
++ <onclick>XBMC.PlayerControl(Forward)</onclick>
++ <visible>VideoPlayer.Content(LiveTV)</visible>
++ <enable>false</enable>
++ <animation effect="fade" start="100" end="50" time="100" condition="true">Conditional</animation>
++ </control>
++ <control type="button" id="700">
++ <posx>200</posx>
++ <posy>0</posy>
++ <width>40</width>
++ <height>40</height>
++ <label>-</label>
++ <texturefocus>OSDChannelUPFO.png</texturefocus>
++ <texturenofocus>OSDChannelUPNF.png</texturenofocus>
++ <onleft>704</onleft>
++ <onright>705</onright>
++ <onup>300</onup>
++ <ondown>200</ondown>
++ <onclick>XBMC.PlayerControl(Previous)</onclick>
++ <visible>VideoPlayer.Content(LiveTV)</visible>
++ </control>
++ <control type="button" id="705">
++ <posx>240</posx>
++ <posy>0</posy>
++ <width>40</width>
++ <height>40</height>
++ <label>-</label>
++ <texturefocus>OSDChannelDownFO.png</texturefocus>
++ <texturenofocus>OSDChannelDownNF.png</texturenofocus>
++ <onleft>700</onleft>
++ <onright>706</onright>
++ <onup>300</onup>
++ <ondown>200</ondown>
++ <onclick>XBMC.PlayerControl(Next)</onclick>
++ <visible>VideoPlayer.Content(LiveTV)</visible>
++ </control>
++ <control type="button" id="706">
++ <posx>280</posx>
++ <posy>0</posy>
++ <width>40</width>
++ <height>40</height>
++ <label>-</label>
++ <texturefocus>OSDRecordOffFO.png</texturefocus>
++ <texturenofocus>OSDRecordOffNF.png</texturenofocus>
++ <onleft>705</onleft>
++ <onright>701</onright>
++ <onup>300</onup>
++ <ondown>200</ondown>
++ <onclick>XBMC.PlayerControl(record)</onclick>
++ <enable>Player.CanRecord</enable>
++ <animation effect="fade" start="100" end="30" time="100" condition="!Player.CanRecord">Conditional</animation>
++ <visible>VideoPlayer.Content(LiveTV)</visible>
+ </control>
+ </control>
+ <!-- Music Info -->
+@@ -229,4 +369,4 @@
+ <include>SmallVideoInfo</include>
+ </control>
+ </controls>
+-</window>
+\ No newline at end of file
++</window>
+diff --git a/addons/skin.confluence/720p/Settings.xml b/addons/skin.confluence/720p/Settings.xml
+index e55cf06..2a1ce69 100644
+--- a/addons/skin.confluence/720p/Settings.xml
++++ b/addons/skin.confluence/720p/Settings.xml
+@@ -129,42 +129,48 @@
+ <icon>special://skin/backgrounds/videos.jpg</icon>
+ </item>
+ <item id="3">
++ <label>31502</label>
++ <label2>31409</label2>
++ <onclick>ActivateWindow(PVRSettings)</onclick>
++ <icon>special://skin/backgrounds/tv.jpg</icon>
++ </item>
++ <item id="4">
+ <label>2</label>
+ <label2>31402</label2>
+ <onclick>ActivateWindow(MusicSettings)</onclick>
+ <icon>special://skin/backgrounds/music.jpg</icon>
+ </item>
+- <item id="4">
++ <item id="5">
+ <label>1</label>
+ <label2>31403</label2>
+ <onclick>ActivateWindow(PicturesSettings)</onclick>
+ <icon>special://skin/backgrounds/pictures.jpg</icon>
+ </item>
+- <item id="5">
++ <item id="6">
+ <label>8</label>
+ <label2>31404</label2>
+ <onclick>ActivateWindow(WeatherSettings)</onclick>
+ <icon>special://skin/backgrounds/weather.jpg</icon>
+ </item>
+- <item id="6">
++ <item id="7">
+ <label>24001</label>
+ <label2>31408</label2>
+ <onclick>ActivateWindow(AddonBrowser)</onclick>
+ <icon>special://skin/backgrounds/addons.jpg</icon>
+ </item>
+- <item id="7">
++ <item id="8">
+ <label>705</label>
+ <label2>31405</label2>
+ <onclick>ActivateWindow(NetworkSettings)</onclick>
+ <icon>special://skin/backgrounds/network.jpg</icon>
+ </item>
+- <item id="8">
++ <item id="9">
+ <label>13000</label>
+ <label2>31406</label2>
+ <onclick>ActivateWindow(SystemSettings)</onclick>
+ <icon>special://skin/backgrounds/system.jpg</icon>
+ </item>
+- <item id="9">
++ <item id="10">
+ <label>166</label>
+ <label2>31407</label2>
+ <onclick>ActivateWindow(1111)</onclick>
+diff --git a/addons/skin.confluence/720p/SettingsSystemInfo.xml b/addons/skin.confluence/720p/SettingsSystemInfo.xml
+index a90a9f0..c5834ea 100644
+--- a/addons/skin.confluence/720p/SettingsSystemInfo.xml
++++ b/addons/skin.confluence/720p/SettingsSystemInfo.xml
+@@ -147,6 +147,21 @@
+ <pulseonselect>false</pulseonselect>
+ <label>13281</label>
+ </control>
++ <control type="button" id="99">
++ <description>Button PVR</description>
++ <height>60</height>
++ <width>241</width>
++ <textoffsetx>0</textoffsetx>
++ <align>right</align>
++ <aligny>center</aligny>
++ <font>font13_title</font>
++ <textcolor>grey2</textcolor>
++ <focusedcolor>white</focusedcolor>
++ <texturefocus border="5">MenuItemFO.png</texturefocus>
++ <texturenofocus border="5">MenuItemNF.png</texturenofocus>
++ <pulseonselect>false</pulseonselect>
++ <label>19191</label>
++ </control>
+ </control>
+ <control type="image">
+ <posx>268</posx>
+diff --git a/addons/skin.confluence/720p/VideoFullScreen.xml b/addons/skin.confluence/720p/VideoFullScreen.xml
+index e12ec81..703d424 100644
+--- a/addons/skin.confluence/720p/VideoFullScreen.xml
++++ b/addons/skin.confluence/720p/VideoFullScreen.xml
+@@ -3,7 +3,7 @@
+ <controls>
+ <!-- media infos -->
+ <control type="group" id="1">
+- <visible>[Player.ShowInfo | Window.IsActive(VideoOSD)] + ![Window.IsVisible(123) | Window.IsVisible(124) | Window.IsVisible(125)]</visible>
++ <visible>[Player.ShowInfo | Window.IsActive(VideoOSD)] + ![Window.IsVisible(123) | Window.IsVisible(124) | Window.IsVisible(125) | Window.IsVisible(PVROSDChannels) | Window.IsVisible(PVROSDGuide)]</visible>
+ <animation effect="fade" time="200">VisibleChange</animation>
+ <control type="image" id="1">
+ <posx>0</posx>
+@@ -24,7 +24,21 @@
+ <textcolor>white</textcolor>
+ <shadowcolor>black</shadowcolor>
+ <label>$INFO[Player.Chapter,$LOCALIZE[21396]: ]$INFO[Player.ChapterCount, / ]$INFO[Player.ChapterName,[COLOR=grey] - (,)[/COLOR]]</label>
+- <visible>Player.ChapterCount</visible>
++ <visible>Player.ChapterCount + !VideoPlayer.Content(LiveTV)</visible>
++ </control>
++ <control type="label" id="1">
++ <description>Channel Group label</description>
++ <posx>30</posx>
++ <posy>5</posy>
++ <width>1000</width>
++ <height>25</height>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font13</font>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <label>$INFO[VideoPlayer.ChannelGroup,$LOCALIZE[31509]: ]</label>
++ <visible>VideoPlayer.Content(LiveTV)</visible>
+ </control>
+ <control type="label" id="1">
+ <description>Clock label</description>
+@@ -81,11 +95,35 @@
+ <width>910</width>
+ <height>25</height>
+ <align>left</align>
++ <aligny>center</aligny>
+ <font>font13</font>
+ <label>$LOCALIZE[31040]</label>
+ <textcolor>white</textcolor>
+ <shadowcolor>black</shadowcolor>
+- <animation effect="slide" start="0,0" end="0,25" time="0" condition="!VideoPlayer.Content(Movies) + !VideoPlayer.Content(Episodes) + !VideoPlayer.Content(MusicVideos)">conditional</animation>
++ <visible>![VideoPlayer.Content(LiveTV) + Player.Recording]</visible>
++ <animation effect="slide" start="0,0" end="0,25" time="0" condition="!VideoPlayer.Content(Movies) + !VideoPlayer.Content(Episodes) + !VideoPlayer.Content(MusicVideos) + !VideoPlayer.Content(LiveTV)">conditional</animation>
++ </control>
++ <control type="image" id="1">
++ <posy>0</posy>
++ <width>50</width>
++ <height>25</height>
++ <aspectratio align="center" aligny="center">keep</aspectratio>
++ <texture>PVR-IsRecording.png</texture>
++ <visible>VideoPlayer.Content(LiveTV) + Player.Recording</visible>
++ </control>
++ <control type="label" id="1">
++ <description>Heading label</description>
++ <posx>50</posx>
++ <posy>0</posy>
++ <width>860</width>
++ <height>25</height>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font13</font>
++ <label>$LOCALIZE[19158]</label>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <visible>VideoPlayer.Content(LiveTV) + Player.Recording</visible>
+ </control>
+ <control type="label" id="1">
+ <description>Studio label</description>
+@@ -126,6 +164,19 @@
+ <shadowcolor>black</shadowcolor>
+ <visible>VideoPlayer.Content(MusicVideos)</visible>
+ </control>
++ <control type="label" id="1">
++ <description>LiveTV Info label</description>
++ <posx>20</posx>
++ <posy>30</posy>
++ <width>910</width>
++ <height>25</height>
++ <align>left</align>
++ <font>font12</font>
++ <label>$INFO[VideoPlayer.ChannelName]$INFO[VideoPlayer.ChannelNumber, - ([COLOR=blue],[/COLOR])]</label>
++ <textcolor>grey2</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <visible>VideoPlayer.Content(LiveTV)</visible>
++ </control>
+ <control type="grouplist" id="1">
+ <posx>20</posx>
+ <posy>60</posy>
+@@ -176,13 +227,28 @@
+ <font>font12</font>
+ <textcolor>grey</textcolor>
+ <scroll>true</scroll>
+- <visible>!Window.IsVisible(VideoOSD)</visible>
++ <visible>!Window.IsVisible(VideoOSD) + !VideoPlayer.Content(LiveTV)</visible>
++ <animation effect="fade" time="200">VisibleChange</animation>
++ </control>
++ <control type="label" id="1">
++ <posx>0</posx>
++ <posy>120</posy>
++ <width>910</width>
++ <height>25</height>
++ <label>$INFO[VideoPlayer.NextTitle,$LOCALIZE[209]: ]</label>
++ <align>center</align>
++ <aligny>center</aligny>
++ <font>font12</font>
++ <textcolor>grey</textcolor>
++ <scroll>true</scroll>
++ <visible>!Window.IsVisible(VideoOSD) + VideoPlayer.Content(LiveTV)</visible>
+ <animation effect="fade" time="200">VisibleChange</animation>
+ </control>
+ </control>
+ <control type="group" id="1">
+ <posx>330</posx>
+ <posy>85r</posy>
++ <visible>!VideoPlayer.Content(LiveTV) | [VideoPlayer.Content(LiveTV) + VideoPlayer.HasEpg]</visible>
+ <control type="label" id="1">
+ <posx>0</posx>
+ <posy>0</posy>
+@@ -233,7 +299,6 @@
+ <posy>0</posy>
+ <width>1280</width>
+ <height>140</height>
+- <colordiffuse>AAFFFFFF</colordiffuse>
+ <texture>black-back.png</texture>
+ </control>
+ <control type="label" id="10">
+@@ -270,5 +335,252 @@
+ <label>-</label>
+ </control>
+ </control>
++ <control type="selectbutton" id="503">
++ <posx>440</posx>
++ <posy>100</posy>
++ <width>400</width>
++ <height>100</height>
++ <font>font13caps</font>
++ <description>TV Channel Group Select Button</description>
++ <texturebg border="20">OverlayDialogBackground.png</texturebg>
++ <onleft>503</onleft>
++ <onright>503</onright>
++ <onup>500</onup>
++ <ondown>500</ondown>
++ <include>VisibleFadeEffect</include>
++ </control>
++ <control type="group">
++ <visible>Player.ShowCodec + VideoPlayer.Content(LiveTV) + system.getbool(pvrplayback.signalquality)</visible>
++ <posy>165</posy>
++ <control type="image">
++ <description>media info background image</description>
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>1280</width>
++ <height>220</height>
++ <texture>black-back.png</texture>
++ </control>
++ <control type="label">
++ <description>Header</description>
++ <posx>50</posx>
++ <posy>5</posy>
++ <width>1200</width>
++ <height>25</height>
++ <label>$LOCALIZE[19005]</label>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font13_title</font>
++ <textcolor>blue</textcolor>
++ </control>
++ <control type="label">
++ <description>Backend</description>
++ <posx>50</posx>
++ <posy>40</posy>
++ <width>165</width>
++ <height>25</height>
++ <label>$LOCALIZE[19012]:</label>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font12</font>
++ <textcolor>grey2</textcolor>
++ </control>
++ <control type="label">
++ <description>Backend value</description>
++ <posx>220</posx>
++ <posy>40</posy>
++ <width>1000</width>
++ <height>25</height>
++ <label>$INFO[PVR.ActStreamClient]</label>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font12</font>
++ <textcolor>white</textcolor>
++ </control>
++ <control type="label">
++ <description>Device</description>
++ <posx>50</posx>
++ <posy>65</posy>
++ <width>165</width>
++ <height>25</height>
++ <label>$LOCALIZE[19006]:</label>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font12</font>
++ <textcolor>grey2</textcolor>
++ </control>
++ <control type="label">
++ <description>Device value</description>
++ <posx>220</posx>
++ <posy>65</posy>
++ <width>1000</width>
++ <height>25</height>
++ <label>$INFO[PVR.ActStreamDevice]</label>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font12</font>
++ <textcolor>white</textcolor>
++ </control>
++ <control type="label">
++ <description>Status</description>
++ <posx>50</posx>
++ <posy>90</posy>
++ <width>165</width>
++ <height>25</height>
++ <label>$LOCALIZE[19007]:</label>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font12</font>
++ <textcolor>grey2</textcolor>
++ </control>
++ <control type="label">
++ <description>Status value</description>
++ <posx>220</posx>
++ <posy>90</posy>
++ <width>1000</width>
++ <height>25</height>
++ <label>$INFO[PVR.ActStreamStatus]</label>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font12</font>
++ <textcolor>white</textcolor>
++ </control>
++ <control type="label">
++ <description>Signal</description>
++ <posx>50</posx>
++ <posy>115</posy>
++ <width>165</width>
++ <height>25</height>
++ <label>$LOCALIZE[19008]:</label>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font12</font>
++ <textcolor>grey2</textcolor>
++ </control>
++ <control type="progress">
++ <description>Progressbar</description>
++ <posx>220</posx>
++ <posy>122</posy>
++ <width>910</width>
++ <height>14</height>
++ <info>PVR.ActStreamProgrSignal</info>
++ </control>
++ <control type="label">
++ <description>Signal value</description>
++ <posx>1200</posx>
++ <posy>115</posy>
++ <width>180</width>
++ <height>25</height>
++ <label>$INFO[PVR.ActStreamSignal]</label>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font12</font>
++ <textcolor>white</textcolor>
++ </control>
++ <control type="label">
++ <description>SNR</description>
++ <posx>50</posx>
++ <posy>140</posy>
++ <width>165</width>
++ <height>25</height>
++ <label>$LOCALIZE[19009]:</label>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font12</font>
++ <textcolor>grey2</textcolor>
++ </control>
++ <control type="progress">
++ <description>Progressbar</description>
++ <posx>220</posx>
++ <posy>147</posy>
++ <width>910</width>
++ <height>14</height>
++ <overlaytexture>-</overlaytexture>
++ <info>PVR.ActStreamProgrSNR</info>
++ </control>
++ <control type="label">
++ <description>SNR value</description>
++ <posx>1200</posx>
++ <posy>140</posy>
++ <width>180</width>
++ <height>25</height>
++ <label>$INFO[PVR.ActStreamSNR]</label>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font12</font>
++ <textcolor>white</textcolor>
++ </control>
++ <control type="label">
++ <description>BER</description>
++ <posx>50</posx>
++ <posy>165</posy>
++ <width>165</width>
++ <height>25</height>
++ <label>$LOCALIZE[19010]:</label>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font12</font>
++ <textcolor>grey2</textcolor>
++ </control>
++ <control type="label">
++ <description>BER value</description>
++ <posx>220</posx>
++ <posy>165</posy>
++ <width>1000</width>
++ <height>25</height>
++ <label>$INFO[PVR.ActStreamBER]</label>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font12</font>
++ <textcolor>white</textcolor>
++ </control>
++ <control type="label">
++ <description>UNC</description>
++ <posx>430</posx>
++ <posy>165</posy>
++ <width>165</width>
++ <height>25</height>
++ <label>$LOCALIZE[19011]:</label>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font12</font>
++ <textcolor>grey2</textcolor>
++ </control>
++ <control type="label">
++ <description>UNC value</description>
++ <posx>600</posx>
++ <posy>165</posy>
++ <width>1000</width>
++ <height>25</height>
++ <label>$INFO[PVR.ActStreamUNC]</label>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font12</font>
++ <textcolor>white</textcolor>
++ </control>
++ <control type="label">
++ <description>Encryption</description>
++ <posx>50</posx>
++ <posy>190</posy>
++ <width>165</width>
++ <height>25</height>
++ <label>$LOCALIZE[19015]:</label>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font12</font>
++ <textcolor>grey2</textcolor>
++ </control>
++ <control type="label">
++ <description>Encryption value</description>
++ <posx>220</posx>
++ <posy>190</posy>
++ <width>1000</width>
++ <height>25</height>
++ <label>$INFO[PVR.ActStreamEncryptionName]</label>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font12</font>
++ <textcolor>white</textcolor>
++ </control>
++ </control>
+ </controls>
+-</window>
+\ No newline at end of file
++</window>
+diff --git a/addons/skin.confluence/720p/VideoOSD.xml b/addons/skin.confluence/720p/VideoOSD.xml
+index 3cb999a..fa55abe 100644
+--- a/addons/skin.confluence/720p/VideoOSD.xml
++++ b/addons/skin.confluence/720p/VideoOSD.xml
+@@ -1,5 +1,5 @@
+ <window id="2901">
+- <defaultcontrol always="true">602</defaultcontrol>
++ <defaultcontrol always="true">100</defaultcontrol>
+ <include>dialogeffect</include>
+ <coordinates>
+ <system>1</system>
+@@ -39,13 +39,16 @@
+ <textureslidernib>osd_slider_nibNF.png</textureslidernib>
+ <textureslidernibfocus>osd_slider_nib.png</textureslidernibfocus>
+ <animation effect="fade" time="200">VisibleChange</animation>
+- <visible>![Window.IsVisible(SliderDialog) | Window.IsVisible(OSDVideoSettings) | Window.IsVisible(OSDAudioSettings) | Window.IsVisible(VideoBookmarks)]</visible>
++ <visible>![Window.IsVisible(SliderDialog) | Window.IsVisible(OSDVideoSettings) | Window.IsVisible(OSDAudioSettings) | Window.IsVisible(VideoBookmarks) | VideoPlayer.Content(LiveTV)]</visible>
+ </control>
++ <!-- !LiveTV -->
+ <control type="group" id="100">
+ <posx>325</posx>
+ <posy>50r</posy>
++ <defaultcontrol always="true">602</defaultcontrol>
+ <animation effect="fade" time="200">VisibleChange</animation>
+ <visible>![Window.IsVisible(SliderDialog) | Window.IsVisible(OSDVideoSettings) | Window.IsVisible(OSDAudioSettings) | Window.IsVisible(VideoBookmarks)]</visible>
++ <visible>!VideoPlayer.Content(LiveTV)</visible>
+ <control type="button" id="600">
+ <posx>0</posx>
+ <posy>0</posy>
+@@ -141,11 +144,181 @@
+ <onclick>PlayerControl(Next)</onclick>
+ </control>
+ </control>
++
++ <!-- LiveTV -->
++ <control type="group" id="100">
++ <posx>190</posx>
++ <posy>50r</posy>
++ <defaultcontrol always="true">601</defaultcontrol>
++ <animation effect="fade" time="200">VisibleChange</animation>
++ <visible>![Window.IsVisible(SliderDialog) | Window.IsVisible(OSDVideoSettings) | Window.IsVisible(OSDAudioSettings) | Window.IsVisible(VideoBookmarks) | Window.IsVisible(PVROSDChannels) | Window.IsVisible(PVROSDGuide)]</visible>
++ <visible>VideoPlayer.Content(LiveTV)</visible>
++ <control type="button" id="600">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>45</width>
++ <height>45</height>
++ <label>210</label>
++ <font>-</font>
++ <texturefocus>OSDChannelUPFO.png</texturefocus>
++ <texturenofocus>OSDChannelUPNF.png</texturenofocus>
++ <onleft>704</onleft>
++ <onright>601</onright>
++ <onup>1000</onup>
++ <ondown>1000</ondown>
++ <onclick>PlayerControl(Previous)</onclick>
++ </control>
++ <control type="button" id="601">
++ <posx>45</posx>
++ <posy>0</posy>
++ <width>45</width>
++ <height>45</height>
++ <label>31354</label>
++ <font>-</font>
++ <texturefocus>OSDChannelDownFO.png</texturefocus>
++ <texturenofocus>OSDChannelDownNF.png</texturenofocus>
++ <onleft>600</onleft>
++ <onright>607</onright>
++ <onup>1000</onup>
++ <ondown>1000</ondown>
++ <onclick>PlayerControl(Next)</onclick>
++ </control>
++
++ <control type="button" id="607">
++ <posx>90</posx>
++ <posy>0</posy>
++ <width>45</width>
++ <height>45</height>
++ <label>-</label>
++ <texturefocus>OSDRewindFO.png</texturefocus>
++ <texturenofocus>OSDRewindNF.png</texturenofocus>
++ <onleft>601</onleft>
++ <onright>608</onright>
++ <onup>1000</onup>
++ <ondown>1000</ondown>
++ <onclick>XBMC.PlayerControl(Rewind)</onclick>
++ </control>
++ <control type="button" id="609">
++ <posx>180</posx>
++ <posy>0</posy>
++ <width>45</width>
++ <height>45</height>
++ <label>-</label>
++ <texturefocus>OSDForwardFO.png</texturefocus>
++ <texturenofocus>OSDForwardNF.png</texturenofocus>
++ <onleft>608</onleft>
++ <onright>602</onright>
++ <onup>1000</onup>
++ <ondown>1000</ondown>
++ <onclick>XBMC.PlayerControl(Forward)</onclick>
++ </control>
++ <control type="togglebutton" id="608">
++ <posx>135</posx>
++ <posy>0</posy>
++ <width>45</width>
++ <height>45</height>
++ <label>-</label>
++ <texturefocus>OSDPauseFO.png</texturefocus>
++ <texturenofocus>OSDPauseNF.png</texturenofocus>
++ <usealttexture>Player.Paused | Player.Forwarding | Player.Rewinding</usealttexture>
++ <alttexturefocus>OSDPlayFO.png</alttexturefocus>
++ <alttexturenofocus>OSDPlayNF.png</alttexturenofocus>
++ <onleft>606</onleft>
++ <onright>609</onright>
++ <onup>1000</onup>
++ <ondown>1000</ondown>
++ <onclick>XBMC.PlayerControl(Play)</onclick>
++ </control>
++
++ <control type="togglebutton" id="602">
++ <posx>225</posx>
++ <posy>0</posy>
++ <width>45</width>
++ <height>45</height>
++ <label>31351</label>
++ <altlabel>208</altlabel>
++ <font>-</font>
++ <texturefocus>OSDPauseFO.png</texturefocus>
++ <texturenofocus>OSDPauseNF.png</texturenofocus>
++ <usealttexture>Player.Paused | Player.Forwarding | Player.Rewinding</usealttexture>
++ <alttexturefocus>OSDPlayFO.png</alttexturefocus>
++ <alttexturenofocus>OSDPlayNF.png</alttexturenofocus>
++ <onleft>609</onleft>
++ <onright>603</onright>
++ <onup>1000</onup>
++ <ondown>1000</ondown>
++ <onclick>PlayerControl(Play)</onclick>
++ <visible>False</visible>
++ </control>
++ <control type="button" id="603">
++ <posx>270</posx>
++ <posy>0</posy>
++ <width>45</width>
++ <height>45</height>
++ <label>31351</label>
++ <altlabel>208</altlabel>
++ <font>-</font>
++ <texturefocus>OSDStopFO.png</texturefocus>
++ <texturenofocus>OSDStopNF.png</texturenofocus>
++ <onleft>602</onleft>
++ <onright>604</onright>
++ <onup>1000</onup>
++ <ondown>1000</ondown>
++ <onclick>PlayerControl(Stop)</onclick>
++ </control>
++ <control type="button" id="604">
++ <posx>315</posx>
++ <posy>0</posy>
++ <width>45</width>
++ <height>45</height>
++ <label>19019</label>
++ <font>-</font>
++ <texturefocus>OSDChannelListFO.png</texturefocus>
++ <texturenofocus>OSDChannelListNF.png</texturenofocus>
++ <onleft>603</onleft>
++ <onright>605</onright>
++ <onup>1000</onup>
++ <ondown>1000</ondown>
++ <onclick>ActivateWindow(PVROSDChannels)</onclick>
++ <onclick>Dialog.Close(VideoOSD)</onclick>
++ </control>
++ <control type="button" id="605">
++ <posx>360</posx>
++ <posy>0</posy>
++ <width>45</width>
++ <height>45</height>
++ <label>$LOCALIZE[19029]$INFO[VideoPlayer.ChannelName, - ]</label>
++ <font>-</font>
++ <texturefocus>OSDepgFO.png</texturefocus>
++ <texturenofocus>OSDepgNF.png</texturenofocus>
++ <onleft>604</onleft>
++ <onright>701</onright>
++ <onup>1000</onup>
++ <ondown>1000</ondown>
++ <onclick>ActivateWindow(PVROSDGuide)</onclick>
++ <onclick>Dialog.Close(VideoOSD)</onclick>
++ </control>
++ <control type="label" id="606">
++ <posx>425</posx>
++ <posy>0</posy>
++ <width>450</width>
++ <height>45</height>
++ <label>$INFO[VideoPlayer.NextTitle,$LOCALIZE[209]: ]</label>
++ <align>center</align>
++ <aligny>center</aligny>
++ <font>font12</font>
++ <textcolor>grey</textcolor>
++ <scroll>false</scroll>
++ </control>
++ </control>
++
++ <!-- !LiveTV -->
+ <control type="group">
+ <posx>250r</posx>
+ <posy>50r</posy>
+ <animation effect="fade" time="200">VisibleChange</animation>
+ <visible>![Window.IsVisible(SliderDialog) | Window.IsVisible(OSDVideoSettings) | Window.IsVisible(OSDAudioSettings) | Window.IsVisible(VideoBookmarks)]</visible>
++ <visible>!VideoPlayer.Content(LiveTV)</visible>
+ <control type="togglebutton" id="701">
+ <posx>0</posx>
+ <posy>0</posy>
+@@ -231,5 +404,80 @@
+ <animation effect="fade" start="100" end="50" time="100" condition="!VideoPlayer.HasMenu">Conditional</animation>
+ </control>
+ </control>
++
++ <!-- LiveTV -->
++ <control type="group">
++ <posx>200r</posx>
++ <posy>50r</posy>
++ <animation effect="fade" time="200">VisibleChange</animation>
++ <visible>![Window.IsVisible(SliderDialog) | Window.IsVisible(OSDVideoSettings) | Window.IsVisible(OSDAudioSettings) | Window.IsVisible(VideoBookmarks) | Window.IsVisible(PVROSDChannels) | Window.IsVisible(PVROSDGuide)]</visible>
++ <visible>VideoPlayer.Content(LiveTV)</visible>
++ <control type="button" id="701">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>45</width>
++ <height>45</height>
++ <label>31356</label>
++ <font>-</font>
++ <texturefocus>OSDTeleTextFO.png</texturefocus>
++ <texturenofocus>OSDTeleTextNF.png</texturenofocus>
++ <onleft>605</onleft>
++ <onright>702</onright>
++ <onup>1000</onup>
++ <ondown>1000</ondown>
++ <onclick>ActivateWindow(Teletext)</onclick>
++ </control>
++ <control type="button" id="702">
++ <posx>45</posx>
++ <posy>0</posy>
++ <width>45</width>
++ <height>45</height>
++ <label>13395</label>
++ <font>-</font>
++ <texturefocus>OSDVideoFO.png</texturefocus>
++ <texturenofocus>OSDVideoNF.png</texturenofocus>
++ <onleft>701</onleft>
++ <onright>703</onright>
++ <onup>1000</onup>
++ <ondown>1000</ondown>
++ <onclick>ActivateWindow(OSDVideoSettings)</onclick>
++ </control>
++ <control type="button" id="703">
++ <posx>90</posx>
++ <posy>0</posy>
++ <width>45</width>
++ <height>45</height>
++ <label>13396</label>
++ <font>-</font>
++ <texturefocus>OSDAudioFO.png</texturefocus>
++ <texturenofocus>OSDAudioNF.png</texturenofocus>
++ <onleft>702</onleft>
++ <onright>704</onright>
++ <onup>1000</onup>
++ <ondown>1000</ondown>
++ <onclick>ActivateWindow(OSDAudioSettings)</onclick>
++ </control>
++ <control type="togglebutton" id="704">
++ <posx>135</posx>
++ <posy>0</posy>
++ <width>45</width>
++ <height>45</height>
++ <label>31351</label>
++ <altlabel>208</altlabel>
++ <font>-</font>
++ <texturefocus>OSDRecordOffFO.png</texturefocus>
++ <texturenofocus>OSDRecordOffNF.png</texturenofocus>
++ <usealttexture>Player.Recording</usealttexture>
++ <alttexturefocus>OSDRecordOnFO.png</alttexturefocus>
++ <alttexturenofocus>OSDRecordOnNF.png</alttexturenofocus>
++ <onleft>703</onleft>
++ <onright>600</onright>
++ <onup>1000</onup>
++ <ondown>1000</ondown>
++ <onclick>PlayerControl(Record)</onclick>
++ <enable>Player.CanRecord</enable>
++ <animation effect="fade" start="100" end="50" time="100" condition="!Player.CanRecord">Conditional</animation>
++ </control>
++ </control>
+ </controls>
+- </window>
+\ No newline at end of file
++ </window>
+diff --git a/addons/skin.confluence/720p/ViewsPVR.xml b/addons/skin.confluence/720p/ViewsPVR.xml
+new file mode 100644
+index 0000000..db97def
+--- /dev/null
++++ b/addons/skin.confluence/720p/ViewsPVR.xml
+@@ -0,0 +1,2378 @@
++<includes>
++ <include name="LiveTVChannelView">
++ <control type="group">
++ <description>TV Channels group</description>
++ <visible>Control.IsVisible(11)</visible>
++ <include>VisibleFadeEffect</include>
++ <control type="group">
++ <posx>530</posx>
++ <posy>490</posy>
++ <control type="label">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>690</width>
++ <height>20</height>
++ <font>font13</font>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <scroll>true</scroll>
++ <align>center</align>
++ <aligny>center</aligny>
++ <label>[B]$INFO[Container(11).ListItem.Title][/B]</label>
++ </control>
++ <control type="label">
++ <posx>80</posx>
++ <posy>22</posy>
++ <width>80</width>
++ <height>20</height>
++ <align>right</align>
++ <aligny>center</aligny>
++ <font>font10_title</font>
++ <textcolor>blue</textcolor>
++ <visible>Container(11).ListItem.HasEpg</visible>
++ <label>$INFO[Container(11).ListItem.StartTime]</label>
++ </control>
++ <control type="progress">
++ <description>Progressbar</description>
++ <posx>85</posx>
++ <posy>30</posy>
++ <width>510</width>
++ <height>8</height>
++ <visible>Container(11).ListItem.HasEpg</visible>
++ <info>Container(11).ListItem.Progress</info>
++ </control>
++ <control type="label">
++ <posx>600</posx>
++ <posy>22</posy>
++ <width>80</width>
++ <height>20</height>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font10_title</font>
++ <textcolor>blue</textcolor>
++ <visible>Container(11).ListItem.HasEpg</visible>
++ <label>$INFO[Container(11).ListItem.EndTime]</label>
++ </control>
++ <control type="textbox">
++ <description>Plot Value for TVShow</description>
++ <posx>0</posx>
++ <posy>43</posy>
++ <width>690</width>
++ <height>80</height>
++ <font>font12</font>
++ <align>justify</align>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <pagecontrol>-</pagecontrol>
++ <label>$INFO[Container(11).ListItem.Plot]</label>
++ <autoscroll time="2000" delay="3000" repeat="5000">true</autoscroll>
++ </control>
++ <control type="label">
++ <posx>690</posx>
++ <posy>140</posy>
++ <width>690</width>
++ <height>20</height>
++ <font>font12</font>
++ <textcolor>grey2</textcolor>
++ <align>right</align>
++ <aligny>center</aligny>
++ <scroll>false</scroll>
++ <label>$LOCALIZE[19031]: $INFO[Container(11).ListItem.NextTitle]</label>
++ </control>
++ </control>
++ <control type="list" id="11">
++ <posx>70</posx>
++ <posy>85</posy>
++ <width>390</width>
++ <height>541</height>
++ <onleft>32</onleft>
++ <onright>70</onright>
++ <onup>11</onup>
++ <ondown>11</ondown>
++ <viewtype label="535">list</viewtype>
++ <pagecontrol>70</pagecontrol>
++ <scrolltime>200</scrolltime>
++ <itemlayout height="60" width="390">
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>390</width>
++ <height>61</height>
++ <texture border="2">MenuItemNF.png</texture>
++ <include>VisibleFadeEffect</include>
++ </control>
++ <control type="label">
++ <posx>5</posx>
++ <posy>-4</posy>
++ <width>40</width>
++ <height>35</height>
++ <font>font12</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey</textcolor>
++ <selectedcolor>grey</selectedcolor>
++ <info>ListItem.ChannelNumber</info>
++ </control>
++ <control type="label">
++ <posx>50</posx>
++ <posy>0</posy>
++ <width>270</width>
++ <height>25</height>
++ <font>font13</font>
++ <textcolor>white</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <label>$INFO[ListItem.Label]</label>
++ </control>
++ <control type="label">
++ <posx>50</posx>
++ <posy>25</posy>
++ <width>330</width>
++ <height>20</height>
++ <font>font12</font>
++ <textcolor>grey</textcolor>
++ <selectedcolor>grey</selectedcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <label>$INFO[ListItem.Title]</label>
++ <visible>IsEmpty(Listitem.Icon)</visible>
++ </control>
++ <control type="label">
++ <posx>50</posx>
++ <posy>25</posy>
++ <width>280</width>
++ <height>20</height>
++ <font>font12</font>
++ <textcolor>grey</textcolor>
++ <selectedcolor>grey</selectedcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <label>$INFO[ListItem.Title]</label>
++ <visible>!IsEmpty(Listitem.Icon)</visible>
++ </control>
++ <control type="progress">
++ <description>Progressbar</description>
++ <posx>50</posx>
++ <posy>48</posy>
++ <width>280</width>
++ <height>6</height>
++ <colordiffuse>88FFFFFF</colordiffuse>
++ <visible>ListItem.HasEpg</visible>
++ <info>ListItem.Progress</info>
++ </control>
++ <control type="image">
++ <posx>340</posx>
++ <posy>4</posy>
++ <width>50</width>
++ <height>50</height>
++ <texture>$INFO[ListItem.Icon]</texture>
++ </control>
++ <control type="image">
++ <posx>5</posx>
++ <posy>37</posy>
++ <width>30</width>
++ <height>20</height>
++ <texture>PVR-IsRecording.png</texture>
++ <visible>ListItem.IsRecording</visible>
++ </control>
++ </itemlayout>
++ <focusedlayout height="60" width="390">
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>390</width>
++ <height>61</height>
++ <texture border="2">MenuItemNF.png</texture>
++ <visible>!Control.HasFocus(11)</visible>
++ <include>VisibleFadeEffect</include>
++ </control>
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>390</width>
++ <height>61</height>
++ <texture border="2">MenuItemFO.png</texture>
++ <visible>Control.HasFocus(11)</visible>
++ <include>VisibleFadeEffect</include>
++ </control>
++ <control type="label">
++ <posx>5</posx>
++ <posy>-4</posy>
++ <width>40</width>
++ <height>35</height>
++ <font>font12</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey</textcolor>
++ <selectedcolor>grey</selectedcolor>
++ <info>ListItem.ChannelNumber</info>
++ </control>
++ <control type="label">
++ <posx>50</posx>
++ <posy>0</posy>
++ <width>270</width>
++ <height>25</height>
++ <font>font13</font>
++ <textcolor>white</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <label>$INFO[ListItem.Label]</label>
++ </control>
++ <control type="label">
++ <posx>50</posx>
++ <posy>25</posy>
++ <width>330</width>
++ <height>20</height>
++ <font>font12</font>
++ <textcolor>grey</textcolor>
++ <selectedcolor>grey</selectedcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <label>$INFO[ListItem.Title]</label>
++ <visible>IsEmpty(Listitem.Icon)</visible>
++ </control>
++ <control type="label">
++ <posx>50</posx>
++ <posy>25</posy>
++ <width>280</width>
++ <height>20</height>
++ <font>font12</font>
++ <textcolor>grey</textcolor>
++ <selectedcolor>grey</selectedcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <label>$INFO[ListItem.Title]</label>
++ <visible>!IsEmpty(Listitem.Icon)</visible>
++ </control>
++ <control type="progress">
++ <description>Progressbar</description>
++ <posx>50</posx>
++ <posy>48</posy>
++ <width>280</width>
++ <height>6</height>
++ <colordiffuse>88FFFFFF</colordiffuse>
++ <visible>ListItem.HasEpg</visible>
++ <info>ListItem.Progress</info>
++ </control>
++ <control type="image">
++ <posx>340</posx>
++ <posy>4</posy>
++ <width>50</width>
++ <height>50</height>
++ <texture>$INFO[ListItem.Icon]</texture>
++ </control>
++ <control type="image">
++ <posx>5</posx>
++ <posy>37</posy>
++ <width>30</width>
++ <height>20</height>
++ <texture>PVR-IsRecording.png</texture>
++ <visible>ListItem.IsRecording</visible>
++ </control>
++ </focusedlayout>
++ </control>
++ <control type="scrollbar" id="70">
++ <posx>465</posx>
++ <posy>85</posy>
++ <width>25</width>
++ <height>540</height>
++ <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
++ <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
++ <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
++ <textureslidernib>ScrollBarNib.png</textureslidernib>
++ <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
++ <onleft>11</onleft>
++ <onright>32</onright>
++ <showonepage>false</showonepage>
++ <orientation>vertical</orientation>
++ </control>
++ <control type="label">
++ <description>Page Count Label</description>
++ <posx>40r</posx>
++ <posy>53r</posy>
++ <width>500</width>
++ <height>20</height>
++ <font>font12</font>
++ <textcolor>grey</textcolor>
++ <scroll>false</scroll>
++ <align>right</align>
++ <aligny>center</aligny>
++ <label>([COLOR=blue]$INFO[Container(11).NumItems][/COLOR]) $LOCALIZE[19019] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(11).CurrentPage]/$INFO[Container(11).NumPages][/COLOR])</label>
++ <include>Window_OpenClose_Animation</include>
++ </control>
++ </control>
++ </include>
++
++ <include name="LiveRadioChannelView">
++ <control type="group">
++ <description>Radio Channels group</description>
++ <visible>Control.IsVisible(12)</visible>
++ <include>VisibleFadeEffect</include>
++ <control type="group">
++ <posx>530</posx>
++ <posy>490</posy>
++ <control type="label">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>690</width>
++ <height>20</height>
++ <font>font13</font>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <scroll>true</scroll>
++ <align>center</align>
++ <aligny>center</aligny>
++ <label>[B]$INFO[Container(12).ListItem.Title][/B]</label>
++ </control>
++ <control type="label">
++ <posx>80</posx>
++ <posy>22</posy>
++ <width>80</width>
++ <height>20</height>
++ <align>right</align>
++ <aligny>center</aligny>
++ <font>font10_title</font>
++ <textcolor>blue</textcolor>
++ <label>$INFO[Container(12).ListItem.StartTime]</label>
++ </control>
++ <control type="progress">
++ <description>Progressbar</description>
++ <posx>85</posx>
++ <posy>30</posy>
++ <width>510</width>
++ <height>8</height>
++ <info>Container(12).ListItem.Progress</info>
++ </control>
++ <control type="label">
++ <posx>600</posx>
++ <posy>22</posy>
++ <width>80</width>
++ <height>20</height>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font10_title</font>
++ <textcolor>blue</textcolor>
++ <label>$INFO[Container(12).ListItem.EndTime]</label>
++ </control>
++ <control type="textbox">
++ <description>Plot Value for TVShow</description>
++ <posx>0</posx>
++ <posy>43</posy>
++ <width>690</width>
++ <height>80</height>
++ <font>font12</font>
++ <align>justify</align>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <pagecontrol>-</pagecontrol>
++ <label>$INFO[Container(12).ListItem.Plot]</label>
++ <autoscroll time="2000" delay="3000" repeat="5000">true</autoscroll>
++ </control>
++ <control type="label">
++ <posx>690</posx>
++ <posy>140</posy>
++ <width>690</width>
++ <height>20</height>
++ <font>font12</font>
++ <textcolor>grey2</textcolor>
++ <align>right</align>
++ <aligny>center</aligny>
++ <scroll>false</scroll>
++ <label>$LOCALIZE[19031]: $INFO[Container(12).ListItem.NextTitle]</label>
++ </control>
++ </control>
++ <control type="list" id="12">
++ <posx>70</posx>
++ <posy>85</posy>
++ <width>390</width>
++ <height>541</height>
++ <onleft>33</onleft>
++ <onright>71</onright>
++ <onup>12</onup>
++ <ondown>12</ondown>
++ <viewtype label="535">list</viewtype>
++ <pagecontrol>71</pagecontrol>
++ <scrolltime>200</scrolltime>
++ <itemlayout height="60" width="390">
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>390</width>
++ <height>61</height>
++ <texture border="2">MenuItemNF.png</texture>
++ <include>VisibleFadeEffect</include>
++ </control>
++ <control type="label">
++ <posx>5</posx>
++ <posy>-4</posy>
++ <width>40</width>
++ <height>35</height>
++ <font>font12</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey</textcolor>
++ <selectedcolor>grey</selectedcolor>
++ <info>ListItem.ChannelNumber</info>
++ </control>
++ <control type="label">
++ <posx>50</posx>
++ <posy>0</posy>
++ <width>270</width>
++ <height>25</height>
++ <font>font13</font>
++ <textcolor>white</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <label>$INFO[ListItem.Label]</label>
++ </control>
++ <control type="label">
++ <posx>50</posx>
++ <posy>25</posy>
++ <width>330</width>
++ <height>20</height>
++ <font>font12</font>
++ <textcolor>grey</textcolor>
++ <selectedcolor>grey</selectedcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <label>$INFO[ListItem.Title]</label>
++ <visible>IsEmpty(Listitem.Icon)</visible>
++ </control>
++ <control type="label">
++ <posx>50</posx>
++ <posy>25</posy>
++ <width>280</width>
++ <height>20</height>
++ <font>font12</font>
++ <textcolor>grey</textcolor>
++ <selectedcolor>grey</selectedcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <label>$INFO[ListItem.Title]</label>
++ <visible>!IsEmpty(Listitem.Icon)</visible>
++ </control>
++ <control type="progress">
++ <description>Progressbar</description>
++ <posx>50</posx>
++ <posy>48</posy>
++ <width>280</width>
++ <height>6</height>
++ <colordiffuse>88FFFFFF</colordiffuse>
++ <info>ListItem.Progress</info>
++ </control>
++ <control type="image">
++ <posx>340</posx>
++ <posy>4</posy>
++ <width>50</width>
++ <height>50</height>
++ <texture>$INFO[ListItem.Icon]</texture>
++ </control>
++ <control type="image">
++ <posx>5</posx>
++ <posy>37</posy>
++ <width>30</width>
++ <height>20</height>
++ <texture>PVR-IsRecording.png</texture>
++ <visible>ListItem.IsRecording</visible>
++ </control>
++ </itemlayout>
++ <focusedlayout height="60" width="390">
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>390</width>
++ <height>61</height>
++ <texture border="2">MenuItemNF.png</texture>
++ <visible>!Control.HasFocus(12)</visible>
++ <include>VisibleFadeEffect</include>
++ </control>
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>390</width>
++ <height>61</height>
++ <texture border="2">MenuItemFO.png</texture>
++ <visible>Control.HasFocus(12)</visible>
++ <include>VisibleFadeEffect</include>
++ </control>
++ <control type="label">
++ <posx>5</posx>
++ <posy>-4</posy>
++ <width>40</width>
++ <height>35</height>
++ <font>font12</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey</textcolor>
++ <selectedcolor>grey</selectedcolor>
++ <info>ListItem.ChannelNumber</info>
++ </control>
++ <control type="label">
++ <posx>50</posx>
++ <posy>0</posy>
++ <width>270</width>
++ <height>25</height>
++ <font>font13</font>
++ <textcolor>white</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <label>$INFO[ListItem.Label]</label>
++ </control>
++ <control type="label">
++ <posx>50</posx>
++ <posy>25</posy>
++ <width>330</width>
++ <height>20</height>
++ <font>font12</font>
++ <textcolor>grey</textcolor>
++ <selectedcolor>grey</selectedcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <label>$INFO[ListItem.Title]</label>
++ <visible>IsEmpty(Listitem.Icon)</visible>
++ </control>
++ <control type="label">
++ <posx>50</posx>
++ <posy>25</posy>
++ <width>280</width>
++ <height>20</height>
++ <font>font12</font>
++ <textcolor>grey</textcolor>
++ <selectedcolor>grey</selectedcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <label>$INFO[ListItem.Title]</label>
++ <visible>!IsEmpty(Listitem.Icon)</visible>
++ </control>
++ <control type="progress">
++ <description>Progressbar</description>
++ <posx>50</posx>
++ <posy>48</posy>
++ <width>280</width>
++ <height>6</height>
++ <colordiffuse>88FFFFFF</colordiffuse>
++ <info>ListItem.Progress</info>
++ </control>
++ <control type="image">
++ <posx>340</posx>
++ <posy>4</posy>
++ <width>50</width>
++ <height>50</height>
++ <texture>$INFO[ListItem.Icon]</texture>
++ </control>
++ <control type="image">
++ <posx>5</posx>
++ <posy>37</posy>
++ <width>30</width>
++ <height>20</height>
++ <texture>PVR-IsRecording.png</texture>
++ <visible>ListItem.IsRecording</visible>
++ </control>
++ </focusedlayout>
++ </control>
++ <control type="scrollbar" id="71">
++ <posx>465</posx>
++ <posy>85</posy>
++ <width>25</width>
++ <height>540</height>
++ <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
++ <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
++ <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
++ <textureslidernib>ScrollBarNib.png</textureslidernib>
++ <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
++ <onleft>12</onleft>
++ <onright>33</onright>
++ <showonepage>false</showonepage>
++ <orientation>vertical</orientation>
++ </control>
++ <control type="label">
++ <description>Page Count Label</description>
++ <posx>40r</posx>
++ <posy>53r</posy>
++ <width>500</width>
++ <height>20</height>
++ <font>font12</font>
++ <textcolor>grey</textcolor>
++ <scroll>false</scroll>
++ <align>right</align>
++ <aligny>center</aligny>
++ <label>([COLOR=blue]$INFO[Container(12).NumItems][/COLOR]) $LOCALIZE[19019] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(12).CurrentPage]/$INFO[Container(12).NumPages][/COLOR])</label>
++ <include>Window_OpenClose_Animation</include>
++ </control>
++ </control>
++ </include>
++
++ <include name="LiveTVRecordingsView">
++ <control type="group">
++ <description>Recordings group</description>
++ <visible>Control.IsVisible(13)</visible>
++ <include>VisibleFadeEffect</include>
++ <control type="list" id="13">
++ <posx>70</posx>
++ <posy>75</posy>
++ <width>760</width>
++ <height>561</height>
++ <onleft>34</onleft>
++ <onright>72</onright>
++ <onup>13</onup>
++ <ondown>13</ondown>
++ <viewtype label="535">list</viewtype>
++ <pagecontrol>72</pagecontrol>
++ <scrolltime>200</scrolltime>
++ <itemlayout height="40" width="760">
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>760</width>
++ <height>41</height>
++ <texture border="2">MenuItemNF.png</texture>
++ <include>VisibleFadeEffect</include>
++ </control>
++ <control type="image">
++ <posx>10</posx>
++ <posy>5</posy>
++ <width>30</width>
++ <height>30</height>
++ <texture background="true" fallback="DefaultVideoCover.png">$INFO[ListItem.Icon]</texture>
++ </control>
++ <control type="label">
++ <posx>50</posx>
++ <posy>0</posy>
++ <width>605</width>
++ <height>40</height>
++ <font>font13</font>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <label>$INFO[ListItem.Label]</label>
++ </control>
++ <control type="label">
++ <posx>725</posx>
++ <posy>0</posy>
++ <width>500</width>
++ <height>40</height>
++ <font>font12</font>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <align>right</align>
++ <aligny>center</aligny>
++ <label>$INFO[ListItem.Date]</label>
++ </control>
++ <control type="image">
++ <posx>730</posx>
++ <posy>14</posy>
++ <width>20</width>
++ <height>16</height>
++ <texture>$INFO[ListItem.Overlay]</texture>
++ </control>
++ </itemlayout>
++ <focusedlayout height="40" width="760">
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>760</width>
++ <height>41</height>
++ <texture border="2">MenuItemFO.png</texture>
++ <visible>Control.HasFocus(13)</visible>
++ <include>VisibleFadeEffect</include>
++ </control>
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>760</width>
++ <height>41</height>
++ <texture border="2">MenuItemNF.png</texture>
++ <include>VisibleFadeEffect</include>
++ <visible>!Control.HasFocus(13)</visible>
++ </control>
++ <control type="image">
++ <posx>560</posx>
++ <posy>5</posy>
++ <width>200</width>
++ <height>31</height>
++ <texture border="0,0,14,0">MediaItemDetailBG.png</texture>
++ <visible>Control.HasFocus(13) + !IsEmpty(ListItem.Date)</visible>
++ </control>
++ <control type="image">
++ <posx>10</posx>
++ <posy>5</posy>
++ <width>30</width>
++ <height>30</height>
++ <texture background="true" fallback="DefaultVideoCover.png">$INFO[ListItem.Icon]</texture>
++ </control>
++ <control type="label">
++ <posx>50</posx>
++ <posy>0</posy>
++ <width>630</width>
++ <height>40</height>
++ <font>font13</font>
++ <textcolor>white</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <label>$INFO[ListItem.Label]</label>
++ </control>
++ <control type="label">
++ <posx>725</posx>
++ <posy>0</posy>
++ <width>500</width>
++ <height>40</height>
++ <font>font12</font>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <align>right</align>
++ <aligny>center</aligny>
++ <label>$INFO[ListItem.Date]</label>
++ </control>
++ <control type="image">
++ <posx>730</posx>
++ <posy>14</posy>
++ <width>20</width>
++ <height>16</height>
++ <texture>$INFO[ListItem.Overlay]</texture>
++ </control>
++ </focusedlayout>
++ </control>
++ <control type="scrollbar" id="72">
++ <posx>850</posx>
++ <posy>78</posy>
++ <width>25</width>
++ <height>560</height>
++ <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
++ <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
++ <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
++ <textureslidernib>ScrollBarNib.png</textureslidernib>
++ <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
++ <onleft>13</onleft>
++ <onright>34</onright>
++ <showonepage>false</showonepage>
++ <orientation>vertical</orientation>
++ </control>
++ <control type="label">
++ <description>Page Count Label</description>
++ <posx>40r</posx>
++ <posy>53r</posy>
++ <width>500</width>
++ <height>20</height>
++ <font>font12</font>
++ <textcolor>grey</textcolor>
++ <scroll>false</scroll>
++ <align>right</align>
++ <aligny>center</aligny>
++ <label>([COLOR=blue]$INFO[Container(13).NumItems][/COLOR]) $LOCALIZE[19163] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(13).CurrentPage]/$INFO[Container(13).NumPages][/COLOR])</label>
++ <include>Window_OpenClose_Animation</include>
++ </control>
++ <control type="group">
++ <posx>910</posx>
++ <posy>80</posy>
++ <control type="image">
++ <posx>10</posx>
++ <posy>0</posy>
++ <width>290</width>
++ <height>230</height>
++ <aspectratio aligny="bottom">keep</aspectratio>
++ <fadetime>IconCrossfadeTime</fadetime>
++ <texture fallback="DefaultVideoCover.png">$INFO[Container(13).ListItem.Icon]</texture>
++ <bordertexture border="8">ThumbShadow.png</bordertexture>
++ <bordersize>8</bordersize>
++ </control>
++ <control type="fadelabel">
++ <posx>10</posx>
++ <posy>230</posy>
++ <width>290</width>
++ <height>25</height>
++ <label>$INFO[Container(13).ListItem.Title]</label>
++ <align>center</align>
++ <aligny>center</aligny>
++ <font>font13</font>
++ <textcolor>blue</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <scrollout>false</scrollout>
++ <pauseatend>1000</pauseatend>
++ </control>
++ <control type="textbox">
++ <description>Description Value for TV Show</description>
++ <posx>10</posx>
++ <posy>270</posy>
++ <width>290</width>
++ <height>280</height>
++ <font>font12</font>
++ <align>justify</align>
++ <textcolor>white</textcolor>
++ <label>$INFO[Container(13).ListItem.Plot]</label>
++ <autoscroll time="2000" delay="3000" repeat="5000">Skin.HasSetting(AutoScroll)</autoscroll>
++ </control>
++ </control>
++ </control>
++ </include>
++
++ <include name="EPGTimelineView">
++ <control type="group">
++ <description>TV Guide Timeline</description>
++ <visible>Control.IsVisible(10)</visible>
++ <include>VisibleFadeEffect</include>
++ <control type="epggrid" id="10">
++ <description>EPG Grid</description>
++ <posx>80</posx>
++ <posy>81</posy>
++ <width>1120</width>
++ <height>555</height>
++ <pagecontrol>10</pagecontrol>
++ <scrolltime>350</scrolltime>
++ <timeblocks>40</timeblocks>
++ <rulerunit>6</rulerunit>
++ <onleft>31</onleft>
++ <onright>31</onright>
++ <onup>10</onup>
++ <ondown>10</ondown>
++ <rulerlayout height="35" width="40">
++ <control type="image" id="1">
++ <width>40</width>
++ <height>29</height>
++ <posx>0</posx>
++ <posy>0</posy>
++ <texture border="5">button-nofocus.png</texture>
++ </control>
++ <control type="label" id="2">
++ <posx>10</posx>
++ <posy>0</posy>
++ <width>34</width>
++ <height>29</height>
++ <font>font12</font>
++ <aligny>center</aligny>
++ <selectedcolor>selected</selectedcolor>
++ <align>left</align>
++ <label>$INFO[ListItem.Label]</label>
++ </control>
++ </rulerlayout>
++ <channellayout height="52" width="280">
++ <animation effect="fade" start="110" time="200">UnFocus</animation>
++ <control type="image" id="1">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>270</width>
++ <height>52</height>
++ <texture border="5">button-nofocus.png</texture>
++ </control>
++ <control type="label">
++ <posx>5</posx>
++ <posy>5</posy>
++ <width>40</width>
++ <height>35</height>
++ <font>font12</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey</textcolor>
++ <selectedcolor>grey</selectedcolor>
++ <info>ListItem.ChannelNumber</info>
++ </control>
++ <control type="image">
++ <posx>45</posx>
++ <posy>4</posy>
++ <width>45</width>
++ <height>44</height>
++ <texture>$INFO[ListItem.Icon]</texture>
++ </control>
++ <control type="label" id="1">
++ <posx>94</posx>
++ <posy>0</posy>
++ <width>160</width>
++ <height>52</height>
++ <font>special12</font>
++ <aligny>center</aligny>
++ <selectedcolor>selected</selectedcolor>
++ <align>left</align>
++ <label>$INFO[ListItem.ChannelName]</label>
++ </control>
++ </channellayout>
++ <focusedchannellayout height="52" width="280">
++ <animation effect="fade" start="110" time="200">OnFocus</animation>
++ <control type="image" id="1">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>270</width>
++ <height>52</height>
++ <texture border="5">button-focus.png</texture>
++ </control>
++ <control type="label">
++ <posx>5</posx>
++ <posy>5</posy>
++ <width>40</width>
++ <height>35</height>
++ <font>font12</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey</textcolor>
++ <selectedcolor>grey</selectedcolor>
++ <info>ListItem.ChannelNumber</info>
++ </control>
++ <control type="image">
++ <posx>45</posx>
++ <posy>4</posy>
++ <width>45</width>
++ <height>44</height>
++ <texture>$INFO[ListItem.Icon]</texture>
++ </control>
++ <control type="label" id="1">
++ <posx>94</posx>
++ <posy>0</posy>
++ <width>160</width>
++ <height>52</height>
++ <font>special12</font>
++ <aligny>center</aligny>
++ <selectedcolor>selected</selectedcolor>
++ <align>left</align>
++ <label>$INFO[ListItem.ChannelName]</label>
++ </control>
++ </focusedchannellayout>
++ <itemlayout height="52" width="40">
++ <control type="image" id="2">
++ <width>40</width>
++ <height>52</height>
++ <posx>0</posx>
++ <posy>0</posy>
++ <aspectratio>stretch</aspectratio>
++ <texture border="3">epg-genres/$INFO[ListItem.Property(GenreType)].png</texture>
++ </control>
++ <control type="label" id="1">
++ <posx>6</posx>
++ <posy>3</posy>
++ <width>30</width>
++ <height>25</height>
++ <font>font12</font>
++ <aligny>center</aligny>
++ <selectedcolor>selected</selectedcolor>
++ <align>left</align>
++ <info>ListItem.Label</info>
++ </control>
++ <control type="image">
++ <posx>5</posx>
++ <posy>28</posy>
++ <width>30</width>
++ <height>20</height>
++ <texture>PVR-IsRecording.png</texture>
++ <visible>ListItem.IsRecording</visible>
++ </control>
++ <control type="image">
++ <posx>5</posx>
++ <posy>28</posy>
++ <width>20</width>
++ <height>20</height>
++ <texture>PVR-HasTimer.png</texture>
++ <visible>ListItem.HasTimer + !ListItem.IsRecording</visible>
++ </control>
++ </itemlayout>
++ <focusedlayout height="52" width="40">
++ <control type="image" id="14">
++ <width>40</width>
++ <height>52</height>
++ <posx>0</posx>
++ <posy>0</posy>
++ <texture border="5">folder-focus.png</texture>
++ </control>
++ <control type="image" id="2">
++ <width>40</width>
++ <height>52</height>
++ <posx>0</posx>
++ <posy>0</posy>
++ <aspectratio>stretch</aspectratio>
++ <texture border="3">epg-genres/$INFO[ListItem.Property(GenreType)].png</texture>
++ </control>
++ <control type="label" id="1">
++ <posx>6</posx>
++ <posy>3</posy>
++ <width>30</width>
++ <height>25</height>
++ <font>font12</font>
++ <aligny>center</aligny>
++ <selectedcolor>selected</selectedcolor>
++ <align>left</align>
++ <info>ListItem.Label</info>
++ </control>
++ <control type="image">
++ <posx>5</posx>
++ <posy>28</posy>
++ <width>30</width>
++ <height>20</height>
++ <texture>PVR-IsRecording.png</texture>
++ <visible>ListItem.IsRecording</visible>
++ </control>
++ <control type="image">
++ <posx>5</posx>
++ <posy>28</posy>
++ <width>20</width>
++ <height>20</height>
++ <texture>PVR-HasTimer.png</texture>
++ <visible>ListItem.HasTimer + !ListItem.IsRecording</visible>
++ </control>
++ </focusedlayout>
++ </control>
++ </control>
++ </include>
++
++ <include name="LiveTVTimersView">
++ <control type="group">
++ <description>Timers group</description>
++ <visible>Control.IsVisible(14)</visible>
++ <include>VisibleFadeEffect</include>
++ <control type="group">
++ <posx>80</posx>
++ <posy>60</posy>
++ <control type="label">
++ <description>Channel header label</description>
++ <posx>0</posx>
++ <posy>20</posy>
++ <width>220</width>
++ <height>20</height>
++ <font>font13_title</font>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <align>center</align>
++ <aligny>center</aligny>
++ <label>19029</label>
++ </control>
++ <control type="label">
++ <description>Title header label</description>
++ <posx>220</posx>
++ <posy>20</posy>
++ <width>300</width>
++ <height>20</height>
++ <font>font13_title</font>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <align>center</align>
++ <aligny>center</aligny>
++ <label>369</label>
++ </control>
++ <control type="label">
++ <description>Schedule Time header label</description>
++ <posx>580</posx>
++ <posy>20</posy>
++ <width>300</width>
++ <height>20</height>
++ <font>font13_title</font>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <align>center</align>
++ <aligny>center</aligny>
++ <label>31501</label>
++ </control>
++ <control type="label">
++ <description>Status header label</description>
++ <posx>940</posx>
++ <posy>20</posy>
++ <width>150</width>
++ <height>20</height>
++ <font>font13_title</font>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <align>center</align>
++ <aligny>center</aligny>
++ <label>126</label>
++ </control>
++ <control type="image">
++ <description>separator image</description>
++ <posx>0</posx>
++ <posy>50</posy>
++ <width>1100</width>
++ <height>1</height>
++ <colordiffuse>88FFFFFF</colordiffuse>
++ <texture>separator2.png</texture>
++ </control>
++ <control type="list" id="14">
++ <posx>0</posx>
++ <posy>55</posy>
++ <width>1100</width>
++ <height>480</height>
++ <onup>14</onup>
++ <ondown>14</ondown>
++ <onleft>35</onleft>
++ <onright>73</onright>
++ <pagecontrol>73</pagecontrol>
++ <scrolltime>200</scrolltime>
++ <itemlayout height="40">
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>1100</width>
++ <height>41</height>
++ <texture border="5">MenuItemNF.png</texture>
++ </control>
++ <control type="image">
++ <posx>220</posx>
++ <posy>0</posy>
++ <width>300</width>
++ <height>40</height>
++ <colordiffuse>33FFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ </control>
++ <control type="image">
++ <posx>940</posx>
++ <posy>0</posy>
++ <width>155</width>
++ <height>40</height>
++ <colordiffuse>33FFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ </control>
++ <control type="image">
++ <posx>0</posx>
++ <posy>8</posy>
++ <width>50</width>
++ <height>26</height>
++ <visible>!IsEmpty(ListItem.Date)</visible>
++ <texture border="1">$INFO[ListItem.Icon]</texture>
++ </control>
++ <control type="label">
++ <posx>50</posx>
++ <posy>0</posy>
++ <width>150</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.ChannelName</info>
++ </control>
++ <control type="label">
++ <posx>370</posx>
++ <posy>0</posy>
++ <width>290</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>center</align>
++ <aligny>center</aligny>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.Label</info>
++ </control>
++ <control type="label">
++ <posx>730</posx>
++ <posy>0</posy>
++ <width>400</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>center</align>
++ <aligny>center</aligny>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.Date</info>
++ </control>
++ <control type="label">
++ <posx>1018</posx>
++ <posy>0</posy>
++ <width>170</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>center</align>
++ <aligny>center</aligny>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.Comment</info>
++ </control>
++ </itemlayout>
++ <focusedlayout height="40">
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>1100</width>
++ <height>41</height>
++ <texture border="5">MenuItemNF.png</texture>
++ </control>
++ <control type="image">
++ <posx>220</posx>
++ <posy>0</posy>
++ <width>300</width>
++ <height>40</height>
++ <colordiffuse>33FFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ <visible>!Control.HasFocus(14)</visible>
++ </control>
++ <control type="image">
++ <posx>980</posx>
++ <posy>0</posy>
++ <width>120</width>
++ <height>40</height>
++ <colordiffuse>33FFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ <visible>!Control.HasFocus(14)</visible>
++ </control>
++ <control type="image">
++ <posx>220</posx>
++ <posy>0</posy>
++ <width>300</width>
++ <height>40</height>
++ <colordiffuse>88FFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ <visible>Control.HasFocus(14)</visible>
++ </control>
++ <control type="image">
++ <posx>940</posx>
++ <posy>0</posy>
++ <width>155</width>
++ <height>40</height>
++ <colordiffuse>88FFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ <visible>Control.HasFocus(14)</visible>
++ </control>
++ <control type="image">
++ <posx>0</posx>
++ <posy>8</posy>
++ <width>50</width>
++ <height>26</height>
++ <visible>!IsEmpty(ListItem.Date)</visible>
++ <texture border="1">$INFO[ListItem.Icon]</texture>
++ </control>
++ <control type="label">
++ <posx>50</posx>
++ <posy>0</posy>
++ <width>150</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.ChannelName</info>
++ </control>
++ <control type="label">
++ <posx>370</posx>
++ <posy>0</posy>
++ <width>290</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>center</align>
++ <aligny>center</aligny>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.Label</info>
++ </control>
++ <control type="label">
++ <posx>730</posx>
++ <posy>0</posy>
++ <width>400</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>center</align>
++ <aligny>center</aligny>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.Date</info>
++ </control>
++ <control type="label">
++ <posx>1018</posx>
++ <posy>0</posy>
++ <width>150</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>center</align>
++ <aligny>center</aligny>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.Comment</info>
++ </control>
++ </focusedlayout>
++ </control>
++ <control type="scrollbar" id="73">
++ <posx>1105</posx>
++ <posy>50</posy>
++ <width>25</width>
++ <height>480</height>
++ <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
++ <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
++ <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
++ <textureslidernib>ScrollBarNib.png</textureslidernib>
++ <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
++ <onleft>11</onleft>
++ <onright>35</onright>
++ <showonepage>false</showonepage>
++ <orientation>vertical</orientation>
++ </control>
++ <control type="image">
++ <description>separator image</description>
++ <posx>55</posx>
++ <posy>540</posy>
++ <width>1010</width>
++ <height>1</height>
++ <colordiffuse>88FFFFFF</colordiffuse>
++ <texture>separator2.png</texture>
++ </control>
++ <control type="label">
++ <description>Next timer date</description>
++ <posx>55</posx>
++ <posy>545</posy>
++ <width>1010</width>
++ <height>30</height>
++ <font>font13</font>
++ <align>center</align>
++ <aligny>center</aligny>
++ <scroll>true</scroll>
++ <textcolor>white</textcolor>
++ <label>$INFO[PVR.NextTimer]</label>
++ <visible>PVR.HasTimer</visible>
++ </control>
++ </control>
++ <control type="label">
++ <description>Page Count Label</description>
++ <posx>40r</posx>
++ <posy>53r</posy>
++ <width>500</width>
++ <height>20</height>
++ <font>font12</font>
++ <textcolor>grey</textcolor>
++ <scroll>false</scroll>
++ <align>right</align>
++ <aligny>center</aligny>
++ <label>([COLOR=blue]$INFO[Container(14).NumItems][/COLOR]) $LOCALIZE[19040] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(14).CurrentPage]/$INFO[Container(14).NumPages][/COLOR])</label>
++ <include>Window_OpenClose_Animation</include>
++ </control>
++ </control>
++ </include>
++
++ <include name="LiveTVSearchView">
++ <control type="group">
++ <description>TV Search group</description>
++ <visible>Control.IsVisible(17)</visible>
++ <include>VisibleFadeEffect</include>
++ <control type="group">
++ <posx>80</posx>
++ <posy>60</posy>
++ <control type="label">
++ <description>Channel label</description>
++ <posx>0</posx>
++ <posy>20</posy>
++ <width>250</width>
++ <height>20</height>
++ <font>font13_title</font>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <align>center</align>
++ <aligny>center</aligny>
++ <label>19148</label>
++ </control>
++ <control type="label">
++ <description>Title</description>
++ <posx>290</posx>
++ <posy>20</posy>
++ <width>350</width>
++ <height>20</height>
++ <font>font13_title</font>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <align>left</align>
++ <aligny>center</aligny>
++ <label>369</label>
++ </control>
++ <control type="label">
++ <description>Time label</description>
++ <posx>920</posx>
++ <posy>20</posy>
++ <width>300</width>
++ <height>20</height>
++ <font>font13_title</font>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <align>right</align>
++ <aligny>center</aligny>
++ <label>21820</label>
++ </control>
++ <control type="label">
++ <description>Status header label</description>
++ <posx>960</posx>
++ <posy>20</posy>
++ <width>140</width>
++ <height>20</height>
++ <font>font13_title</font>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <align>center</align>
++ <aligny>center</aligny>
++ <label>126</label>
++ </control>
++ <control type="image">
++ <description>separator image</description>
++ <posx>0</posx>
++ <posy>50</posy>
++ <width>1100</width>
++ <height>1</height>
++ <colordiffuse>88FFFFFF</colordiffuse>
++ <texture>separator2.png</texture>
++ </control>
++ <control type="list" id="17">
++ <posx>0</posx>
++ <posy>55</posy>
++ <width>1100</width>
++ <height>520</height>
++ <onup>17</onup>
++ <ondown>17</ondown>
++ <onleft>36</onleft>
++ <onright>77</onright>
++ <pagecontrol>77</pagecontrol>
++ <scrolltime>200</scrolltime>
++ <itemlayout height="40">
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>1100</width>
++ <height>41</height>
++ <texture border="5">MenuItemNF.png</texture>
++ </control>
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>250</width>
++ <height>40</height>
++ <colordiffuse>33FFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ </control>
++ <control type="image">
++ <posx>960</posx>
++ <posy>0</posy>
++ <width>140</width>
++ <height>40</height>
++ <colordiffuse>33FFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ </control>
++ <control type="image">
++ <posx>10</posx>
++ <posy>5</posy>
++ <width>30</width>
++ <height>30</height>
++ <info>ListItem.Icon</info>
++ </control>
++ <control type="label">
++ <posx>50</posx>
++ <posy>0</posy>
++ <width>190</width>
++ <height>35</height>
++ <font>font12</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.ChannelName</info>
++ </control>
++ <control type="label">
++ <posx>260</posx>
++ <posy>0</posy>
++ <width>650</width>
++ <height>35</height>
++ <font>font13</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.Label</info>
++ </control>
++ <control type="label">
++ <posx>950</posx>
++ <posy>0</posy>
++ <width>500</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>right</align>
++ <aligny>center</aligny>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.Date</info>
++ </control>
++ <control type="image">
++ <posx>970</posx>
++ <posy>10</posy>
++ <width>30</width>
++ <height>20</height>
++ <texture>PVR-IsRecording.png</texture>
++ <visible>ListItem.IsRecording</visible>
++ </control>
++ <control type="label">
++ <posx>1005</posx>
++ <posy>0</posy>
++ <width>80</width>
++ <height>40</height>
++ <font>font10</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <label>19043</label>
++ <visible>ListItem.IsRecording</visible>
++ </control>
++ <control type="image">
++ <posx>970</posx>
++ <posy>10</posy>
++ <width>20</width>
++ <height>20</height>
++ <texture>PVR-HasTimer.png</texture>
++ <visible>ListItem.HasTimer + !ListItem.IsRecording</visible>
++ </control>
++ <control type="label">
++ <posx>1000</posx>
++ <posy>0</posy>
++ <width>80</width>
++ <height>40</height>
++ <font>font10</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <label>31510</label>
++ <visible>ListItem.HasTimer</visible>
++ </control>
++ </itemlayout>
++ <focusedlayout height="40">
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>1100</width>
++ <height>41</height>
++ <texture border="5">MenuItemNF.png</texture>
++ </control>
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>250</width>
++ <height>40</height>
++ <colordiffuse>33FFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ <visible>!Control.HasFocus(17)</visible>
++ </control>
++ <control type="image">
++ <posx>960</posx>
++ <posy>0</posy>
++ <width>140</width>
++ <height>40</height>
++ <colordiffuse>33FFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ <visible>!Control.HasFocus(17)</visible>
++ </control>
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>250</width>
++ <height>40</height>
++ <colordiffuse>88FFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ <visible>Control.HasFocus(17)</visible>
++ </control>
++ <control type="image">
++ <posx>960</posx>
++ <posy>0</posy>
++ <width>140</width>
++ <height>40</height>
++ <colordiffuse>88FFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ <visible>Control.HasFocus(17)</visible>
++ </control>
++ <control type="image">
++ <posx>10</posx>
++ <posy>5</posy>
++ <width>30</width>
++ <height>30</height>
++ <info>ListItem.Icon</info>
++ </control>
++ <control type="label">
++ <posx>50</posx>
++ <posy>0</posy>
++ <width>190</width>
++ <height>35</height>
++ <font>font12</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.ChannelName</info>
++ </control>
++ <control type="label">
++ <posx>260</posx>
++ <posy>0</posy>
++ <width>650</width>
++ <height>35</height>
++ <font>font13</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>white</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.Label</info>
++ </control>
++ <control type="label">
++ <posx>950</posx>
++ <posy>0</posy>
++ <width>500</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>right</align>
++ <aligny>center</aligny>
++ <textcolor>white</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.Date</info>
++ </control>
++ <control type="image">
++ <posx>970</posx>
++ <posy>10</posy>
++ <width>30</width>
++ <height>20</height>
++ <texture>PVR-IsRecording.png</texture>
++ <visible>ListItem.IsRecording</visible>
++ </control>
++ <control type="label">
++ <posx>1005</posx>
++ <posy>0</posy>
++ <width>80</width>
++ <height>40</height>
++ <font>font10</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <label>19043</label>
++ <visible>ListItem.IsRecording</visible>
++ </control>
++ <control type="image">
++ <posx>970</posx>
++ <posy>10</posy>
++ <width>20</width>
++ <height>20</height>
++ <texture>PVR-HasTimer.png</texture>
++ <visible>ListItem.HasTimer + !ListItem.IsRecording</visible>
++ </control>
++ <control type="label">
++ <posx>1000</posx>
++ <posy>0</posy>
++ <width>80</width>
++ <height>40</height>
++ <font>font10</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <label>31510</label>
++ <visible>ListItem.HasTimer</visible>
++ </control>
++ </focusedlayout>
++ </control>
++ <control type="scrollbar" id="77">
++ <posx>1105</posx>
++ <posy>50</posy>
++ <width>25</width>
++ <height>520</height>
++ <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
++ <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
++ <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
++ <textureslidernib>ScrollBarNib.png</textureslidernib>
++ <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
++ <onleft>16</onleft>
++ <onright>36</onright>
++ <showonepage>false</showonepage>
++ <orientation>vertical</orientation>
++ </control>
++ </control>
++ <control type="label">
++ <description>Page Count Label</description>
++ <posx>40r</posx>
++ <posy>53r</posy>
++ <width>500</width>
++ <height>20</height>
++ <font>font12</font>
++ <textcolor>grey</textcolor>
++ <scroll>false</scroll>
++ <align>right</align>
++ <aligny>center</aligny>
++ <label>([COLOR=blue]$INFO[Container(17).NumItems][/COLOR]) $LOCALIZE[31025] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(17).CurrentPage]/$INFO[Container(17).NumPages][/COLOR])</label>
++ </control>
++ </control>
++ </include>
++
++ <include name="LiveTVGuideChannelView">
++ <control type="group">
++ <description>TV Guide Channel</description>
++ <visible>Control.IsVisible(15)</visible>
++ <include>VisibleFadeEffect</include>
++ <control type="group">
++ <posx>80</posx>
++ <posy>60</posy>
++ <control type="label">
++ <description>Date Time label</description>
++ <posx>0</posx>
++ <posy>20</posy>
++ <width>300</width>
++ <height>20</height>
++ <font>font13_title</font>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <align>center</align>
++ <aligny>center</aligny>
++ <label>21820</label>
++ </control>
++ <control type="label">
++ <description>Title</description>
++ <posx>300</posx>
++ <posy>20</posy>
++ <width>600</width>
++ <height>20</height>
++ <font>font13_title</font>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <align>center</align>
++ <aligny>center</aligny>
++ <label>369</label>
++ </control>
++ <control type="label">
++ <description>Status header label</description>
++ <posx>960</posx>
++ <posy>20</posy>
++ <width>140</width>
++ <height>20</height>
++ <font>font13_title</font>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <align>center</align>
++ <aligny>center</aligny>
++ <label>126</label>
++ </control>
++ <control type="image">
++ <description>separator image</description>
++ <posx>0</posx>
++ <posy>50</posy>
++ <width>1100</width>
++ <height>1</height>
++ <colordiffuse>88FFFFFF</colordiffuse>
++ <texture>separator2.png</texture>
++ </control>
++ <control type="list" id="15">
++ <posx>0</posx>
++ <posy>60</posy>
++ <width>1100</width>
++ <height>500</height>
++ <onup>15</onup>
++ <ondown>15</ondown>
++ <onleft>31</onleft>
++ <onright>75</onright>
++ <pagecontrol>75</pagecontrol>
++ <scrolltime>200</scrolltime>
++ <itemlayout height="40">
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>1100</width>
++ <height>41</height>
++ <texture border="5">MenuItemNF.png</texture>
++ </control>
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>300</width>
++ <height>40</height>
++ <colordiffuse>33FFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ </control>
++ <control type="image">
++ <posx>960</posx>
++ <posy>0</posy>
++ <width>140</width>
++ <height>40</height>
++ <colordiffuse>33FFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ </control>
++ <control type="label">
++ <posx>150</posx>
++ <posy>0</posy>
++ <width>280</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>center</align>
++ <aligny>center</aligny>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.Label2</info>
++ </control>
++ <control type="label">
++ <posx>310</posx>
++ <posy>0</posy>
++ <width>640</width>
++ <height>40</height>
++ <font>font13</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.Label</info>
++ </control>
++ <control type="image">
++ <posx>970</posx>
++ <posy>10</posy>
++ <width>30</width>
++ <height>20</height>
++ <texture>PVR-IsRecording.png</texture>
++ <visible>ListItem.IsRecording</visible>
++ </control>
++ <control type="label">
++ <posx>1005</posx>
++ <posy>0</posy>
++ <width>80</width>
++ <height>40</height>
++ <font>font10</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <label>19043</label>
++ <visible>ListItem.IsRecording</visible>
++ </control>
++ <control type="image">
++ <posx>970</posx>
++ <posy>10</posy>
++ <width>20</width>
++ <height>20</height>
++ <texture>PVR-HasTimer.png</texture>
++ <visible>ListItem.HasTimer + !ListItem.IsRecording</visible>
++ </control>
++ <control type="label">
++ <posx>1000</posx>
++ <posy>0</posy>
++ <width>80</width>
++ <height>40</height>
++ <font>font10</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <label>31510</label>
++ <visible>ListItem.HasTimer</visible>
++ </control>
++ </itemlayout>
++ <focusedlayout height="100">
++ <control type="image">
++ <posx>0</posx>
++ <posy>1</posy>
++ <width>1100</width>
++ <height>98</height>
++ <colordiffuse>AAFFFFFF</colordiffuse>
++ <texture border="5">black-back2.png</texture>
++ </control>
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>1100</width>
++ <height>101</height>
++ <texture border="5">MenuItemNF.png</texture>
++ </control>
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>300</width>
++ <height>40</height>
++ <colordiffuse>AAFFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ <visible>!Control.HasFocus(15)</visible>
++ </control>
++ <control type="image">
++ <posx>960</posx>
++ <posy>0</posy>
++ <width>140</width>
++ <height>40</height>
++ <colordiffuse>AAFFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ <visible>!Control.HasFocus(15)</visible>
++ </control>
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>300</width>
++ <height>40</height>
++ <colordiffuse>88FFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ <visible>Control.HasFocus(15)</visible>
++ </control>
++ <control type="image">
++ <posx>960</posx>
++ <posy>0</posy>
++ <width>140</width>
++ <height>40</height>
++ <colordiffuse>88FFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ <visible>Control.HasFocus(15)</visible>
++ </control>
++ <control type="label">
++ <posx>150</posx>
++ <posy>0</posy>
++ <width>280</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>center</align>
++ <aligny>center</aligny>
++ <textcolor>white</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.Label2</info>
++ </control>
++ <control type="label">
++ <posx>310</posx>
++ <posy>0</posy>
++ <width>640</width>
++ <height>40</height>
++ <font>font13</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>white</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.Label</info>
++ </control>
++ <control type="textbox">
++ <description>Plot Value for TVShow</description>
++ <posx>50</posx>
++ <posy>40</posy>
++ <width>1000</width>
++ <height>60</height>
++ <font>font12</font>
++ <align>justify</align>
++ <textcolor>grey2</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <pagecontrol>-</pagecontrol>
++ <label>$INFO[ListItem.Plot]</label>
++ </control>
++ <control type="image">
++ <posx>970</posx>
++ <posy>10</posy>
++ <width>30</width>
++ <height>20</height>
++ <texture>PVR-IsRecording.png</texture>
++ <visible>ListItem.IsRecording</visible>
++ </control>
++ <control type="label">
++ <posx>1005</posx>
++ <posy>0</posy>
++ <width>80</width>
++ <height>40</height>
++ <font>font10</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <label>19043</label>
++ <visible>ListItem.IsRecording</visible>
++ </control>
++ <control type="image">
++ <posx>970</posx>
++ <posy>10</posy>
++ <width>20</width>
++ <height>20</height>
++ <texture>PVR-HasTimer.png</texture>
++ <visible>ListItem.HasTimer + !ListItem.IsRecording</visible>
++ </control>
++ <control type="label">
++ <posx>1000</posx>
++ <posy>0</posy>
++ <width>80</width>
++ <height>40</height>
++ <font>font10</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <label>31510</label>
++ <visible>ListItem.HasTimer</visible>
++ </control>
++ </focusedlayout>
++ </control>
++ <control type="scrollbar" id="75">
++ <posx>1105</posx>
++ <posy>60</posy>
++ <width>25</width>
++ <height>500</height>
++ <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
++ <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
++ <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
++ <textureslidernib>ScrollBarNib.png</textureslidernib>
++ <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
++ <onleft>15</onleft>
++ <onright>31</onright>
++ <showonepage>false</showonepage>
++ <orientation>vertical</orientation>
++ </control>
++ </control>
++ <control type="label">
++ <description>Page Count Label</description>
++ <posx>40r</posx>
++ <posy>53r</posy>
++ <width>500</width>
++ <height>20</height>
++ <font>font12</font>
++ <textcolor>grey</textcolor>
++ <scroll>false</scroll>
++ <align>right</align>
++ <aligny>center</aligny>
++ <label>([COLOR=blue]$INFO[Container(15).NumItems][/COLOR]) $LOCALIZE[31025] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(15).CurrentPage]/$INFO[Container(15).NumPages][/COLOR])</label>
++ </control>
++ </control>
++ </include>
++
++ <include name="LiveTVGuideNowNextView">
++ <control type="group">
++ <description>TV Guide Now/Next</description>
++ <visible>Control.IsVisible(16)</visible>
++ <include>VisibleFadeEffect</include>
++ <control type="group">
++ <posx>80</posx>
++ <posy>60</posy>
++ <control type="label">
++ <description>Time label</description>
++ <posx>0</posx>
++ <posy>20</posy>
++ <width>100</width>
++ <height>20</height>
++ <font>font13_title</font>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <align>center</align>
++ <aligny>center</aligny>
++ <label>555</label>
++ </control>
++ <control type="label">
++ <description>Channel label</description>
++ <posx>100</posx>
++ <posy>20</posy>
++ <width>250</width>
++ <height>20</height>
++ <font>font13_title</font>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <align>center</align>
++ <aligny>center</aligny>
++ <label>19148</label>
++ </control>
++ <control type="label">
++ <description>Title</description>
++ <posx>350</posx>
++ <posy>20</posy>
++ <width>550</width>
++ <height>20</height>
++ <font>font13_title</font>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <align>center</align>
++ <aligny>center</aligny>
++ <label>369</label>
++ </control>
++ <control type="label">
++ <description>Status header label</description>
++ <posx>960</posx>
++ <posy>20</posy>
++ <width>140</width>
++ <height>20</height>
++ <font>font13_title</font>
++ <textcolor>white</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <align>center</align>
++ <aligny>center</aligny>
++ <label>126</label>
++ </control>
++ <control type="image">
++ <description>separator image</description>
++ <posx>0</posx>
++ <posy>50</posy>
++ <width>1100</width>
++ <height>1</height>
++ <colordiffuse>88FFFFFF</colordiffuse>
++ <texture>separator2.png</texture>
++ </control>
++ <control type="list" id="16">
++ <posx>0</posx>
++ <posy>60</posy>
++ <width>1100</width>
++ <height>500</height>
++ <onup>16</onup>
++ <ondown>16</ondown>
++ <onleft>31</onleft>
++ <onright>76</onright>
++ <pagecontrol>76</pagecontrol>
++ <scrolltime>200</scrolltime>
++ <itemlayout height="40">
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>1100</width>
++ <height>41</height>
++ <texture border="5">MenuItemNF.png</texture>
++ </control>
++ <control type="image">
++ <posx>100</posx>
++ <posy>0</posy>
++ <width>250</width>
++ <height>40</height>
++ <colordiffuse>33FFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ </control>
++ <control type="image">
++ <posx>960</posx>
++ <posy>0</posy>
++ <width>140</width>
++ <height>40</height>
++ <colordiffuse>33FFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ </control>
++ <control type="label">
++ <posx>50</posx>
++ <posy>0</posy>
++ <width>100</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>center</align>
++ <aligny>center</aligny>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.StartTime</info>
++ </control>
++ <control type="image">
++ <posx>110</posx>
++ <posy>5</posy>
++ <width>30</width>
++ <height>30</height>
++ <info>ListItem.Icon</info>
++ </control>
++ <control type="label">
++ <posx>150</posx>
++ <posy>0</posy>
++ <width>190</width>
++ <height>35</height>
++ <font>font12</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.ChannelName</info>
++ </control>
++ <control type="label">
++ <posx>360</posx>
++ <posy>0</posy>
++ <width>590</width>
++ <height>35</height>
++ <font>font13</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.Label</info>
++ </control>
++ <control type="image">
++ <posx>970</posx>
++ <posy>10</posy>
++ <width>30</width>
++ <height>20</height>
++ <texture>PVR-IsRecording.png</texture>
++ <visible>ListItem.IsRecording</visible>
++ </control>
++ <control type="label">
++ <posx>1005</posx>
++ <posy>0</posy>
++ <width>80</width>
++ <height>40</height>
++ <font>font10</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <label>19043</label>
++ <visible>ListItem.IsRecording</visible>
++ </control>
++ <control type="image">
++ <posx>970</posx>
++ <posy>10</posy>
++ <width>20</width>
++ <height>20</height>
++ <texture>PVR-HasTimer.png</texture>
++ <visible>ListItem.HasTimer + !ListItem.IsRecording</visible>
++ </control>
++ <control type="label">
++ <posx>1000</posx>
++ <posy>0</posy>
++ <width>80</width>
++ <height>40</height>
++ <font>font10</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <label>31510</label>
++ <visible>ListItem.HasTimer</visible>
++ </control>
++ </itemlayout>
++ <focusedlayout height="100">
++ <control type="image">
++ <posx>0</posx>
++ <posy>1</posy>
++ <width>1100</width>
++ <height>98</height>
++ <colordiffuse>AAFFFFFF</colordiffuse>
++ <texture border="5">black-back2.png</texture>
++ </control>
++ <control type="image">
++ <posx>0</posx>
++ <posy>0</posy>
++ <width>1100</width>
++ <height>100</height>
++ <texture border="5">MenuItemNF.png</texture>
++ </control>
++ <control type="image">
++ <posx>100</posx>
++ <posy>0</posy>
++ <width>250</width>
++ <height>40</height>
++ <colordiffuse>AAFFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ <visible>!Control.HasFocus(16)</visible>
++ </control>
++ <control type="image">
++ <posx>960</posx>
++ <posy>0</posy>
++ <width>140</width>
++ <height>40</height>
++ <colordiffuse>AAFFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ <visible>!Control.HasFocus(16)</visible>
++ </control>
++ <control type="image">
++ <posx>100</posx>
++ <posy>0</posy>
++ <width>250</width>
++ <height>40</height>
++ <colordiffuse>88FFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ <visible>Control.HasFocus(16)</visible>
++ </control>
++ <control type="image">
++ <posx>960</posx>
++ <posy>0</posy>
++ <width>140</width>
++ <height>40</height>
++ <colordiffuse>88FFFFFF</colordiffuse>
++ <texture border="5">StackFO.png</texture>
++ <visible>Control.HasFocus(16)</visible>
++ </control>
++ <control type="label">
++ <posx>50</posx>
++ <posy>0</posy>
++ <width>100</width>
++ <height>40</height>
++ <font>font12</font>
++ <align>center</align>
++ <aligny>center</aligny>
++ <textcolor>white</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.StartTime</info>
++ </control>
++ <control type="image">
++ <posx>110</posx>
++ <posy>5</posy>
++ <width>30</width>
++ <height>30</height>
++ <info>ListItem.Icon</info>
++ </control>
++ <control type="label">
++ <posx>150</posx>
++ <posy>0</posy>
++ <width>190</width>
++ <height>35</height>
++ <font>font12</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>white</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.ChannelName</info>
++ </control>
++ <control type="label">
++ <posx>360</posx>
++ <posy>0</posy>
++ <width>590</width>
++ <height>35</height>
++ <font>font13</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>white</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <info>ListItem.Label</info>
++ </control>
++ <control type="textbox">
++ <description>Plot Value for TVShow</description>
++ <posx>50</posx>
++ <posy>40</posy>
++ <width>1000</width>
++ <height>60</height>
++ <font>font12</font>
++ <align>justify</align>
++ <textcolor>grey2</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <pagecontrol>-</pagecontrol>
++ <label>$INFO[ListItem.Plot]</label>
++ </control>
++ <control type="image">
++ <posx>970</posx>
++ <posy>10</posy>
++ <width>30</width>
++ <height>20</height>
++ <texture>PVR-IsRecording.png</texture>
++ <visible>ListItem.IsRecording</visible>
++ </control>
++ <control type="label">
++ <posx>1005</posx>
++ <posy>0</posy>
++ <width>80</width>
++ <height>40</height>
++ <font>font10</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <label>19043</label>
++ <visible>ListItem.IsRecording</visible>
++ </control>
++ <control type="image">
++ <posx>970</posx>
++ <posy>10</posy>
++ <width>20</width>
++ <height>20</height>
++ <texture>PVR-HasTimer.png</texture>
++ <visible>ListItem.HasTimer + !ListItem.IsRecording</visible>
++ </control>
++ <control type="label">
++ <posx>1000</posx>
++ <posy>0</posy>
++ <width>80</width>
++ <height>40</height>
++ <font>font10</font>
++ <align>left</align>
++ <aligny>center</aligny>
++ <textcolor>grey2</textcolor>
++ <selectedcolor>selected</selectedcolor>
++ <label>31510</label>
++ <visible>ListItem.HasTimer</visible>
++ </control>
++ </focusedlayout>
++ </control>
++ <control type="scrollbar" id="76">
++ <posx>1105</posx>
++ <posy>60</posy>
++ <width>25</width>
++ <height>500</height>
++ <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
++ <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
++ <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
++ <textureslidernib>ScrollBarNib.png</textureslidernib>
++ <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
++ <onleft>16</onleft>
++ <onright>31</onright>
++ <showonepage>false</showonepage>
++ <orientation>vertical</orientation>
++ </control>
++ </control>
++ <control type="label">
++ <description>Page Count Label</description>
++ <posx>40r</posx>
++ <posy>53r</posy>
++ <width>500</width>
++ <height>20</height>
++ <font>font12</font>
++ <textcolor>grey</textcolor>
++ <scroll>false</scroll>
++ <align>right</align>
++ <aligny>center</aligny>
++ <label>([COLOR=blue]$INFO[Container(16).NumItems][/COLOR]) $LOCALIZE[31025] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(16).CurrentPage]/$INFO[Container(16).NumPages][/COLOR])</label>
++ </control>
++ </control>
++ </include>
++</includes>
+\ No newline at end of file
+diff --git a/addons/skin.confluence/720p/custom_SkinSetting_1111.xml b/addons/skin.confluence/720p/custom_SkinSetting_1111.xml
+index e5d699d..931bac3 100644
+--- a/addons/skin.confluence/720p/custom_SkinSetting_1111.xml
++++ b/addons/skin.confluence/720p/custom_SkinSetting_1111.xml
+@@ -751,6 +751,14 @@
+ <thumb>$INFO[Skin.String(Home_Custom_Back_TVShow_Folder)]</thumb>
+ <visible>Skin.HasSetting(HomeMenuNoTVShowsButton) + Library.HasContent(TVShows)</visible>
+ </item>
++ <item id="12">
++ <label>31502</label>
++ <label2>Home_Custom_Back_TV_Folder</label2>
++ <onclick>-</onclick>
++ <icon>special://skin/backgrounds/tv.jpg</icon>
++ <thumb>$INFO[Skin.String(Home_Custom_Back_TV_Folder)]</thumb>
++ <visible>System.GetBool(pvrmanager.enabled)</visible>
++ </item>
+ <item id="2">
+ <label>2</label>
+ <label2>Home_Custom_Back_Music_Folder</label2>
+diff --git a/addons/skin.confluence/720p/defaults.xml b/addons/skin.confluence/720p/defaults.xml
+index ed1111d..05c80e5 100644
+--- a/addons/skin.confluence/720p/defaults.xml
++++ b/addons/skin.confluence/720p/defaults.xml
+@@ -123,6 +123,7 @@
+ <disabledcolor>grey3</disabledcolor>
+ <textoffsetx>7</textoffsetx>
+ <aligny>center</aligny>
++ <pulseonselect>no</pulseonselect>
+ </default>
+ <default type="selectbutton">
+ <posx>490</posx>
+diff --git a/addons/skin.confluence/720p/includes.xml b/addons/skin.confluence/720p/includes.xml
+index b563db5..4843834 100644
+--- a/addons/skin.confluence/720p/includes.xml
++++ b/addons/skin.confluence/720p/includes.xml
+@@ -6,6 +6,7 @@
+ <include file="ViewsPictures.xml" />
+ <include file="ViewsAddonBrowser.xml" />
+ <include file="ViewsLiveTV.xml" />
++ <include file="ViewsPVR.xml" />
+ <include file="IncludesCodecFlagging.xml" />
+ <include file="IncludesHomeRecentlyAdded.xml" />
+ <include file="IncludesHomeMenuItems.xml" />
+@@ -28,7 +29,7 @@
+ <texture>black-back.png</texture>
+ <animation effect="fade" time="400">Visible</animation>
+ <animation effect="fade" time="200">Hidden</animation>
+- <visible>Window.IsActive(MovieInformation) | Window.IsActive(MusicInformation) | Window.IsActive(SongInformation) | Window.IsActive(FileBrowser) | Window.IsActive(TextViewer) | Window.IsActive(AddonSettings) | Window.IsActive(ContentSettings) | Window.IsActive(SelectDialog) | Window.IsActive(FileStackingDialog) | Window.IsActive(MediaSource) | Window.IsActive(PictureInfo) | Window.IsActive(PlayerControls) | Window.IsActive(VirtualKeyboard) | Window.IsActive(NumericInput) | Window.IsActive(ProfileSettings) | Window.IsActive(LockSettings) | Window.IsActive(SmartPlaylistEditor) | Window.IsActive(SmartPlaylistRule) | Window.IsActive(script-Apple_Movie_Trailers-settings.xml) | Window.IsActive(script-Apple_Movie_Trailers-chooser.xml) | Window.IsActive(script-Apple_Movie_Trailers-search.xml) | Window.IsActive(script-Apple_Movie_Trailers-showtimes.xml) | Window.IsActive(script-XBMC_Lyrics-settings.xml) | Window.IsActive(script-RSS_Editor-rssEditor.xml) | Window.IsActive(script-RSS_Editor-setEditor.xml) | Window.IsActive(AddonInformation) | Window.IsActive(Peripherals) | Window.IsActive(PeripheralSettings)</visible>
++ <visible>Window.IsActive(MovieInformation) | Window.IsActive(MusicInformation) | Window.IsActive(SongInformation) | Window.IsActive(FileBrowser) | Window.IsActive(TextViewer) | Window.IsActive(AddonSettings) | Window.IsActive(ContentSettings) | Window.IsActive(SelectDialog) | Window.IsActive(FileStackingDialog) | Window.IsActive(MediaSource) | Window.IsActive(PictureInfo) | Window.IsActive(PlayerControls) | Window.IsActive(VirtualKeyboard) | Window.IsActive(NumericInput) | Window.IsActive(ProfileSettings) | Window.IsActive(LockSettings) | Window.IsActive(SmartPlaylistEditor) | Window.IsActive(SmartPlaylistRule) | Window.IsActive(script-Apple_Movie_Trailers-settings.xml) | Window.IsActive(script-Apple_Movie_Trailers-chooser.xml) | Window.IsActive(script-Apple_Movie_Trailers-search.xml) | Window.IsActive(script-Apple_Movie_Trailers-showtimes.xml) | Window.IsActive(script-XBMC_Lyrics-settings.xml) | Window.IsActive(script-RSS_Editor-rssEditor.xml) | Window.IsActive(script-RSS_Editor-setEditor.xml) | Window.IsActive(AddonInformation) | Window.IsActive(Peripherals) | Window.IsActive(PeripheralSettings) | Window.IsActive(PVRChannelManager)</visible>
+ </control>
+ </include>
+ <include name="WindowTitleCommons">
+@@ -246,6 +247,19 @@
+ <shadowcolor>black</shadowcolor>
+ <visible>Player.HasVideo + VideoPlayer.Content(MusicVideos)</visible>
+ </control>
++ <control type="label">
++ <posx>85</posx>
++ <posy>30r</posy>
++ <width>700</width>
++ <height>20</height>
++ <label>$INFO[VideoPlayer.ChannelName]$INFO[VideoPlayer.ChannelNumber, - ([COLOR=blue],[/COLOR])]</label>
++ <align>left</align>
++ <aligny>center</aligny>
++ <font>font12</font>
++ <textcolor>grey</textcolor>
++ <shadowcolor>black</shadowcolor>
++ <visible>Player.HasVideo + VideoPlayer.Content(LiveTV)</visible>
++ </control>
+ </control>
+ </include>
+ <include name="CommonPageCount">
+@@ -513,8 +527,124 @@
+ <description>Fake Button to fix Player Controls Navigation</description>
+ <visible>false</visible>
+ </control>
++ <control type="group" id="9006">
++ <width>250</width>
++ <height>90</height>
++ <visible>VideoPlayer.Content(LiveTV)</visible>
++ <include>VisibleFadeEffect</include>
++ <control type="button" id="600">
++ <posx>20</posx>
++ <posy>2</posy>
++ <width>39</width>
++ <height>39</height>
++ <label>-</label>
++ <texturefocus>OSDChannelUPFO.png</texturefocus>
++ <texturenofocus>OSDChannelUPNF.png</texturenofocus>
++ <onleft>50</onleft>
++ <onright>601</onright>
++ <onup>610</onup>
++ <ondown>611</ondown>
++ <onclick>XBMC.PlayerControl(Previous)</onclick>
++ </control>
++ <control type="button" id="601">
++ <posx>60</posx>
++ <posy>2</posy>
++ <width>39</width>
++ <height>39</height>
++ <label>-</label>
++ <texturefocus>OSDChannelDownFO.png</texturefocus>
++ <texturenofocus>OSDChannelDownNF.png</texturenofocus>
++ <onleft>600</onleft>
++ <onright>603</onright>
++ <onup>610</onup>
++ <ondown>611</ondown>
++ <onclick>XBMC.PlayerControl(Next)</onclick>
++ </control>
++ <control type="button" id="603">
++ <posx>100</posx>
++ <posy>2</posy>
++ <width>39</width>
++ <height>39</height>
++ <label>-</label>
++ <texturefocus>OSDStopFO.png</texturefocus>
++ <texturenofocus>OSDStopNF.png</texturenofocus>
++ <onleft>601</onleft>
++ <onright>604</onright>
++ <onup>610</onup>
++ <ondown>611</ondown>
++ <onclick>down</onclick>
++ <onclick>XBMC.PlayerControl(Stop)</onclick>
++ </control>
++ <control type="button" id="604">
++ <posx>180</posx>
++ <posy>2</posy>
++ <width>39</width>
++ <height>39</height>
++ <label>-</label>
++ <texturefocus>OSDRecordOffFO.png</texturefocus>
++ <texturenofocus>OSDRecordOffNF.png</texturenofocus>
++ <onleft>603</onleft>
++ <onright>50</onright>
++ <onup>610</onup>
++ <ondown>611</ondown>
++ <onclick>XBMC.PlayerControl(record)</onclick>
++ <enable>Player.CanRecord</enable>
++ <animation effect="fade" start="100" end="30" time="100" condition="!Player.CanRecord">Conditional</animation>
++ </control>
++
++ <control type="button" id="605">
++ <posx>20</posx>
++ <posy>42</posy>
++ <width>39</width>
++ <height>39</height>
++ <label>-</label>
++ <texturefocus>OSDRewindFO.png</texturefocus>
++ <texturenofocus>OSDRewindNF.png</texturenofocus>
++ <onleft>603</onleft>
++ <onright>607</onright>
++ <onup>610</onup>
++ <ondown>611</ondown>
++ <onback>50</onback>
++ <onclick>XBMC.PlayerControl(Rewind)</onclick>
++ </control>
++ <control type="button" id="606">
++ <posx>60</posx>
++ <posy>42</posy>
++ <width>39</width>
++ <height>39</height>
++ <label>-</label>
++ <texturefocus>OSDForwardFO.png</texturefocus>
++ <texturenofocus>OSDForwardNF.png</texturenofocus>
++ <onleft>607</onleft>
++ <onright>600</onright>
++ <onup>610</onup>
++ <ondown>611</ondown>
++ <onback>50</onback>
++ <onclick>XBMC.PlayerControl(Forward)</onclick>
++ </control>
++ <control type="togglebutton" id="607">
++ <posx>100</posx>
++ <posy>42</posy>
++ <width>39</width>
++ <height>39</height>
++ <label>-</label>
++ <texturefocus>OSDPauseFO.png</texturefocus>
++ <texturenofocus>OSDPauseNF.png</texturenofocus>
++ <usealttexture>Player.Paused | Player.Forwarding | Player.Rewinding</usealttexture>
++ <alttexturefocus>OSDPlayFO.png</alttexturefocus>
++ <alttexturenofocus>OSDPlayNF.png</alttexturenofocus>
++ <onleft>605</onleft>
++ <onright>606</onright>
++ <onup>610</onup>
++ <ondown>611</ondown>
++ <onback>50</onback>
++ <onclick>XBMC.PlayerControl(Play)</onclick>
++ </control>
++
++ </control>
+ <control type="group" id="9005">
+ <visible>[Player.HasAudio | Player.HasVideo]</visible>
++ <visible>!VideoPlayer.Content(LiveTV)</visible>
+ <include>VisibleFadeEffect</include>
+ <width>250</width>
+ <height>45</height>
+diff --git a/addons/skin.confluence/backgrounds/tv.jpg b/addons/skin.confluence/backgrounds/tv.jpg
+new file mode 100644
+index 0000000..9414690
+Binary files /dev/null and b/addons/skin.confluence/backgrounds/tv.jpg differ
+diff --git a/addons/skin.confluence/language/Czech/strings.xml b/addons/skin.confluence/language/Czech/strings.xml
+index 2d5551d..e67752a 100644
+--- a/addons/skin.confluence/language/Czech/strings.xml
++++ b/addons/skin.confluence/language/Czech/strings.xml
+@@ -2,7 +2,7 @@
+ <!--Translator: ezechiel1917-->
+ <!--Date of translation: 26.09.2010-->
+ <!--Updated 22.11.2011 by mirek-->
+-<!--Updated 06.01.2012 by babÄa-->
++<!--Updated 06.01.2012 by babÄa: Live TV section-->
+ <strings>
+ <!-- Misc labels -->
+ <string id="31000">Změnit</string>
+@@ -166,9 +166,22 @@
+ <string id="31406">[B]MOŽNOSTI SYSTÉMU[/B][CR][CR]Nastavení obrazovky · Konfigurace výstupu zvuku · Nastavení dálkového ovládání[CR]Možnosti úsporného režimu · Ukládání informací o ladÄ›ní · RodiÄovský zámek</string>
+ <string id="31407">[B]MOŽNOSTI VZHLEDU[/B][CR][CR]Přizpůsobení vzhledu Confluence · Správa sekcí na úvodní obrazovce[CR]Nastavení pozadí vzhledu</string>
+ <string id="31408">[B]MOŽNOSTI DOPLŇKŮ[/B][CR][CR]Správa nainstalovaných doplňků · Vyhledávání a instalace doplňků z xbmc.org[CR]Nastavení doplňků</string>
++ <string id="31409">[B]MOŽNOSTI PŘEHRÃVÃNà TELEVIZNÃHO STREAMU[/B][CR][CR]ZmÄ›nit zobrazení pÅ™es celou obrazovku · Správa EPG dat</string>
+
+ <string id="31421">PÅ™ihlášení uživatele[CR]PokraÄujte výbÄ›rem profilu</string>
+
++ <!-- Extra Unified PVR labels -->
++ <string id="31500">ÄŒasovaÄe</string>
++ <string id="31501">Naplánovaný Äas</string>
++ <string id="31502">Televize</string>
++ <string id="31503">Přidat skupinu</string>
++ <string id="31504">Přejmenovat skupinu</string>
++ <string id="31505">Smazat skupinu</string>
++ <string id="31506">Dostupné[CR]skupiny</string>
++ <string id="31509">Skupina programů</string>
++ <string id="31510">Timer Set</string> <!-- check this later -->
++ <string id="31511">Nastavení programu</string>
++
+ <!-- Weather plugin -->
+ <string id="31900">Mapy poÄasí</string>
+ <string id="31901">PÅ™edpovÄ›Ä na 36 hodin</string>
+diff --git a/addons/skin.confluence/language/Dutch/strings.xml b/addons/skin.confluence/language/Dutch/strings.xml
+index b328697..d735a59 100644
+--- a/addons/skin.confluence/language/Dutch/strings.xml
++++ b/addons/skin.confluence/language/Dutch/strings.xml
+@@ -168,6 +168,17 @@
+ <string id="31408">[B]ADD-ONS CONFIGUREREN[/B][CR][CR]Beheer uw geïnstalleerde Add-ons · Zoek en installeer Add-ons via xbmc.org · Wijzig Add-oninstellingen</string>
+
+ <string id="31421">Selecteer uw XBMC-gebruikersprofiel[CR]om in te loggen</string>
++
++ <!-- Extra Unified PVR labels -->
++ <string id="31500">Geplande opnames</string>
++ <string id="31501">Opname starttijd</string>
++ <string id="31502">Televisie</string>
++ <string id="31503">Groep toevoegen</string>
++ <string id="31504">Groep hernoemen</string>
++ <string id="31505">Groep verwijderen</string>
++ <string id="31506">Beschikbare[CR]Groepen</string>
++ <string id="31509">Groep</string>
++ <string id="31510">Geplande opname ingesteld</string>
+
+ <!-- Weather plugin -->
+ <string id="31900">Weer Kaarten</string>
+diff --git a/addons/skin.confluence/language/English/strings.xml b/addons/skin.confluence/language/English/strings.xml
+index d32977a..ab4678b 100644
+--- a/addons/skin.confluence/language/English/strings.xml
++++ b/addons/skin.confluence/language/English/strings.xml
+@@ -162,8 +162,21 @@
+ <string id="31406">[B]CONFIGURE SYSTEM SETTINGS[/B][CR][CR]Setup and calibrate displays · Configure audio output · Setup remote controls[CR]Set power saving options · Enable debugging · Setup master lock</string>
+ <string id="31407">[B]CONFIGURE SKIN SETTINGS[/B][CR][CR]Setup the Confluence skin · Add and remove home menu items[CR]Change skin backgrounds</string>
+ <string id="31408">[B]CONFIGURE ADD-ONS[/B][CR][CR]Manage your installed Add-ons · Browse for and install Add-ons from xbmc.org[CR]Modify Add-on settings</string>
++ <string id="31409">[B]CONFIGURE TV SETTINGS[/B][CR][CR]Change fullscreen info · Manage EPG data settings</string>
+
+ <string id="31421">Select your XBMC user Profile[CR]to login and continue</string>
++
++ <!-- Extra Unified PVR labels -->
++ <string id="31500">Recording Timers</string>
++ <string id="31501">Scheduled Time</string>
++ <string id="31502">Live TV</string>
++ <string id="31503">Add Group</string>
++ <string id="31504">Rename Group</string>
++ <string id="31505">Delete Group</string>
++ <string id="31506">Available[CR]Groups</string>
++ <string id="31509">Channel Group</string>
++ <string id="31510">Timer Set</string>
++ <string id="31511">Channel Options</string>
+
+ <!-- Weather plugin -->
+ <string id="31900">Weather Maps</string>
+@@ -177,4 +190,4 @@
+ <string id="31908">Chance of Precipitation</string>
+ <string id="31909">Fetching forecast info...</string>
+
+- </strings>
++</strings>
+diff --git a/addons/skin.confluence/language/Finnish/strings.xml b/addons/skin.confluence/language/Finnish/strings.xml
+index ed70f2c..5191c52 100644
+--- a/addons/skin.confluence/language/Finnish/strings.xml
++++ b/addons/skin.confluence/language/Finnish/strings.xml
+@@ -164,9 +164,22 @@
+ <string id="31406">[B]Muokkaa järjestelmän asetuksia[/B][CR][CR]Aseta ja kalibroi näyttö · Määritä äänilähtö · Aseta kauko-ohjaus[CR]Aseta sähkönsäästöasetukset · Ota debuggaus käyttöön · Muokkaa pääkäyttäjän lukituksia</string>
+ <string id="31407">[B]Muokkaa ulkoasun asetuksia[/B][CR][CR]Aseta Confluence-ulkoasun asetukset · Lisää ja poista päävalikon kohteita[CR]Vaihda ulkoasun taustakuvia</string>
+ <string id="31408">[B]Muokkaa lisäosia[/B][CR][CR]Hallitse asennettuja lisäosia · Valitse ja asenna lisäosia xbmc.org:sta[CR]Muokkaa lisäosien asetuksia</string>
++ <string id="31409">[B]Muokkaa TV-asetuksia[/B][CR][CR]Aseta kanavien toiston asetukset · Hallitse ohjelmaoppaan asetuksia</string>
+
+ <string id="31421">Valitse XBMC-käyttäjäprofiili[CR]kirjautuaksesi sisään</string>
+
++ <!-- Extra Unified PVR labels -->
++ <string id="31500">Nauhoitusajastukset</string>
++ <string id="31501">Kellonaika</string>
++ <string id="31502">TV-lähetys</string>
++ <string id="31503">Lisää ryhmä</string>
++ <string id="31504">Muuta ryhmän nimeä</string>
++ <string id="31505">Poista ryhmä</string>
++ <string id="31506">Saatavilla olevat[CR]ryhmät</string>
++ <string id="31509">Kanavaryhmä</string>
++ <string id="31510">Ajastettu</string>
++ <string id="31511">Kanavan valinnat</string>
++
+ <!-- Weather plugin -->
+ <string id="31900">Sääkartat</string>
+ <string id="31901">36 tunnin ennuste</string>
+diff --git a/addons/skin.confluence/language/French/strings.xml b/addons/skin.confluence/language/French/strings.xml
+index 293d29a..674225a 100644
+--- a/addons/skin.confluence/language/French/strings.xml
++++ b/addons/skin.confluence/language/French/strings.xml
+@@ -161,9 +161,22 @@
+ <string id="31406">[B]CONFIGURATION DES PARAMÈTRES SYSTÈME[/B][CR][CR]Configurer les sorties vidéo et audio · Configurer les périphériques de contrôle[CR]Définir les options de gestion d'énergie · Activer le débogage · Configurer la sécurité</string>
+ <string id="31407">[B]CONFIGURATION DES PARAMÈTRES THÈME[/B][CR][CR]Configuration du skin Confluence · Ajouter et supprimer des éléments du menu d'accueil[CR]Changer les arrière-plans du skin</string>
+ <string id="31408">[B]CONFIGURATION DES ADD-ONS[/B][CR][CR]Gérer les Add-ons installés· Recherche et installation d'Add-ons depuis xbmc.org[CR]Modifier les paramètres Add-on</string>
++ <string id="31409">[B]CONFIGURATION DES PARAMÈTRES TV[/B][CR][CR]Changer l'affichage plein écran · Gestion des paramètres de l'EPG</string>
+
+ <string id="31421">Sélectionnez un profil d'utilisateur[CR]pour vous connecter et continuer</string>
+-
++
++ <!-- Extra Unified PVR labels -->
++ <string id="31500">Programmations</string>
++ <string id="31501">Date/Heure</string>
++ <string id="31502">TV</string>
++ <string id="31503">Ajouter un Groupe</string>
++ <string id="31504">Renommer le Groupe</string>
++ <string id="31505">Supprimer le Groupe</string>
++ <string id="31506">Groupes[CR]Disponibles</string>
++ <string id="31509">Groupe de chaînes</string>
++ <string id="31510">Programmer</string>
++ <string id="31511">Paramètres de la chaîne</string>
++
+ <!-- Weather plugin -->
+ <string id="31900">Cartes Météo</string>
+ <string id="31901">Prévisions sur 36 Heures</string>
+@@ -175,5 +188,4 @@
+ <string id="31907">Bulletin vidéo [COLOR=grey2](affichage en plein écran)[/COLOR]</string>
+ <string id="31908">Risque de pluie</string>
+ <string id="31909">Récupération des prévisions météo...</string>
+-
+ </strings>
+diff --git a/addons/skin.confluence/language/German/strings.xml b/addons/skin.confluence/language/German/strings.xml
+index e698959..855f347 100644
+--- a/addons/skin.confluence/language/German/strings.xml
++++ b/addons/skin.confluence/language/German/strings.xml
+@@ -6,7 +6,8 @@
+ <string id="31003">Power Optionen</string>
+ <string id="31004">In Arbeit...</string>
+ <string id="31005">Verberge Info</string>
+- <string id="31006">Anzeige Optionen</string>
++ <string id="31006">Ansichtsoptionen</string>
++
+ <string id="31007">Plugins</string>
+ <string id="31008">Vollbild</string>
+
+@@ -36,8 +37,8 @@
+ <string id="31047">Aktuelle Einstellung</string>
+ <string id="31048">Visualisierungs Einstellungen</string>
+ <string id="31049">Schluss Zeit</string>
+- <string id="31050">Sortieren: Aufsteigend</string>
+- <string id="31051">Sortieren: Absteigend</string>
++ <string id="31050">Sortiere: Aufsteigend</string>
++ <string id="31051">Sortiere: Absteigend</string>
+
+ <string id="31055">Öffne Wiedergabeliste</string>
+ <string id="31056">Sichere Wiedergabeliste</string>
+@@ -66,7 +67,7 @@
+ <string id="31116"></string>
+ <string id="31117">Zeige kürzlich hinzugefügte Videos</string>
+ <string id="31118">Hauptfenster Programme-Untermenü</string>
+- <string id="31119">Verstecke Hintergrund Fanart</string>
++ <string id="31119">Verberge Hintegrund Fanart</string>
+ <string id="31120">BUTTON BESCHRIFTUNG</string>
+ <string id="31121"></string>
+ <string id="31122">Wetter Seite</string>
+@@ -86,6 +87,8 @@
+ <string id="31136">Homefenster Bilder-Untermenü</string>
+
+
++ <string id="31133">Untertitel Add-on</string>
++
+ <string id="31140">Musik OSD</string>
+ <string id="31141">Video OSD</string>
+
+@@ -148,9 +151,23 @@
+ <string id="31405">[B]KONFIGURIERE NETZWERK EINSTELLUNGEN[/B][CR][CR]Einrichten der Steuerung von XBMC via UPnP und HTTP · Konfiguriere Datei Zugriff[CR]Setze Internet Zugriffs Optionen</string>
+ <string id="31406">[B]KONFIGURIERE SYSTEM EINSTELLUNGEN[/B][CR][CR]Setze und kalibriere Displays · Konfiguriere Audioausgabe · Setze Fernbedienungs Einstellungen · Setze Energiespar Optionen · Aktiviere Debugging · Einrichten Master Sperre</string>
+ <string id="31407">[B]KONFIGURIERE SKIN EINSTELLUNGEN[/B][CR][CR]Einrichten des Confluence Skin · Hinzufügen und entfernen der Home Menü Einträge[CR]Wechseln der Skin Hintergründe</string>
+- <string id="31408">[B]KONFIGURIERE ADD-ONS[/B][CR][CR]Organisiere die installierten Add-ons · Installiere Add-ons von xbmc.org[CR]Add-on Einstellungen anpassen</string>
++ <string id="31408">[B]KONFIGURIERE ERWEITERUNGEN[/B][CR][CR]Organisiere die installierten Add-ons · Installiere Add-ons von xbmc.org[CR]Add-on Einstellungen anpassen</string>
++ <string id="31409">[B]KONFIGURIERE TV EINSTELLUNGEN[/B][CR][CR]Verändere Vollbild Modus · Verwalte EPG Daten Einstellungen</string>
++
+ <string id="31421">Wähle Dein XBMC Benutzer Profil[CR]Zum Anmelden und Weitermachen</string>
+
++ <!-- Extra Unified PVR labels -->
++ <string id="31500">Recording Timers</string>
++ <string id="31501">Scheduled Time</string>
++ <string id="31502">Live TV</string>
++ <string id="31503">Gruppe hinzufügen</string>
++ <string id="31504">Gruppe umbenennen</string>
++ <string id="31505">Gruppe löschen</string>
++ <string id="31506">Verfügbare[CR]Gruppen</string>
++ <string id="31509">Kanalgruppe</string>
++ <string id="31510">Timer Set</string>
++ <string id="31511">Kanal Optionen</string>
++
+ <string id="31900">Wetterkarten</string>
+ <string id="31901">36 Stunden Prognose</string>
+ <string id="31902">Stündliche Prognose</string>
+diff --git a/addons/skin.confluence/language/Greek/strings.xml b/addons/skin.confluence/language/Greek/strings.xml
+index cba0444..f3f7754 100644
+--- a/addons/skin.confluence/language/Greek/strings.xml
++++ b/addons/skin.confluence/language/Greek/strings.xml
+@@ -1,5 +1,5 @@
+-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+-<strings>
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<strings> <!-- Last update (by CutSickAss): 23/03/2012 -->
+ <!-- Misc labels -->
+ <string id="31000">Αλλαγή</string>
+ <string id="31001">Αγαπητό</string>
+@@ -7,16 +7,17 @@
+ <string id="31003">Επιλογές λειτουÏγίας</string>
+ <string id="31004">Απασχολημένο...</string>
+ <string id="31005">ΑπόκÏυψη ΠληÏοφοÏιών</string>
++ <string id="31006">ΠÏοβολή Επιλογών</string>
++ <string id="31007">Plugin</string>
++ <string id="31008">ΠλήÏης Οθόνη</string>
+
+- <string id="31007">Επεκτάσεις</string>
+-
+- <string id="31020">ΠÏόσφατα αναÏτημένα</string>
+- <string id="31021">ΑÏχεία βίντεο</string>
+- <string id="31022">ΑÏχεία μουσικής</string>
++ <string id="31020">ΠÏοστέθηκαν Ï€Ïόσφατα</string>
++ <string id="31021">Βίντεο - ΑÏχεία</string>
++ <string id="31022">Μουσική - ΑÏχεία</string>
+ <string id="31023">ΑναπαÏαγωγή</string>
+ <string id="31024">Σελίδα</string>
+ <string id="31025">Αντικείμενα</string>
+- <string id="31026">ΠÏόσθετες επιλογές</string>
++ <string id="31026">ΔιάφοÏες επιλογές</string>
+ <string id="31027">Τοποθεσία</string>
+
+ <!-- View Type labels -->
+@@ -28,124 +29,152 @@
+ <string id="31033">ΠληÏοφοÏίες</string>
+
+ <!-- Extra labels -->
+- <string id="31040">ΤώÏα αναπαÏάγεται</string>
++ <string id="31040">ΤώÏα Εκτελείται</string>
+
+- <string id="31042">ΑναπαÏάγεται</string>
++ <string id="31042">ΑÎΑΠΑΡΑΓΩΓΗ</string>
+ <string id="31043">ΠΑΥΣΗ</string>
+- <string id="31044">ΠΡΟΩΘΗΣΗ ΜΠΡΟΣΤΑ</string>
+- <string id="31045">ΠΡΟΩΘΗΣΗ ΠΙΣΩ</string>
+- <string id="31046">Ιδιότητες ήχου</string>
+- <string id="31047">ΤÏέχουσα απεικονίση</string>
+- <string id="31048">Απεικονίσεις</string>
++ <string id="31044">ΜΠΡΟΣΤΑ</string>
++ <string id="31045">ΠΙΣΩ</string>
++ <string id="31046">Ιδιότητες Ήχου</string>
++ <string id="31047">ΤÏέχον ΠÏοκαθοÏισμένο</string>
++ <string id="31048">ΠÏοκαθοÏισμένες Οπτικοποιήσεις</string>
+ <string id="31049">ΧÏόνος λήξης</string>
++ <string id="31050">Ταξ.: ΑÏξουσα</string>
++ <string id="31051">Ταξ.: Φθίνουσα</string>
+
+
+ <!-- Playlist Editor labels -->
+ <string id="31055">Άνοιγμα λίστας αναπαÏαγωγής</string>
+ <string id="31056">Αποθήκευση λίστας αναπαÏαγωγής</string>
+ <string id="31057">Κλείσιμο λίστας αναπαÏαγωγής</string>
+- <string id="31058">ΣÏστημα αÏχείων μουσικής</string>
++ <string id="31058">Μουσικά αÏχεία συστήματος</string>
+ <string id="31059">ΤÏέχουσα λίστα αναπαÏαγωγής</string>
+- <string id="31060">Αυτό το αÏχείο είναι στοιβαγμένο, επιλέξτε το κομμάτι που επιθυμείτε για αναπαÏαγωγή.</string>
+- <string id="31061">Επιλέχθηκε η Ï„Ïέχουσα</string>
+-
++ <string id="31060">Αυτό το αÏχείο είναι στοιβαγμένο, επιλέξτε από που να γίνει αναπαÏαγωγή.</string>
++ <string id="31061">ΤÏέχουσα Επιλογή</string>
++
+ <!-- Skin Settings labels -->
+ <string id="31100"></string>
+ <string id="31101">Επιλογές αÏχικής οθόνης</string>
+ <string id="31102">ΥπόβαθÏο</string>
+- <string id="31103">Εμφάνιση "ΠαÏση" κατά την παÏουσίαση φωτογÏαφιών</string>
+- <string id="31104">ΑναπαÏαγωγή Διαφημιστικών ταινιών σε παÏάθυÏο[COLOR=grey3](Μόνο οι πληÏοφοÏίες βίντεο)[/COLOR]</string>
+- <string id="31105"></string>
+- <string id="31106">ΠÏόσθετες επιλογές</string>
++ <string id="31103">Εμφάνιση 'ΠαÏσης' σε παÏουσίαση διαφανειών</string>
++ <string id="31104">ΑναπαÏαγωγή Διαφημιστικών σε παÏάθυÏο [COLOR=grey3](Μόνο οι πληÏοφοÏίες βίντεο)[/COLOR]</string>
++ <string id="31105">Το πλήκτÏο "Βίντεο" πάντα οδηγεί στα "ΑÏχεία" βίντεο</string>
++ <string id="31106">ΔιάφοÏες επιλογές</string>
+ <string id="31107">ΑπόκÏυψη των αναγνώσιμων σημαιών από τα ονόματα αÏχείων βίντεο [COLOR=grey3](Blu-ray, HD-DVD)[/COLOR]</string>
+- <string id="31108">ΑπόκÏυψη πλήκτÏων από το κεντÏικό Î¼ÎµÎ½Î¿Ï ÎµÏ€Î¹Î»Î¿Î³ÏŽÎ½</string>
++
++ <string id="31108">ΑπόκÏυψη πλήκτÏων του ΚεντÏÎ¹ÎºÎ¿Ï ÎœÎµÎ½Î¿Ï</string>
+ <string id="31109">ΥπόβαθÏα αÏχικής οθόνης</string>
+- <string id="31110">ΕπεξεÏγασία υπόβαθÏου για το αÏχικό πλήκτÏο</string>
++ <string id="31110">ΕπεξεÏγασία υποβάθÏου για το αÏχικό πλήκτÏο</string>
+ <string id="31111">ΑπόκÏυψη</string>
+ <string id="31112">Επιλογές</string>
+- <string id="31113">Εικόνα</string>
+- <string id="31114">Φάκελος</string>
++ <string id="31113">Μία Εικόνα</string>
++ <string id="31114">Πολλαπλές εικόνες</string>
+ <string id="31115">ΠÏοσαÏμογή</string>
+ <string id="31116"></string>
+ <string id="31117">Εμφάνιση των Ï€Ïόσφατα Ï€Ïοστιθέμενων βίντεο</string>
+- <string id="31118">Î¥Ï€Î¿Î¼ÎµÎ½Î¿Ï Î±Ïχικής οθόνης</string>
+- <string id="31119"></string>
++ <string id="31118">Î¥Ï€Î¿Î¼ÎµÎ½Î¿Ï Î•Ï†Î±Ïμογών αÏχικής οθόνης</string>
++ <string id="31119">ΑπόκÏυψη Fanart υποβάθÏου</string>
+ <string id="31120">ΕΤΙΚΕΤΑ ΠΛΗΚΤΡΟΥ</string>
+- <string id="31121"></string>
+- <string id="31122">Οθόνη καιÏοÏ</string>
+- <string id="31123">ΧÏήση του "Αφίσα" αντί του "Πανό" για τις τηλεοπτικές σειÏές</string>
+- <string id="31124">Εμφάνιση ως υπόβαθÏο το βίντεο που "ΤώÏα αναπαÏάγεται"</string>
+- <string id="31125">Εμφάνιση ως υπόβαθÏο η απεικόνιση που "ΤώÏα αναπαÏάγεται"</string>
++ <string id="31121"></string> <!-- blanked 2010-11-12 -->
++ <string id="31122">Σελίδα ΚαιÏοÏ</string>
++ <string id="31123">ΧÏήση "Αφισών" αντί για "Πανό" για τις τηλεοπτικές σειÏές</string>
++ <string id="31124">Εμφάνιση ως υπόβαθÏο το βίντεο που "ΤώÏα Εκτελείται"</string>
++ <string id="31125">Εμφάνιση ως υπόβαθÏο η οπτικοποίηση που "ΤώÏα Εκτελείται"</string>
+
+- <string id="31126"></string>
+- <string id="31127"></string>
+- <string id="31128">Στίχοι XBMC</string>
+- <string id="31129"></string>
+- <string id="31130"></string>
++ <string id="31126">ΑναπαÏαγωγή Ï„Ïαγουδιών σειÏών (Ï€Ïόσθετο TvTunes)</string>
++ <string id="31127">TvTunes</string>
++ <string id="31128">Στίχοι</string>
++ <string id="31129"></string> <!-- blanked 2010-11-12 -->
++ <string id="31130"></string> <!-- blanked 2010-11-12 -->
++ <string id="31131"></string> <!-- blanked 2010-12-23 -->
++ <string id="31132">ΠÏόσθετο στίχων</string>
++ <string id="31133">ΠÏόσθετο υποτίτλων</string>
++ <string id="31134">Î¥Ï€Î¿Î¼ÎµÎ½Î¿Ï Î’Î¯Î½Ï„ÎµÎ¿ αÏχικής οθόνης</string>
++ <string id="31135">Î¥Ï€Î¿Î¼ÎµÎ½Î¿Ï ÎœÎ¿Ï…ÏƒÎ¹ÎºÎ®Ï‚ αÏχικής οθόνης</string>
++ <string id="31136">Î¥Ï€Î¿Î¼ÎµÎ½Î¿Ï Î•Î¹ÎºÏŒÎ½Ï‰Î½ αÏχικής οθόνης</string>
+
+- <string id="31140">Aπεικόνισεις οθόνης (OSD) Μουσική</string>
+- <string id="31141">Aπεικόνισεις οθόνης (OSD) Βίντεο</string>
++ <string id="31140">Aπεικονίσεις οθόνης (OSD) Μουσικής</string>
++ <string id="31141">Aπεικονίσεις οθόνης (OSD) Βίντεο</string>
+
+ <!-- Script labels -->
+ <string id="31200">ΣυντομεÏσεις</string>
+ <string id="31201">ΚατηγοÏίες</string>
+- <string id="31202">Εμφάνιση συντελεστών</string>
++ <string id="31202">Εμφάνιση διανομής</string>
+ <string id="31203">Επιλογή Ï„ÏαγουδιοÏ</string>
+ <string id="31204">Επιλογή Συνδέσμων</string>
+- <string id="31205">ΠÏοέλευση στίχων Ï„ÏαγουδιοÏ</string>
++ <string id="31205">ΠÏοέλευση στίχων</string>
+
+ <!-- Extra labels -->
+- <string id="31300">ΤÏέχουσες συνθήκες</string>
++ <string id="31300">ΤÏέχουσα θεÏμοκÏασία</string>
+ <string id="31301">Τελευταία ενημέÏωση</string>
+ <string id="31302">ΜενοÏ</string>
+- <string id="31303"></string>
+- <string id="31304">ΦωτογÏαφίες</string>
+- <string id="31305">Δεν εντοπίσθηκε δίσκος πολυμέσων</string>
++ <string id="31303">Πηγή δεδομένων</string>
++ <string id="31304">Εικόνα</string>
++ <string id="31305">Δεν εντοπίστηκε οπτικός δίσκος</string>
+ <string id="31306">Εξαγωγή</string>
+ <string id="31307">ΑπόκÏυψη Fanart</string>
+ <string id="31308">ΛεπτομέÏειες ταινίας</string>
+- <string id="31309">Δεσμευμένη μνήμη:</string>
++ <string id="31309">ΧÏήση μνήμης:</string>
+ <string id="31310">ΑÏιθμός κομματιοÏ</string>
+- <string id="31311">Μη διαθέσιμη[CR][CR]εικόνα Fanart[CR][CR]Για να οÏίσετε πιέστε το πλήκτÏο</string>
+- <string id="31312">ΕνεÏγός καταγÏαφέας</string>
+- <string id="31313">Επιλογή καταγÏαφέα</string>
+- <string id="31314">Επιλόγες αναζήτησης πεÏιεχομένου</string>
++ <string id="31311">Εικόνα Fanart[CR][CR]Μη διαθέσιμη[CR][CR] Πιέστε το πλήκτÏο για να θέσετε</string>
++ <string id="31312">ΤÏέχον Scraper</string>
++ <string id="31313">Επιλογή Scraper</string>
++ <string id="31314">Επιλογές ΣάÏωσης ΠεÏιεχομένου</string>
+ <string id="31315">Βασικό</string>
+ <string id="31316"></string>
+ <string id="31317">ΟÏισμός διαδÏομής Fanart</string>
+ <string id="31318">ΜικÏÏŒ Fanart</string>
+ <string id="31319">Επιλεγμένο Ï€Ïοφίλ</string>
+- <string id="31320">Τελευταία είσοδος στο</string>
+- <string id="31321">Επιλογέας Ï„Ïαγουδιών Karaoke</string>
+- <string id="31322">ΑιθέÏιο</string>
+- <string id="31323">ΠÏόσφατες ταινίες</string>
+- <string id="31324">ΠÏόσφατα επεισόδια</string>
++ <string id="31320">Τελευταία είσοδος</string>
++ <string id="31321">Επιλογέας Ï„ÏÎ±Î³Î¿Ï…Î´Î¹Î¿Ï Karaoke</string>
++ <string id="31322">ΠÏώτη ΠÏοβολή</string>
++ <string id="31323">ÎεώτεÏες Ταινίες</string>
++ <string id="31324">ÎεώτεÏα Επεισόδια</string>
+ <string id="31325">Επιλογές λίστας αναπαÏαγωγής</string>
+ <string id="31326">ΔημιουÏγήθηκε</string>
+ <string id="31327">Ανάλυση</string>
+- <string id="31328">ΠÏοστέθηκε Ï€Ïόσφατα</string>
+- <string id="31329">[B]ΕφαÏμογή χÏονοδιακόπτη![/B] [COLOR=grey2] - Αυτόματος τεÏματισμός λειτουÏγίας σε[/COLOR]</string>
+- <string id="31330">Πιέστε το πλήκτÏο για να αναπαÏαχθεί[CR][CR]το διαφημιστικό ταινίας</string>
++ <string id="31328">ΠÏοστέθηκαν ΠÏόσφατα</string>
++ <string id="31329">[B]ΕφαÏμογή χÏονοδιακόπτη![/B] [COLOR=grey2] - Αυτόματος τεÏματισμός συστήματος σε[/COLOR]</string>
++ <string id="31330">Πιέστε το πλήκτÏο για αναπαÏαγωγή[CR][CR]του Î”Î¹Î±Ï†Î·Î¼Î¹ÏƒÏ„Î¹ÎºÎ¿Ï Î¤Î±Î¹Î½Î¯Î±Ï‚</string>
++ <string id="31331">ΛεπτομέÏειες Άλμπουμ</string>
+
+ <!-- Video and Music OSD Labels -->
+ <string id="31351">ΠαÏση</string>
+ <string id="31352">Διακοπή</string>
+- <string id="31353">ΠÏοώθηση μπÏοστά</string>
+- <string id="31354">ΠÏοώθηση πίσω</string>
++ <string id="31353">ΜπÏοστά</string>
++ <string id="31354">Πίσω</string>
+ <string id="31355">ÎœÎµÎ½Î¿Ï Ï„Î±Î¹Î½Î¯Î±Ï‚</string>
+ <string id="31356">Λήψη υποτίτλων</string>
+ <string id="31357"></string>
+
++ <!-- Skin Fontsets -->
++ <string id="31390">ΠÏοεπιλογή</string>
++ <string id="31391">ΠÏοεπιλογή χωÏίς Κεφαλαία</string>
++ <string id="31392">Βασισμένη σε Arial</string>
++
+ <!-- Description Labels -->
+- <string id="31400">[B]ΠΡΟΣΑΡΜΟΓΗ ΡΥΘΜΙΣΕΩΠΕΞΑΤΟΜΙΚΕΥΣΗΣ[/B][CR][CR]Αλλαγή κελÏφους · Αλλαγή χώÏας και γλώσσας συστήματος · ΟÏισμός των γενικών επιλόγων εμφάνισης[CR]ΠÏοσαÏμογή της Ï€ÏοφÏλαξης οθόνης</string>
+- <string id="31401">[B]ΠΡΟΣΑΡΜΟΓΗ ΡΥΘΜΙΣΕΩΠΒΙÎΤΕΟ[/B][CR][CR]ΔιαχείÏιση της συλλογής βίντεο · ΔιαμόÏφωση γÏαμματοσειÏάς υποτίτλων[CR]ΠÏοσαÏμογή της ποιότητας ανάλυσης των βίντεο και ÏÏθμιση του αναπαÏαγωγέα</string>
+- <string id="31402">[B]ΠΡΟΣΑΡΜΟΓΗ ΡΥΘΜΙΣΕΩΠΜΟΥΣΙΚΗΣ[/B][CR][CR]ΔιαχείÏιση της συλλογής Ï„Ïαγουδιών · Eπιλογές εμφάνισης της λίστας μουσικής[CR]ΠÏοσαÏμογή Ïυθμίσεων και αλλαγή γÏαμματοσειÏάς για Karaoke</string>
+- <string id="31403">[B]ΠΡΟΣΑΡΜΟΓΗ ΡΥΘΜΙΣΕΩΠΦΩΤΟΓΡΑΦΙΩÎ[/B][CR][CR]ΡÏθμιση επιλογών Ï€Ïοβολής φωτογÏαφιών · ΡÏθμιση παÏουσίασης διαφανειών</string>
+- <string id="31404">[B]ΠΡΟΣΑΡΜΟΓΗ ΡΥΘΜΙΣΕΩΠΚΑΙΡΟΥ[/B][CR][CR]ΟÏισμός Ï„Ïιών πόλεων για τις οποίες θα γίνει Ï€Ïόγνωση καιÏοÏ</string>
+- <string id="31405">[B]ΠΡΟΣΑΡΜΟΓΗ ΡΥΘΜΙΣΕΩΠΔΙΚΤΥΟΥ[/B][CR][CR]ΠÏοσαÏμογή Ï€Ïωτοκόλλων επικοινωνίας και μεταφοÏάς κοινόχÏηστων αÏχείων[CR]ΡÏθμιση κοινόχÏηστων αÏχείων πολυμέσων · ΟÏισμός Ïυθμίσεων δικτÏου</string>
+- <string id="31406">[B]ΠΡΟΣΑΡΜΟΓΗ ΡΥΘΜΙΣΕΩΠΣΥΣΤΗΜΑΤΟΣ[/B][CR][CR]Βαθμονόμιση γÏÎ±Ï†Î¹ÎºÎ¿Ï Ï€ÎµÏιβάλλοντος · ΡÏθμιση Ï…Î»Î¹ÎºÎ¿Ï Î®Ï‡Î¿Ï… · Εγκατάσταση τηλεχειÏιστηÏίων[CR]Αλλαγή και Ï„Ïοποποίηση του Ï„Ïόπου τεÏÎ¼Î±Ï„Î¹ÏƒÎ¼Î¿Ï Ï„Î¿Ï… Ï€ÏογÏάμματος · ΡÏθμιση κεντÏÎ¹ÎºÎ¿Ï ÎºÎ»ÎµÎ¹Î´ÏŽÎ¼Î±Ï„Î¿Ï‚</string>
+- <string id="31407">[B]ΠΡΟΣΑΡΜΟΓΗ ΡΥΘΜΙΣΕΩΠΚΕΛΥΦΟΥΣ[/B][CR][CR]Αλλαγή και Ï„Ïοποποίηση υπόβαθÏων · ΠÏοσθήκη ή απομάκÏυνση πλήκτÏων από την αÏχική οθόνη[CR]ΔημιουÏγία συντομεÏσεων Ï€Ïος τα scripts Ï€Ïοκειμένου να ενσωματωθοÏν στο κέλυφος</string>
+- <string id="31408">[B]ΠΡΟΣΑΡΜΟΓΗ ΡΥΘΜΙΣΕΩΠΠΡΟΣΘΕΤΩÎ[/B][CR][CR]ΔιαχείÏιση των ΠÏόσθετων σας · Αναζήτηση και εγκατάσταση ΠÏόσθετων από το XBMC.org[CR]ΤÏοποποίηση Ïυθμίσεων των ΠÏόσθετων</string>
++ <string id="31400">[B]ΡΥΘΜΙΣΕΙΣ ΕΞΑΤΟΜΙΚΕΥΣΗΣ[/B][CR][CR]Αλλαγή κελÏφους · Αλλαγή χώÏας και γλώσσας συστήματος · Επιλογές καταλόγου αÏχείων[CR]ΠÏοσαÏμογή της Ï€ÏοφÏλαξης οθόνης</string>
++ <string id="31401">[B]ΡΥΘΜΙΣΕΙΣ ΒΙÎΤΕΟ[/B][CR][CR]ΔιαχείÏιση της συλλογής βίντεο · ΡÏθμιση αναπαÏαγωγής βίντεο · Επιλογές καταλόγου ταινιών[CR]ΡÏθμιση γÏαμματοσειÏάς υποτίτλων</string>
++ <string id="31402">[B]ΡΥΘΜΙΣΕΙΣ ΜΟΥΣΙΚΗΣ[/B][CR][CR]ΔιαχείÏιση της συλλογής Ï„Ïαγουδιών · ΡÏθμιση αναπαÏαγωγής μουσικής · Επιλογές καταλόγου μουσικής[CR]ΡÏθμιση υποβολής Ï„ÏÎ±Î³Î¿Ï…Î´Î¹Î¿Ï Â· Επιλογές karaoke</string>
++ <string id="31403">[B]ΡΥΘΜΙΣΕΙΣ ΕΙΚΟÎΩÎ[/B][CR][CR]Επιλογές καταλόγου εικόνων · ΡÏθμιση παÏουσίασης διαφανειών</string>
++ <string id="31404">[B]ΡΥΘΜΙΣΕΙΣ ΚΑΙΡΟΥ[/B][CR][CR]ΟÏισμός Ï„Ïιών πόλεων για τις οποίες θα γίνεται Ï€Ïόγνωση καιÏοÏ</string>
++ <string id="31405">[B]ΡΥΘΜΙΣΕΙΣ ΔΙΚΤΥΟΥ[/B][CR][CR]ΡÏθμιση χειÏÎ¹ÏƒÎ¼Î¿Ï Ï„Î¿Ï… XBMC μέσω UPnP και HTTP · ΡÏθμιση κοινής χÏήσης αÏχείων[CR]ΟÏισμός Ïυθμίσεων διαδικτÏου</string>
++ <string id="31406">[B]ΡΥΘΜΙΣΕΙΣ ΣΥΣΤΗΜΑΤΟΣ[/B][CR][CR]ΔιαμόÏφωση και βαθμονόμηση οθονών · ΡÏθμιση εξόδου ήχου · Εγκατάσταση τηλεχειÏιστηÏίων · ΡÏθμιση εξοικονόμησης ενέÏγειας · ΕνεÏγοποίηση καταγÏαφής σφαλμάτων (debug) · ΡÏθμιση κεντÏÎ¹ÎºÎ¿Ï ÎºÎ»ÎµÎ¹Î´ÏŽÎ¼Î±Ï„Î¿Ï‚</string>
++ <string id="31407">[B]ΡΥΘΜΙΣΕΙΣ ΚΕΛΥΦΟΥΣ[/B][CR][CR]ΡÏθμιση του κελÏφους Confluence · ΠÏοσθήκη και απομάκÏυνση αντικειμένων από την αÏχική οθόνη[CR]Αλλαγή υποβάθÏου κελÏφους</string>
++ <string id="31408">[B]ΡΥΘΜΙΣΕΙΣ ΠΡΟΣΘΕΤΩÎ[/B][CR][CR]ΔιαχείÏιση εγκατεστημένων ΠÏόσθετων · Αναζήτηση και εγκατάσταση ΠÏόσθετων από το xbmc.org[CR]ΤÏοποποίηση Ïυθμίσεων των ΠÏόσθετων</string>
++
++ <string id="31421">Επιλέξτε Ï€Ïοφίλ χÏήστη για το XBMC[CR]για να συνδεθείτε και να συνεχίσετε</string>
+
+- <string id="31421">Επιλέξτε Ï€Ïοφίλ χÏήστη για το XBMC[CR]για να συνδεθείτε και συνεχίστε</string>
+-</strings>
++ <!-- Weather plugin -->
++ <string id="31900">ΧάÏτες ΚαιÏοÏ</string>
++ <string id="31901">36ωÏη ΠÏόβλεψη</string>
++ <string id="31902">ΩÏιαία ΠÏόβλεψη</string>
++ <string id="31903">ΠÏόβλεψη ΣαββατοκÏÏιακου</string>
++ <string id="31904">ΠÏόβλεψη 10 ημεÏών</string>
++ <string id="31905">ΠÏόβλεψη</string>
++ <string id="31906">ΧάÏτες &amp; Βίντεο</string>
++ <string id="31907">Βίντεο ΠÏόβλεψης [COLOR=grey2](ΠλήÏης Οθόνη)[/COLOR]</string>
++ <string id="31908">Πιθανότητα ΥετοÏ</string>
++ <string id="31909">Ανάκτηση Ï€Ïόβλεψης καιÏοÏ...</string>
++
++ </strings>
+diff --git a/addons/skin.confluence/language/Japanese/strings.xml b/addons/skin.confluence/language/Japanese/strings.xml
+index da68259..f80b29c 100644
+--- a/addons/skin.confluence/language/Japanese/strings.xml
++++ b/addons/skin.confluence/language/Japanese/strings.xml
+@@ -1,148 +1,182 @@
+ <?xml version="1.0" encoding="utf-8" standalone="yes"?>
+ <!--Language file translated with Team XBMC Translator-->
+-<!--Translator: kyouhei-->
+-<!--Twitter: @kyouhei-->
+-<!--Date of translation: 02/20/2011-->
++<!--Translator: Kohji 'Shaolin' MATSUBAYASHI (kohji405mi16)-->
++<!--Email: shaolin@vinelinux.org-->
++<!--Date of translation: 03/31/2012-->
++<!--$Revision$-->
+ <strings>
+- <string id="31000">Change Your</string>
++ <string id="31000"></string>
+ <string id="31001">Love</string>
+ <string id="31002">Hate</string>
+- <string id="31003">é›»æºè¨­å®š</string>
++ <string id="31003">é›»æºã‚ªãƒ—ション</string>
+ <string id="31004">処ç†ä¸­...</string>
+- <string id="31005">Hide INFO</string>
+- <string id="31007">Plugins</string>
+- <string id="31020">最近追加</string>
++ <string id="31005">情報を隠ã™</string>
++ <string id="31006">表示オプション</string>
++
++ <string id="31007">プラグイン</string>
++
++ <string id="31020">最新ã®è¿½åŠ </string>
+ <string id="31021">ビデオ - ファイル</string>
+ <string id="31022">ミュージック - ファイル</string>
+- <string id="31023">Playing</string>
++ <string id="31023">å†ç”Ÿä¸­</string>
+ <string id="31024">ページ</string>
+ <string id="31025">アイテム</string>
+- <string id="31026">Misc Options</string>
+- <string id="31027">Location</string>
+- <string id="31028">Poster Wrap</string>
+- <string id="31029">Fanart</string>
+- <string id="31030">Full list</string>
+- <string id="31031">Pic Thumbs</string>
+- <string id="31032">Image Wrap</string>
+- <string id="31033">Info</string>
+- <string id="31040">Now Playing</string>
++ <string id="31026">ãã®ä»–ã®ã‚ªãƒ—ション</string>
++ <string id="31027">地域</string>
++
++ <!-- View Type labels -->
++ <string id="31028">ãƒã‚¹ã‚¿ãƒ¼ãƒ©ãƒƒãƒ—</string>
++ <string id="31029">ファンアート</string>
++ <string id="31030">フルリスト</string>
++ <string id="31031">サムãƒãƒ¼ãƒ«</string>
++ <string id="31032">イメージラップ</string>
++ <string id="31033">情報</string>
++
++ <!-- Extra labels -->
++ <string id="31040">ç¾åœ¨å†ç”Ÿä¸­</string>
++
+ <string id="31042">å†ç”Ÿä¸­</string>
+ <string id="31043">é™æ­¢ä¸­</string>
+ <string id="31044">æ—©é€ã‚Š</string>
+ <string id="31045">巻戻ã—</string>
+- <string id="31046">Audio Properties</string>
+- <string id="31047">Current Preset</string>
++ <string id="31046">オーディオ設定</string>
++ <string id="31047">ç¾åœ¨ã®è¨­å®š</string>
+ <string id="31048">Visualization Presets</string>
+ <string id="31049">End Time</string>
+- <string id="31055">Open playlist</string>
+- <string id="31056">Save playlist</string>
+- <string id="31057">Close playlist</string>
+- <string id="31058">System music files</string>
+- <string id="31059">Current playlist</string>
+- <string id="31060">This file is stacked, select the part you want to play from.</string>
+- <string id="31061">Current Selected</string>
+- <string id="31100">
+- </string>
+- <string id="31101">メニュー画é¢è¨­å®š</string>
++ <string id="31050">ソート: 昇順</string>
++ <string id="31051">ソート: é™é †</string>
++
++ <!-- Playlist Editor labels -->
++ <string id="31055">プレイリストを開ã</string>
++ <string id="31056">プレイリストをä¿å­˜</string>
++ <string id="31057">プレイリストを閉ã˜ã‚‹</string>
++ <string id="31058">システム音楽ファイル</string>
++ <string id="31059">ç¾åœ¨ã®ãƒ—レイリスト</string>
++ <string id="31060">ã“ã®ãƒ•ã‚¡ã‚¤ãƒ«å†…ã‹ã‚‰å†ç”Ÿã—ãŸã„パートをé¸æŠžã—ã¦ãã ã•ã„。</string>
++ <string id="31061">ç¾åœ¨é¸æŠžä¸­</string>
++
++ <!-- Skin Settings labels -->
++ <string id="31100"></string>
++ <string id="31101">ホーム画é¢ã‚ªãƒ—ション</string>
+ <string id="31102">背景</string>
+- <string id="31103">ピクãƒãƒ£ã‚¹ãƒ©ã‚¤ãƒ‰ã‚·ãƒ§ãƒ¼ã§"é™æ­¢ä¸­"ã¨è¡¨ç¤º</string>
+- <string id="31104">Play Trailers in a window [COLOR=grey3](Video Information Dialog Only)[/COLOR]</string>
+- <string id="31105"></string>
++ <string id="31103">ピクãƒãƒ£ã‚¹ãƒ©ã‚¤ãƒ‰ã‚·ãƒ§ãƒ¼ã§ã€Œé™æ­¢ä¸­ã€ã¨è¡¨ç¤º</string>
++ <string id="31104">ウィンドウ内ã§äºˆå‘Šã‚’å†ç”Ÿ [COLOR=grey3](ビデオ情報ダイアログã®ã¿)[/COLOR]</string>
++ <string id="31105">「ビデオã€ãƒœã‚¿ãƒ³ã§å¸¸ã«ã€Œãƒ•ã‚¡ã‚¤ãƒ«ã€ã‚’é¸æŠž</string>
+ <string id="31106">ãã®ä»–ã®è¨­å®š</string>
+ <string id="31107">Hide Flagging read from video filenames [COLOR=grey3](Blu-ray, HD-DVD)[/COLOR]</string>
+- <string id="31108">Hide Main Menu Buttons</string>
+- <string id="31109">Media backgrounds</string>
+- <string id="31110">Edit Background for Media Type</string>
+- <string id="31111">Hide</string>
+- <string id="31112">Options</string>
+- <string id="31113">Single Image</string>
+- <string id="31114">Multi Image</string>
+- <string id="31115">Customizer</string>
+- <string id="31116">
+- </string>
++ <string id="31108">メインメニューã‹ã‚‰éš ã™ãƒœã‚¿ãƒ³</string>
++ <string id="31109">メディア別ã®èƒŒæ™¯è¨­å®š</string>
++ <string id="31110">編集ã™ã‚‹ãƒ¡ãƒ‡ã‚£ã‚¢ã‚¿ã‚¤ãƒ—ã‚’é¸æŠž</string>
++ <string id="31111">éš ã™</string>
++ <string id="31112">オプション</string>
++ <string id="31113">å˜ä¸€ã‚¤ãƒ¡ãƒ¼ã‚¸</string>
++ <string id="31114">複数イメージ</string>
++ <string id="31115">カスタマイズ</string>
++ <string id="31116"></string>
+ <string id="31117">最近追加ã—ãŸãƒ“デオを表示</string>
+- <string id="31118">Home Page Programs Submenu</string>
+- <string id="31119">
+- </string>
+- <string id="31120">BUTTON LABEL</string>
+- <string id="31121">
+- </string>
+- <string id="31122">Weather Page</string>
++ <string id="31118">ホーム画é¢ã€Œãƒ—ログラムã€ã®ã‚µãƒ–メニュー</string>
++ <string id="31119">背景ã®ãƒ•ã‚¡ãƒ³ã‚¢ãƒ¼ãƒˆã‚’éš ã™</string>
++ <string id="31120">ボタンラベル</string>
++ <string id="31121"></string> <!-- blanked 2010-11-12 -->
++ <string id="31122">天気予報ページ</string>
+ <string id="31123">Use "Posters" instead of "Banners" for TV Shows</string>
+- <string id="31124">Show Background "Now Playing" Video</string>
+- <string id="31125">Show Background "Now Playing" Visualization</string>
+- <string id="31126">
+- </string>
+- <string id="31127">
+- </string>
+- <string id="31128">Lyrics</string>
+- <string id="31129">
+- </string>
+- <string id="31130">
+- </string>
+- <string id="31131">Use Horizontal Home Menu</string>
+- <string id="31132">Lyrics Add-on</string>
+- <string id="31133">Subtitle Add-on</string>
+- <string id="31140">Music OSD</string>
+- <string id="31141">Video OSD</string>
+- <string id="31200">Shortcuts</string>
+- <string id="31201">Categories</string>
+- <string id="31202">Show Cast</string>
+- <string id="31203">Choose Your Song</string>
+- <string id="31204">Section Links</string>
+- <string id="31205">Lyrics Source</string>
+- <string id="31300">Current Temp</string>
+- <string id="31301">Last Updated</string>
+- <string id="31302">Menu</string>
+- <string id="31303">
+- </string>
+- <string id="31304">Picture</string>
+- <string id="31305">No Disc Media Detected</string>
+- <string id="31306">Eject</string>
+- <string id="31307">Hide Fanart</string>
+- <string id="31308">Movie Details</string>
+- <string id="31309">Memory Used:</string>
+- <string id="31310">Track Number</string>
+- <string id="31311">Fanart image[CR][CR]Unavailable[CR][CR] Click button to set</string>
+- <string id="31312">Current Scraper</string>
+- <string id="31313">Choose a Scraper</string>
+- <string id="31314">Content Scanning Options</string>
+- <string id="31315">Basic</string>
++ <string id="31124">背景ã«ã€Œç¾åœ¨å†ç”Ÿä¸­ã€ãƒ“デオを表示</string>
++ <string id="31125">背景ã«ã€Œç¾åœ¨å†ç”Ÿä¸­ã€è¦–覚化を表示</string>
++ <string id="31126">Play TV theme songs in video library (TvTunes add-on)</string>
++ <string id="31127">TvTunes</string>
++ <string id="31128">歌詞</string>
++ <string id="31129"></string> <!-- blanked 2010-11-12 -->
++ <string id="31130"></string> <!-- blanked 2010-11-12 -->
++
++<string id="31131"></string> <!-- blanked 2010-12-23 -->
++ <string id="31132">歌詞アドオン</string>
++ <string id="31133">字幕アドオン</string>
++ <string id="31134">ホーム画é¢ã€Œãƒ“デオã€ã®ã‚µãƒ–メニュー</string>
++ <string id="31135">ホーム画é¢ã€ŒãƒŸãƒ¥ãƒ¼ã‚¸ãƒƒã‚¯ã€ã®ã‚µãƒ–メニュー</string>
++ <string id="31136">ホーム画é¢ã€Œãƒ”クãƒãƒ£ã€ã®ã‚µãƒ–メニュー</string>
++
++ <string id="31140">ミュージック OSD</string>
++ <string id="31141">ビデオ OSD</string>
++
++ <!-- Script labels -->
++ <string id="31200">ショートカット</string>
++ <string id="31201">カテゴリ</string>
++ <string id="31202">キャストを表示</string>
++ <string id="31203">曲をé¸æŠž</string>
++ <string id="31204">セクションリンク</string>
++ <string id="31205">歌詞ã®æƒ…å ±æº</string>
++
++ <!-- Extra labels -->
++ <string id="31300">ç¾åœ¨ã®æ°—温</string>
++ <string id="31301">最終更新時刻</string>
++ <string id="31302">メニュー</string>
++ <string id="31303">気象データæ供元</string>
++ <string id="31304">ピクãƒãƒ£</string>
++ <string id="31305">ディスクメディアãŒæ¤œå‡ºã•ã‚Œã¾ã›ã‚“ã§ã—ãŸ</string>
++ <string id="31306">イジェクト</string>
++ <string id="31307">ファンアートを隠ã™</string>
++ <string id="31308">ムービーã®è©³ç´°</string>
++ <string id="31309">メモリ使用é‡:</string>
++ <string id="31310">トラック番å·</string>
++ <string id="31311">ファンアート画åƒ[CR][CR]使用ä¸å¯[CR][CR] ボタンを押ã—ã¦è¨­å®š</string>
++ <string id="31312">ç¾åœ¨ã®ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿ã‚¹ã‚¯ãƒ¬ãƒ¼ãƒ‘ー</string>
++ <string id="31313">メタデータスクレーパーをé¸æŠž</string>
++ <string id="31314">コンテントスキャンオプション</string>
++ <string id="31315">標準</string>
+ <string id="31316">
+ </string>
+- <string id="31317">Set Fanart Path</string>
+- <string id="31318">Small Fanart</string>
+- <string id="31319">Selected Profile</string>
++ <string id="31317">ファンアートã®ãƒ‘スを設定</string>
++ <string id="31318">å°ã•ãªãƒ•ã‚¡ãƒ³ã‚¢ãƒ¼ãƒˆ</string>
++ <string id="31319">é¸æŠžã•ã‚ŒãŸãƒ—ロファイル</string>
+ <string id="31320">最終ログイン</string>
+- <string id="31321">Karaoke Song Selector</string>
+- <string id="31322">Aired</string>
+- <string id="31323">Latest Movies</string>
+- <string id="31324">Latest Episodes</string>
+- <string id="31325">Playlist Options</string>
+- <string id="31326">Created</string>
+- <string id="31327">Resolution</string>
++ <string id="31321">カラオケソングセレクター</string>
++ <string id="31322">放映日</string>
++ <string id="31323">最新ムービー</string>
++ <string id="31324">最新エピソード</string>
++ <string id="31325">プレイリストオプション</string>
++ <string id="31326">制作日</string>
++ <string id="31327">解åƒåº¦</string>
+ <string id="31328">最近追加</string>
+- <string id="31329">[B]Timer set![/B] [COLOR=grey2] - System auto shutdown in[/COLOR]</string>
+- <string id="31330">Click button to play[CR][CR]Movie trailer</string>
+- <string id="31351">Pause</string>
+- <string id="31352">Stop</string>
+- <string id="31353">Fast Forward</string>
+- <string id="31354">Rewind</string>
+- <string id="31355">Movie menu</string>
+- <string id="31356">Download Subtitles</string>
+- <string id="31357">
+- </string>
+- <string id="31390">Skin default</string>
+- <string id="31391">Skin default with no Caps</string>
+- <string id="31392">Arial based</string>
++ <string id="31329">[B]タイマーセット完了![/B] [COLOR=grey2] - システムãŒã‚·ãƒ£ãƒƒãƒˆãƒ€ã‚¦ãƒ³ã—ã¾ã™[/COLOR]</string>
++ <string id="31330">ボタンを押ã—ã¦å†ç”Ÿ[CR][CR]ムービー予告編</string>
++ <string id="31331">アルãƒãƒ è©³ç´°</string>
++
++ <!-- Video and Music OSD Labels -->
++ <string id="31351">一時åœæ­¢</string>
++ <string id="31352">åœæ­¢</string>
++ <string id="31353">æ—©é€ã‚Š</string>
++ <string id="31354">å·»ã戻ã—</string>
++ <string id="31355">ムービーメニュー</string>
++ <string id="31356">字幕をダウンロード</string>
++ <string id="31357"></string>
++
++ <!-- Skin Fontsets -->
++ <string id="31390">スキンã®ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆ</string>
++ <string id="31391">スキンã®ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆ (å°æ–‡å­—ã®ã¿)</string>
++ <string id="31392">Arial ベース</string>
++
++ <!-- Description Labels -->
+ <string id="31400">[B]外観設定[/B][CR][CR]スキンé¸æŠž - 言語ã¨åœ°åŸŸã®è¨­å®š - ファイル一覧設定変更[CR]スクリーンセーãƒãƒ¼è¨­å®š</string>
+ <string id="31401">[B]ビデオ設定[/B][CR][CR]ãƒ“ãƒ‡ã‚ªãƒ©ã‚¤ãƒ–ãƒ©ãƒªç®¡ç† - ビデオå†ç”Ÿè¨­å®š - ビデオ一覧設定変更[CR]字幕用フォント変更</string>
+ <string id="31402">[B]ミュージック設定[/B][CR][CR]ãƒŸãƒ¥ãƒ¼ã‚¸ãƒƒã‚¯ãƒ©ã‚¤ãƒ–ãƒ©ãƒªç®¡ç† - ミュージックå†ç”Ÿè¨­å®š - ミュージック一覧設定変更[CR]楽曲é€ä¿¡è¨­å®š - カラオケ設定</string>
+ <string id="31403">[B]ピクãƒãƒ£è¨­å®š[/B][CR][CR]ピクãƒãƒ£ä¸€è¦§è¨­å®šå¤‰æ›´[CR]スライドショー設定</string>
+ <string id="31404">[B]天気予報設定[/B][CR][CR]天気情報å–得都市é¸æŠž[CR]天気予報プラグインé¸æŠž</string>
+- <string id="31405">[B]ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯è¨­å®š[/B][CR][CR]UPnPãŠã‚ˆã³HTTPã«ã‚ˆã‚‹XBMCã®æ“作設定 - ファイル共有設定[CR]インターãƒãƒƒãƒˆæŽ¥ç¶šè¨­å®š</string>
++ <string id="31405">[B]ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯è¨­å®š[/B][CR][CR]UPnP ãŠã‚ˆã³ HTTP ã«ã‚ˆã‚‹ XBMCã®æ“作設定 - ファイル共有設定[CR]インターãƒãƒƒãƒˆæŽ¥ç¶šè¨­å®š</string>
+ <string id="31406">[B]システム設定[/B][CR][CR]ç”»é¢ã®è¨­å®šãƒ»èª¿æ•´ - オーディオ出力設定 - リモートコントローラー設定[CR]çœé›»åŠ›è¨­å®š - デãƒãƒƒã‚°ã®æœ‰åŠ¹åŒ– - マスターロック設定</string>
+- <string id="31407">[B]スキン設定[/B][CR][CR]Confluenceスキン設定 - ホームメニュー項目ã®è¿½åŠ ã¨å‰Šé™¤[CR]スキン背景設定</string>
++ <string id="31407">[B]スキン設定[/B][CR][CR]Confluence スキン設定 - ホームメニュー項目ã®è¿½åŠ ã¨å‰Šé™¤[CR]スキン背景設定</string>
+ <string id="31408">[B]アドオン設定[/B][CR][CR]インストール済ã¿ã‚¢ãƒ‰ã‚ªãƒ³ç®¡ç† - xbmc.orgã‹ã‚‰ã‚¢ãƒ‰ã‚ªãƒ³ã‚’検索・インストール[CR]アドオン設定変更</string>
+- <string id="31421">Select your XBMC user Profile[CR]to login and continue</string>
++ <string id="31421">XBMC ユーザープロファイルをé¸æŠžã—ã¦[CR]ログインã—ã¦ãã ã•ã„</string>
++
++ <!-- Weather plugin -->
++ <string id="31900">天気図</string>
++ <string id="31901">36時間予報</string>
++ <string id="31902">1時間ã”ã¨ã®äºˆå ±</string>
++ <string id="31903">週末ã®äºˆå ±</string>
++ <string id="31904">10日予報</string>
++ <string id="31905">予報</string>
++ <string id="31906">天気図 &amp; ビデオ</string>
++ <string id="31907">ビデオ予報 [COLOR=grey2](フルスクリーンå†ç”Ÿ)[/COLOR]</string>
++ <string id="31908">é™æ°´ç¢ºçŽ‡</string>
++ <string id="31909">天気予報データå–得中...</string>
++
+ </strings>
+diff --git a/addons/skin.confluence/language/Portuguese (Brazil)/strings.xml b/addons/skin.confluence/language/Portuguese (Brazil)/strings.xml
+index baf62a5..2c93aa0 100644
+--- a/addons/skin.confluence/language/Portuguese (Brazil)/strings.xml
++++ b/addons/skin.confluence/language/Portuguese (Brazil)/strings.xml
+@@ -59,7 +59,7 @@
+ <string id="31102">Fundo</string>
+ <string id="31103">Exibir "Pausado" em exibição de imagens (slideshow)</string>
+ <string id="31104">Tocar trailers em uma janela [COLOR=grey3](Só Diálogo de Informações do Vídeo)[/COLOR]</string>
+- <string id="31105"></string>
++ <string id="31105">"Botão Videos" sempre vai para arquivos de vídeos</string>
+ <string id="31106">Opções diversas</string>
+ <string id="31107">Ocultar marcadores lidos do arquivo de vídeo [COLOR=grey3](Blu-ray, HD-DVD)[/COLOR]</string>
+ <string id="31108">Ocultar botões do Menu Principal</string>
+@@ -72,6 +72,7 @@
+ <string id="31115">Personalizador</string>
+ <string id="31117">Exibir vídeos adicionados recentemente</string>
+ <string id="31118">Submenu Programas da Página Principal</string>
++ <string id="31119">Ocultar fanart fundo da visualização</string>
+ <string id="31120">RÓTULO DE BOTÃO</string>
+ <string id="31122">Página do Tempo</string>
+ <string id="31123">Usar "Pôsters" ao invés de "Faixas" para Seriados</string>
+diff --git a/addons/skin.confluence/language/Slovak/strings.xml b/addons/skin.confluence/language/Slovak/strings.xml
+index a27326a..09a872f 100644
+--- a/addons/skin.confluence/language/Slovak/strings.xml
++++ b/addons/skin.confluence/language/Slovak/strings.xml
+@@ -1,203 +1,164 @@
+-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+-<!--Translator: Michal Iro, mixo@frco.sk-->
+-<!--Date of translation: 21/08/2011-->
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<!--Language file translated with Team XBMC Translator-->
++<!--Translator: Michal Iro-->
++<!--Email: mixo@frco.sk-->
++<!--Date of translation: 04/09/2012-->
++<!--$Revision$-->
+ <strings>
+- <!-- Misc labels -->
+ <string id="31000">Zmeniť</string>
+ <string id="31001">Milovať</string>
+ <string id="31002">Nenávidieť</string>
+ <string id="31003">Napájanie</string>
+-
+ <string id="31004">Pracujem...</string>
+ <string id="31005">Skryť informácie</string>
+- <string id="31006">Nastavenie zobrazenia</string>
++ <string id="31006">Možnosti zobrazenia</string>
+ <string id="31007">Pluginy</string>
+ <string id="31008">Celá obrazovka</string>
+-
+ <string id="31020">Posledné pridané</string>
+-
+ <string id="31021">Video - Súbory</string>
+- <string id="31022">Music - Súbory</string>
+- <string id="31023">Prehráva sa</string>
++ <string id="31022">Hudba - Súbory</string>
++ <string id="31023">Práve hrá</string>
+ <string id="31024">Strana</string>
+ <string id="31025">Položky</string>
+- <string id="31026">Ďaľšie nastavenia</string>
+-
+- <string id="31027">Umiestnenie</string>
+-
+- <!-- View Type labels -->
++ <string id="31026">Ďalšie možnosti</string>
++ <string id="31027">Mesto</string>
+ <string id="31028">Obaly</string>
+ <string id="31029">Fanart</string>
+ <string id="31030">Celý Zoznam</string>
+ <string id="31031">Náhľady</string>
+-
+ <string id="31032">Obrázky</string>
+- <string id="31033">Info</string>
+-
+- <!-- Extra labels -->
+- <string id="31040">Práve hráme</string>
+-
++ <string id="31033">Informácie</string>
++ <string id="31040">Práve hrá</string>
+ <string id="31042">Prehráva sa</string>
+ <string id="31043">PAUZA</string>
+-
+ <string id="31044">PRETÃÄŒA SA DOPREDU</string>
+ <string id="31045">PRETÃÄŒA SA DOZADU</string>
+ <string id="31046">Vlastnosti zvuku</string>
+ <string id="31047">Aktuálna predvolba</string>
+ <string id="31048">Predvolby Vizualizácií</string>
+- <string id="31049">Film konÄí</string>
+-
+- <string id="31050">Zoradiť: Vzostupne</string>
+- <string id="31051">Zoradiť: Zostupne</string>
+-
+-
+- <!-- Playlist Editor labels -->
++ <string id="31049">Koniec filmu</string>
++ <string id="31050">Ukázať: Vzostupne</string>
++ <string id="31051">Ukázať: Zostupne</string>
+ <string id="31055">Otvoriť playlist</string>
+ <string id="31056">Uložiť playlist</string>
+ <string id="31057">Zatvoriť playlist</string>
+-
+ <string id="31058">Hudobné súbory systému</string>
+ <string id="31059">Aktuálny playlist</string>
+ <string id="31060">Tento súbor sa skladá z viac Äastí. Vyberte tu, od ktorej chcete spustit prehrávaníe.</string>
+ <string id="31061">Aktuálne vybraný</string>
+-
+- <!-- Skin Settings labels -->
+- <string id="31100"></string>
++ <string id="31100">
++ </string>
+ <string id="31101">Informácie na Domovskej stránke</string>
+-
+ <string id="31102">Pozadie</string>
+- <string id="31103">Zobraziť "PAUZA" pri prezentácií obrázkov</string>
+- <string id="31104">Prehrávať trailer v okne [COLOR=grey3](iba z okna Informácie o videu)[/COLOR]</string>
+- <string id="31105">"Videá" tlaÄítko vždy ide do "Súbory"</string>
++ <string id="31103">Zobrazovať 'Pauza' pri prezentácií obrázkov</string>
++ <string id="31104">Prehrávať ukážky v okne [COLOR=grey3](iba z okna Detailné Informácie)[/COLOR]</string>
++ <string id="31105">Odkaz 'Videá' automaticky otvorí podmenu 'Súbory'</string>
+ <string id="31106">Rôzne nastavenia</string>
+- <string id="31107">Nezobrazovať ikonky zdroja videí [COLOR=grey3](Blu-ray, HD-DVD)[/COLOR]</string>
+-
+- <string id="31108">SchovaÅ¥ tlaÄítka hlavného menu</string>
++ <string id="31107">Nezobrazovať ikonky zdroja videa [COLOR=grey3](Blu-ray, HD-DVD)[/COLOR]</string>
++ <string id="31108">Skryť položky hlavného menu</string>
+ <string id="31109">Pozadie Domovskej stránky</string>
+- <string id="31110">Zmeniť pozadie domovskej stránky - sekcia</string>
+- <string id="31111">Schovať</string>
++ <string id="31110">Zmeniť pozadie domovskej stránky, pozadie sekciíam</string>
++ <string id="31111">Skryť</string>
+ <string id="31112">Možnosti</string>
+ <string id="31113">Jeden obrázok</string>
+-
+ <string id="31114">Viac obrázkov</string>
+ <string id="31115">Prispôsobenie</string>
+- <string id="31116"></string>
+- <string id="31117">Zobraziť Posledné pridané</string>
++ <string id="31116">
++ </string>
++ <string id="31117">Zobrazovať Posledné pridané</string>
+ <string id="31118">Domovská stránka</string>
+- <string id="31119">Schovať Fanart na pozadí</string>
+-
++ <string id="31119">Skryť Fanart na pozadí</string>
+ <string id="31120">NÃZOV POLOŽKY</string>
+- <string id="31121"></string> <!-- blanked 2010-11-12 -->
++ <string id="31121">
++ </string>
+ <string id="31122">PoÄasie</string>
+- <string id="31123">Použiť u TV seriálov klasické náhlady namiesto širokouhlých</string>
+- <string id="31124">Zobraziť na pozadí "Práve hráme" Video</string>
+- <string id="31125">Zobraziť na pozadí "Práve hráme" Vizualizáciu</string>
+-
++ <string id="31123">Používať klasické náhľady 'Plagáty' namiesto 'Banerov'</string>
++ <string id="31124">Zobrazovať práve prehrávané video na pozadí</string>
++ <string id="31125">Zobrazovať vizualizáciu na pozadí</string>
+ <string id="31126">Prehrávať skladbu TV seriálu vo video knižnici (TvTunes doplnok)</string>
+ <string id="31127">TvTunes</string>
+ <string id="31128">Texty skladieb</string>
+- <string id="31129"></string> <!-- blanked 2010-11-12 -->
+- <string id="31130"></string> <!-- blanked 2010-11-12 -->
+-
+- <string id="31131"></string> <!-- blanked 2010-12-23 -->
+- <string id="31132">Texty skladieb Doplnok</string>
+- <string id="31133">Titulky Doplnok</string>
+- <string id="31134">Domovská stránka Videá Podmenu</string>
+- <string id="31135">Domovská stránka Hudba Podmenu</string>
+- <string id="31136">Domovská stránka Obrázky Podmenu</string>
+-
++ <string id="31129">
++ </string>
++ <string id="31130">
++ </string>
++ <string id="31131">
++ </string>
++ <string id="31132">Doplnok Texty skladieb</string>
++ <string id="31133">Doplnok Titulky</string>
++ <string id="31134">Domovská stránka / podmenu videá</string>
++ <string id="31135">Domovská stránka / podmenu hudba</string>
++ <string id="31136">Domovská stránka / podmenu obrázky</string>
+ <string id="31140">Hudobný OSD</string>
+ <string id="31141">Video OSD</string>
+-
+- <!-- Script labels -->
+ <string id="31200">Odkazy</string>
+ <string id="31201">Kategórie</string>
+- <string id="31202">ZobraziÅ¥ úÄinkujúcich</string>
+-
++ <string id="31202">ZobrazovaÅ¥ úÄinkujúcich</string>
+ <string id="31203">Vyberte Vašu skladbu</string>
+ <string id="31204">Odkazy Sekcií</string>
+ <string id="31205">Zdroj textov skladieb</string>
+-
+- <!-- Extra labels -->
+ <string id="31300">Aktuálna teplota</string>
+ <string id="31301">Naposledy aktualizované</string>
+-
+ <string id="31302">Menu</string>
+- <string id="31303"></string>
++ <string id="31303">PoÄasie Vám prináša</string>
+ <string id="31304">Obrázok</string>
+ <string id="31305">Židny disk nie je vložený</string>
+ <string id="31306">Vysunúť</string>
+- <string id="31307">Schovať Fanart</string>
+-
+- <string id="31308">Detaili o filme</string>
++ <string id="31307">Skryť Fanart</string>
++ <string id="31308">Informácie</string>
+ <string id="31309">Použitá pamäť:</string>
+ <string id="31310">Číslo skladby</string>
+ <string id="31311">Fanart obrázok[CR][CR]Nedostupný[CR][CR] Klik pre nastavenie</string>
+ <string id="31312">Aktuálny sÅ¥ahovaÄ</string>
+ <string id="31313">Vyber sÅ¥ahovaÄ</string>
+-
+ <string id="31314">Možnosti vyhľadávania obsahu</string>
+ <string id="31315">Základné</string>
+- <string id="31316"></string>
+- <string id="31317">Umiestneni FanArtu</string>
++ <string id="31316">
++ </string>
++ <string id="31317">Nájsť FanArt</string>
+ <string id="31318">Malý Fanart</string>
+ <string id="31319">Vybratý Profil</string>
+-
+ <string id="31320">Posledné prihlásenie</string>
+ <string id="31321">Výber Karaoke skladby</string>
+ <string id="31322">Vysielané</string>
+ <string id="31323">Posledné Filmy</string>
+ <string id="31324">Posledné Epizódy</string>
+ <string id="31325">Nastavenia Playlistu</string>
+-
+ <string id="31326">Vytvorené</string>
+ <string id="31327">Rozlíšenie</string>
+ <string id="31328">Posledné pridané</string>
+ <string id="31329">[B]ÄŒasovaÄ nastavený![/B] [COLOR=grey2] - Systém sa automaticky vypne za[/COLOR]</string>
+- <string id="31330">Pre prehratie traileru[CR][CR]stlaÄte tlaÄítko</string>
++ <string id="31330">Pre spustenie ukážky[CR][CR]stlaÄte tlaÄítko</string>
+ <string id="31331">Album Details</string>
+-
+- <!-- Video and Music OSD Labels -->
+ <string id="31351">Pauza</string>
+ <string id="31352">Zastaviť</string>
+ <string id="31353">PretoÄiÅ¥ dopredu</string>
+ <string id="31354">PretoÄiÅ¥ dozadu</string>
+ <string id="31355">Menu filmu</string>
+-
+ <string id="31356">Stiahnuť titulky</string>
+- <string id="31357"></string>
+-
+- <!-- Skin Fontsets -->
+- <string id="31390">Predvolený Skin</string>
+- <string id="31391">Predvolený Skin bez Caps</string>
++ <string id="31357">
++ </string>
++ <string id="31390">Predvolený vzhľad</string>
++ <string id="31391">Predvolený vzhľad bez veľkých písmen</string>
+ <string id="31392">Arial</string>
+-
+- <!-- Description Labels -->
+ <string id="31400">[B]MOŽNOSTI PROSTREDIA[/B][CR][CR]Zmena vzhľadu · Výber jazyka a oblasti · Možnosti zobrazenia súborov[CR]Nastavenie Å¡poriÄa obrazovky</string>
+- <string id="31401">[B]MOŽNOSTI VIDEA[/B][CR][CR]Správa knižnice videí · Možnosti prehrávania videa · Možnosti zobrazenia video súborov[CR]Nastavenie fontu titulkov</string>
+- <string id="31402">[B]MOŽNOSTI HUDBY[/B][CR][CR]Správa knižnice hudby · Možnosti prehrávania hudby · Možnosti zobrazenia hud. súborov[CR]Služby pre odosielanie informácií o prehrávaných skladbách · Nastavenie karaoke</string>
+- <string id="31403">[B]MOŽNOSTI OBRÃZKOV[/B][CR][CR]Možnosti zobrazenia obrázkov · Konfigurácia prezentácie</string>
+- <string id="31404">[B]MOŽNOSTI POÄŒASIA[/B][CR][CR]Nastavenie oblastí pre predpoveÄ poÄasia</string>
+-
++ <string id="31401">[B]MOŽNOSTI VIDEA[/B][CR][CR]Správa knižnice videí · Možnosti prehrávania videa · Zobrazenie video súborov[CR]Nastavenie titulkov</string>
++ <string id="31402">[B]MOŽNOSTI HUDBY[/B][CR][CR]Správa knižnice hudby · Možnosti prehrávania hudby · Zobrazenie hudobných súborov[CR]Služby pre odosielanie informácií o prehrávaných skladbách · Karaoke nastavenia</string>
++ <string id="31403">[B]MOŽNOSTI OBRÃZKOV[/B][CR][CR]Možnosti zobrazenia obrázkov · Nastavenie prezentácie</string>
++ <string id="31404">[B]MOŽNOSTI POÄŒASIA[/B][CR][CR]Nastavenie oblastí pre ktoré si prajete získavaÅ¥ predpoveÄ poÄasia</string>
+ <string id="31405">[B]MOŽNOSTI SIETE[/B][CR][CR]Možnosti ovládania XBMC cez UPnP a HTTP · Nastavenie zdieľania médií[CR]Nastavenie prístupu na internet</string>
+- <string id="31406">[B]MOŽNOSTI SYSTÉMU[/B][CR][CR]Nastavenie a kalibrácia obrazovky · Konfigurácia výstupu zvuku · Nastavenie ovládaÄa[CR]Možnosti úsporného režimu · Ukladanie informácií · Konfigurácia rodiÄovského zámku</string>
+- <string id="31407">[B]MOŽNOSTI VZHĽADU[/B][CR][CR]Prispôsobenie vzhľadu Confluence · Správa sekcií na domovskej stránke[CR]Nastavenia pozadí vzhľadu</string>
++ <string id="31406">[B]MOŽNOSTI SYSTÉMU[/B][CR][CR]Nastavenie obrazovky · Nastavenie zvukového výstupu[CR]Možnosti úsporného režimu · Ukladanie informácií · Konfigurácia rodiÄovského zámku</string>
++ <string id="31407">[B]MOŽNOSTI VZHĽADU[/B][CR][CR]Prispôsobenie vzhľadu Confluence · Správa sekcií na domovskej stránke[CR]Nastavenie pozadia vzhľadu</string>
+ <string id="31408">[B]MOŽNOSTI DOPLNKOV[/B][CR][CR]Správa nainštalovaných doplnkov · Vyhľadávanie a inštalácia doplnkov z xbmc.org[CR]Nastavenie doplnkov</string>
+-
+ <string id="31421">Prihlásenie užívateľa[CR]PokraÄujte výberom profilu</string>
+-
+- <!-- Weather plugin -->
+-
+- <string id="31900">Mapy PoÄasia</string>
+- <string id="31901">36 Hodinová PredpoveÄ</string>
+- <string id="31902">Hodinová PredpoveÄ</string>
++ <string id="31900">Mapy poÄasia</string>
++ <string id="31901">36 Hodinová predpoveÄ</string>
++ <string id="31902">Hodinová predpoveÄ</string>
+ <string id="31903">PredpoveÄ na víkend</string>
+ <string id="31904">PredpoveÄ na 10 dní</string>
+ <string id="31905">PredpoveÄ</string>
+-
+ <string id="31906">Mapy &amp; Video</string>
+ <string id="31907">Video PredpoveÄ [COLOR=grey2](Prehrávanie na celej obrazovke)[/COLOR]</string>
+ <string id="31908">Šanca zrážok</string>
+- <string id="31909">ZisÅ¥ujem info o poÄasí...</string>
+-</strings>
+-
++ <string id="31909">ZisÅ¥ujem informácie o poÄasí...</string>
++</strings>
+\ No newline at end of file
+diff --git a/addons/skin.confluence/language/Slovenian/strings.xml b/addons/skin.confluence/language/Slovenian/strings.xml
+index 4ff3252..e75f9f0 100644
+--- a/addons/skin.confluence/language/Slovenian/strings.xml
++++ b/addons/skin.confluence/language/Slovenian/strings.xml
+@@ -162,8 +162,21 @@
+ <string id="31406">[B]PRILAGODITE NASTAVITVE SISTEMA[/B][CR][CR]Nastavite in prilagodite zaslon • Nastavite zvoÄni izhod • Nastavite oddaljeno upravljanje[CR]Nastavite varÄevanje z energijo • VkljuÄite razhroÅ¡Äevanje • DoloÄite glavno geslo</string>
+ <string id="31407">[B]PRILAGODITE NASTAVITVE PREOBLEKE[/B][CR][CR]Nastavite preobleko Confluence • Dodajte in odstranite elemente na domaÄem oknu[CR]Spremenite ozadja preobleke</string>
+ <string id="31408">[B]PRILAGODITE DODATKE[/B][CR][CR]Upravljajte nameÅ¡Äene dodatke • Brskajte in namestite nove dodatke iz xbmc.org[CR]Nastavite dodatke</string>
++ <string id="31409">[B]PRILAGODITE NASTAVITVE TV[/B][CR][CR]Spremenite informacije v celozaslonskem naÄinu • Upravljajte z nastavitvami EPG</string>
+
+ <string id="31421">Izberite vaš XBMC uporabniški profil[CR]za prijavo in nadaljujte</string>
++
++ <!-- Extra Unified PVR labels -->
++ <string id="31500">ÄŒasovniki snemanja</string>
++ <string id="31501">Nastavljen Äas</string>
++ <string id="31502">TV v živo</string>
++ <string id="31503">Dodaj skupino</string>
++ <string id="31504">Preimenuj skupino</string>
++ <string id="31505">Izbriši skupino</string>
++ <string id="31506">Skupina[CR]na razpolago</string>
++ <string id="31509">Skupina programov</string>
++ <string id="31510">Nastavljen Äasovnik</string>
++ <string id="31511">Možnosti programa</string>
+
+ <!-- Weather plugin -->
+ <string id="31900">Vremenska slika</string>
+diff --git a/addons/skin.confluence/media/OSDChannelDownFO.png b/addons/skin.confluence/media/OSDChannelDownFO.png
+new file mode 100644
+index 0000000..5116c32
+Binary files /dev/null and b/addons/skin.confluence/media/OSDChannelDownFO.png differ
+diff --git a/addons/skin.confluence/media/OSDChannelDownNF.png b/addons/skin.confluence/media/OSDChannelDownNF.png
+new file mode 100644
+index 0000000..6795c43
+Binary files /dev/null and b/addons/skin.confluence/media/OSDChannelDownNF.png differ
+diff --git a/addons/skin.confluence/media/OSDChannelListFO.png b/addons/skin.confluence/media/OSDChannelListFO.png
+new file mode 100644
+index 0000000..a08bc13
+Binary files /dev/null and b/addons/skin.confluence/media/OSDChannelListFO.png differ
+diff --git a/addons/skin.confluence/media/OSDChannelListNF.png b/addons/skin.confluence/media/OSDChannelListNF.png
+new file mode 100644
+index 0000000..8339fdc
+Binary files /dev/null and b/addons/skin.confluence/media/OSDChannelListNF.png differ
+diff --git a/addons/skin.confluence/media/OSDChannelUPFO.png b/addons/skin.confluence/media/OSDChannelUPFO.png
+new file mode 100644
+index 0000000..a3e6dba
+Binary files /dev/null and b/addons/skin.confluence/media/OSDChannelUPFO.png differ
+diff --git a/addons/skin.confluence/media/OSDChannelUPNF.png b/addons/skin.confluence/media/OSDChannelUPNF.png
+new file mode 100644
+index 0000000..47e6e33
+Binary files /dev/null and b/addons/skin.confluence/media/OSDChannelUPNF.png differ
+diff --git a/addons/skin.confluence/media/OSDRecord2.png b/addons/skin.confluence/media/OSDRecord2.png
+deleted file mode 100644
+index cb4fcf9..0000000
+Binary files a/addons/skin.confluence/media/OSDRecord2.png and /dev/null differ
+diff --git a/addons/skin.confluence/media/OSDRecordFO.png b/addons/skin.confluence/media/OSDRecordFO.png
+deleted file mode 100644
+index fd2bb18..0000000
+Binary files a/addons/skin.confluence/media/OSDRecordFO.png and /dev/null differ
+diff --git a/addons/skin.confluence/media/OSDRecordNF.png b/addons/skin.confluence/media/OSDRecordNF.png
+deleted file mode 100644
+index db3d575..0000000
+Binary files a/addons/skin.confluence/media/OSDRecordNF.png and /dev/null differ
+diff --git a/addons/skin.confluence/media/OSDRecordOff.png b/addons/skin.confluence/media/OSDRecordOff.png
+deleted file mode 100644
+index cb73c77..0000000
+Binary files a/addons/skin.confluence/media/OSDRecordOff.png and /dev/null differ
+diff --git a/addons/skin.confluence/media/OSDRecordOffFO.png b/addons/skin.confluence/media/OSDRecordOffFO.png
+new file mode 100644
+index 0000000..fd2bb18
+Binary files /dev/null and b/addons/skin.confluence/media/OSDRecordOffFO.png differ
+diff --git a/addons/skin.confluence/media/OSDRecordOffNF.png b/addons/skin.confluence/media/OSDRecordOffNF.png
+new file mode 100644
+index 0000000..db3d575
+Binary files /dev/null and b/addons/skin.confluence/media/OSDRecordOffNF.png differ
+diff --git a/addons/skin.confluence/media/OSDRecordOnFO.png b/addons/skin.confluence/media/OSDRecordOnFO.png
+new file mode 100644
+index 0000000..cb4fcf9
+Binary files /dev/null and b/addons/skin.confluence/media/OSDRecordOnFO.png differ
+diff --git a/addons/skin.confluence/media/OSDRecordOnNF.png b/addons/skin.confluence/media/OSDRecordOnNF.png
+new file mode 100644
+index 0000000..b335818
+Binary files /dev/null and b/addons/skin.confluence/media/OSDRecordOnNF.png differ
+diff --git a/addons/skin.confluence/media/OSDTeleTextFO.png b/addons/skin.confluence/media/OSDTeleTextFO.png
+new file mode 100644
+index 0000000..53eb576
+Binary files /dev/null and b/addons/skin.confluence/media/OSDTeleTextFO.png differ
+diff --git a/addons/skin.confluence/media/OSDTeleTextNF.png b/addons/skin.confluence/media/OSDTeleTextNF.png
+new file mode 100644
+index 0000000..111c068
+Binary files /dev/null and b/addons/skin.confluence/media/OSDTeleTextNF.png differ
+diff --git a/addons/skin.confluence/media/OSDepgFO.png b/addons/skin.confluence/media/OSDepgFO.png
+new file mode 100644
+index 0000000..141f7ad
+Binary files /dev/null and b/addons/skin.confluence/media/OSDepgFO.png differ
+diff --git a/addons/skin.confluence/media/OSDepgNF.png b/addons/skin.confluence/media/OSDepgNF.png
+new file mode 100644
+index 0000000..cf9a86b
+Binary files /dev/null and b/addons/skin.confluence/media/OSDepgNF.png differ
+diff --git a/addons/skin.confluence/media/PVR-HasTimer.png b/addons/skin.confluence/media/PVR-HasTimer.png
+new file mode 100644
+index 0000000..99c2ee9
+Binary files /dev/null and b/addons/skin.confluence/media/PVR-HasTimer.png differ
+diff --git a/addons/skin.confluence/media/PVR-IsRecording.png b/addons/skin.confluence/media/PVR-IsRecording.png
+new file mode 100644
+index 0000000..e64b346
+Binary files /dev/null and b/addons/skin.confluence/media/PVR-IsRecording.png differ
+diff --git a/addons/skin.confluence/media/StackNF.png b/addons/skin.confluence/media/StackNF.png
+index 17e50526..0cbcd83 100644
+Binary files a/addons/skin.confluence/media/StackNF.png and b/addons/skin.confluence/media/StackNF.png differ
+diff --git a/addons/skin.confluence/media/epg-genres/0.png b/addons/skin.confluence/media/epg-genres/0.png
+new file mode 100644
+index 0000000..80c6192
+Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/0.png differ
+diff --git a/addons/skin.confluence/media/epg-genres/112.png b/addons/skin.confluence/media/epg-genres/112.png
+new file mode 100644
+index 0000000..7701fc6
+Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/112.png differ
+diff --git a/addons/skin.confluence/media/epg-genres/128.png b/addons/skin.confluence/media/epg-genres/128.png
+new file mode 100644
+index 0000000..3071d4e
+Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/128.png differ
+diff --git a/addons/skin.confluence/media/epg-genres/144.png b/addons/skin.confluence/media/epg-genres/144.png
+new file mode 100644
+index 0000000..a9325a6
+Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/144.png differ
+diff --git a/addons/skin.confluence/media/epg-genres/16.png b/addons/skin.confluence/media/epg-genres/16.png
+new file mode 100644
+index 0000000..ffaae9e
+Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/16.png differ
+diff --git a/addons/skin.confluence/media/epg-genres/160.png b/addons/skin.confluence/media/epg-genres/160.png
+new file mode 100644
+index 0000000..8d825ab
+Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/160.png differ
+diff --git a/addons/skin.confluence/media/epg-genres/176.png b/addons/skin.confluence/media/epg-genres/176.png
+new file mode 100644
+index 0000000..7ae6320
+Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/176.png differ
+diff --git a/addons/skin.confluence/media/epg-genres/192.png b/addons/skin.confluence/media/epg-genres/192.png
+new file mode 100644
+index 0000000..80c6192
+Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/192.png differ
+diff --git a/addons/skin.confluence/media/epg-genres/208.png b/addons/skin.confluence/media/epg-genres/208.png
+new file mode 100644
+index 0000000..80c6192
+Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/208.png differ
+diff --git a/addons/skin.confluence/media/epg-genres/224.png b/addons/skin.confluence/media/epg-genres/224.png
+new file mode 100644
+index 0000000..80c6192
+Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/224.png differ
+diff --git a/addons/skin.confluence/media/epg-genres/240.png b/addons/skin.confluence/media/epg-genres/240.png
+new file mode 100644
+index 0000000..80c6192
+Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/240.png differ
+diff --git a/addons/skin.confluence/media/epg-genres/32.png b/addons/skin.confluence/media/epg-genres/32.png
+new file mode 100644
+index 0000000..be2bc8e
+Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/32.png differ
+diff --git a/addons/skin.confluence/media/epg-genres/48.png b/addons/skin.confluence/media/epg-genres/48.png
+new file mode 100644
+index 0000000..57b4dee
+Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/48.png differ
+diff --git a/addons/skin.confluence/media/epg-genres/64.png b/addons/skin.confluence/media/epg-genres/64.png
+new file mode 100644
+index 0000000..d85eb05
+Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/64.png differ
+diff --git a/addons/skin.confluence/media/epg-genres/80.png b/addons/skin.confluence/media/epg-genres/80.png
+new file mode 100644
+index 0000000..cdd6da3
+Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/80.png differ
+diff --git a/addons/skin.confluence/media/epg-genres/96.png b/addons/skin.confluence/media/epg-genres/96.png
+new file mode 100644
+index 0000000..ba92ae3
+Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/96.png differ
+diff --git a/addons/skin.confluence/media/epg-genres/genre-numbers.txt b/addons/skin.confluence/media/epg-genres/genre-numbers.txt
+new file mode 100644
+index 0000000..031732b
+--- /dev/null
++++ b/addons/skin.confluence/media/epg-genres/genre-numbers.txt
+@@ -0,0 +1,18 @@
++Genre Numbers set internally by XBMC
++
++0 = other/unknown
++16 = moviedrama
++32 = news
++48 = show
++64 = sports
++80 = child
++96 = music
++112 = arts
++128 = social
++144 = science
++160 = hobby
++176 = special
++192 = other/unknown
++208 = other/unknown
++224 = other/unknown
++240 = other/unknown
+diff --git a/addons/skin.confluence/media/gradient.png b/addons/skin.confluence/media/gradient.png
+new file mode 100644
+index 0000000..7db158f
+Binary files /dev/null and b/addons/skin.confluence/media/gradient.png differ
+diff --git a/addons/skin.confluence/media/home-power-inhibit-FO.png b/addons/skin.confluence/media/home-power-inhibit-FO.png
+new file mode 100644
+index 0000000..3656ffa
+Binary files /dev/null and b/addons/skin.confluence/media/home-power-inhibit-FO.png differ
+diff --git a/addons/skin.confluence/media/home-power-inhibit.png b/addons/skin.confluence/media/home-power-inhibit.png
+new file mode 100644
+index 0000000..b90d844
+Binary files /dev/null and b/addons/skin.confluence/media/home-power-inhibit.png differ
+diff --git a/addons/webinterface.default/js/iscroll.js b/addons/webinterface.default/js/iscroll.js
+new file mode 100644
+index 0000000..693e0e7
+--- /dev/null
++++ b/addons/webinterface.default/js/iscroll.js
+@@ -0,0 +1,709 @@
++/**
++ *
++ * Find more about the scrolling function at
++ * http://cubiq.org/iscroll
++ *
++ * Copyright (c) 2010 Matteo Spinelli, http://cubiq.org/
++ * Released under MIT license
++ * http://cubiq.org/dropbox/mit-license.txt
++ *
++ * Version 3.6 - Last updated: 2010.08.24
++ *
++ */
++
++(function(){
++function iScroll (el, options) {
++ var that = this, i;
++ that.element = typeof el == 'object' ? el : document.getElementById(el);
++ that.wrapper = that.element.parentNode;
++
++ that.element.style.webkitTransitionProperty = '-webkit-transform';
++ that.element.style.webkitTransitionTimingFunction = 'cubic-bezier(0,0,0.25,1)';
++ that.element.style.webkitTransitionDuration = '0';
++ that.element.style.webkitTransform = translateOpen + '0,0' + translateClose;
++
++ // Default options
++ that.options = {
++ bounce: has3d,
++ momentum: has3d,
++ checkDOMChanges: true,
++ topOnDOMChanges: false,
++ hScrollbar: has3d,
++ vScrollbar: has3d,
++ fadeScrollbar: isIphone || isIpad || !isTouch,
++ shrinkScrollbar: isIphone || isIpad || !isTouch,
++ desktopCompatibility: false,
++ overflow: 'hidden',
++ snap: false
++ };
++
++ // User defined options
++ if (typeof options == 'object') {
++ for (i in options) {
++ that.options[i] = options[i];
++ }
++ }
++
++ if (that.options.desktopCompatibility) {
++ that.options.overflow = 'hidden';
++ }
++
++ that.wrapper.style.overflow = that.options.overflow;
++
++ that.refresh();
++
++ window.addEventListener('onorientationchange' in window ? 'orientationchange' : 'resize', that, false);
++
++ if (isTouch || that.options.desktopCompatibility) {
++ that.element.addEventListener(START_EVENT, that, false);
++ that.element.addEventListener(MOVE_EVENT, that, false);
++ that.element.addEventListener(END_EVENT, that, false);
++ }
++
++ if (that.options.checkDOMChanges) {
++ that.element.addEventListener('DOMSubtreeModified', that, false);
++ }
++}
++
++iScroll.prototype = {
++ x: 0,
++ y: 0,
++ enabled: true,
++
++ handleEvent: function (e) {
++ var that = this;
++
++ switch (e.type) {
++ case START_EVENT:
++ that.touchStart(e);
++ break;
++ case MOVE_EVENT:
++ that.touchMove(e);
++ break;
++ case END_EVENT:
++ that.touchEnd(e);
++ break;
++ case 'webkitTransitionEnd':
++ that.transitionEnd();
++ break;
++ case 'orientationchange':
++ case 'resize':
++ that.refresh();
++ break;
++ case 'DOMSubtreeModified':
++ that.onDOMModified(e);
++ break;
++ }
++ },
++
++ onDOMModified: function (e) {
++ var that = this;
++
++ // (Hopefully) execute onDOMModified only once
++ if (e.target.parentNode != that.element) {
++ return;
++ }
++
++ setTimeout(function () { that.refresh(); }, 0);
++
++ if (that.options.topOnDOMChanges && (that.x!=0 || that.y!=0)) {
++ that.scrollTo(0,0,'0');
++ }
++ },
++
++ refresh: function () {
++ var that = this,
++ resetX = this.x, resetY = this.y,
++ snap;
++
++ that.scrollWidth = that.wrapper.clientWidth;
++ that.scrollHeight = that.wrapper.clientHeight;
++ that.scrollerWidth = that.element.offsetWidth;
++ that.scrollerHeight = that.element.offsetHeight;
++ that.maxScrollX = that.scrollWidth - that.scrollerWidth;
++ that.maxScrollY = that.scrollHeight - that.scrollerHeight;
++ that.directionX = 0;
++ that.directionY = 0;
++
++ if (that.scrollX) {
++ if (that.maxScrollX >= 0) {
++ resetX = 0;
++ } else if (that.x < that.maxScrollX) {
++ resetX = that.maxScrollX;
++ }
++ }
++ if (that.scrollY) {
++ if (that.maxScrollY >= 0) {
++ resetY = 0;
++ } else if (that.y < that.maxScrollY) {
++ resetY = that.maxScrollY;
++ }
++ }
++ // Snap
++ if (that.options.snap) {
++ that.maxPageX = -Math.floor(that.maxScrollX/that.scrollWidth);
++ that.maxPageY = -Math.floor(that.maxScrollY/that.scrollHeight);
++
++ snap = that.snap(resetX, resetY);
++ resetX = snap.x;
++ resetY = snap.y;
++ }
++
++ if (resetX!=that.x || resetY!=that.y) {
++ that.setTransitionTime('0');
++ that.setPosition(resetX, resetY, true);
++ }
++
++ that.scrollX = that.scrollerWidth > that.scrollWidth;
++ that.scrollY = !that.scrollX || that.scrollerHeight > that.scrollHeight;
++
++ // Update horizontal scrollbar
++ if (that.options.hScrollbar && that.scrollX) {
++ that.scrollBarX = that.scrollBarX || new scrollbar('horizontal', that.wrapper, that.options.fadeScrollbar, that.options.shrinkScrollbar);
++ that.scrollBarX.init(that.scrollWidth, that.scrollerWidth);
++ } else if (that.scrollBarX) {
++ that.scrollBarX = that.scrollBarX.remove();
++ }
++
++ // Update vertical scrollbar
++ if (that.options.vScrollbar && that.scrollY && that.scrollerHeight > that.scrollHeight) {
++ that.scrollBarY = that.scrollBarY || new scrollbar('vertical', that.wrapper, that.options.fadeScrollbar, that.options.shrinkScrollbar);
++ that.scrollBarY.init(that.scrollHeight, that.scrollerHeight);
++ } else if (that.scrollBarY) {
++ that.scrollBarY = that.scrollBarY.remove();
++ }
++ },
++
++ setPosition: function (x, y, hideScrollBars) {
++ var that = this;
++
++ that.x = x;
++ that.y = y;
++
++ that.element.style.webkitTransform = translateOpen + that.x + 'px,' + that.y + 'px' + translateClose;
++
++ // Move the scrollbars
++ if (!hideScrollBars) {
++ if (that.scrollBarX) {
++ that.scrollBarX.setPosition(that.x);
++ }
++ if (that.scrollBarY) {
++ that.scrollBarY.setPosition(that.y);
++ }
++ }
++ },
++
++ setTransitionTime: function(time) {
++ var that = this;
++
++ time = time || '0';
++ that.element.style.webkitTransitionDuration = time;
++
++ if (that.scrollBarX) {
++ that.scrollBarX.bar.style.webkitTransitionDuration = time;
++ that.scrollBarX.wrapper.style.webkitTransitionDuration = has3d && that.options.fadeScrollbar ? '300ms' : '0';
++ }
++ if (that.scrollBarY) {
++ that.scrollBarY.bar.style.webkitTransitionDuration = time;
++ that.scrollBarY.wrapper.style.webkitTransitionDuration = has3d && that.options.fadeScrollbar ? '300ms' : '0';
++ }
++ },
++
++ touchStart: function(e) {
++ var that = this,
++ matrix;
++
++ e.preventDefault();
++ e.stopPropagation();
++
++ if (!that.enabled) {
++ return;
++ }
++
++ that.scrolling = true; // This is probably not needed, but may be useful if iScroll is used in conjuction with other frameworks
++
++ that.moved = false;
++ that.dist = 0;
++
++ that.setTransitionTime('0');
++
++ // Check if the scroller is really where it should be
++ if (that.options.momentum || that.options.snap) {
++ matrix = new WebKitCSSMatrix(window.getComputedStyle(that.element).webkitTransform);
++ if (matrix.e != that.x || matrix.f != that.y) {
++ document.removeEventListener('webkitTransitionEnd', that, false);
++ that.setPosition(matrix.e, matrix.f);
++ that.moved = true;
++ }
++ }
++
++ that.touchStartX = isTouch ? e.changedTouches[0].pageX : e.pageX;
++ that.scrollStartX = that.x;
++
++ that.touchStartY = isTouch ? e.changedTouches[0].pageY : e.pageY;
++ that.scrollStartY = that.y;
++
++ that.scrollStartTime = e.timeStamp;
++
++ that.directionX = 0;
++ that.directionY = 0;
++ },
++
++ touchMove: function(e) {
++ var that = this,
++ pageX = isTouch ? e.changedTouches[0].pageX : e.pageX,
++ pageY = isTouch ? e.changedTouches[0].pageY : e.pageY,
++ leftDelta = that.scrollX ? pageX - that.touchStartX : 0,
++ topDelta = that.scrollY ? pageY - that.touchStartY : 0,
++ newX = that.x + leftDelta,
++ newY = that.y + topDelta;
++
++ if (!that.scrolling) {
++ return;
++ }
++
++ //e.preventDefault();
++ e.stopPropagation(); // Stopping propagation just saves some cpu cycles (I presume)
++
++ that.touchStartX = pageX;
++ that.touchStartY = pageY;
++
++ // Slow down if outside of the boundaries
++ if (newX >= 0 || newX < that.maxScrollX) {
++ newX = that.options.bounce ? Math.round(that.x + leftDelta / 3) : (newX >= 0 || that.maxScrollX>=0) ? 0 : that.maxScrollX;
++ }
++ if (newY >= 0 || newY < that.maxScrollY) {
++ newY = that.options.bounce ? Math.round(that.y + topDelta / 3) : (newY >= 0 || that.maxScrollY>=0) ? 0 : that.maxScrollY;
++ }
++
++ if (that.dist > 5) { // 5 pixels threshold is needed on Android, but also on iPhone looks more natural
++ that.setPosition(newX, newY);
++ that.moved = true;
++ that.directionX = leftDelta > 0 ? -1 : 1;
++ that.directionY = topDelta > 0 ? -1 : 1;
++ } else {
++ that.dist+= Math.abs(leftDelta) + Math.abs(topDelta);
++ }
++ },
++
++ touchEnd: function(e) {
++ var that = this,
++ time = e.timeStamp - that.scrollStartTime,
++ point = isTouch ? e.changedTouches[0] : e,
++ target, ev,
++ momentumX, momentumY,
++ newDuration = 0,
++ newPositionX = that.x, newPositionY = that.y,
++ snap;
++
++ if (!that.scrolling) {
++ return;
++ }
++ that.scrolling = false;
++
++ if (!that.moved) {
++ that.resetPosition();
++
++ if (isTouch) {
++ // Find the last touched element
++ target = point.target;
++ while (target.nodeType != 1) {
++ target = target.parentNode;
++ }
++
++ // Create the fake event
++ target.style.pointerEvents = 'auto';
++ ev = document.createEvent('MouseEvents');
++ ev.initMouseEvent('click', true, true, e.view, 1,
++ point.screenX, point.screenY, point.clientX, point.clientY,
++ e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
++ 0, null);
++ ev._fake = true;
++ target.dispatchEvent(ev);
++ }
++
++ return;
++ }
++
++ if (!that.options.snap && time > 250) { // Prevent slingshot effect
++ that.resetPosition();
++ return;
++ }
++
++ if (that.options.momentum) {
++ momentumX = that.scrollX === true
++ ? that.momentum(that.x - that.scrollStartX,
++ time,
++ that.options.bounce ? -that.x + that.scrollWidth/5 : -that.x,
++ that.options.bounce ? that.x + that.scrollerWidth - that.scrollWidth + that.scrollWidth/5 : that.x + that.scrollerWidth - that.scrollWidth)
++ : { dist: 0, time: 0 };
++
++ momentumY = that.scrollY === true
++ ? that.momentum(that.y - that.scrollStartY,
++ time,
++ that.options.bounce ? -that.y + that.scrollHeight/5 : -that.y,
++ that.options.bounce ? (that.maxScrollY < 0 ? that.y + that.scrollerHeight - that.scrollHeight : 0) + that.scrollHeight/5 : that.y + that.scrollerHeight - that.scrollHeight)
++ : { dist: 0, time: 0 };
++
++ newDuration = Math.max(Math.max(momentumX.time, momentumY.time), 1); // The minimum animation length must be 1ms
++ newPositionX = that.x + momentumX.dist;
++ newPositionY = that.y + momentumY.dist;
++ }
++
++ if (that.options.snap) {
++ snap = that.snap(newPositionX, newPositionY);
++ newPositionX = snap.x;
++ newPositionY = snap.y;
++ newDuration = Math.max(snap.time, newDuration);
++ }
++
++ that.scrollTo(newPositionX, newPositionY, newDuration + 'ms');
++ },
++
++ transitionEnd: function () {
++ var that = this;
++ document.removeEventListener('webkitTransitionEnd', that, false);
++ that.resetPosition();
++ },
++
++ resetPosition: function () {
++ var that = this,
++ resetX = that.x,
++ resetY = that.y;
++
++ if (that.x >= 0) {
++ resetX = 0;
++ } else if (that.x < that.maxScrollX) {
++ resetX = that.maxScrollX;
++ }
++
++ if (that.y >= 0 || that.maxScrollY > 0) {
++ resetY = 0;
++ } else if (that.y < that.maxScrollY) {
++ resetY = that.maxScrollY;
++ }
++
++ if (resetX != that.x || resetY != that.y) {
++ that.scrollTo(resetX, resetY);
++ } else {
++ if (that.moved) {
++ that.onScrollEnd(); // Execute custom code on scroll end
++ that.moved = false;
++ }
++
++ // Hide the scrollbars
++ if (that.scrollBarX) {
++ that.scrollBarX.hide();
++ }
++ if (that.scrollBarY) {
++ that.scrollBarY.hide();
++ }
++ }
++ },
++
++ snap: function (x, y) {
++ var that = this, time;
++
++ if (that.directionX > 0) {
++ x = Math.floor(x/that.scrollWidth);
++ } else if (that.directionX < 0) {
++ x = Math.ceil(x/that.scrollWidth);
++ } else {
++ x = Math.round(x/that.scrollWidth);
++ }
++ that.pageX = -x;
++ x = x * that.scrollWidth;
++ if (x > 0) {
++ x = that.pageX = 0;
++ } else if (x < that.maxScrollX) {
++ that.pageX = that.maxPageX;
++ x = that.maxScrollX;
++ }
++
++ if (that.directionY > 0) {
++ y = Math.floor(y/that.scrollHeight);
++ } else if (that.directionY < 0) {
++ y = Math.ceil(y/that.scrollHeight);
++ } else {
++ y = Math.round(y/that.scrollHeight);
++ }
++ that.pageY = -y;
++ y = y * that.scrollHeight;
++ if (y > 0) {
++ y = that.pageY = 0;
++ } else if (y < that.maxScrollY) {
++ that.pageY = that.maxPageY;
++ y = that.maxScrollY;
++ }
++
++ // Snap with constant speed (proportional duration)
++ time = Math.round(Math.max(
++ Math.abs(that.x - x) / that.scrollWidth * 500,
++ Math.abs(that.y - y) / that.scrollHeight * 500
++ ));
++
++ return { x: x, y: y, time: time };
++ },
++
++ scrollTo: function (destX, destY, runtime) {
++ var that = this;
++
++ if (that.x == destX && that.y == destY) {
++ that.resetPosition();
++ return;
++ }
++
++ that.moved = true;
++ that.setTransitionTime(runtime || '350ms');
++ that.setPosition(destX, destY);
++
++ if (runtime==='0' || runtime=='0s' || runtime=='0ms') {
++ that.resetPosition();
++ } else {
++ document.addEventListener('webkitTransitionEnd', that, false); // At the end of the transition check if we are still inside of the boundaries
++ }
++ },
++
++ scrollToPage: function (pageX, pageY, runtime) {
++ var that = this, snap;
++
++ if (!that.options.snap) {
++ that.pageX = -Math.round(that.x / that.scrollWidth);
++ that.pageY = -Math.round(that.y / that.scrollHeight);
++ }
++
++ if (pageX == 'next') {
++ pageX = ++that.pageX;
++ } else if (pageX == 'prev') {
++ pageX = --that.pageX;
++ }
++
++ if (pageY == 'next') {
++ pageY = ++that.pageY;
++ } else if (pageY == 'prev') {
++ pageY = --that.pageY;
++ }
++
++ pageX = -pageX*that.scrollWidth;
++ pageY = -pageY*that.scrollHeight;
++
++ snap = that.snap(pageX, pageY);
++ pageX = snap.x;
++ pageY = snap.y;
++
++ that.scrollTo(pageX, pageY, runtime || '500ms');
++ },
++
++ scrollToElement: function (el, runtime) {
++ el = typeof el == 'object' ? el : this.element.querySelector(el);
++
++ if (!el) {
++ return;
++ }
++
++ var that = this,
++ x = that.scrollX ? -el.offsetLeft : 0,
++ y = that.scrollY ? -el.offsetTop : 0;
++
++ if (x >= 0) {
++ x = 0;
++ } else if (x < that.maxScrollX) {
++ x = that.maxScrollX;
++ }
++
++ if (y >= 0) {
++ y = 0;
++ } else if (y < that.maxScrollY) {
++ y = that.maxScrollY;
++ }
++
++ that.scrollTo(x, y, runtime);
++ },
++
++ momentum: function (dist, time, maxDistUpper, maxDistLower) {
++ var friction = 2.5,
++ deceleration = 1.2,
++ speed = Math.abs(dist) / time * 1000,
++ newDist = speed * speed / friction / 1000,
++ newTime = 0;
++
++ // Proportinally reduce speed if we are outside of the boundaries
++ if (dist > 0 && newDist > maxDistUpper) {
++ speed = speed * maxDistUpper / newDist / friction;
++ newDist = maxDistUpper;
++ } else if (dist < 0 && newDist > maxDistLower) {
++ speed = speed * maxDistLower / newDist / friction;
++ newDist = maxDistLower;
++ }
++
++ newDist = newDist * (dist < 0 ? -1 : 1);
++ newTime = speed / deceleration;
++
++ return { dist: Math.round(newDist), time: Math.round(newTime) };
++ },
++
++ onScrollEnd: function () {},
++
++ destroy: function (full) {
++ var that = this;
++
++ window.removeEventListener('onorientationchange' in window ? 'orientationchange' : 'resize', that, false);
++ that.element.removeEventListener(START_EVENT, that, false);
++ that.element.removeEventListener(MOVE_EVENT, that, false);
++ that.element.removeEventListener(END_EVENT, that, false);
++ document.removeEventListener('webkitTransitionEnd', that, false);
++
++ if (that.options.checkDOMChanges) {
++ that.element.removeEventListener('DOMSubtreeModified', that, false);
++ }
++
++ if (that.scrollBarX) {
++ that.scrollBarX = that.scrollBarX.remove();
++ }
++
++ if (that.scrollBarY) {
++ that.scrollBarY = that.scrollBarY.remove();
++ }
++
++ if (full) {
++ that.wrapper.parentNode.removeChild(that.wrapper);
++ }
++
++ return null;
++ }
++};
++
++function scrollbar (dir, wrapper, fade, shrink) {
++ var that = this, style;
++
++ that.dir = dir;
++ that.fade = fade;
++ that.shrink = shrink;
++ that.uid = ++uid;
++
++ // Create main scrollbar
++ that.bar = document.createElement('div');
++
++ style = 'position:absolute;top:0;left:0;-webkit-transition-timing-function:cubic-bezier(0,0,0.25,1);pointer-events:none;-webkit-transition-duration:0;-webkit-transition-delay:0;-webkit-transition-property:-webkit-transform;z-index:10;background:rgba(0,0,0,0.5);' +
++ '-webkit-transform:' + translateOpen + '0,0' + translateClose + ';' +
++ (dir == 'horizontal' ? '-webkit-border-radius:3px 2px;min-width:6px;min-height:5px' : '-webkit-border-radius:2px 3px;min-width:5px;min-height:6px');
++
++ that.bar.setAttribute('style', style);
++
++ // Create scrollbar wrapper
++ that.wrapper = document.createElement('div');
++ style = '-webkit-mask:-webkit-canvas(scrollbar' + that.uid + that.dir + ');position:absolute;z-index:10;pointer-events:none;overflow:hidden;opacity:0;-webkit-transition-duration:' + (fade ? '300ms' : '0') + ';-webkit-transition-delay:0;-webkit-transition-property:opacity;' +
++ (that.dir == 'horizontal' ? 'bottom:2px;left:2px;right:7px;height:5px' : 'top:2px;right:2px;bottom:7px;width:5px;');
++ that.wrapper.setAttribute('style', style);
++
++ // Add scrollbar to the DOM
++ that.wrapper.appendChild(that.bar);
++ wrapper.appendChild(that.wrapper);
++}
++
++scrollbar.prototype = {
++ init: function (scroll, size) {
++ var that = this,
++ ctx;
++
++ // Create scrollbar mask
++ if (that.dir == 'horizontal') {
++ if (that.maxSize != that.wrapper.offsetWidth) {
++ that.maxSize = that.wrapper.offsetWidth;
++ ctx = document.getCSSCanvasContext("2d", "scrollbar" + that.uid + that.dir, that.maxSize, 5);
++ ctx.fillStyle = "rgb(0,0,0)";
++ ctx.beginPath();
++ ctx.arc(2.5, 2.5, 2.5, Math.PI/2, -Math.PI/2, false);
++ ctx.lineTo(that.maxSize-2.5, 0);
++ ctx.arc(that.maxSize-2.5, 2.5, 2.5, -Math.PI/2, Math.PI/2, false);
++ ctx.closePath();
++ ctx.fill();
++ }
++ } else {
++ if (that.maxSize != that.wrapper.offsetHeight) {
++ that.maxSize = that.wrapper.offsetHeight;
++ ctx = document.getCSSCanvasContext("2d", "scrollbar" + that.uid + that.dir, 5, that.maxSize);
++ ctx.fillStyle = "rgb(0,0,0)";
++ ctx.beginPath();
++ ctx.arc(2.5, 2.5, 2.5, Math.PI, 0, false);
++ ctx.lineTo(5, that.maxSize-2.5);
++ ctx.arc(2.5, that.maxSize-2.5, 2.5, 0, Math.PI, false);
++ ctx.closePath();
++ ctx.fill();
++ }
++ }
++
++ that.size = Math.max(Math.round(that.maxSize * that.maxSize / size), 6);
++ that.maxScroll = that.maxSize - that.size;
++ that.toWrapperProp = that.maxScroll / (scroll - size);
++ that.bar.style[that.dir == 'horizontal' ? 'width' : 'height'] = that.size + 'px';
++ },
++
++ setPosition: function (pos) {
++ var that = this;
++
++ if (that.wrapper.style.opacity != '1') {
++ that.show();
++ }
++
++ pos = Math.round(that.toWrapperProp * pos);
++
++ if (pos < 0) {
++ pos = that.shrink ? pos + pos*3 : 0;
++ if (that.size + pos < 7) {
++ pos = -that.size + 6;
++ }
++ } else if (pos > that.maxScroll) {
++ pos = that.shrink ? pos + (pos-that.maxScroll)*3 : that.maxScroll;
++ if (that.size + that.maxScroll - pos < 7) {
++ pos = that.size + that.maxScroll - 6;
++ }
++ }
++
++ pos = that.dir == 'horizontal'
++ ? translateOpen + pos + 'px,0' + translateClose
++ : translateOpen + '0,' + pos + 'px' + translateClose;
++
++ that.bar.style.webkitTransform = pos;
++ },
++
++ show: function () {
++ if (has3d) {
++ this.wrapper.style.webkitTransitionDelay = '0';
++ }
++ this.wrapper.style.opacity = '1';
++ },
++
++ hide: function () {
++ if (has3d) {
++ this.wrapper.style.webkitTransitionDelay = '350ms';
++ }
++ this.wrapper.style.opacity = '0';
++ },
++
++ remove: function () {
++ this.wrapper.parentNode.removeChild(this.wrapper);
++ return null;
++ }
++};
++
++// Is translate3d compatible?
++var has3d = ('WebKitCSSMatrix' in window && 'm11' in new WebKitCSSMatrix()),
++ // Device sniffing
++ isIphone = (/iphone/gi).test(navigator.appVersion),
++ isIpad = (/ipad/gi).test(navigator.appVersion),
++ isAndroid = (/android/gi).test(navigator.appVersion),
++ isTouch = isIphone || isIpad || isAndroid,
++ // Event sniffing
++ START_EVENT = isTouch ? 'touchstart' : 'mousedown',
++ MOVE_EVENT = isTouch ? 'touchmove' : 'mousemove',
++ END_EVENT = isTouch ? 'touchend' : 'mouseup',
++ // Translate3d helper
++ translateOpen = 'translate' + (has3d ? '3d(' : '('),
++ translateClose = has3d ? ',0)' : ')',
++ // Unique ID
++ uid = 0;
++
++// Expose iScroll to the world
++window.iScroll = iScroll;
++})();
+\ No newline at end of file
+diff --git a/configure.in b/configure.in
+index bca9239..4e178d3 100755
+--- a/configure.in
++++ b/configure.in
+@@ -572,9 +572,9 @@ if test "$host_vendor" = "apple" ; then
+ LIBS="$LIBS -framework ApplicationServices"
+ fi
+ elif test "$use_arch" = "arm"; then
+- CFLAGS="$CFLAGS -mfloat-abi=softfp -mno-apcs-stack-check"
+- CXXFLAGS="$CXXFLAGS -mfloat-abi=softfp -mno-apcs-stack-check"
+- FFMPEG_EXTRACFLAGS="-mfloat-abi=softfp"
++ CFLAGS="$CFLAGS -mno-apcs-stack-check"
++ CXXFLAGS="$CXXFLAGS -mno-apcs-stack-check"
++ FFMPEG_EXTRACFLAGS=""
+ if test "$use_tegra" = "yes"; then
+ # Compile for ARMv7a architecture, need to test gcc for vfpv3-d16 support
+ SAVE_CFLAGS="$CFLAGS"
+@@ -589,9 +589,6 @@ elif test "$use_arch" = "arm"; then
+ CXXFLAGS="$CXXFLAGS -Wa,-march=armv6 -mtune=cortex-a8 -mthumb-interwork"
+ use_cpu=cortex-a8])
+ else
+- # Compile for ARMv7a architecture, CortexA8 cpu and check for enabled NEON coprocessor
+- CFLAGS="$CFLAGS -Wa,-march=armv7a -mcpu=cortex-a8"
+- CXXFLAGS="$CXXFLAGS -Wa,-march=armv7a -mcpu=cortex-a8"
+ if test "$use_neon" = "yes"; then
+ CFLAGS="$CFLAGS -mfpu=neon -mvectorize-with-neon-quad"
+ CXXFLAGS="$CXXFLAGS -mfpu=neon -mvectorize-with-neon-quad"
+@@ -1248,7 +1245,11 @@ if test "$use_external_ffmpeg" = "yes"; then
+ AC_DEFINE([USE_EXTERNAL_FFMPEG], [1], [Whether to use external FFmpeg libraries.])
+
+ # Disable vdpau support if external libavcodec doesn't have it
+- AC_CHECK_LIB([avcodec], [ff_vdpau_vc1_decode_picture],,
++ AC_RUN_IFELSE(
++ AC_LANG_PROGRAM([[#include <libavcodec/avcodec.h>]],
++ [[avcodec_register_all();
++ AVCodec *codec = avcodec_find_decoder_by_name("vc1_vdpau");
++ return (codec) ? 0 : 1;]]),,
+ [if test "x$use_vdpau" = "xyes"; then
+ AC_MSG_ERROR($ffmpeg_vdpau_not_supported)
+ else
+@@ -1256,6 +1257,23 @@ if test "$use_external_ffmpeg" = "yes"; then
+ AC_MSG_RESULT($ffmpeg_vdpau_not_supported)
+ fi])
+
++ # Other headers to include if available.
++ AC_CHECK_HEADERS([libavutil/mathematics.h],,)
++
++ # Check if <libavfilter/vsrc_buffer.h> exists and defines old
++ # av_vsrc_buffer_add_frame() from SoC. This avoids multiple declarations of
++ # av_vsrc_buffer_add_frame().
++ AC_COMPILE_IFELSE(
++ AC_LANG_SOURCE([[
++ #include <libavfilter/vsrc_buffer.h>
++ void foo(void)
++ {
++ AVRational a;
++ av_vsrc_buffer_add_frame(NULL, NULL, 0, a);
++ }
++ ]]), AC_DEFINE([USE_OLD_AV_VSRC_BUFFER_ADD_FRAME],
++ [1], [Check if SoC av_vsrc_buffer_add_frame() is defined in libavfilter/vsrc_buffer.h.]),)
++
+ # Check for 'PIX_FMT_VDPAU_MPEG4' from libavutil
+ if test "x$use_vdpau" != "xno"; then
+ AC_LANG_PUSH([C++])
+@@ -1894,6 +1912,16 @@ OUTPUT_FILES="Makefile \
+ xbmc/visualizations/OpenGLSpectrum/Makefile \
+ xbmc/visualizations/WaveForm/Makefile \
+ xbmc/visualizations/iTunes/Makefile \
++ xbmc/pvrclients/Makefile.include \
++ xbmc/pvrclients/MediaPortal/Makefile \
++ xbmc/pvrclients/mythtv/Makefile \
++ xbmc/pvrclients/pvr-demo/Makefile \
++ xbmc/pvrclients/mythtv-cmyth/Makefile \
++ xbmc/pvrclients/tvheadend/Makefile \
++ xbmc/pvrclients/vdr-vnsi/Makefile \
++ lib/addons/library.xbmc.addon/Makefile \
++ lib/addons/library.xbmc.gui/Makefile \
++ lib/addons/library.xbmc.pvr/Makefile \
+ tools/Linux/xbmc.sh \
+ tools/Linux/xbmc-standalone.sh \
+ tools/TexturePacker/Makefile \
+diff --git a/language/Czech/strings.xml b/language/Czech/strings.xml
+index f226720..deed7ce 100644
+--- a/language/Czech/strings.xml
++++ b/language/Czech/strings.xml
+@@ -3,7 +3,7 @@
+ <!--Translator: ezechiel1917-->
+ <!--Date of translation: 26.09.2010-->
+ <!--Updated 03.12.2011 by mirek-->
+-<!--Updated 06.01.2012 by babÄa-->
++<!--Updated 06.01.2012 by babÄa: Live TV section-->
+ <strings>
+ <string id="0">Programy</string>
+ <string id="1">Obrázky</string>
+@@ -1545,7 +1545,449 @@
+
+ <string id="17500">Zobrazovat odpoÄet do uspání</string>
+
++ <!-- strings 19000 thru 19999 used for tv interface -->
+ <string id="19000">Přepnout na kanál</string>
++ <string id="19001">OddÄ›lte jednotlivá hledaná slova klíÄovými slovy AND, OR, NOT.</string>
++ <string id="19002">nebo pro vyhledání přesné fráze přesně vepište přesně text (např. "Čaroděj ze země Oz").</string>
++ <string id="19003">Najít podobné programy</string>
++ <string id="19004">NaÄítání EPG</string>
++ <string id="19005">Informace o PVR streamu</string>
++ <string id="19006">Přijímající zařízení</string>
++ <string id="19007">Stav zařízení</string>
++ <string id="19008">Kvalita signálu</string>
++ <string id="19009">SNR</string>
++ <string id="19010">BER</string>
++ <string id="19011">UNC</string>
++ <string id="19012">PVR Backend</string>
++ <string id="19013">Volně šířené programy</string>
++ <string id="19014">Fixed</string>
++ <string id="19015">Šifrování</string>
++ <string id="19016">PVR Backend %i - %s</string>
++ <string id="19017">Nahrané pořady</string>
++ <string id="19018">Složka pro umístění náhledů</string>
++ <string id="19019">Programy</string>
++ <string id="19020">Televize</string>
++ <string id="19021">Rádio</string>
++ <string id="19022">Skryté</string>
++ <string id="19023">Televize</string>
++ <string id="19024">Rádia</string>
++ <string id="19025">Plánovaná nahrávání</string>
++ <string id="19026">Naplánovat nové nahrávání...</string>
++ <string id="19027">Žádné výsledky vyhledávání</string>
++ <string id="19028">Žádné EPG</string>
++ <string id="19029">Kanál</string>
++ <string id="19030">PrávÄ› teÄ</string>
++ <string id="19031">Následuje</string>
++ <string id="19032">Časová osa</string>
++ <string id="19033">Informace</string>
++ <string id="19034">Již běží nahrávání tohoto programu.</string>
++ <string id="19035">Program nelze z nějakého důvodu spustit. Více informací v logu.</string>
++ <string id="19036">Nahrávka nelze z nějakého důvodu přehrát. Více informací v logu.</string>
++ <string id="19037">Zobrazit kvalitu signálu</string>
++ <string id="19038">Toto bohužel není PVR backendem podporováno.</string>
++ <string id="19039">Opravdu chcete skrýt tuto stanici?</string>
++ <string id="19040">PlánovaÄ</string>
++ <string id="19041">Opravdu chcete přejmenovat tuto nahrávku?</string>
++ <string id="19042">Opravdu chcete pÅ™ejmenovat tento ÄasovaÄ?</string>
++ <string id="19043">Nahrávání</string>
++ <string id="19044">Prosím zkontrolujte Vaše nastavení nebo mrkněte do logu pro více informací.</string>
++ <string id="19045">Není aktivní žádný PVR klient. BuÄ se teprve spouÅ¡tí, nebo není žádný nakonfigurován. PopřípadÄ› mrknÄ›te do logu pro více informací.</string>
++ <string id="19046">Nová stanice</string>
++ <string id="19047">Informace o programu</string>
++ <string id="19048">Upravit skupiny</string>
++ <string id="19049">Zobrazit stanici</string>
++ <string id="19050">Zobrazit viditelné stanice</string>
++ <string id="19051">Zobrazit skryté stanice</string>
++ <string id="19052">Přesunout stanici do:</string>
++ <string id="19053">Podrobnosti o nahrávce</string>
++ <string id="19054">Skrýt stanici</string>
++ <string id="19055">Žádné informace</string>
++ <string id="19056">Nový ÄasovaÄ</string>
++ <string id="19057">Upravit ÄasovaÄ</string>
++ <string id="19058">ÄŒasovaÄ je aktivní</string>
++ <string id="19059">Zastavit nahrávání</string>
++ <string id="19060">Odstranit Äasovat</string>
++ <string id="19061">Nový ÄasovaÄ</string>
++ <string id="19062">Řadit dle: Stanice</string>
++ <string id="19063">SkoÄit na zaÄátek</string>
++ <string id="19064">SkoÄit na konec</string>
++ <string id="19065">Výchozí zobrazení EPG</string>
++ <string id="19066">NaÄítání seznamu TV nahrávek</string>
++ <string id="19067">Tato položka se již nahrává.</string>
++ <string id="19068">Tato nahrávka nelze z nějakého důvodu smazat. Mrkněte do logu pro více informací.</string>
++ <string id="19069">EPG</string>
++ <string id="19070">Skenovat EPG informace každých</string> <!-- babÄa: check this later -->
++ <string id="19071">Aktualizovat EPG každých</string>
++ <string id="19072">Neukládat EPG data do databáze XBMC</string>
++ <string id="19073">Zpomalit přepínání programů o</string>
++ <string id="19074">Aktivní:</string>
++ <string id="19075">Název:</string>
++ <string id="19076">Složka:</string>
++ <string id="19077">Rádio:</string>
++ <string id="19078">Televize:</string>
++ <string id="19079">Den:</string>
++ <string id="19080">ZaÄátek:</string>
++ <string id="19081">Konec:</string>
++ <string id="19082">Priorita:</string>
++ <string id="19083">Doba trvání (dnů):</string>
++ <string id="19084">První den:</string>
++ <string id="19085">Neznámý kanál %u</string>
++ <string id="19086">Po-__-__-__-__-__-__</string>
++ <string id="19087">__-Út-__-__-__-__-__</string>
++ <string id="19088">__-__-St-__-__-__-__</string>
++ <string id="19089">__-__-__-ÄŒt-__-__-__</string>
++ <string id="19090">__-__-__-__-Pá-__-__</string>
++ <string id="19091">__-__-__-__-__-So-__</string>
++ <string id="19092">__-__-__-__-__-__-Ne</string>
++ <string id="19093">Po-Út-St-Čt-Pá-__-__</string>
++ <string id="19094">Po-Út-St-Čt-Pá-So-__</string>
++ <string id="19095">Po-Út-St-Čt-Pá-So-Ne</string>
++ <string id="19096">__-__-__-__-__-So-Ne</string>
++ <string id="19097">Zadejte název nahrávky</string>
++ <string id="19098">Upozornění</string>
++ <string id="19099">Nastaven ÄasovaÄ</string>
++ <string id="19100">Opravdu chcete tento kanál odstranit? VÄetnÄ› vÅ¡ech nastavených ÄasovaÄů pro tento kanál.</string>
++ <string id="19101">Tato stanice je zrovna používaná pro přehrávání.</string>
++ <string id="19102">Prosím přepněte na jinou stanici.</string>
++ <string id="19103">Vyhledat chybějící ikonky</string>
++ <string id="19104">Zadejte jméno složky pro nahrávky programů</string>
++ <string id="19105">Velikost:</string>
++ <string id="19106">Příští ÄasovaÄ nastaven na</string>
++ <string id="19107">v</string>
++ <string id="19108">Recordings not in sync. Check the log for details.</string> <!-- babÄa: check this later -->
++ <string id="19109">ÄŒasovaÄ se nepodaÅ™ilo uložit. Detaily se nacházejí v logu.</string>
++ <string id="19110">Nastala neoÄekávaná chyba. Zkuste to pozdÄ›ji, nebo mrknÄ›te do logu pro podrobnosti.</string>
++ <string id="19111">Chyba PVR backendu. Detaily se nacházejí v logu.</string>
++ <string id="19112">Timers not in sync. Detaily se nacházejí v logu.</string> <!-- babÄa: check this later -->
++ <string id="19113">Další</string>
++ <string id="19114">Verze</string>
++ <string id="19115">Adresa</string>
++ <string id="19116">Velikost disku</string>
++ <string id="19117">Vyhledat programy</string>
++ <string id="19118">Během prohledávání kanálů není možné PVR používat.</string>
++ <string id="19119">Na jakém serveru chcete vyhledávat?</string>
++ <string id="19120">PoÄet klientů</string>
++ <string id="19121">Předejít opakování</string>
++ <string id="19122">Tento ÄasovaÄ právÄ› nahrává. PÅ™esto ho chcete odstranit?</string>
++ <string id="19123">Pouze volně šířené programy</string>
++ <string id="19124">Ignorovat nastavené ÄasovaÄe</string>
++ <string id="19125">Ignorovat běžící nahrávání</string>
++ <string id="19126">ÄŒas zaÄátku</string>
++ <string id="19127">ÄŒas ukonÄení</string>
++ <string id="19128">Datum zaÄátku</string>
++ <string id="19129">Datum ukonÄení</string>
++ <string id="19130">Minimální délka nahrávání</string>
++ <string id="19131">Maximální délka nahrávání</string>
++ <string id="19132">Zahrnout neznámé/neurÄené žánry</string>
++ <string id="19133">Vyhledat text</string>
++ <string id="19134">Zahrnout popis</string>
++ <string id="19135">Záleží na velikosti písmen</string>
++ <string id="19136">Stanice nedostupná</string>
++ <string id="19137">Nenastaveny žádné skupiny</string>
++ <string id="19138">Nejdříve musíte vytvořit novou skupinu programů.</string>
++ <string id="19139">Název nové skupiny</string>
++ <string id="19141">Skupina</string>
++ <string id="19142">Vyhledávání</string>
++ <string id="19143">Nastavit skupiny programů</string>
++ <string id="19144">Nejsou nastaveny žádné skupiny</string>
++ <string id="19145">Seskupeno</string>
++ <string id="19146">Skupiny</string>
++ <string id="19147">PVR backend tuto akci nepodporuje. Mrkněte do logu pro podrobnosti.</string>
++ <string id="19148">Stanice</string>
++ <string id="19149">Po</string>
++ <string id="19150">Út</string>
++ <string id="19151">St</string>
++ <string id="19152">ÄŒt</string>
++ <string id="19153">Pá</string>
++ <string id="19154">So</string>
++ <string id="19155">Ne</string>
++ <string id="19156">od</string>
++ <string id="19157">Příští nahrávání</string>
++ <string id="19158">Právě se nahrává</string>
++ <string id="19159">od</string>
++ <string id="19160">do</string>
++ <string id="19161">Aktivní</string>
++ <string id="19162">Nahrávání aktivní</string>
++ <string id="19163">Nahrávky</string>
++ <string id="19164">Nahrávání se nepodařilo spustit. Mrkněte do logu pro podrobnosti.</string>
++ <string id="19165">Přepnout sem</string>
++ <string id="19166">PVR informace</string>
++ <string id="19167">Vyhledat chybějící ikonky programů</string>
++ <string id="19168">PÅ™epínat stanice bez potÅ™eby stisku tlaÄítka OK</string>
++ <string id="19169">Skrýt boxík s informacemi o videu</string>
++ <string id="19170">Timeout when starting playback</string> <!-- babÄa: what does this do? -->
++ <string id="19171">Spustit nahrávání na pozadí</string>
++ <string id="19172">Výchozí délka nahrávání (instantní nahrávání)</string>
++ <string id="19173">Výchozí priorita nahrávání</string>
++ <string id="19174">Výchozí životnost nahrávky</string>
++ <string id="19175">Spouštět plánované nahrávání dřív o</string>
++ <string id="19176">KonÄit plánované nahrávání pozdÄ›ji o</string>
++ <string id="19177">Přehrávání</string>
++ <string id="19178">Zobrazovat informace o kanálu při jejich přepínání</string>
++ <string id="19179">Automaticky skrývat informace o kanálu</string>
++ <string id="19180">Televize</string>
++ <string id="19181">Menu/OSD</string>
++ <string id="19182">PoÄet dní zobrazených v EPG</string>
++ <string id="19184">Délka zobrazení informací o kanálu</string>
++ <string id="19185">Resetovat databázi PVR</string>
++ <string id="19186">Všechna data v PVR databázi budou smazána. Tedy nastavení PVR, kanálů apod.</string>
++ <string id="19187">Resetovat databázi EPG</string>
++ <string id="19188">EPG se maže z databáze</string>
++ <string id="19189">Po startu spustit posledně naladěný kanál</string>
++ <string id="19190">Na pozadí</string>
++ <string id="19191">Služba PVR</string>
++ <string id="19192">Žádný z připojených PVR backendů nepodporuje vyhledávání stanic.</string>
++ <string id="19193">Vyhledávání stanic se nezdařilo spustit. Mrkněte do logu pro podrobnosti.</string>
++ <string id="19194">PokraÄovat?</string>
++ <string id="19195">Client actions</string> <!-- babÄa: what does this do? -->
++ <string id="19196">PVR client specific actions</string>
++ <string id="19197">Nahrávání zaÄalo: %s</string>
++ <string id="19198">Nahrávání skonÄilo: %s</string>
++ <string id="19199">Správce programů</string>
++ <string id="19200">Zdroj EPG:</string>
++ <string id="19201">Název:</string>
++ <string id="19202">Ikona:</string>
++ <string id="19203">Upravit stanici</string>
++ <string id="19204">Nová stanice</string>
++ <string id="19205">Správa skupin</string>
++ <string id="19206">Povolit EPG:</string>
++ <string id="19207">Skupina:</string>
++ <string id="19208">Zadejte jméno nové stanice</string>
++ <string id="19209">XBMC virtuální backend</string>
++ <string id="19210">Klient</string>
++ <string id="19211">Smazat stanici</string>
++ <string id="19212">Tento seznam obsahuje změny</string>
++ <string id="19213">Zvolit backend</string>
++ <string id="19214">Zadejte platnou URL adresu nového kanálu</string>
++ <string id="19215">PVR backend nepodporuje plánování.</string>
++ <string id="19216">Všechna rádia</string>
++ <string id="19217">VÅ¡echny televize</string>
++ <string id="19218">Viditelný</string>
++ <string id="19219">Neseskupené stanice</string>
++ <string id="19220">Stanice v</string>
++ <string id="19221">Synchronizovat skupiny stanic s backendy</string>
++ <string id="19222">EPG</string>
++ <string id="19223">Nepodařilo se aktivovat žádný PVR doplněk. Zkontrolujte nastavení nebo mrkněte do logu.</string>
++ <string id="19224">Nahrávání zrušeno</string>
++ <string id="19225">Nahrávání naplánováno</string>
++ <string id="19226">Nahrávání spuštěno</string>
++ <string id="19227">Nahrávání dokonÄeno</string>
++ <string id="19228">Nahrávání smazáno</string>
++ <string id="19229">Zavřít OSD při přepnutí stanice</string>
++ <string id="19230">Neaktualizovat EPG během sledování TV</string>
++ <string id="19231">Pořadí stanic vždy převzít z nastavení backendů</string>
++ <string id="19232">Smazat výsledky vyhledávání</string>
++ <string id="19233">Zobrazit upozornÄ›ní na zmÄ›ny ÄasovaÄů</string>
++ <string id="19234">PÅ™evzít Äísla stanic z nastavení backendu (lze pouze pokud používáte jediný PVR doplnÄ›k)</string>
++ <string id="19235">Manažer PVR se spouští</string>
++ <string id="19236">naÄítání programů</string>
++ <string id="19237">naÄítání ÄasovaÄů</string>
++ <string id="19238">naÄítání nahrávek</string>
++ <string id="19239">spouštění procesů na pozadí</string>
++ <string id="19240">Nemáte aktivovaný žádný PVR plugin</string>
++ <string id="19241">TV sekce v XBMC byla aktivována, aniž by byl povolen některý</string>
++ <string id="19242">PVR doplněk (frontend). Aktivujte alespoň jeden frontend</string>
++ <string id="19243">pro možnost využívání PVR funkcionality v XBMC.</string>
++
++ <string id="19244">PÅ™epnout Backend do stavu neÄinnosti po</string>
++ <string id="19245">Nastavit příkaz pro probuzení (cmd [timestamp])</string>
++ <string id="19246">Probudit backend pÅ™ed zaÄátkem nahrávání</string>
++ <string id="19247">DennÄ› probouzet</string>
++ <string id="19248">Čas probuzení (HH:MM:SS)</string>
++
++ <string id="19499">Jiné</string>
++ <string id="19500">Film/Drama</string>
++ <string id="19501">Detektivní/Thriller</string>
++ <string id="19502">Dobrodružný/Western/VáleÄný</string>
++ <string id="19503">Sci-Fi/Fantasy/Horor</string>
++ <string id="19504">Komedie</string>
++ <string id="19505">Telenovela/Melodrama/Folklór</string>
++ <string id="19506">Romantický</string>
++ <string id="19507">Vážný/Klasický/Duchovní/Historický</string>
++ <string id="19508">Pro dospělé</string>
++ <string id="19509"></string>
++ <string id="19510"></string>
++ <string id="19511"></string>
++ <string id="19512"></string>
++ <string id="19513"></string>
++ <string id="19514"></string>
++ <string id="19515"></string>
++ <string id="19516">Zprávy – komentáře</string>
++ <string id="19517">Zprávy – poÄasí</string>
++ <string id="19518">News Magazine</string>
++ <string id="19519">Documentary</string>
++ <string id="19520">Discussion/Interview/Debate</string>
++ <string id="19521"></string>
++ <string id="19522"></string>
++ <string id="19523"></string>
++ <string id="19524"></string>
++ <string id="19525"></string>
++ <string id="19526"></string>
++ <string id="19527"></string>
++ <string id="19528"></string>
++ <string id="19529"></string>
++ <string id="19530"></string>
++ <string id="19531"></string>
++ <string id="19532">Představení/Soutěž</string>
++ <string id="19533">Soutěž</string>
++ <string id="19534">Estráda</string>
++ <string id="19535">Talk show</string>
++ <string id="19536"></string>
++ <string id="19537"></string>
++ <string id="19538"></string>
++ <string id="19539"></string>
++ <string id="19540"></string>
++ <string id="19541"></string>
++ <string id="19542"></string>
++ <string id="19543"></string>
++ <string id="19544"></string>
++ <string id="19545"></string>
++ <string id="19546"></string>
++ <string id="19547"></string>
++ <string id="19548">Sport</string>
++ <string id="19549">Speciální událost</string>
++ <string id="19550">Sportovní magazín</string>
++ <string id="19551">Fotbal</string>
++ <string id="19552">Tenis/squash</string>
++ <string id="19553">Týmové sporty</string>
++ <string id="19554">Atletika</string>
++ <string id="19555">Automobilové sporty</string>
++ <string id="19556">Vodní sporty</string>
++ <string id="19557">Zimní sporty</string>
++ <string id="19558">Jezdectví</string>
++ <string id="19559">Bojovné sporty</string>
++ <string id="19560"></string>
++ <string id="19561"></string>
++ <string id="19562"></string>
++ <string id="19563"></string>
++ <string id="19564">Program pro děti</string>
++ <string id="19565">Program pro předškolní děti</string>
++ <string id="19566">Zábavný pořad pro děti 6 – 14 let</string>
++ <string id="19567">Zábavný pořad pro děti 10 – 16 let</string>
++ <string id="19568">NauÄný/výukový program</string>
++ <string id="19569">Kreslený/Loutkový</string>
++ <string id="19570"></string>
++ <string id="19571"></string>
++ <string id="19572"></string>
++ <string id="19573"></string>
++ <string id="19574"></string>
++ <string id="19575"></string>
++ <string id="19576"></string>
++ <string id="19577"></string>
++ <string id="19578"></string>
++ <string id="19579"></string>
++ <string id="19580">Hudba/Balet/Tanec</string>
++ <string id="19581">Rock/Pop</string>
++ <string id="19582">Vážná/klasická hudba</string>
++ <string id="19583">Lidová hudba</string>
++ <string id="19584">Muzikál/Opera</string>
++ <string id="19585">Balet</string>
++ <string id="19586"></string>
++ <string id="19587"></string>
++ <string id="19588"></string>
++ <string id="19589"></string>
++ <string id="19590"></string>
++ <string id="19591"></string>
++ <string id="19592"></string>
++ <string id="19593"></string>
++ <string id="19594"></string>
++ <string id="19595"></string>
++ <string id="19596">Umění/Kultura</string>
++ <string id="19597">Herecké umění</string>
++ <string id="19598">Krásná umění</string>
++ <string id="19599">Náboženský</string>
++ <string id="19600">Popular Culture/Traditional Arts</string>
++ <string id="19601">Literatura</string>
++ <string id="19602">Film/Kino</string>
++ <string id="19603">Experimentální film/video</string>
++ <string id="19604">Broadcasting/Press</string>
++ <string id="19605">New Media</string>
++ <string id="19606">Arts/Culture Magazines</string>
++ <string id="19607">Móda</string>
++ <string id="19608"></string>
++ <string id="19609"></string>
++ <string id="19610"></string>
++ <string id="19611"></string>
++ <string id="19612">Social/Political/Economics</string>
++ <string id="19613">Magazín/Dokumentární</string>
++ <string id="19614">Economics/Social Advisory</string>
++ <string id="19615">Významná osobnost</string>
++ <string id="19616"></string>
++ <string id="19617"></string>
++ <string id="19618"></string>
++ <string id="19619"></string>
++ <string id="19620"></string>
++ <string id="19621"></string>
++ <string id="19622"></string>
++ <string id="19623"></string>
++ <string id="19624"></string>
++ <string id="19625"></string>
++ <string id="19626"></string>
++ <string id="19627"></string>
++ <string id="19628">Výukový/věda/fakta</string>
++ <string id="19629">Příroda/zvířata/životní prostředí</string>
++ <string id="19630">Technologie/přírodní vědy</string>
++ <string id="19631">Lékařství/fyziologie/psychologie</string>
++ <string id="19632">Expedice</string>
++ <string id="19633">SpoleÄenské/Duchovní vÄ›dy</string>
++ <string id="19634">Further Education</string>
++ <string id="19635">Jazyky</string>
++ <string id="19636"></string>
++ <string id="19637"></string>
++ <string id="19638"></string>
++ <string id="19639"></string>
++ <string id="19640"></string>
++ <string id="19641"></string>
++ <string id="19642"></string>
++ <string id="19643"></string>
++ <string id="19644">Volný Äas</string>
++ <string id="19645">Turistika/Cestování</string>
++ <string id="19646">Řemeslo</string>
++ <string id="19647">Motorismus</string>
++ <string id="19648">Fitness a zdraví</string>
++ <string id="19649">Vaření</string>
++ <string id="19650">Reklama/Teleshopping</string>
++ <string id="19651">ZahradniÄení</string>
++ <string id="19652"></string>
++ <string id="19653"></string>
++ <string id="19654"></string>
++ <string id="19655"></string>
++ <string id="19656"></string>
++ <string id="19657"></string>
++ <string id="19658"></string>
++ <string id="19659"></string>
++ <string id="19660">Special Characteristics</string>
++ <string id="19661">Original Language</string>
++ <string id="19662">Black &amp; White</string>
++ <string id="19663">Neuvedený</string>
++ <string id="19664">Živé vysílání</string>
++ <string id="19665"></string>
++ <string id="19666"></string>
++ <string id="19667"></string>
++ <string id="19668"></string>
++ <string id="19669"></string>
++ <string id="19670"></string>
++ <string id="19671"></string>
++ <string id="19672"></string>
++ <string id="19673"></string>
++ <string id="19674"></string>
++ <string id="19675"></string>
++ <string id="19676">Drama</string>
++ <string id="19677">Detektivní/Thriller</string>
++ <string id="19678">Dobrodružný/Western/VáleÄný</string>
++ <string id="19679">Sci-Fi/Fantasy/Horor</string>
++ <string id="19680">Komedie</string>
++ <string id="19681">Telenovela/Melodrama/Folklór</string>
++ <string id="19682">Romantický</string>
++ <string id="19683">Vážný/Klasický/Duchovní/Historický</string>
++ <string id="19684">Pro dospělé</string>
++ <string id="19685"></string>
++ <string id="19686"></string>
++ <string id="19687"></string>
++ <string id="19688"></string>
++ <string id="19689"></string>
++ <string id="19690"></string>
++ <string id="19691"></string>
+
+ <string id="20000">Složka pro ukládání hudby</string>
+ <string id="20001">Použít externí DVD pÅ™ehrávaÄ</string>
+diff --git a/language/Dutch/strings.xml b/language/Dutch/strings.xml
+index f1e3419..643e4ef 100644
+--- a/language/Dutch/strings.xml
++++ b/language/Dutch/strings.xml
+@@ -1,5 +1,10 @@
+ <?xml version="1.0" encoding="utf-8" standalone="yes"?>
+ <!--Language file translated with Team XBMC Translator-->
++<!--Translator: Lars Op den Kamp-->
++<!--Email: lars@opdenkamp.eu-->
++<!--Date of translation: 03/05/2011-->
++<!--$Revision$-->
++<!--Based on english strings version 32586-->
+ <strings>
+ <string id="0">Programma's</string>
+ <string id="1">Afbeeldingen</string>
+@@ -1010,7 +1015,7 @@
+ <string id="13013">Opnieuw opstarten</string>
+ <string id="13014">Minimaliseren</string>
+ <string id="13015">Actie voor aan/uitknop</string>
+- <string id="13016">Systeem Uitschakelen</string>
++ <string id="13016">Uitschakelen</string>
+
+ <string id="13020">Er is een andere sessie actief, wellicht SSH?</string>
+ <string id="13021">Verwisselbare harde schijf aankoppelen?</string>
+@@ -1102,10 +1107,8 @@
+
+ <string id="13283">Besturingssysteem:</string>
+ <string id="13284">Kloksnelheid:</string>
+-
+ <string id="13286">Video-encoder:</string>
+ <string id="13287">Schermresolutie:</string>
+-
+ <string id="13292">A/V-kabel:</string>
+
+ <string id="13294">DVD-regio:</string>
+@@ -1250,7 +1253,7 @@
+ <string id="13551">Uit</string>
+ <string id="13552">%.1f Seconde</string>
+ <string id="13553">%.1f Seconden</string>
+-
++
+ <string id="13600">Type Apple afstandsbediening</string>
+
+ <string id="13602">Gebruik afstandsbediening om XBMC zelf op te staren</string>
+@@ -1539,13 +1542,546 @@
+ <string id="16322">Spline36</string>
+ <string id="16323">Spline36 geoptimaliseerd</string>
+ <string id="16324">Software menging</string>
++ <string id="16325">Auto - ION geoptimaliseerd</string>
+
+ <string id="16400">Kwaliteitsverbetering video</string>
+
+ <string id="17500">Uitschakeltijd beeldscherm</string>
+-
++ <string id="17997">%i MB</string>
++ <string id="17998">%i uren</string>
++ <string id="17999">%i dagen</string>
+ <string id="19000">Kanaal wijzigen</string>
+-
++ <string id="19001">Scheid de zoekwoorden door middel van AND, OR en/of NOT om de zoekopdracht te specificeren.</string>
++ <string id="19002">Of gebruik zinnen om op een tekst te zoeken, bijv. "Toen was geluk heel gewoon".</string>
++ <string id="19003">Zoek vergelijkbaar programma</string>
++ <string id="19004">Gids van clients wordt geladen</string>
++ <string id="19005">PVR stream informatie</string>
++ <string id="19006">Ontvanger</string>
++ <string id="19007">Status ontvanger</string>
++ <string id="19008">Signaalkwaliteit</string>
++ <string id="19009">SNR</string>
++ <string id="19010">BER</string>
++ <string id="19011">UNC</string>
++ <string id="19012">Server</string>
++ <string id="19013">Ongecodeerd</string>
++ <string id="19014">Vast</string>
++ <string id="19015">Versleuteling</string>
++ <string id="19016">Server %i - %s</string>
++ <string id="19017">TV Opnames</string>
++ <string id="19018">Standaard map voor TV thumbnails</string>
++ <string id="19019">Kanalen</string>
++ <string id="19020">TV</string>
++ <string id="19021">Radio</string>
++ <string id="19022">Verborgen</string>
++ <string id="19023">TV kanalen</string>
++ <string id="19024">Radio kanalen</string>
++ <string id="19025">Toekomstige opnames</string>
++ <string id="19026">Voeg opname toe...</string>
++ <string id="19027">Geen zoekresultaten</string>
++ <string id="19028">Geen gids aanwezig</string>
++ <string id="19029">TV gids</string>
++ <string id="19030">Nu</string>
++ <string id="19031">Straks</string>
++ <string id="19032">Tijdslijn</string>
++ <string id="19033">Informatie</string>
++ <string id="19034">Opname is al gestart</string>
++ <string id="19035">Kanaal kan niet worden weergegeven</string>
++ <string id="19036">Opname kan niet worden weergegeven</string>
++ <string id="19037">Toon signaal kwaliteit</string>
++ <string id="19038">Op dit moment niet ondersteund!</string>
++ <string id="19039">Bevestig verbergen van het kanaal</string>
++ <string id="19040">Geplande opname</string>
++ <string id="19041">Hernoem opname?</string>
++ <string id="19042">Hernoem geplande opname?</string>
++ <string id="19043">Opname</string>
++ <string id="19044">Controleer de instellingen van de server</string>
++ <string id="19045">Geen PVR clients beschikbaar</string>
++ <string id="19046">Nieuw kanaal</string>
++ <string id="19047">Programma info</string>
++ <string id="19048">Beheer groepen</string>
++ <string id="19049">Toon kanaal</string>
++ <string id="19050">Toon normale kanalen</string>
++ <string id="19051">Toon verborgen kanalen</string>
++ <string id="19052">Verplaats kanaal naar:</string>
++ <string id="19053">Opname informatie</string>
++ <string id="19054">Verberg kanaal</string>
++ <string id="19055">Geen informatie beschikbaar!</string>
++ <string id="19056">Nieuwe geplande opname</string>
++ <string id="19057">Pas geplande opname aan</string>
++ <string id="19058">Geplande opname aan/uit</string>
++ <string id="19059">Stop opname</string>
++ <string id="19060">Annuleer opname</string>
++ <string id="19061">Opnemen</string>
++ <string id="19062">Sorteer op: Kanaal</string>
++ <string id="19063">Ga naar begin</string>
++ <string id="19064">Ga naar einde</string>
++ <string id="19065">Standaardgids</string>
++ <string id="19066">Opname informatie wordt geladen</string>
++ <string id="19067">Dit programma wordt al opgenomen</string>
++ <string id="19068">Kon de opname niet verwijderen</string>
++ <string id="19069">TV gids</string>
++ <string id="19070">TV gids scan timeout</string>
++ <string id="19071">TV gids update timeout</string>
++ <string id="19072">TV gids niet opslaan in de database</string>
++ <string id="19073">Stel wisselen kanaal uit</string>
++ <string id="19074">Actief:</string>
++ <string id="19075">Naam:</string>
++ <string id="19076">Map:</string>
++ <string id="19077">Radio:</string>
++ <string id="19078">Kanaal:</string>
++ <string id="19079">Dag:</string>
++ <string id="19080">Begin:</string>
++ <string id="19081">Einde:</string>
++ <string id="19082">Prioriteit:</string>
++ <string id="19083">Levensduur (dagen):</string>
++ <string id="19084">Eerste dag:</string>
++ <string id="19085">Onbekend kanaal: %u</string>
++ <string id="19086">Ma-__-__-__-__-__-__</string>
++ <string id="19087">__-Di-__-__-__-__-__</string>
++ <string id="19088">__-__-Wo-__-__-__-__</string>
++ <string id="19089">__-__-__-Do-__-__-__</string>
++ <string id="19090">__-__-__-__-Vr-__-__</string>
++ <string id="19091">__-__-__-__-__-Za-__</string>
++ <string id="19092">__-__-__-__-__-__-Zo</string>
++ <string id="19093">Ma-Di-Wo-Do-Vr-__-__</string>
++ <string id="19094">Ma-Di-Wo-Do-Vr-Za-__</string>
++ <string id="19095">Ma-Di-Wo-Do-Vr-Za-Zo</string>
++ <string id="19096">__-__-__-__-__-Za-Zo</string>
++ <string id="19097">Naam van de opname</string>
++ <string id="19098">Waarschuwing</string>
++ <string id="19099">Opname aanwezig</string>
++ <string id="19100">Verwijder kanaal en opname?</string>
++ <string id="19101">Kanaal wordt afgespeeld</string>
++ <string id="19102">Schakel over naar een ander kanaal!</string>
++ <string id="19103">Zoek missende iconen</string>
++ <string id="19104">Geef naam van de opname map</string>
++ <string id="19105">Grootte:</string>
++ <string id="19106">Volgende opname op</string>
++ <string id="19107">om</string>
++ <string id="19108">Opnames niet gesynchroniseerd</string>
++ <string id="19109">Kon de opname niet opslaan!</string>
++ <string id="19110">Probeer nog eens...</string>
++ <string id="19111">Server fout!</string>
++ <string id="19112">Opnames niet gesynchroniseerd</string>
++ <string id="19113">Volgende</string>
++ <string id="19114">Versie</string>
++ <string id="19115">Adres</string>
++ <string id="19116">Schijf grootte</string>
++ <string id="19117">Zoek naar kanalen</string>
++ <string id="19118">Kan de instellingen van de TV niet aanpassen tijdens het zoeken!</string>
++ <string id="19119">Op welke server wilt u zoeken?</string>
++ <string id="19120">Client nummer</string>
++ <string id="19121">Vermijd herhalingen</string>
++ <string id="19122">Opname loopt nog. Wilt u deze verwijderen?</string>
++ <string id="19123">Alleen ongecodeerd</string>
++ <string id="19124">Negeer bij lopende opnames</string>
++ <string id="19125">Negeer bij opgenomen programma's</string>
++ <string id="19126">Starttijd</string>
++ <string id="19127">Eindtijd</string>
++ <string id="19128">Startdatum</string>
++ <string id="19129">Einddatum</string>
++ <string id="19130">Minimale lengte</string>
++ <string id="19131">Maximale lengte</string>
++ <string id="19132">Neem onbekende genres op</string>
++ <string id="19133">Zoekopdracht</string>
++ <string id="19134">Neem omschrijving op</string>
++ <string id="19135">Hoofdlettergevoelig</string>
++ <string id="19136">Kanaal niet beschikbaar</string>
++ <string id="19137">Geen groepen gedifinieerd</string>
++ <string id="19138">Maak er eerst een aan</string>
++ <string id="19139">Naam nieuwe groep</string>
++ <string id="19141">Groep</string>
++ <string id="19142">Zoek in gids</string>
++ <string id="19143">Beheer groepen</string>
++ <string id="19144">Geen groepen</string>
++ <string id="19145">Gegroepeerd</string>
++ <string id="19146">Groepen</string>
++ <string id="19147">PVR-Server is niet compatibel!</string>
++ <string id="19148">Kanaal</string>
++ <string id="19149">Ma</string>
++ <string id="19150">Di</string>
++ <string id="19151">Wo</string>
++ <string id="19152">Do</string>
++ <string id="19153">Vr</string>
++ <string id="19154">Za</string>
++ <string id="19155">Zo</string>
++ <string id="19156">van</string>
++ <string id="19157">Volgende opname</string>
++ <string id="19158">Wordt nu opgenomen</string>
++ <string id="19159">van</string>
++ <string id="19160">tot</string>
++ <string id="19161">op</string>
++ <string id="19162">Wordt opgenomen</string>
++ <string id="19163">Opnames</string>
++ <string id="19164">Kan de opname niet starten</string>
++ <string id="19165">Wijzig kanaal</string>
++ <string id="19166">PVR informatie</string>
++ <string id="19167">Zoek missende iconen</string>
++ <string id="19168">Wijzig kanaal zonder op OK te drukken</string>
++ <string id="19169">Verberg video lengte informatie</string>
++ <string id="19170">Scan timeout voor kanaal afspelen</string>
++ <string id="19171">Start afspelen geminimaliseerd</string>
++ <string id="19172">Lengte directe opname</string>
++ <string id="19173">Standaard prioriteit</string>
++ <string id="19174">Standaard levensduur</string>
++ <string id="19175">Marge opname begin</string>
++ <string id="19176">Marge opname einde</string>
++ <string id="19177">Afspelen</string>
++ <string id="19178">Info bij wisselen kanaal</string>
++ <string id="19179">Timeout kanaal informatie</string>
++ <string id="19180">TV</string>
++ <string id="19181">Menu/OSD</string>
++ <string id="19182">Weergegeven dagen in de TV gids</string>
++ <string id="19184">Kanaal info tijd</string>
++ <string id="19185">Wis TV database</string>
++ <string id="19186">Alle gegevens in de TV database worden gewist</string>
++ <string id="19187">Wis TV gids en lees nieuwe in</string>
++ <string id="19188">TV gids wordt gewist</string>
++ <string id="19189">Hervat laatste kanaal bij opstarten</string>
++ <string id="19190">Geminimaliseerd</string>
++ <string id="19191">PVR service</string>
++ <string id="19192">Geen server ondersteunt het scannen naar kanalen</string>
++ <string id="19193">Kon het scannen naar kanalen niet starten</string>
++ <string id="19194">Hervatten?</string>
++ <string id="19195">Client acties</string>
++ <string id="19196">PVR client specifieke acties</string>
++ <string id="19197">Opname gestart om: %s</string>
++ <string id="19198">Opname beeindigd om: %s</string>
++ <string id="19199">Beheer kanalen</string>
++ <string id="19200">TV gids bron:</string>
++ <string id="19201">Naam kanaal:</string>
++ <string id="19202">Kanaal icoon:</string>
++ <string id="19203">Pas kanaal aan</string>
++ <string id="19204">Nieuw kanaal</string>
++ <string id="19205">Beheer groepen</string>
++ <string id="19206">Activeer TV gids:</string>
++ <string id="19207">Groep:</string>
++ <string id="19208">Voer de naam van het nieuwe kanaal in</string>
++ <string id="19209">XBMC intern virtueel kanaal</string>
++ <string id="19210">Client</string>
++ <string id="19211">Verwijder kanaal</string>
++ <string id="19212">Lijst bevat wijzigigen</string>
++ <string id="19213">Selecteer client voor het kanaal</string>
++ <string id="19214">Voer een geldige URL in voor het kanaal</string>
++ <string id="19215">De PVR server ondersteunt geen timers!</string>
++ <string id="19216">Alle radio kanalen</string>
++ <string id="19217">Alle TV kanalen</string>
++ <string id="19218">Zichtbare</string>
++ <string id="19219">Ongegroepeerde kanalen</string>
++ <string id="19220">Kanalen in</string>
++ <string id="19221">Synchroniseer groepen met backends</string>
++ <string id="19222">Gids</string>
++ <string id="19223">Kon geen PVR client inschakelen. Kijk je instellingen na of je log.</string>
++ <string id="19224">Opname geannuleerd</string>
++ <string id="19225">Opname gepland</string>
++ <string id="19226">Opname gestart</string>
++ <string id="19227">Opname voltooid</string>
++ <string id="19228">Opname verwijderd</string>
++ <string id="19229">Sluit kanelen OSD na wijzigen kanaal</string>
++ <string id="19230">Geen EPG updates tijdens het afspelen van een TV stream</string>
++ <string id="19231">Gebruik altijd de volgorde van kanalen van backend(s)</string>
++ <string id="19232">Zoekresultaten wissen</string>
++ <string id="19233">Toon een melding bij timer updates</string>
++ <string id="19234">Gebruik kanaalnummers van de backend (werkt alleen als 1 PVR addon actief is)</string>
++
++ <string id="19499">Anders/Onbekend</string>
++ <string id="19500">Film/Drama</string>
++ <string id="19501">Detective/Thriller</string>
++ <string id="19502">Avontuur/Western/Oorlog</string>
++ <string id="19503">Science Fiction/Fantasie/Horror</string>
++ <string id="19504">Komedie</string>
++ <string id="19505">Soap/Melodrama/Folklore</string>
++ <string id="19506">Romantisch</string>
++ <string id="19507">Serieus/Klassiek/Religie/Historische Film/Drama</string>
++ <string id="19508">Volwassenen Film/Drama</string>
++ <string id="19509">
++ </string>
++ <string id="19510">
++ </string>
++ <string id="19511">
++ </string>
++ <string id="19512">
++ </string>
++ <string id="19513">
++ </string>
++ <string id="19514">
++ </string>
++ <string id="19515">
++ </string>
++ <string id="19516">Nieuws/Actualiteiten</string>
++ <string id="19517">Nieuws/Weer</string>
++ <string id="19518">Nieuws magazine</string>
++ <string id="19519">Documentaire</string>
++ <string id="19520">Discussie/Interview/Debat</string>
++ <string id="19521">
++ </string>
++ <string id="19522">
++ </string>
++ <string id="19523">
++ </string>
++ <string id="19524">
++ </string>
++ <string id="19525">
++ </string>
++ <string id="19526">
++ </string>
++ <string id="19527">
++ </string>
++ <string id="19528">
++ </string>
++ <string id="19529">
++ </string>
++ <string id="19530">
++ </string>
++ <string id="19531">
++ </string>
++ <string id="19532">Show/Spelshow</string>
++ <string id="19533">Spelshow/Quiz/Wedstrijd</string>
++ <string id="19534">Variété</string>
++ <string id="19535">Praatprogramma</string>
++ <string id="19536">
++ </string>
++ <string id="19537">
++ </string>
++ <string id="19538">
++ </string>
++ <string id="19539">
++ </string>
++ <string id="19540">
++ </string>
++ <string id="19541">
++ </string>
++ <string id="19542">
++ </string>
++ <string id="19543">
++ </string>
++ <string id="19544">
++ </string>
++ <string id="19545">
++ </string>
++ <string id="19546">
++ </string>
++ <string id="19547">
++ </string>
++ <string id="19548">Sport</string>
++ <string id="19549">Speciaal</string>
++ <string id="19550">Sport Magazine</string>
++ <string id="19551">Voetbal</string>
++ <string id="19552">Tennis/Squash</string>
++ <string id="19553">Team Sport</string>
++ <string id="19554">Atletiek</string>
++ <string id="19555">Motorsport</string>
++ <string id="19556">Watersport</string>
++ <string id="19557">Wintersport</string>
++ <string id="19558">Ruitersport</string>
++ <string id="19559">Vechtsport</string>
++ <string id="19560">
++ </string>
++ <string id="19561">
++ </string>
++ <string id="19562">
++ </string>
++ <string id="19563">
++ </string>
++ <string id="19564">Kinderen/jeugdprogramma's</string>
++ <string id="19565">Peuter programma's</string>
++ <string id="19566">Entertainment programma's voor 6 tot 14</string>
++ <string id="19567">Entertainment programma's voor 10 tot 16</string>
++ <string id="19568">Informatie/Educatie/School Programma's</string>
++ <string id="19569">Cartoons/Marionetten</string>
++ <string id="19570">
++ </string>
++ <string id="19571">
++ </string>
++ <string id="19572">
++ </string>
++ <string id="19573">
++ </string>
++ <string id="19574">
++ </string>
++ <string id="19575">
++ </string>
++ <string id="19576">
++ </string>
++ <string id="19577">
++ </string>
++ <string id="19578">
++ </string>
++ <string id="19579">
++ </string>
++ <string id="19580">Muziek/Ballet/Dans</string>
++ <string id="19581">Rock/Pop</string>
++ <string id="19582">Serieus/Klassieke muziek</string>
++ <string id="19583">Folklore/Tradionele muziek</string>
++ <string id="19584">Muziek/Opera</string>
++ <string id="19585">Ballet</string>
++ <string id="19586">
++ </string>
++ <string id="19587">
++ </string>
++ <string id="19588">
++ </string>
++ <string id="19589">
++ </string>
++ <string id="19590">
++ </string>
++ <string id="19591">
++ </string>
++ <string id="19592">
++ </string>
++ <string id="19593">
++ </string>
++ <string id="19594">
++ </string>
++ <string id="19595">
++ </string>
++ <string id="19596">Kunst/Cultuur</string>
++ <string id="19597">Uitvoerende kunsten</string>
++ <string id="19598">Fijne kunsten</string>
++ <string id="19599">Religie</string>
++ <string id="19600">Populaire cultuur/Tradionele kunst</string>
++ <string id="19601">Literatuur</string>
++ <string id="19602">Film/Cinema</string>
++ <string id="19603">Experimentele film/Video</string>
++ <string id="19604">Uitzending/Pers</string>
++ <string id="19605">Nieuwe media</string>
++ <string id="19606">Kunst/Cultuur magazines</string>
++ <string id="19607">Mode</string>
++ <string id="19608">
++ </string>
++ <string id="19609">
++ </string>
++ <string id="19610">
++ </string>
++ <string id="19611">
++ </string>
++ <string id="19612">Sociaal/Politiek/Economisch</string>
++ <string id="19613">Magazines/Verslagen/Documentaires</string>
++ <string id="19614">Economisch/Maatschappelijk advies</string>
++ <string id="19615">Opmerkelijke personen</string>
++ <string id="19616">
++ </string>
++ <string id="19617">
++ </string>
++ <string id="19618">
++ </string>
++ <string id="19619">
++ </string>
++ <string id="19620">
++ </string>
++ <string id="19621">
++ </string>
++ <string id="19622">
++ </string>
++ <string id="19623">
++ </string>
++ <string id="19624">
++ </string>
++ <string id="19625">
++ </string>
++ <string id="19626">
++ </string>
++ <string id="19627">
++ </string>
++ <string id="19628">Educatie/Wetenschap/Feiten</string>
++ <string id="19629">Natuur/Dieren/Milieu</string>
++ <string id="19630">Technologie/Natuurwetenschappen</string>
++ <string id="19631">Geneeskunde/Physiologie/Psychologie</string>
++ <string id="19632">Buitenland/Expedities</string>
++ <string id="19633">Sociale/Spirituele wetenschap</string>
++ <string id="19634">Verder leren</string>
++ <string id="19635">Talen</string>
++ <string id="19636">
++ </string>
++ <string id="19637">
++ </string>
++ <string id="19638">
++ </string>
++ <string id="19639">
++ </string>
++ <string id="19640">
++ </string>
++ <string id="19641">
++ </string>
++ <string id="19642">
++ </string>
++ <string id="19643">
++ </string>
++ <string id="19644">Vrije tijd/Hobbies</string>
++ <string id="19645">Tourisme/Reizen</string>
++ <string id="19646">Handwerk</string>
++ <string id="19647">Autorijden</string>
++ <string id="19648">Fitness &amp; Gezondheid</string>
++ <string id="19649">Koken</string>
++ <string id="19650">Reclame/Winkelen</string>
++ <string id="19651">Tuinieren</string>
++ <string id="19652">
++ </string>
++ <string id="19653">
++ </string>
++ <string id="19654">
++ </string>
++ <string id="19655">
++ </string>
++ <string id="19656">
++ </string>
++ <string id="19657">
++ </string>
++ <string id="19658">
++ </string>
++ <string id="19659">
++ </string>
++ <string id="19660">Bijzondere kenmerken</string>
++ <string id="19661">Oude talen</string>
++ <string id="19662">Zwart/Wit</string>
++ <string id="19663">Niet gepubliceerd</string>
++ <string id="19664">Live uitzending</string>
++ <string id="19665">
++ </string>
++ <string id="19666">
++ </string>
++ <string id="19667">
++ </string>
++ <string id="19668">
++ </string>
++ <string id="19669">
++ </string>
++ <string id="19670">
++ </string>
++ <string id="19671">
++ </string>
++ <string id="19672">
++ </string>
++ <string id="19673">
++ </string>
++ <string id="19674">
++ </string>
++ <string id="19675">
++ </string>
++ <string id="19676">Drama</string>
++ <string id="19677">Detective/Thriller</string>
++ <string id="19678">Avontuur/Western/Oorlog</string>
++ <string id="19679">Science Fiction/Fantasie/Horror</string>
++ <string id="19680">Comedy</string>
++ <string id="19681">Soap/Melodrama/Folklore</string>
++ <string id="19682">Romantisch</string>
++ <string id="19683">Serieus/Klassiek-Regionaal/Historisch</string>
++ <string id="19684">Volwassenen</string>
++ <string id="19685">
++ </string>
++ <string id="19686">
++ </string>
++ <string id="19687">
++ </string>
++ <string id="19688">
++ </string>
++ <string id="19689">
++ </string>
++ <string id="19690">
++ </string>
++ <string id="19691">
++ </string>
++
+ <string id="20000">Opgeslagen muziek map...</string>
+ <string id="20001">Andere DVD-speler gebruiken</string>
+ <string id="20002">- Locatie van de DVD-speler</string>
+@@ -1765,6 +2301,8 @@
+ <string id="20310">LCD dimmen tijdens afspelen</string>
+ <string id="20311">Onbekend of onboard (beveiligd)</string>
+ <string id="20312">LCD dimmen tijdens pauze</string>
++ <string id="20314">Video - Bibliotheek</string>
++ <string id="20316">Sorteer: ID</string>
+
+ <string id="20314">Video - Bibliotheek</string>
+
+@@ -2182,6 +2720,7 @@
+ <string id="24016">Albuminformatie</string>
+ <string id="24017">Artiestinformatie</string>
+ <string id="24018">Processen</string>
++ <string id="24019">PVR clients</string>
+
+ <string id="24020">Configureren</string>
+ <string id="24021">Uitschakelen</string>
+@@ -2404,3 +2943,4 @@
+ <string id="36017">Adapter gevonden, maar libcec is niet beschikbaar</string>
+ <string id="36018">Gebruik de taalinstelling van de TV</string>
+ </strings>
++
+diff --git a/language/English/strings.xml b/language/English/strings.xml
+index ca2ac4d..42ad8c0 100644
+--- a/language/English/strings.xml
++++ b/language/English/strings.xml
+@@ -1,5 +1,5 @@
+ <?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
+-<!--$Revision$-->
++<!--$Revision: 32586 $-->
+ <strings>
+ <string id="0">Programs</string>
+ <string id="1">Pictures</string>
+@@ -1008,6 +1008,8 @@
+ <string id="13014">Minimize</string>
+ <string id="13015">Power button action</string>
+ <string id="13016">Power off System</string>
++ <string id="13017">Inhibit idle shutdown</string>
++ <string id="13018">Allow idle shutdown</string>
+
+ <string id="13020">Is another session active, perhaps over ssh?</string>
+ <string id="13021">Mounted removable harddrive</string>
+@@ -1537,13 +1539,469 @@
+ <string id="16321">DXVA Best</string>
+ <string id="16322">Spline36</string>
+ <string id="16323">Spline36 optimized</string>
++
+ <string id="16324">Software Blend</string>
++ <string id="16325">Auto - ION Optimized</string>
+
+ <string id="16400">Post-processing</string>
+
+ <string id="17500">Display sleep timeout</string>
+
++ <string id="17997">%i MByte</string>
++ <string id="17998">%i hours</string>
++ <string id="17999">%i days</string>
++
++ <!-- strings 19000 thru 19999 used for tv interface -->
+ <string id="19000">Switch to channel</string>
++ <string id="19001">Separate the search words by using AND, OR and/or NOT.</string>
++ <string id="19002">or use phrases to find an exact match, like "The wizard of Oz".</string>
++ <string id="19003">Find similar programme</string>
++ <string id="19004">Importing EPG from clients</string>
++ <string id="19005">PVR stream information</string>
++ <string id="19006">Receiving device</string>
++ <string id="19007">Device status</string>
++ <string id="19008">Signal quality</string>
++ <string id="19009">SNR</string>
++ <string id="19010">BER</string>
++ <string id="19011">UNC</string>
++ <string id="19012">PVR Backend</string>
++ <string id="19013">Free to air</string>
++ <string id="19014">Fixed</string>
++ <string id="19015">Encryption</string>
++ <string id="19016">PVR Backend %i - %s</string>
++ <string id="19017">TV recordings</string>
++ <string id="19018">Default folder for PVR thumbnails</string>
++ <string id="19019">Channels</string>
++ <string id="19020">TV</string>
++ <string id="19021">Radio</string>
++ <string id="19022">Hidden</string>
++ <string id="19023">TV channels</string>
++ <string id="19024">Radio channels</string>
++ <string id="19025">Upcoming recordings</string>
++ <string id="19026">Add timer...</string>
++ <string id="19027">No search results</string>
++ <string id="19028">No EPG entries</string>
++ <string id="19029">Channel</string>
++ <string id="19030">Now</string>
++ <string id="19031">Next</string>
++ <string id="19032">Timeline</string>
++ <string id="19033">Information</string>
++ <string id="19034">Already started recording on this channel</string>
++ <string id="19035">This channel cannot be played. Check the log for details.</string>
++ <string id="19036">This recording cannot be played. Check the log for details.</string>
++ <string id="19037">Show signal quality</string>
++ <string id="19038">Not supported by the PVR backend.</string>
++ <string id="19039">Are you sure you want to hide this channel?</string>
++ <string id="19040">Timer</string>
++ <string id="19041">Are you sure you want to rename this recording?</string>
++ <string id="19042">Are you sure you want to rename this timer?</string>
++ <string id="19043">Recording</string>
++ <string id="19044">Please check your configuration or check the log for details.</string>
++ <string id="19045">No PVR clients have been started yet. Wait for the PVR clients to start up or check the log for details.</string>
++ <string id="19046">New channel</string>
++ <string id="19047">Programme info</string>
++ <string id="19048">Group management</string>
++ <string id="19049">Show channel</string>
++ <string id="19050">Show visible channels</string>
++ <string id="19051">Show hidden channels</string>
++ <string id="19052">Move channel to:</string>
++ <string id="19053">Recording information</string>
++ <string id="19054">Hide channel</string>
++ <string id="19055">No information available</string>
++ <string id="19056">New timer</string>
++ <string id="19057">Edit timer</string>
++ <string id="19058">Timer enabled</string>
++ <string id="19059">Stop recording</string>
++ <string id="19060">Delete timer</string>
++ <string id="19061">Add timer</string>
++ <string id="19062">Sort by: Channel</string>
++ <string id="19063">Go to begin</string>
++ <string id="19064">Go to end</string>
++ <string id="19065">Default EPG window</string>
++ <string id="19066">Loading recordings from clients</string>
++ <string id="19067">This event is already being recorded.</string>
++ <string id="19068">This recording could not be deleted. Check the log for details.</string>
++ <string id="19069">EPG</string>
++ <string id="19070">EPG scan timeout</string>
++ <string id="19071">EPG update interval</string>
++ <string id="19072">Do not store the EPG in the database</string>
++ <string id="19073">Delay channel switch</string>
++ <string id="19074">Active:</string>
++ <string id="19075">Name:</string>
++ <string id="19076">Folder:</string>
++ <string id="19077">Radio:</string>
++ <string id="19078">Channel:</string>
++ <string id="19079">Day:</string>
++ <string id="19080">Begin:</string>
++ <string id="19081">End:</string>
++ <string id="19082">Priority:</string>
++ <string id="19083">Lifetime (days):</string>
++ <string id="19084">First day:</string>
++ <string id="19085">Unknown channel %u</string>
++ <string id="19086">Mo-__-__-__-__-__-__</string>
++ <string id="19087">__-Tu-__-__-__-__-__</string>
++ <string id="19088">__-__-We-__-__-__-__</string>
++ <string id="19089">__-__-__-Th-__-__-__</string>
++ <string id="19090">__-__-__-__-Fr-__-__</string>
++ <string id="19091">__-__-__-__-__-Sa-__</string>
++ <string id="19092">__-__-__-__-__-__-Su</string>
++ <string id="19093">Mo-Tu-We-Th-Fr-__-__</string>
++ <string id="19094">Mo-Tu-We-Th-Fr-Sa-__</string>
++ <string id="19095">Mo-Tu-We-Th-Fr-Sa-Su</string>
++ <string id="19096">__-__-__-__-__-Sa-Su</string>
++ <string id="19097">Enter the name for the recording</string>
++ <string id="19098">Warning</string>
++ <string id="19099">Timer present</string>
++ <string id="19100">Are you sure you want to delete this channel, including all timers on this channel?</string>
++ <string id="19101">This channel is currently being used for playback.</string>
++ <string id="19102">Please switch to another channel.</string>
++ <string id="19103">Scan for missing icons</string>
++ <string id="19104">Enter the name of the folder for the recording</string>
++ <string id="19105">Size:</string>
++ <string id="19106">Next timer on</string>
++ <string id="19107">at</string>
++ <string id="19108">Recordings not in sync. Check the log for details.</string>
++ <string id="19109">Couldn't save timer. Check the log for details.</string>
++ <string id="19110">An unexpected error occurred. Try again later or check the log for details.</string>
++ <string id="19111">PVR backend error. Check the log for details.</string>
++ <string id="19112">Timers not in sync. Check the log for details.</string>
++ <string id="19113">Next</string>
++ <string id="19114">Version</string>
++ <string id="19115">Address</string>
++ <string id="19116">Disksize</string>
++ <string id="19117">Search for channels</string>
++ <string id="19118">Cannot use PVR functions while searching.</string>
++ <string id="19119">On which server you want to search?</string>
++ <string id="19120">Client number</string>
++ <string id="19121">Avoid repeats</string>
++ <string id="19122">This timer is still recording. Are you sure you want to delete this timer?</string>
++ <string id="19123">Free to air channels only</string>
++ <string id="19124">Ignore present timers</string>
++ <string id="19125">Ignore present recordings</string>
++ <string id="19126">Start time</string>
++ <string id="19127">End time</string>
++ <string id="19128">Start date</string>
++ <string id="19129">End date</string>
++ <string id="19130">Minimum duration</string>
++ <string id="19131">Maximum duration</string>
++ <string id="19132">Include unknown genres</string>
++ <string id="19133">Search string</string>
++ <string id="19134">Include description</string>
++ <string id="19135">Case sensitive</string>
++ <string id="19136">Channel unavailable</string>
++ <string id="19137">No groups defined</string>
++ <string id="19138">Please create a group first</string>
++ <string id="19139">Name of the new group</string>
++ <string id="19141">Group</string>
++ <string id="19142">Search guide</string>
++ <string id="19143">Group management</string>
++ <string id="19144">No groups defined</string>
++ <string id="19145">Grouped</string>
++ <string id="19146">Groups</string>
++ <string id="19147">The PVR backend does not support this action. Check the log for details.</string>
++ <string id="19148">Channel</string>
++ <string id="19149">Mo</string>
++ <string id="19150">Tu</string>
++ <string id="19151">We</string>
++ <string id="19152">Th</string>
++ <string id="19153">Fr</string>
++ <string id="19154">Sa</string>
++ <string id="19155">Su</string>
++ <string id="19156">from</string>
++ <string id="19157">Next recording</string>
++ <string id="19158">Currently recording</string>
++ <string id="19159">from</string>
++ <string id="19160">to</string>
++ <string id="19161">On</string>
++ <string id="19162">Recording active</string>
++ <string id="19163">Recordings</string>
++ <string id="19164">Cannot start recording. Check the log for details.</string>
++ <string id="19165">Switch</string>
++ <string id="19166">PVR information</string>
++ <string id="19167">Scan for missing icons</string>
++ <string id="19168">Switch channel without pressing OK</string>
++ <string id="19169">Hide video information box</string>
++ <string id="19170">Timeout when starting playback</string>
++ <string id="19171">Start playback minimized</string>
++ <string id="19172">Instant recording duration</string>
++ <string id="19173">Default recording priority</string>
++ <string id="19174">Default recording lifetime</string>
++ <string id="19175">Margin at the start of a recording</string>
++ <string id="19176">Margin at the end of a recording</string>
++ <string id="19177">Playback</string>
++ <string id="19178">Show channel information when switching channels</string>
++ <string id="19179">Automatically hide channel information</string>
++ <string id="19180">TV</string>
++ <string id="19181">Menu/OSD</string>
++ <string id="19182">Days to display in the EPG</string>
++ <string id="19184">Channel information duration</string>
++ <string id="19185">Reset the PVR database</string>
++ <string id="19186">All data in the PVR database is being erased</string>
++ <string id="19187">Reset the EPG database</string>
++ <string id="19188">EPG is being reset</string>
++ <string id="19189">Continue last channel on startup</string>
++ <string id="19190">Minimized</string>
++ <string id="19191">PVR service</string>
++ <string id="19192">None of the connected PVR backends supports scanning for channels.</string>
++ <string id="19193">The channel scan cannot be started. Check the log for details.</string>
++ <string id="19194">Continue?</string>
++ <string id="19195">Client actions</string>
++ <string id="19196">PVR client specific actions</string>
++ <string id="19197">Recording started on: %s</string>
++ <string id="19198">Recording finished on: %s</string>
++ <string id="19199">Channel manager</string>
++ <string id="19200">EPG source:</string>
++ <string id="19201">Channel name:</string>
++ <string id="19202">Channel icon:</string>
++ <string id="19203">Edit channel</string>
++ <string id="19204">New channel</string>
++ <string id="19205">Group management</string>
++ <string id="19206">Activate EPG:</string>
++ <string id="19207">Group:</string>
++ <string id="19208">Enter the name of the new channel</string>
++ <string id="19209">XBMC virtual backend</string>
++ <string id="19210">Client</string>
++ <string id="19211">Delete channel</string>
++ <string id="19212">This list contains changes</string>
++ <string id="19213">Select backend</string>
++ <string id="19214">Enter a valid URL for the new channel</string>
++ <string id="19215">The PVR backend does not support timers.</string>
++ <string id="19216">All radio channels</string>
++ <string id="19217">All TV channels</string>
++ <string id="19218">Visible</string>
++ <string id="19219">Ungrouped channels</string>
++ <string id="19220">Channels in</string>
++ <string id="19221">Synchronise channel groups with backends</string>
++ <string id="19222">EPG</string>
++ <string id="19223">No PVR add-on could be enabled. Check your settings or the log for more info.</string>
++ <string id="19224">Recording aborted</string>
++ <string id="19225">Recording scheduled</string>
++ <string id="19226">Recording started</string>
++ <string id="19227">Recording completed</string>
++ <string id="19228">Recording deleted</string>
++ <string id="19229">Close channel OSD after switching channels</string>
++ <string id="19230">Prevent EPG updates while playing a TV stream</string>
++ <string id="19231">Always use the channel order from the backend(s)</string>
++ <string id="19232">Clear search results</string>
++ <string id="19233">Display a notification on timer updates</string>
++ <string id="19234">Use backend channels numbers (only works with 1 enabled PVR addon)</string>
++ <string id="19235">PVR manager is starting up</string>
++ <string id="19236">importing channels</string>
++ <string id="19237">importing timers</string>
++ <string id="19238">importing recordings</string>
++ <string id="19239">starting background threads</string>
++ <string id="19240">No PVR add-on enabled</string>
++ <string id="19241">The PVR manager has been enabled without any</string>
++ <string id="19242">enabled PVR add-on. Enable at least one add-on</string>
++ <string id="19243">in order to use the PVR functionality.</string>
++
++ <string id="19244">Backend idle time</string>
++ <string id="19245">Set wakup command (cmd [timestamp])</string>
++ <string id="19246">Wakup before recording</string>
++ <string id="19247">Daily wakeup</string>
++ <string id="19248">Daily wakeup time (HH:MM:SS)</string>
++ <string id="19249">Filter channels</string>
++ <string id="19250">Loading EPG from database</string>
++ <string id="19251">Update EPG information</string>
++ <string id="19252">Schedule EPG update for this channel?</string>
++ <string id="19253">EPG update scheduled for channel</string>
++ <string id="19254">EPG update failed for channel</string>
++ <string id="19255">Start recording</string>
++ <string id="19256">Stop recording</string>
++
++ <string id="19499">Other/Unknown</string>
++ <string id="19500">Movie/Drama</string>
++ <string id="19501">Detective/Thriller</string>
++ <string id="19502">Adventure/Western/War</string>
++ <string id="19503">Science Fiction/Fantasy/Horror</string>
++ <string id="19504">Comedy</string>
++ <string id="19505">Soap/Melodrama/Folkloric</string>
++ <string id="19506">Romance</string>
++ <string id="19507">Serious/Classical/Religious/Historical Movie/Drama</string>
++ <string id="19508">Adult Movie/Drama</string>
++ <string id="19509"></string>
++ <string id="19510"></string>
++ <string id="19511"></string>
++ <string id="19512"></string>
++ <string id="19513"></string>
++ <string id="19514"></string>
++ <string id="19515"></string>
++ <string id="19516">News/Current Affairs</string>
++ <string id="19517">News/Weather Report</string>
++ <string id="19518">News Magazine</string>
++ <string id="19519">Documentary</string>
++ <string id="19520">Discussion/Interview/Debate</string>
++ <string id="19521"></string>
++ <string id="19522"></string>
++ <string id="19523"></string>
++ <string id="19524"></string>
++ <string id="19525"></string>
++ <string id="19526"></string>
++ <string id="19527"></string>
++ <string id="19528"></string>
++ <string id="19529"></string>
++ <string id="19530"></string>
++ <string id="19531"></string>
++ <string id="19532">Show/Game Show</string>
++ <string id="19533">Game Show/Quiz/Contest</string>
++ <string id="19534">Variety Show</string>
++ <string id="19535">Talk Show</string>
++ <string id="19536"></string>
++ <string id="19537"></string>
++ <string id="19538"></string>
++ <string id="19539"></string>
++ <string id="19540"></string>
++ <string id="19541"></string>
++ <string id="19542"></string>
++ <string id="19543"></string>
++ <string id="19544"></string>
++ <string id="19545"></string>
++ <string id="19546"></string>
++ <string id="19547"></string>
++ <string id="19548">Sports</string>
++ <string id="19549">Special Event</string>
++ <string id="19550">Sport Magazine</string>
++ <string id="19551">Football</string>
++ <string id="19552">Tennis/Squash</string>
++ <string id="19553">Team Sports</string>
++ <string id="19554">Athletics</string>
++ <string id="19555">Motor Sport</string>
++ <string id="19556">Water Sport</string>
++ <string id="19557">Winter Sports</string>
++ <string id="19558">Equestrian</string>
++ <string id="19559">Martial Sports</string>
++ <string id="19560"></string>
++ <string id="19561"></string>
++ <string id="19562"></string>
++ <string id="19563"></string>
++ <string id="19564">Children's/Youth Programmes</string>
++ <string id="19565">Pre-school Children's Programmes</string>
++ <string id="19566">Entertainment Programmes for 6 to 14</string>
++ <string id="19567">Entertainment Programmes for 10 to 16</string>
++ <string id="19568">Informational/Educational/School Programme</string>
++ <string id="19569">Cartoons/Puppets</string>
++ <string id="19570"></string>
++ <string id="19571"></string>
++ <string id="19572"></string>
++ <string id="19573"></string>
++ <string id="19574"></string>
++ <string id="19575"></string>
++ <string id="19576"></string>
++ <string id="19577"></string>
++ <string id="19578"></string>
++ <string id="19579"></string>
++ <string id="19580">Music/Ballet/Dance</string>
++ <string id="19581">Rock/Pop</string>
++ <string id="19582">Serious/Classical Music</string>
++ <string id="19583">Folk/Traditional Music</string>
++ <string id="19584">Musical/Opera</string>
++ <string id="19585">Ballet</string>
++ <string id="19586"></string>
++ <string id="19587"></string>
++ <string id="19588"></string>
++ <string id="19589"></string>
++ <string id="19590"></string>
++ <string id="19591"></string>
++ <string id="19592"></string>
++ <string id="19593"></string>
++ <string id="19594"></string>
++ <string id="19595"></string>
++ <string id="19596">Arts/Culture</string>
++ <string id="19597">Performing Arts</string>
++ <string id="19598">Fine Arts</string>
++ <string id="19599">Religion</string>
++ <string id="19600">Popular Culture/Traditional Arts</string>
++ <string id="19601">Literature</string>
++ <string id="19602">Film/Cinema</string>
++ <string id="19603">Experimental Film/Video</string>
++ <string id="19604">Broadcasting/Press</string>
++ <string id="19605">New Media</string>
++ <string id="19606">Arts/Culture Magazines</string>
++ <string id="19607">Fashion</string>
++ <string id="19608"></string>
++ <string id="19609"></string>
++ <string id="19610"></string>
++ <string id="19611"></string>
++ <string id="19612">Social/Political/Economics</string>
++ <string id="19613">Magazines/Reports/Documentary</string>
++ <string id="19614">Economics/Social Advisory</string>
++ <string id="19615">Remarkable People</string>
++ <string id="19616"></string>
++ <string id="19617"></string>
++ <string id="19618"></string>
++ <string id="19619"></string>
++ <string id="19620"></string>
++ <string id="19621"></string>
++ <string id="19622"></string>
++ <string id="19623"></string>
++ <string id="19624"></string>
++ <string id="19625"></string>
++ <string id="19626"></string>
++ <string id="19627"></string>
++ <string id="19628">Education/Science/Factual</string>
++ <string id="19629">Nature/Animals/Environment</string>
++ <string id="19630">Technology/Natural Sciences</string>
++ <string id="19631">Medicine/Physiology/Psychology</string>
++ <string id="19632">Foreign Countries/Expeditions</string>
++ <string id="19633">Social/Spiritual Sciences</string>
++ <string id="19634">Further Education</string>
++ <string id="19635">Languages</string>
++ <string id="19636"></string>
++ <string id="19637"></string>
++ <string id="19638"></string>
++ <string id="19639"></string>
++ <string id="19640"></string>
++ <string id="19641"></string>
++ <string id="19642"></string>
++ <string id="19643"></string>
++ <string id="19644">Leisure/Hobbies</string>
++ <string id="19645">Tourism/Travel</string>
++ <string id="19646">Handicraft</string>
++ <string id="19647">Motoring</string>
++ <string id="19648">Fitness &amp; Health</string>
++ <string id="19649">Cooking</string>
++ <string id="19650">Advertisement/Shopping</string>
++ <string id="19651">Gardening</string>
++ <string id="19652"></string>
++ <string id="19653"></string>
++ <string id="19654"></string>
++ <string id="19655"></string>
++ <string id="19656"></string>
++ <string id="19657"></string>
++ <string id="19658"></string>
++ <string id="19659"></string>
++ <string id="19660">Special Characteristics</string>
++ <string id="19661">Original Language</string>
++ <string id="19662">Black &amp; White</string>
++ <string id="19663">Unpublished</string>
++ <string id="19664">Live Broadcast</string>
++ <string id="19665"></string>
++ <string id="19666"></string>
++ <string id="19667"></string>
++ <string id="19668"></string>
++ <string id="19669"></string>
++ <string id="19670"></string>
++ <string id="19671"></string>
++ <string id="19672"></string>
++ <string id="19673"></string>
++ <string id="19674"></string>
++ <string id="19675"></string>
++ <string id="19676">Drama</string>
++ <string id="19677">Detective/Thriller</string>
++ <string id="19678">Adventure/Western/War</string>
++ <string id="19679">Science Fiction/Fantasy/Horror</string>
++ <string id="19680">Comedy</string>
++ <string id="19681">Soap/Melodrama/Folkloric</string>
++ <string id="19682">Romance</string>
++ <string id="19683">Serious/ClassicalReligion/Historical</string>
++ <string id="19684">Adult</string>
++ <string id="19685"></string>
++ <string id="19686"></string>
++ <string id="19687"></string>
++ <string id="19688"></string>
++ <string id="19689"></string>
++ <string id="19690"></string>
++ <string id="19691"></string>
+
+ <string id="20000">Saved music folder</string>
+ <string id="20001">Use external DVD player</string>
+@@ -2182,6 +2640,7 @@
+ <string id="24016">Album information</string>
+ <string id="24017">Artist information</string>
+ <string id="24018">Services</string>
++ <string id="24019">PVR clients</string>
+
+ <string id="24020">Configure</string>
+ <string id="24021">Disable</string>
+@@ -2217,7 +2676,7 @@
+ <string id="24059">Would you like to enable this Add-on?</string>
+ <string id="24060">Would you like to disable this Add-on?</string>
+ <string id="24061">Add-on update available!</string>
+- <string id="24062">Enabled Add-ons</string>
++ <string id="24062">Installed Add-ons</string>
+ <string id="24063">Auto update</string>
+ <string id="24064">Add-on enabled</string>
+ <string id="24065">Add-on updated</string>
+diff --git a/language/Finnish/strings.xml b/language/Finnish/strings.xml
+index 22c0631..d0bfce0 100644
+--- a/language/Finnish/strings.xml
++++ b/language/Finnish/strings.xml
+@@ -1011,6 +1011,8 @@
+ <string id="13014">Pienennä</string>
+ <string id="13015">Virtakytkimen toiminto</string>
+ <string id="13016">Sammuta järjestelmä</string>
++ <string id="13017">Estä sammuttaminen toimettomana</string>
++ <string id="13018">Salli sammuttaminen toimettomana</string>
+
+ <string id="13020">Onko toinen istunto käynnissä, ehkä ssh:n kautta?</string>
+ <string id="13021">Liitetty siirrettävä kiintolevy</string>
+@@ -1541,12 +1543,460 @@
+ <string id="16322">Spline36</string>
+ <string id="16323">Spline36 optimoitu</string>
+ <string id="16324">Ohjelmistopohjainen sekoitus</string>
++ <string id="16325">Automaattinen - ION optimoitu</string>
+
+ <string id="16400">Videon jälkikäsittely</string>
+
+ <string id="17500">Näytön pimennyksen viive</string>
+
++ <string id="17997">%i Mtavua</string>
++ <string id="17998">%i tuntia</string>
++ <string id="17999">%i päivää</string>
++
++ <!-- strings 19000 thru 19999 used for tv interface -->
+ <string id="19000">Vaihda kanavalle</string>
++ <string id="19001">Erottele eri hakusanat käyttämällä "AND", "OR" ja/tai "NOT" -operaattoreita hakulauseessasi.</string>
++ <string id="19002">Tai käytä lainausmerkkejä löytääksesi kokonaisia virkkeitä esim. "The wizard of Oz".</string>
++ <string id="19003">Etsi samankaltaisia ohjelmia</string>
++ <string id="19004">Tuodaan ohjelmaoppaan tiedot asiakkailta</string>
++ <string id="19005">PVR-virran tiedot</string>
++ <string id="19006">Vastaanottava laite</string>
++ <string id="19007">Vastaanoton tila</string>
++ <string id="19008">Signaalin laatu</string>
++ <string id="19009">Signaali-kohinasuh.</string>
++ <string id="19010">Bittivirhesuhde</string>
++ <string id="19011">Korjaamat. lohkot</string>
++ <string id="19012">PVR-taustaosa</string>
++ <string id="19013">Vapaa kanava</string>
++ <string id="19014">Kiinteä</string>
++ <string id="19015">Salausjärjestelmä</string>
++ <string id="19016">PVR-taustaosa %i - %s</string>
++ <string id="19017">Nauhoitukset</string>
++ <string id="19018">PVR-pienoiskuvakkeiden oletuskansio</string>
++ <string id="19019">Kanavat</string>
++ <string id="19020">TV</string>
++ <string id="19021">Radio</string>
++ <string id="19022">Piilotetut</string>
++ <string id="19023">TV-kanavat</string>
++ <string id="19024">Radiokanavat</string>
++ <string id="19025">Tulevat nauhoitukset</string>
++ <string id="19026">Lisää ajastus...</string>
++ <string id="19027">Ei hakutuloksia</string>
++ <string id="19028">Ei ohjelmatietoja</string>
++ <string id="19029">Kanava</string>
++ <string id="19030">Nyt</string>
++ <string id="19031">Seuraavaksi</string>
++ <string id="19032">Aikajana</string>
++ <string id="19033">Tietoja</string>
++ <string id="19034">Tämän kanavan nauhoittaminen on jo aloitettu</string>
++ <string id="19035">Kanavaa ei voi toistaa. Katso lokitiedostosta lisätietoja.</string>
++ <string id="19036">Nauhoitusta ei voi toistaa. Katso lokitiedostosta lisätietoja.</string>
++ <string id="19037">Näytä signaalin laatu</string>
++ <string id="19038">PVR-taustaosa ei tue toimintoa.</string>
++ <string id="19039">Haluatko varmasti piilottaa tämän kanavan?</string>
++ <string id="19040">Ajastukset</string>
++ <string id="19041">Haluatko varmasti muuttaa tämän nauhoituksen nimen?</string>
++ <string id="19042">Haluatko varmasti muuttaa tämän ajastuksen nimen?</string>
++ <string id="19043">Nauhoitus</string>
++ <string id="19044">Tarkista asetukset tai katso lokitiedostosta lisätietoja.</string>
++ <string id="19045">PVR-asiakkaita ei ole käynnistetty vielä. Odota kunnes PVR-asiakkaat käynnistyy tai katso lokitiedostosta lisätietoja.</string>
++ <string id="19046">Uusi kanava</string>
++ <string id="19047">Ohjelman tiedot</string>
++ <string id="19048">Ryhmien hallinta</string>
++ <string id="19049">Näytä kanava</string>
++ <string id="19050">Näytä näkyvät kanavat</string>
++ <string id="19051">Näytä piilotetut kanavat</string>
++ <string id="19052">Siirrä kanava paikkaan:</string>
++ <string id="19053">Nauhoituksen tiedot</string>
++ <string id="19054">Piilota kanava</string>
++ <string id="19055">Ei tietoja saatavilla</string>
++ <string id="19056">Uusi ajastus</string>
++ <string id="19057">Muokkaa ajastusta</string>
++ <string id="19058">Ajastus käytössä</string>
++ <string id="19059">Pysäytä nauhoitus</string>
++ <string id="19060">Poista ajastus</string>
++ <string id="19061">Lisää ajastus</string>
++ <string id="19062">Järjestä: Kanavan mukaan</string>
++ <string id="19063">Mene alkuun</string>
++ <string id="19064">Mene loppuun</string>
++ <string id="19065">Ohjelmaoppaan oletusnäkymä</string>
++ <string id="19066">Ladataan nauhoitusten tietoja asiakkailta</string>
++ <string id="19067">Tämän ohjelman nauhoitus on jo aloitettu.</string>
++ <string id="19068">Tätä nauhoitusta ei voitu poistaa. Katso lokitiedostosta lisätietoja.</string>
++ <string id="19069">Ohjelmaopas</string>
++ <string id="19070">Ohjelmatietojen haun aikakatkaisu</string>
++ <string id="19071">Ohjelmatietojen päivitysväli</string>
++ <string id="19072">Älä tallenna ohjelmatietoja tietokantaan</string>
++ <string id="19073">Viivytä kanavanvaihtoa</string>
++ <string id="19074">Käytössä:</string>
++ <string id="19075">Nimi:</string>
++ <string id="19076">Kansio:</string>
++ <string id="19077">Radio:</string>
++ <string id="19078">Kanava:</string>
++ <string id="19079">Päivämäärä:</string>
++ <string id="19080">Alkaa:</string>
++ <string id="19081">Loppuu:</string>
++ <string id="19082">Tärkeys:</string>
++ <string id="19083">Elinikä (päivää):</string>
++ <string id="19084">Ensimmäinen päivä:</string>
++ <string id="19085">Tuntematon kanava %u</string>
++ <string id="19086">Ma-__-__-__-__-__-__</string>
++ <string id="19087">__-Ti-__-__-__-__-__</string>
++ <string id="19088">__-__-Ke-__-__-__-__</string>
++ <string id="19089">__-__-__-To-__-__-__</string>
++ <string id="19090">__-__-__-__-Pe-__-__</string>
++ <string id="19091">__-__-__-__-__-La-__</string>
++ <string id="19092">__-__-__-__-__-__-Su</string>
++ <string id="19093">Ma-Ti-Ke-To-Pe-__-__</string>
++ <string id="19094">Ma-Ti-Ke-To-Pe-La-__</string>
++ <string id="19095">Ma-Ti-Ke-To-Pe-La-Su</string>
++ <string id="19096">__-__-__-__-__-La-Su</string>
++ <string id="19097">Anna nauhoitukselle nimi</string>
++ <string id="19098">Varoitus</string>
++ <string id="19099">Ajastus on olemassa</string>
++ <string id="19100">Haluatko varmasti poistaa tämän kanavan ja kaikki sen sisältämät ajastukset?</string>
++ <string id="19101">Tätä kanavaa toistetaan tällä hetkellä.</string>
++ <string id="19102">Vaihda toiselle kanavalle.</string>
++ <string id="19103">Hae puuttuvat kuvakkeet</string>
++ <string id="19104">Anna nauhoituskansion nimi</string>
++ <string id="19105">Koko:</string>
++ <string id="19106">Seuraava ajastus</string>
++ <string id="19107">klo</string>
++ <string id="19108">Nauhoitustiedot ei ole ajan tasalla. Katso lokitiedostosta lisätietoja.</string>
++ <string id="19109">Ajastusta ei voitu tallentaa. Katso lokitiedostosta lisätietoja.</string>
++ <string id="19110">Tapahtui odottamaton virhe. Yritä myöhemmin uudelleen tai katso lokitiedostosta lisätietoja.</string>
++ <string id="19111">PVR-taustaosan virhe. Katso lokitiedostosta lisätietoja.</string>
++ <string id="19112">Ajastustiedot ei ole ajan tasalla. Katso lokitiedostosta lisätietoja.</string>
++ <string id="19113">Seuraavaksi</string>
++ <string id="19114">Versio</string>
++ <string id="19115">Osoite</string>
++ <string id="19116">Levyn koko</string>
++ <string id="19117">Hae kanavia</string>
++ <string id="19118">PVR-toimintoja ei voi käyttää haun aikana.</string>
++ <string id="19119">Miltä palvelimelta haluat hakea?</string>
++ <string id="19120">Asiakas nro</string>
++ <string id="19121">Vältä uusintoja</string>
++ <string id="19122">Nauhoitus on vielä käynnissä. Haluatko varmasti poistaa tämän ajastuksen?</string>
++ <string id="19123">Vain vapaat kanavat</string>
++ <string id="19124">Älä huomioi nykyisiä ajastuksia</string>
++ <string id="19125">Älä huomioi nykyisiä nauhoituksia</string>
++ <string id="19126">Aloitusaika</string>
++ <string id="19127">Päättymisaika</string>
++ <string id="19128">Aloituspäivämäärä</string>
++ <string id="19129">Päättymispäivämäärä</string>
++ <string id="19130">Kesto vähintään</string>
++ <string id="19131">Kesto enintään</string>
++ <string id="19132">Sisällytä tuntemattomat lajityypit</string>
++ <string id="19133">Hakulause</string>
++ <string id="19134">Sisällytä kuvaus</string>
++ <string id="19135">Merkkikokoriippuvainen</string>
++ <string id="19136">Kanava ei saatavilla</string>
++ <string id="19137">Ryhmiä ei määritelty</string>
++ <string id="19138">Luo ensin ainakin yksi ryhmä</string>
++ <string id="19139">Uuden ryhmän nimi</string>
++ <string id="19141">Ryhmä</string>
++ <string id="19142">Etsi ohjelmaoppaasta</string>
++ <string id="19143">Ryhmien hallinta</string>
++ <string id="19144">Ei ryhmiä määriteltynä</string>
++ <string id="19145">Ryhmitelty</string>
++ <string id="19146">Ryhmät</string>
++ <string id="19147">PVR-taustaosa ei tue tätä toimintoa. Katso lokitiedostosta lisätietoja.</string>
++ <string id="19148">Kanava</string>
++ <string id="19149">Ma</string>
++ <string id="19150">Ti</string>
++ <string id="19151">Ke</string>
++ <string id="19152">To</string>
++ <string id="19153">Pe</string>
++ <string id="19154">La</string>
++ <string id="19155">Su</string>
++ <string id="19156"></string>
++ <string id="19157">Seuraava nauhoitus</string>
++ <string id="19158">Nauhoitetaan tällä hetkellä</string>
++ <string id="19159">klo</string>
++ <string id="19160">-</string>
++ <string id="19161"></string>
++ <string id="19162">Nauhoitus käynnissä</string>
++ <string id="19163">Nauhoitukset</string>
++ <string id="19164">Nauhoitusta ei voi aloittaa. Katso lokitiedostosta lisätietoja.</string>
++ <string id="19165">Vaihda</string>
++ <string id="19166">PVR-tietoja</string>
++ <string id="19167">Hae puuttuvat kuvakkeet</string>
++ <string id="19168">Kanavan vaihto ilman OK-näppäintä</string>
++ <string id="19169">Piilota videon tietolaatikko</string>
++ <string id="19170">Toiston aloituksen aikakatkaisu</string>
++ <string id="19171">Aloita toisto pienennettynä</string>
++ <string id="19172">Pikanauhoituksen kestoaika</string>
++ <string id="19173">Nauhoituksen oletustärkeys</string>
++ <string id="19174">Nauhoituksen oletuselinikä</string>
++ <string id="19175">Lisäaika nauhoituksen alkuun</string>
++ <string id="19176">Lisäaika nauhoituksen loppuun</string>
++ <string id="19177">Toisto</string>
++ <string id="19178">Näytä kanavan tiedot kanavanvaihdon yhteydessä</string>
++ <string id="19179">Piilota kanavan tiedot automaattisesti</string>
++ <string id="19180">TV</string>
++ <string id="19181">Valikko/OSD</string>
++ <string id="19182">Ohjelmaoppaan pituus</string>
++ <string id="19184">Kanavan tiedot ruudulla</string>
++ <string id="19185">Tyhjennä PVR-tietokanta</string>
++ <string id="19186">Kaikki PVR-tietokannan tiedot poistetaan!</string>
++ <string id="19187">Tyhjennä ohjelmaoppaan tietokanta</string>
++ <string id="19188">Ohjelmaoppaan tietokanta tyhjennetään!</string>
++ <string id="19189">Jatka edellisen kanavan katselua käynnistettäessä</string>
++ <string id="19190">Pienennetty</string>
++ <string id="19191">PVR-palvelu</string>
++ <string id="19192">Yksikään yhdistetty PVR-taustaosa ei tue kanavien hakemista.</string>
++ <string id="19193">Kanavahakua ei voitu aloittaa. Katso lokitiedostosta lisätietoja.</string>
++ <string id="19194">Jatketaanko?</string>
++ <string id="19195">Asiakkaan toiminnot</string>
++ <string id="19196">PVR-asiakaskohtaiset toiminnot</string>
++ <string id="19197">Nauhoitus alkoi: %s</string>
++ <string id="19198">Nauhoitus päättyi: %s</string>
++ <string id="19199">Kanavien hallinta</string>
++ <string id="19200">Ohjelmaoppaan lähde:</string>
++ <string id="19201">Kanavan nimi:</string>
++ <string id="19202">Kanavan kuvake:</string>
++ <string id="19203">Muokkaa kanavaa</string>
++ <string id="19204">Uusi kanava</string>
++ <string id="19205">Ryhmien hallinta</string>
++ <string id="19206">Ohjelmaopas käytössä:</string>
++ <string id="19207">Ryhmä:</string>
++ <string id="19208">Anna uuden kanavan nimi</string>
++ <string id="19209">XBMC:n virtuaalitaustaosa</string>
++ <string id="19210">Asiakas</string>
++ <string id="19211">Poista kanava</string>
++ <string id="19212">Lista sisältää muutoksia</string>
++ <string id="19213">Valitse taustaosa</string>
++ <string id="19214">Anna uuden kanavan osoite</string>
++ <string id="19215">PVR-taustaosa ei tue ajastuksia.</string>
++ <string id="19216">Kaikki radiokanavat</string>
++ <string id="19217">Kaikki TV-kanavat</string>
++ <string id="19218">Näkyvät</string>
++ <string id="19219">Ryhmittelemättömät kanavat</string>
++ <string id="19220">Kanavat ryhmässä</string>
++ <string id="19221">Synkronoi kanavaryhmät taustaosien kanssa</string>
++ <string id="19222">Ohjelmaopas</string>
++ <string id="19223">Yhtään PVR-lisäosaa ei voitu ottaa käyttöön. Tarkista asetukset tai katso lokitiedostosta lisätietoja.</string>
++ <string id="19224">Nauhoitus peruutettu</string>
++ <string id="19225">Nauhoitus ajastettu</string>
++ <string id="19226">Nauhoitus aloitettu</string>
++ <string id="19227">Nauhoitus päättynyt</string>
++ <string id="19228">Nauhoitus poistettu</string>
++ <string id="19229">Sulje OSD kanavanvaihdon jälkeen</string>
++ <string id="19230">Älä päivitä ohjelmaopasta TV:n toiston aikana</string>
++ <string id="19231">Käytä taustaosien kanavajärjestystä</string>
++ <string id="19232">Tyhjennä hakutulokset</string>
++ <string id="19233">Näytä ilmoitus ajastustietojen päivityksen yhteydessä</string>
++ <string id="19234">Käytä taustaosan kanavanumeroita (vain 1 PVR-lisäosa kerrallaan)</string>
++ <string id="19235">PVR-hallinta käynnistyy</string>
++ <string id="19236">tuodaan kanavat</string>
++ <string id="19237">tuodaan ajastukset</string>
++ <string id="19238">tuodaan nauhoitukset</string>
++ <string id="19239">käynnistetään taustasäikeet</string>
++ <string id="19240">Ei käytössä olevia PVR-lisäosia</string>
++ <string id="19241">PVR-hallinta on otettu käyttöön ilman yhtään</string>
++ <string id="19242">käytössä olevaa PVR-lisäosaa. Ota käyttöön vähintään</string>
++ <string id="19243">yksi lisäosa mikäli haluat käyttää PVR toimintoja.</string>
++
++ <string id="19244">Taustaosan toimettomuusaika</string>
++ <string id="19245">Herätyskomento (komento [aikaleima])</string>
++ <string id="19246">Herätysaika ennen nauhoitusta</string>
++ <string id="19247">Päivittäinen herätys</string>
++ <string id="19248">Päivittäinen herätysaika (TT:MM:SS)</string>
++ <string id="19249">Suodata kanavia</string>
++
++ <string id="19499">Muu/Tuntematon</string>
++ <string id="19500">Elokuva/Draama</string>
++ <string id="19501">Salapoliisi/Jännitys</string>
++ <string id="19502">Seikkailu/Lännenelokuva/Sota</string>
++ <string id="19503">Sci-Fi/Fantasia/Kauhu</string>
++ <string id="19504">Komedia</string>
++ <string id="19505">Saippua/Tunteellinen/Perinteinen</string>
++ <string id="19506">Romanssi</string>
++ <string id="19507">Vakava/Klassinen/Uskonnollinen/Historiallinen elokuva/Draama</string>
++ <string id="19508">Aikuisten Elokuva/Draama</string>
++ <string id="19509"></string>
++ <string id="19510"></string>
++ <string id="19511"></string>
++ <string id="19512"></string>
++ <string id="19513"></string>
++ <string id="19514"></string>
++ <string id="19515"></string>
++ <string id="19516">Uutiset/Ajankohtaiset asiat</string>
++ <string id="19517">Uutiset/Säätiedot</string>
++ <string id="19518">Uutismakasiini</string>
++ <string id="19519">Dokumentti</string>
++ <string id="19520">Keskustelu/Haastattelu/Väittely</string>
++ <string id="19521"></string>
++ <string id="19522"></string>
++ <string id="19523"></string>
++ <string id="19524"></string>
++ <string id="19525"></string>
++ <string id="19526"></string>
++ <string id="19527"></string>
++ <string id="19528"></string>
++ <string id="19529"></string>
++ <string id="19530"></string>
++ <string id="19531"></string>
++ <string id="19532">Show/Pelishow</string>
++ <string id="19533">Pelishow/Tietovisa/Kilpailu</string>
++ <string id="19534">Vaihteleva show</string>
++ <string id="19535">Keskusteluohjelma</string>
++ <string id="19536"></string>
++ <string id="19537"></string>
++ <string id="19538"></string>
++ <string id="19539"></string>
++ <string id="19540"></string>
++ <string id="19541"></string>
++ <string id="19542"></string>
++ <string id="19543"></string>
++ <string id="19544"></string>
++ <string id="19545"></string>
++ <string id="19546"></string>
++ <string id="19547"></string>
++ <string id="19548">Urheilu</string>
++ <string id="19549">Erikoistapahtuma</string>
++ <string id="19550">Urheilumakasiini</string>
++ <string id="19551">Jalkapallo</string>
++ <string id="19552">Tennis/Squash</string>
++ <string id="19553">Joukkuelajit</string>
++ <string id="19554">Yleisurheilu</string>
++ <string id="19555">Moottoriurheilu</string>
++ <string id="19556">Vesiurheilu</string>
++ <string id="19557">Talviurheilu</string>
++ <string id="19558">Ratsastus</string>
++ <string id="19559">Kamppailulajit</string>
++ <string id="19560"></string>
++ <string id="19561"></string>
++ <string id="19562"></string>
++ <string id="19563"></string>
++ <string id="19564">Lasten/Nuorten ohjelmat</string>
++ <string id="19565">Esikouluikäisten lasten ohjelmat</string>
++ <string id="19566">Viihdeohjelmat 6-14 vuotiaille</string>
++ <string id="19567">Viihdeohjelmat 10-16 vuotiaille</string>
++ <string id="19568">Tiedottava/Opettavainen/Koulutusohjelma</string>
++ <string id="19569">Piirretty/Nukketeatteri</string>
++ <string id="19570"></string>
++ <string id="19571"></string>
++ <string id="19572"></string>
++ <string id="19573"></string>
++ <string id="19574"></string>
++ <string id="19575"></string>
++ <string id="19576"></string>
++ <string id="19577"></string>
++ <string id="19578"></string>
++ <string id="19579"></string>
++ <string id="19580">Musiikki/Baletti/Tanssi</string>
++ <string id="19581">Rock/Pop</string>
++ <string id="19582">Vakava/Klassinen Musiikki</string>
++ <string id="19583">Kansallinen/Perinteinen Musiikki</string>
++ <string id="19584">Musikaali/Ooppera</string>
++ <string id="19585">Baletti</string>
++ <string id="19586"></string>
++ <string id="19587"></string>
++ <string id="19588"></string>
++ <string id="19589"></string>
++ <string id="19590"></string>
++ <string id="19591"></string>
++ <string id="19592"></string>
++ <string id="19593"></string>
++ <string id="19594"></string>
++ <string id="19595"></string>
++ <string id="19596">Taide/Kulttuuri</string>
++ <string id="19597">Ilmaisu- ja näyttämötaiteet</string>
++ <string id="19598">Kaunotaiteet</string>
++ <string id="19599">Uskonto</string>
++ <string id="19600">Populaarikulttuuri/Perinteiset taiteet</string>
++ <string id="19601">Kirjallisuus</string>
++ <string id="19602">Filmi/Elokuva</string>
++ <string id="19603">Kokeellinen filmi/Video</string>
++ <string id="19604">Televisiointi/Lehdistö</string>
++ <string id="19605">Uusi media</string>
++ <string id="19606">Taide/Kulttuurimakasiinit</string>
++ <string id="19607">Muoti</string>
++ <string id="19608"></string>
++ <string id="19609"></string>
++ <string id="19610"></string>
++ <string id="19611"></string>
++ <string id="19612">Yhteiskunnallinen/Poliittinen/Taloustieto</string>
++ <string id="19613">Ajankohtaisohjelma/Katsaus/Dokumentti</string>
++ <string id="19614">Taloustieto/Yhteiskuntaneuvonta</string>
++ <string id="19615">Tunnettuja ihmisiä</string>
++ <string id="19616"></string>
++ <string id="19617"></string>
++ <string id="19618"></string>
++ <string id="19619"></string>
++ <string id="19620"></string>
++ <string id="19621"></string>
++ <string id="19622"></string>
++ <string id="19623"></string>
++ <string id="19624"></string>
++ <string id="19625"></string>
++ <string id="19626"></string>
++ <string id="19627"></string>
++ <string id="19628">Koulutus/Tiede/Asiaohjelma</string>
++ <string id="19629">Luonto/Eläimet/Ympäristö</string>
++ <string id="19630">Teknologia/Luonnontieteet</string>
++ <string id="19631">Lääketiede/Fysiologia/Psykologia</string>
++ <string id="19632">Ulkomaat/Tutkimusmatkat</string>
++ <string id="19633">Sosiaaliset/Hengelliset Tieteet</string>
++ <string id="19634">Jatko-opiskelu</string>
++ <string id="19635">Kielet</string>
++ <string id="19636"></string>
++ <string id="19637"></string>
++ <string id="19638"></string>
++ <string id="19639"></string>
++ <string id="19640"></string>
++ <string id="19641"></string>
++ <string id="19642"></string>
++ <string id="19643"></string>
++ <string id="19644">Vapaa-aika/Harrastukset</string>
++ <string id="19645">Turismi/Matkustus</string>
++ <string id="19646">Käsityö</string>
++ <string id="19647">Autoilu</string>
++ <string id="19648">Kuntoilu &amp; Terveys</string>
++ <string id="19649">Ruuanlaitto</string>
++ <string id="19650">Mainokset/Ostokset</string>
++ <string id="19651">Puutarhan hoito</string>
++ <string id="19652"></string>
++ <string id="19653"></string>
++ <string id="19654"></string>
++ <string id="19655"></string>
++ <string id="19656"></string>
++ <string id="19657"></string>
++ <string id="19658"></string>
++ <string id="19659"></string>
++ <string id="19660">Erikoisominaisuudet</string>
++ <string id="19661">Alkuperäisellä kielellä</string>
++ <string id="19662">Mustavalkoinen</string>
++ <string id="19663">Julkaisematon</string>
++ <string id="19664">Suora lähetys</string>
++ <string id="19665"></string>
++ <string id="19666"></string>
++ <string id="19667"></string>
++ <string id="19668"></string>
++ <string id="19669"></string>
++ <string id="19670"></string>
++ <string id="19671"></string>
++ <string id="19672"></string>
++ <string id="19673"></string>
++ <string id="19674"></string>
++ <string id="19675"></string>
++ <string id="19676">Draama</string>
++ <string id="19677">Salapoliisi/Jännitys</string>
++ <string id="19678">Seikkailu/Lännenelokuva/Kauhu</string>
++ <string id="19679">Sci-Fi/Fantasia/Kauhu</string>
++ <string id="19680">Komedia</string>
++ <string id="19681">Saippua/Tunteellinen/Perinteinen</string>
++ <string id="19682">Romanssi</string>
++ <string id="19683">Vakava/Klassinen/Uskonnollinen/Historiallinen</string>
++ <string id="19684">Aikuiset</string>
++ <string id="19685"></string>
++ <string id="19686"></string>
++ <string id="19687"></string>
++ <string id="19688"></string>
++ <string id="19689"></string>
++ <string id="19690"></string>
++ <string id="19691"></string>
+
+ <string id="20000">Tallennuskansio</string>
+ <string id="20001">Käytä ulkoista DVD-toisto-ohjelmaa</string>
+@@ -2185,6 +2635,7 @@
+ <string id="24016">Albumitiedot</string>
+ <string id="24017">Esittäjätiedot</string>
+ <string id="24018">Palvelut</string>
++ <string id="24019">PVR-asiakkaat</string>
+
+ <string id="24020">Asetukset</string>
+ <string id="24021">Poista käytöstä</string>
+diff --git a/language/French/strings.xml b/language/French/strings.xml
+index 64b3a5a..e90c52b 100644
+--- a/language/French/strings.xml
++++ b/language/French/strings.xml
+@@ -1545,9 +1545,435 @@
+ <string id="17997">%i Mo</string>
+ <string id="17998">%i heures</string>
+ <string id="17999">%i jours</string>
+-
++
++ <!-- strings 19000 thru 19999 used for tv interface -->
+ <string id="19000">Changer de canal</string>
+-
++ <string id="19001">Séparez les mots clés par AND, OR et/ou NOT.</string>
++ <string id="19002">ou utilisez les guillements pour rechercher une correspondance exacte, comme "Le magicien d'Oz".</string>
++ <string id="19003">Chercher un programme similaire</string>
++ <string id="19004">Importation des données EPG</string>
++ <string id="19005">Information sur le stream PVR</string>
++ <string id="19006">Appareil de réception</string>
++ <string id="19007">Statut de l'appareil de réception</string>
++ <string id="19008">Qualité du signal</string>
++ <string id="19009">SNR</string>
++ <string id="19010">BER</string>
++ <string id="19011">UNC</string>
++ <string id="19012">Serveur PVR</string>
++ <string id="19013">Gratuite</string>
++ <string id="19014">Fixée</string>
++ <string id="19015">Cryptage</string>
++ <string id="19016">Serveur PVR %i - %s</string>
++ <string id="19017">Enregistrements TV</string>
++ <string id="19018">Dossier par défaut pour les miniatures</string>
++ <string id="19019">Chaînes</string>
++ <string id="19020">TV</string>
++ <string id="19021">Radio</string>
++ <string id="19022">Caché</string>
++ <string id="19023">Chaînes TV</string>
++ <string id="19024">Stations Radio</string>
++ <string id="19025">Prochains enregistrements</string>
++ <string id="19026">Ajouter une programmation...</string>
++ <string id="19027">Aucun résultat</string>
++ <string id="19028">Pas d'entrée EPG</string>
++ <string id="19029">Chaîne</string>
++ <string id="19030">Maintenant</string>
++ <string id="19031">Suivant</string>
++ <string id="19032">Calendrier</string>
++ <string id="19033">Information</string>
++ <string id="19034">Un enregistrement a déjà commencé sur cette chaîne</string>
++ <string id="19035">Cette chaîne ne peut pas être lue. Voir le log pour plus de détails.</string>
++ <string id="19036">Cet enregistrement ne peut pas être lue. Voir le log pour plus de détails.</string>
++ <string id="19037">Voir la qualité du signal</string>
++ <string id="19038">Non supporté par le serveur PVR.</string>
++ <string id="19039">Êtes-vous sûr de vouloir cacher cette chaîne ?</string>
++ <string id="19040">Programmation</string>
++ <string id="19041">Êtes-vous sûr de vouloir renommer cet enregistrement ?</string>
++ <string id="19042">Êtes-vous sûr de vouloir renommer cette programmaiton ?</string>
++ <string id="19043">Enregistrement</string>
++ <string id="19044">Veuillez vérifier votre configuration ou voir le log pour plus de détails.</string>
++ <string id="19045">Aucun client PVR n'a été démarré pour l'instant. Attendez que les clients PVR démarrent ou regardez le log pour plus de détails.</string>
++ <string id="19046">Nouvelle chaîne</string>
++ <string id="19047">Infos programme</string>
++ <string id="19048">Gestion des groupes</string>
++ <string id="19049">Voir la chaîne</string>
++ <string id="19050">Voir les chaînes visibles</string>
++ <string id="19051">Voir les chaînes cachées</string>
++ <string id="19052">Déplacer la chaîne vers :</string>
++ <string id="19053">Recording information</string>
++ <string id="19054">Cacher la chaîne</string>
++ <string id="19055">Pas d'information disponible</string>
++ <string id="19056">Nouvelle programmation</string>
++ <string id="19057">Modifier la programmation</string>
++ <string id="19058">Programmation activée</string>
++ <string id="19059">Arrêter l'enregistrement</string>
++ <string id="19060">Supprimer la programmation</string>
++ <string id="19061">Ajouter une programmation</string>
++ <string id="19062">Trier par : Chaîne</string>
++ <string id="19063">Aller au début</string>
++ <string id="19064">Aller à la fin</string>
++ <string id="19065">Fenêtre EPG par défaut</string>
++ <string id="19066">Chargement des enregistrements</string>
++ <string id="19067">Ce programme est déjà en cours d'enregistrement.</string>
++ <string id="19068">Cet enregistrement n'a pas pu être supprimé. Voir le log pour plus de détails.</string>
++ <string id="19069">EPG</string>
++ <string id="19070">Echec du scan EPG</string>
++ <string id="19071">Intervalle de mise à jour de l'EPG</string>
++ <string id="19072">Ne pas enregistrer l'EPG dans la base de données</string>
++ <string id="19073">Délai au changement de chaîne</string>
++ <string id="19074">Active :</string>
++ <string id="19075">Nom :</string>
++ <string id="19076">Dossier :</string>
++ <string id="19077">Radio :</string>
++ <string id="19078">Chaîne :</string>
++ <string id="19079">Jour :</string>
++ <string id="19080">Début :</string>
++ <string id="19081">Fin :</string>
++ <string id="19082">Priorité :</string>
++ <string id="19083">Durée de vie (jours) :</string>
++ <string id="19084">Premier jour :</string>
++ <string id="19085">Chaîne inconnue %u</string>
++ <string id="19086">Lu-__-__-__-__-__-__</string>
++ <string id="19087">__-Ma-__-__-__-__-__</string>
++ <string id="19088">__-__-Me-__-__-__-__</string>
++ <string id="19089">__-__-__-Je-__-__-__</string>
++ <string id="19090">__-__-__-__-Ve-__-__</string>
++ <string id="19091">__-__-__-__-__-Sa-__</string>
++ <string id="19092">__-__-__-__-__-__-Di</string>
++ <string id="19093">Lu-Ma-Me-Je-Ve-__-__</string>
++ <string id="19094">Lu-Ma-Me-Je-Ve-Sa-__</string>
++ <string id="19095">Lu-Ma-Me-Je-Ve-Sa-Di</string>
++ <string id="19096">__-__-__-__-__-Sa-Di</string>
++ <string id="19097">Entrez un nom pour l'enregistrement</string>
++ <string id="19098">Attention</string>
++ <string id="19099">Programmation existante</string>
++ <string id="19100">Êtes vous sûr de vouloir supprimer cette chaîne, ainsi que toutes les programmations associées ?</string>
++ <string id="19101">Cette chaîne est actuellement utilisée en lecture.</string>
++ <string id="19102">Veuillez changer de chaîne.</string>
++ <string id="19103">Scan des icônes manquants</string>
++ <string id="19104">Entrez le nom du dossier pour l'enregistrement</string>
++ <string id="19105">Taille :</string>
++ <string id="19106">Prochaine programmation le</string>
++ <string id="19107">à</string>
++ <string id="19108">Enregistrements non synchros. Voir le log pour plus de détails.</string>
++ <string id="19109">Impossible de sauvegarder la programmation. Voir le log pour plus de détails.</string>
++ <string id="19110">Erreur. Réessayez plus tard ou regardez le log pour plus de détails.</string>
++ <string id="19111">Erreur du serveur PVR. Voir le log pour plus de détails.</string>
++ <string id="19112">Programmations non synchros. Voir le log pour plus de détails.</string>
++ <string id="19113">Suivant</string>
++ <string id="19114">Version</string>
++ <string id="19115">Adresse</string>
++ <string id="19116">Taille du disque</string>
++ <string id="19117">Rechercher des chaînes</string>
++ <string id="19118">Impossible d'utiliser les fonctions PVR pendant</string>
++ <string id="19119">Sur quel serveur voulez-vous rechercher ?</string>
++ <string id="19120">Nombre de clients</string>
++ <string id="19121">Eviter les répétitions</string>
++ <string id="19122">L'enregistrement est encore en cours. Êtes-vous sûr de vouloir supprimer cette programmation ?</string>
++ <string id="19123">Chaînes gratuites seulement</string>
++ <string id="19124">Ignorer les programmations existantes</string>
++ <string id="19125">Ignorer les enregistrements existants</string>
++ <string id="19126">Heure de début</string>
++ <string id="19127">Heure de fin</string>
++ <string id="19128">Date de début</string>
++ <string id="19129">Date de fin</string>
++ <string id="19130">Durée minimum</string>
++ <string id="19131">Durée maximum</string>
++ <string id="19132">Inclure les genres inconnus</string>
++ <string id="19133">Recherche</string>
++ <string id="19134">Inclure la description</string>
++ <string id="19135">Sensible à la casse</string>
++ <string id="19136">Chaîne indisponible</string>
++ <string id="19137">Aucun groupe defini</string>
++ <string id="19138">Veuillez créer un groupe</string>
++ <string id="19139">Nom du nouveau groupe</string>
++ <string id="19141">Groupe</string>
++ <string id="19142">Rechercher dans le guide</string>
++ <string id="19143">Gestion des groupes</string>
++ <string id="19144">Aucun groupe défini</string>
++ <string id="19145">Groupé</string>
++ <string id="19146">Groupes</string>
++ <string id="19147">Le serveur PVR ne supporte pas cette action. Voir le log pour plus de détails.</string>
++ <string id="19148">Chaîne</string>
++ <string id="19149">Lu</string>
++ <string id="19150">Ma</string>
++ <string id="19151">Me</string>
++ <string id="19152">Je</string>
++ <string id="19153">Ve</string>
++ <string id="19154">Sa</string>
++ <string id="19155">Di</string>
++ <string id="19156">depuis</string>
++ <string id="19157">Prochain enregistrement</string>
++ <string id="19158">Enregistrement en cours</string>
++ <string id="19159">de</string>
++ <string id="19160">à</string>
++ <string id="19161">Sur</string>
++ <string id="19162">Enregistrement actif</string>
++ <string id="19163">Enregistrements</string>
++ <string id="19164">Impossible de démarrer l'enregistrement. Voir le log pour plus de détails.</string>
++ <string id="19165">Regarder</string>
++ <string id="19166">Information PVR</string>
++ <string id="19167">Scan des icônes manquants</string>
++ <string id="19168">Changer de chaîne sans appuyer sur OK</string>
++ <string id="19169">Cacher la boîte d'information vidéo</string>
++ <string id="19170">Temps d'attente maximum au démarrage de la lecture</string>
++ <string id="19171">Démarrer avec la lecture réduite</string>
++ <string id="19172">Durée par défaut des enregistrements instantannés</string>
++ <string id="19173">Priorité par défaut des enregistrements</string>
++ <string id="19174">Durée de vie par défaut des enregistrements</string>
++ <string id="19175">Marge au début de l'enregistrement</string>
++ <string id="19176">Marge à la fin de l'enregistrement</string>
++ <string id="19177">Lecture</string>
++ <string id="19178">Voir les informations sur la chaîne lors du zapping</string>
++ <string id="19179">Cacher automatiquement l'information des chaînes</string>
++ <string id="19180">TV</string>
++ <string id="19181">Menu/OSD</string>
++ <string id="19182">Jours à afficher dans l'EPG</string>
++ <string id="19184">Durée d'affichage des informations de la chaîne</string>
++ <string id="19185">Réinitialiser la base de données PVR</string>
++ <string id="19186">Toutes les données dans la base de données PVR vont être effacées</string>
++ <string id="19187">Réinitialiser la base de données EPG</string>
++ <string id="19188">L'EPG va être réinitialisé</string>
++ <string id="19189">Afficher la dernière chaîne au démarrage</string>
++ <string id="19190">Réduit</string>
++ <string id="19191">Service PVR</string>
++ <string id="19192">Aucun des serveur PVR connectés ne supporte le scan des chaînes.</string>
++ <string id="19193">Le scan des chaînes ne peut pas démarrer. Voir le log pour plus de détails.</string>
++ <string id="19194">la recherche. Continuer ?</string>
++ <string id="19195">Actions client</string>
++ <string id="19196">Actions spécifiques du client PVR</string>
++ <string id="19197">Enregistrement démarré le : %s</string>
++ <string id="19198">Enregistrement fini le : %s</string>
++ <string id="19199">Gestion des chaînes</string>
++ <string id="19200">Source EPG :</string>
++ <string id="19201">Nom de la chaine :</string>
++ <string id="19202">Icône de la chaîne :</string>
++ <string id="19203">Modifier la chaîne</string>
++ <string id="19204">Nouvelle chaîne</string>
++ <string id="19205">Gestion des groupes</string>
++ <string id="19206">Activer EPG :</string>
++ <string id="19207">Groupe :</string>
++ <string id="19208">Entrer le nom de la nouvelle chaîne</string>
++ <string id="19209">serveur virtuel XBMC</string>
++ <string id="19210">Client</string>
++ <string id="19211">Supprimer la chaîne</string>
++ <string id="19212">Cette liste contient des changements</string>
++ <string id="19213">Choisir le serveur</string>
++ <string id="19214">Entrez une URL valide pour la nouvelle chaîne</string>
++ <string id="19215">Le serveur PVR ne supporte pas les programmations.</string>
++ <string id="19216">Toutes les stations radio</string>
++ <string id="19217">Toutes les chaînes TV</string>
++ <string id="19218">Visible</string>
++ <string id="19219">Chaînes disponibles</string>
++ <string id="19220">Chaînes dans</string>
++ <string id="19221">Synchroniser les groupes de chaînes avec le serveur PVR</string>
++ <string id="19222">EPG</string>
++ <string id="19223">Aucun addon PVR n'a pu être activé. Voir le log pour plus de détails.</string>
++ <string id="19224">Enregistrement annulé</string>
++ <string id="19225">Enregistrement programmé</string>
++ <string id="19226">Enregistrement démarré</string>
++ <string id="19227">Enregistrement terminé</string>
++ <string id="19228">Enregistrement supprimé</string>
++ <string id="19229">Fermer l'OSD de la chaîne après le zapping</string>
++ <string id="19230">Désactiver les mises à jour d'EPG pendant la lecture d'une chaîne TV</string>
++ <string id="19231">Toujours utiliser l'ordre des chaînes du serveur PVR</string>
++ <string id="19232">Effacer les résultats</string>
++ <string id="19233">Afficher une notification au changement d'état des programmations</string>
++
++ <string id="19499">Autre/Inconnu</string>
++ <string id="19500">Film/Drame</string>
++ <string id="19501">Policier/Thriller</string>
++ <string id="19502">Aventure/Western/Guerre</string>
++ <string id="19503">Science Fiction/Fantaisie/Horreur</string>
++ <string id="19504">Comédie</string>
++ <string id="19505">Série B/Mélodrame/Folklore</string>
++ <string id="19506">Romance</string>
++ <string id="19507">Serieux/Classique/Religion/Film Historique/Drame</string>
++ <string id="19508">Film pour Adultes/Drame</string>
++ <string id="19509"></string>
++ <string id="19510"></string>
++ <string id="19511"></string>
++ <string id="19512"></string>
++ <string id="19513"></string>
++ <string id="19514"></string>
++ <string id="19515"></string>
++ <string id="19516">Actualité</string>
++ <string id="19517">Bulletin Météo</string>
++ <string id="19518">Magazine d'actualité</string>
++ <string id="19519">Documentaire</string>
++ <string id="19520">Discussion/Interview/Débat</string>
++ <string id="19521"></string>
++ <string id="19522"></string>
++ <string id="19523"></string>
++ <string id="19524"></string>
++ <string id="19525"></string>
++ <string id="19526"></string>
++ <string id="19527"></string>
++ <string id="19528"></string>
++ <string id="19529"></string>
++ <string id="19530"></string>
++ <string id="19531"></string>
++ <string id="19532">Spectacle/Jeu Télévisé</string>
++ <string id="19533">Jeu Télévisé/Quizz/Concours</string>
++ <string id="19534">Emission de Variétés</string>
++ <string id="19535">Talk Show</string>
++ <string id="19536"></string>
++ <string id="19537"></string>
++ <string id="19538"></string>
++ <string id="19539"></string>
++ <string id="19540"></string>
++ <string id="19541"></string>
++ <string id="19542"></string>
++ <string id="19543"></string>
++ <string id="19544"></string>
++ <string id="19545"></string>
++ <string id="19546"></string>
++ <string id="19547"></string>
++ <string id="19548">Sports</string>
++ <string id="19549">Evénement spécial</string>
++ <string id="19550">Magazine Sportif</string>
++ <string id="19551">Football</string>
++ <string id="19552">Tennis/Squash</string>
++ <string id="19553">Sports en Equipe</string>
++ <string id="19554">Athlétisme</string>
++ <string id="19555">Sport Automobile</string>
++ <string id="19556">Sport Aquatique</string>
++ <string id="19557">Sports d'Hiver</string>
++ <string id="19558">Equitation</string>
++ <string id="19559">Arts Martiaux</string>
++ <string id="19560"></string>
++ <string id="19561"></string>
++ <string id="19562"></string>
++ <string id="19563"></string>
++ <string id="19564">Programme Enfants/Jeunes</string>
++ <string id="19565">Programme Bébés</string>
++ <string id="19566">Divertissement 6-14 ans</string>
++ <string id="19567">Divertissement 10-16 ans</string>
++ <string id="19568">Programme Educatif</string>
++ <string id="19569">Dessin Animé/Marionnettes</string>
++ <string id="19570"></string>
++ <string id="19571"></string>
++ <string id="19572"></string>
++ <string id="19573"></string>
++ <string id="19574"></string>
++ <string id="19575"></string>
++ <string id="19576"></string>
++ <string id="19577"></string>
++ <string id="19578"></string>
++ <string id="19579"></string>
++ <string id="19580">Musique/Ballet/Danse</string>
++ <string id="19581">Rock/Pop</string>
++ <string id="19582">Musique Classique</string>
++ <string id="19583">Folklore/Musique Traditionelle</string>
++ <string id="19584">Comédie Musicale/Opera</string>
++ <string id="19585">Ballet</string>
++ <string id="19586"></string>
++ <string id="19587"></string>
++ <string id="19588"></string>
++ <string id="19589"></string>
++ <string id="19590"></string>
++ <string id="19591"></string>
++ <string id="19592"></string>
++ <string id="19593"></string>
++ <string id="19594"></string>
++ <string id="19595"></string>
++ <string id="19596">Arts/Culture</string>
++ <string id="19597">Arts du Spectacle</string>
++ <string id="19598">Beaux Arts</string>
++ <string id="19599">Religion</string>
++ <string id="19600">Culture Populaire/Arts Traditionels</string>
++ <string id="19601">Littérature</string>
++ <string id="19602">Film/Cinéma</string>
++ <string id="19603">Film/Video Experimental</string>
++ <string id="19604">Diffusion/Presse</string>
++ <string id="19605">Nouveaux Médias</string>
++ <string id="19606">Magazine d'Art/Culture</string>
++ <string id="19607">Mode</string>
++ <string id="19608"></string>
++ <string id="19609"></string>
++ <string id="19610"></string>
++ <string id="19611"></string>
++ <string id="19612">Société/Politique/Economie</string>
++ <string id="19613">Magazine/Reportage/Documentaire</string>
++ <string id="19614">Economie/Société</string>
++ <string id="19615">People</string>
++ <string id="19616"></string>
++ <string id="19617"></string>
++ <string id="19618"></string>
++ <string id="19619"></string>
++ <string id="19620"></string>
++ <string id="19621"></string>
++ <string id="19622"></string>
++ <string id="19623"></string>
++ <string id="19624"></string>
++ <string id="19625"></string>
++ <string id="19626"></string>
++ <string id="19627"></string>
++ <string id="19628">Education/Science/Faits</string>
++ <string id="19629">Nature/Animaux/Environnement</string>
++ <string id="19630">Technologie/Sciences Naturelles</string>
++ <string id="19631">Médecine/Anatomie/Psychologie</string>
++ <string id="19632">Pays Etrangers/Expéditions</string>
++ <string id="19633">Société/Sciences Spirituelles</string>
++ <string id="19634">Enseignement Postscolaire</string>
++ <string id="19635">Langues</string>
++ <string id="19636"></string>
++ <string id="19637"></string>
++ <string id="19638"></string>
++ <string id="19639"></string>
++ <string id="19640"></string>
++ <string id="19641"></string>
++ <string id="19642"></string>
++ <string id="19643"></string>
++ <string id="19644">Loisirs</string>
++ <string id="19645">Tourisme/Voyages</string>
++ <string id="19646">Artisanat</string>
++ <string id="19647">Automobile</string>
++ <string id="19648">Fitness &amp; Santé</string>
++ <string id="19649">Cuisine</string>
++ <string id="19650">Publicité/Shopping</string>
++ <string id="19651">Jardinage</string>
++ <string id="19652"></string>
++ <string id="19653"></string>
++ <string id="19654"></string>
++ <string id="19655"></string>
++ <string id="19656"></string>
++ <string id="19657"></string>
++ <string id="19658"></string>
++ <string id="19659"></string>
++ <string id="19660">Characteristiques Speciales</string>
++ <string id="19661">Langue Originale</string>
++ <string id="19662">Noir &amp; Blanc</string>
++ <string id="19663">Non publié</string>
++ <string id="19664">Live</string>
++ <string id="19665"></string>
++ <string id="19666"></string>
++ <string id="19667"></string>
++ <string id="19668"></string>
++ <string id="19669"></string>
++ <string id="19670"></string>
++ <string id="19671"></string>
++ <string id="19672"></string>
++ <string id="19673"></string>
++ <string id="19674"></string>
++ <string id="19675"></string>
++ <string id="19676">Drame</string>
++ <string id="19677">Policier/Thriller</string>
++ <string id="19678">Aventure/Western/Guerre</string>
++ <string id="19679">Science Fiction/Fantaisie/Horreur</string>
++ <string id="19680">Comédie</string>
++ <string id="19681">Soap/Melodrame/Folklore</string>
++ <string id="19682">Romance</string>
++ <string id="19683">Documentaire/Religion/Historique</string>
++ <string id="19684">Adulte</string>
++ <string id="19685"></string>
++ <string id="19686"></string>
++ <string id="19687"></string>
++ <string id="19688"></string>
++ <string id="19689"></string>
++ <string id="19690"></string>
++ <string id="19691"></string>
++
+ <string id="20000">Dossier des enregistrements musique</string>
+ <string id="20001">Utiliser un lecteur DVD externe</string>
+ <string id="20002">Lecteur DVD externe</string>
+diff --git a/language/German/strings.xml b/language/German/strings.xml
+index 94b203e..49fa3f2 100644
+--- a/language/German/strings.xml
++++ b/language/German/strings.xml
+@@ -1026,6 +1026,8 @@
+ <string id="13014">Minimieren</string>
+ <string id="13015">Power-Button Aktion</string>
+ <string id="13016">Herunterfahren</string>
++ <string id="13017">Blockiere Herunterfahren</string>
++ <string id="13018">Erlaube Herunterfahren</string>
+
+ <string id="13020">Ist noch eine weitere Sitzung aktiv (evtl. SSH)? </string>
+ <string id="13021">Wechselfestplatte wurde angeschlossen</string>
+@@ -1573,7 +1575,442 @@
+
+ <string id="17500">Bildschirm Sleep Timeout</string>
+
++ <string id="17998">%i Stunden</string>
++ <string id="17999">%i Tage</string>
++
++ <!-- strings 19000 thru 19999 used for tv interface -->
+ <string id="19000">Zum Kanal wechseln</string>
++ <string id="19001">Benutzen Sie AND, OR und NOT in Verbindung mit Ihren Suchbegriffen, um detaillierter zu suchen.</string>
++ <string id="19002">Oder benutzen Sie ganze Sätze in Ausrufezeichen z.B.: "Der Zauberer von Oz".</string>
++ <string id="19003">Finde ähnliche Sendung</string>
++ <string id="19004">Lade EPG von den PVR-Klienten</string>
++ <string id="19005">PVR Empfang-Informationen</string>
++ <string id="19006">Empfangsgerät</string>
++ <string id="19007">Empfangsstatus</string>
++ <string id="19008">Signal Qualität</string>
++ <string id="19009">SNR</string>
++ <string id="19010">BER</string>
++ <string id="19011">UNC</string>
++ <string id="19012">Backend</string>
++ <string id="19013">Frei empfangbar</string>
++ <string id="19014">Fest</string>
++ <string id="19015">Verschlüsselung</string>
++ <string id="19016">Server %i - %s</string>
++ <string id="19017">TV Aufnahmen</string>
++ <string id="19018">Standardordner für TV Thumbnails</string>
++ <string id="19019">Kanäle</string>
++ <string id="19020">TV</string>
++ <string id="19021">Radio</string>
++ <string id="19022">Versteckte</string>
++ <string id="19023">TV Kanäle</string>
++ <string id="19024">Radio Kanäle</string>
++ <string id="19025">Bevorstehende Aufnahmen</string>
++ <string id="19026">Timer hinzufügen...</string>
++ <string id="19027">Es liegen keine Suchergebnisse vor</string>
++ <string id="19028">Es sind keine EPG Einträge vorhanden</string>
++ <string id="19029">Programm</string>
++ <string id="19030">Jetzt</string>
++ <string id="19031">Nächstes</string>
++ <string id="19032">Zeitleiste</string>
++ <string id="19033">Information</string>
++ <string id="19034">Aufnahme läuft bereits</string>
++ <string id="19035">Kanal konnte nicht wiedergegeben werden</string>
++ <string id="19036">Aufnahme konnte nicht wiedergegeben werden</string>
++ <string id="19037">Signalqualität anzeigen</string>
++ <string id="19038">Wird derzeit nicht unterstützt!</string>
++ <string id="19039">Verstecken bestätigen</string>
++ <string id="19040">Timer</string>
++ <string id="19041">Aufnahme umbenennen?</string>
++ <string id="19042">Timer umbenennen?</string>
++ <string id="19043">Aufnahme</string>
++ <string id="19044">Bitte überprüfen Sie ihre Einstellungen oder den Server</string>
++ <string id="19045">Keine PVR Klienten verfügbar</string>
++ <string id="19046">Neuer Kanal</string>
++ <string id="19047">Titel-Informationen</string>
++ <string id="19048">Gruppenverwaltung</string>
++ <string id="19049">Zeige Kanal</string>
++ <string id="19050">Zeige normale Kanäle</string>
++ <string id="19051">Zeige versteckte Kanäle</string>
++ <string id="19052">Kanal verschieben nach:</string>
++ <string id="19053">Aufnahme-Informationen</string>
++ <string id="19054">Kanal verstecken</string>
++ <string id="19055">Keine Informationen verfügbar!</string>
++ <string id="19056">Neuer Timer</string>
++ <string id="19057">Timer bearbeiten</string>
++ <string id="19058">Timer aktivieren/deaktivieren</string>
++ <string id="19059">Aufnahme abbrechen</string>
++ <string id="19060">Timer löschen</string>
++ <string id="19061">Timer hinzufügen</string>
++ <string id="19062">Nach Kanal</string>
++ <string id="19063">Gehe zum Anfang</string>
++ <string id="19064">Gehe zum Ende</string>
++ <string id="19065">Standard Programmanzeige</string>
++ <string id="19066">Lade Aufnahmeinformationen von den PVR-Klienten</string>
++ <string id="19067">Es ist bereits ein Timer vorhanden</string>
++ <string id="19068">Konnte Aufnahme nicht löschen!</string>
++ <string id="19069">EPG</string>
++ <string id="19070">Zeit bis zur EPG-Suche</string>
++ <string id="19071">Zeit bis zur EPG-Aktualisierung</string>
++ <string id="19072">Ignoriere Datenbank für PVR-Server EPG</string>
++ <string id="19073">Zeitlimit für Kanaleingabe</string>
++ <string id="19074">Aktiv:</string>
++ <string id="19075">Name:</string>
++ <string id="19076">Ordner:</string>
++ <string id="19077">Radio:</string>
++ <string id="19078">Kanal:</string>
++ <string id="19079">Tag:</string>
++ <string id="19080">Anfang:</string>
++ <string id="19081">Ende:</string>
++ <string id="19082">Priorität:</string>
++ <string id="19084">Erster Tag:</string>
++ <string id="19085">Unbekannter Kanal %u</string>
++ <string id="19086">Mo-__-__-__-__-__-__</string>
++ <string id="19087">__-Di-__-__-__-__-__</string>
++ <string id="19088">__-__-Mi-__-__-__-__</string>
++ <string id="19089">__-__-__-Do-__-__-__</string>
++ <string id="19090">__-__-__-__-Fr-__-__</string>
++ <string id="19091">__-__-__-__-__-Sa-__</string>
++ <string id="19092">__-__-__-__-__-__-So</string>
++ <string id="19093">Mo-Di-Mi-Do-Fr-__-__</string>
++ <string id="19094">Mo-Di-Mi-Do-Fr-Sa-__</string>
++ <string id="19095">Mo-Di-Mi-Do-Fr-Sa-So</string>
++ <string id="19096">__-__-__-__-__-Sa-So</string>
++ <string id="19097">Bitte geben Sie den Aufnahmenamen ein</string>
++ <string id="19098">Warnung</string>
++ <string id="19099">Timer vorhanden</string>
++ <string id="19100">Kanal und Timer löschen?</string>
++ <string id="19101">Kanalwiedergabe läuft</string>
++ <string id="19102">Bitte schalten Sie auf einen anderen Kanal!</string>
++ <string id="19103">Suche fehlende Kanallogos</string>
++ <string id="19104">Bitte geben Sie den Aufnahmeordner ein</string>
++ <string id="19105">Größe:</string>
++ <string id="19106">Nächster Timer am</string>
++ <string id="19107">um</string>
++ <string id="19108">Aufnahmen nicht synchron!</string>
++ <string id="19109">Konnte Timer nicht speichern!</string>
++ <string id="19110">Erneut versuchen...</string>
++ <string id="19111">Server Fehler!</string>
++ <string id="19112">Timers nicht synchron!</string>
++ <string id="19113">Danach</string>
++ <string id="19114">Version</string>
++ <string id="19115">Addresse</string>
++ <string id="19116">Festplattengröße</string>
++ <string id="19117">Kanäle suchen</string>
++ <string id="19118">TV ist während der Kanalsuche nicht möglich!</string>
++ <string id="19119">Auf welchem Server soll gesucht werden?</string>
++ <string id="19120">Klientnummer</string>
++ <string id="19121">Keine Wiederholungen</string>
++ <string id="19122">Timer zeichnet auf - trotzdem löschen?</string>
++ <string id="19123">Nur frei empfangbare Kanäle</string>
++ <string id="19124">Bei vorhandenem Timer ignorieren</string>
++ <string id="19125">Bei vorhandener Aufnahmen ignorieren</string>
++ <string id="19126">Start Uhrzeit</string>
++ <string id="19127">Stop Uhrzeit</string>
++ <string id="19128">Start Datum</string>
++ <string id="19129">Stop Datum</string>
++ <string id="19130">Minimale Dauer</string>
++ <string id="19131">Maximale Dauer</string>
++ <string id="19132">Inklusive unbekannter Genres</string>
++ <string id="19133">Suchbegriff</string>
++ <string id="19134">Inklusive Beschreibung</string>
++ <string id="19135">Groß-/Kleinschreibung</string>
++ <string id="19136">Kanal nicht verfügbar</string>
++ <string id="19137">Keine Gruppen definiert</string>
++ <string id="19138">Bitte erstellen Sie zuerst eine</string>
++ <string id="19139">Neuer Gruppenname</string>
++ <string id="19141">Gruppe</string>
++ <string id="19142">EPG durchsuchen</string>
++ <string id="19143">Gruppenverwaltung</string>
++ <string id="19144">Ungruppiert</string>
++ <string id="19145">Keine Gruppen</string>
++ <string id="19146">Gruppen</string>
++ <string id="19147">PVR-Server ist nicht kompatibel!</string>
++ <string id="19148">Kanal</string>
++ <string id="19149">Mo</string>
++ <string id="19150">Di</string>
++ <string id="19151">Mi</string>
++ <string id="19152">Do</string>
++ <string id="19153">Fr</string>
++ <string id="19154">Sa</string>
++ <string id="19155">So</string>
++ <string id="19156">ab</string>
++ <string id="19157">Nächste Aufnahme</string>
++ <string id="19158">Jetzige Aufnahme</string>
++ <string id="19159">von</string>
++ <string id="19160">bis</string>
++ <string id="19161">Auf</string>
++ <string id="19162">Aufnahme läuft</string>
++ <string id="19163">Aufnahmen</string>
++ <string id="19164">Konnte Aufnahme nicht starten</string>
++ <string id="19165">Umschalten</string>
++ <string id="19166">PVR-Informationen</string>
++ <string id="19167">Suche fehlende Kanallogos</string>
++ <string id="19168">Beim umschalten auf OK verzichten</string>
++ <string id="19169">Infobox für Videolänge verbergen</string>
++ <string id="19170">Suchüberlauf vor Kanalwiedergabe</string>
++ <string id="19171">Wiedergabe minimiert starten</string>
++ <string id="19172">Dauer der Direktaufzeichnung</string>
++ <string id="19173">Standard-Priorität</string>
++ <string id="19174">Standard-Lebensdauer</string>
++ <string id="19175">Vorlauf zum Timer-Beginn</string>
++ <string id="19176">Nachlauf am Timer-Ende</string>
++ <string id="19177">Wiedergabe</string>
++ <string id="19178">Info beim Kanalwechsel</string>
++ <string id="19179">Angeforderte Kanalinfo schließen</string>
++ <string id="19180">TV</string>
++ <string id="19181">Menü/OSD</string>
++ <string id="19182">Tage für TV-Programm Anzeige</string>
++ <string id="19183">Alte EPG-Daten anzeigen</string>
++ <string id="19184">Anzeigedauer für Kanalinfo</string>
++ <string id="19185">TV Datenbank zurücksetzten</string>
++ <string id="19186">Alle Daten in der Datenbank werden gelöscht</string>
++ <string id="19187">EPG Daten löschen und neu einlesen</string>
++ <string id="19188">Alle EPG Daten werden gelöscht</string>
++ <string id="19189">Nach Start letzten Kanal wiedergeben</string>
++ <string id="19190">Minimiert</string>
++ <string id="19191">PVR-Service</string>
++ <string id="19192">Keiner der PVR-Server unterstützt die Kanalsuche</string>
++ <string id="19193">Kanalsuche konnte nicht durchgeführt werden</string>
++ <string id="19194">Fortfahren?</string>
++ <string id="19195">Klientaktionen</string>
++ <string id="19196">PVR Klient spezifische Aktionen</string>
++ <string id="19197">Aufnahme gestarted auf: %s</string>
++ <string id="19198">Aufnahme beendet auf: %s</string>
++ <string id="19199">Kanalverwaltung</string>
++ <string id="19200">EPG Quelle:</string>
++ <string id="19201">Kanalname:</string>
++ <string id="19202">Kanallogo:</string>
++ <string id="19203">Kanal bearbeiten</string>
++ <string id="19204">Neuer Kanal</string>
++ <string id="19205">Gruppenverwaltung</string>
++ <string id="19206">EPG Aktivieren:</string>
++ <string id="19207">Gruppe:</string>
++ <string id="19208">Bitte geben Sie den neuen Kanalnamen ein</string>
++ <string id="19209">XBMC interner virtueller Kanal</string>
++ <string id="19210">Klient</string>
++ <string id="19211">Kanal löschen</string>
++ <string id="19212">Liste enthält Änderungen</string>
++ <string id="19213">Wählen Sie die Kanalquelle</string>
++ <string id="19214">Bitte geben Sie eine URL für den Kanal ein</string>
++ <string id="19215">Der PVR-Server unterstützt keine Timer!</string>
++ <string id="19216">Alle Radio Kanäle</string>
++ <string id="19217">Alle TV Kanäle</string>
++
++ <string id="19219">Ungruppierte Kanäle</string>
++ <string id="19220">Kanäle in</string>
++ <string id="19221">Synchronisiere Kanalgruppen mit den Backends</string>
++
++ <string id="19223">Es konnte kein PVR Add-on aktiviert werden. Überprüfen Sie Ihre Einstellungen und das Log.</string>
++ <string id="19224">Aufnahme abgebrochen</string>
++ <string id="19225">Aufnahme geplant</string>
++ <string id="19226">Aufnahme gestartet</string>
++ <string id="19227">Aufnahme beendet</string>
++ <string id="19228">Aufnahme gelöscht</string>
++ <string id="19229">Schließe das Kanal-OSD nach einem Kanalwechsel</string>
++ <string id="19230">Deaktiviere EPG Aktualisierungen während der TV Wiedergabe</string>
++ <string id="19231">Immer die Kanalreihenfolge der Backends verwenden</string>
++ <string id="19233">Zeige eine Benachrichtigung bei Timer Aktualisierungen</string>
++ <string id="19234">Verwenden Backend Kanal Nummerierung (funktioniert nur mit 1 aktiv PVR Addons)</string>
++
++ <string id="19244">Backend Inaktivität</string>
++ <string id="19245">Aufwachzeit Kommando (cmd [timestamp])</string>
++ <string id="19246">Vor Aufnahmebeginn aufwachen</string>
++ <string id="19247">Tägliches aufwachen</string>
++ <string id="19248">Tägliche Aufwachzeit (HH:MM:SS)</string>
++
++ <string id="19499">Andere/Unbekannt</string>
++ <string id="19500">Movie/Drama</string>
++ <string id="19501">Detektivfilm/Thriller</string>
++ <string id="19502">Abenteuer/Western/Krieg</string>
++ <string id="19503">Science Fiction/Fantasy/Horror</string>
++ <string id="19504">Komödie</string>
++ <string id="19505">Seifenoper/Melodram/Folklore</string>
++ <string id="19506">Romanze</string>
++ <string id="19507">Serie/Klassik/Religion/Historienfilm</string>
++ <string id="19508">Erwachsenenfilm</string>
++ <string id="19509"></string>
++ <string id="19510"></string>
++ <string id="19511"></string>
++ <string id="19512"></string>
++ <string id="19513"></string>
++ <string id="19514"></string>
++ <string id="19515"></string>
++ <string id="19516">Nachrichten/Aktuelle Meldungen</string>
++ <string id="19517">Nachrichten/Wettervorhersage</string>
++ <string id="19518">Nachrichtenmagazin</string>
++ <string id="19519">Dokumentation</string>
++ <string id="19520">Diskussion/Interview/Debatte</string>
++ <string id="19521"></string>
++ <string id="19522"></string>
++ <string id="19523"></string>
++ <string id="19524"></string>
++ <string id="19525"></string>
++ <string id="19526"></string>
++ <string id="19527"></string>
++ <string id="19528"></string>
++ <string id="19529"></string>
++ <string id="19530"></string>
++ <string id="19531"></string>
++ <string id="19532">Show/Game Show</string>
++ <string id="19533">Game Show/Quiz/Contest</string>
++ <string id="19534">Varietevorführung</string>
++ <string id="19535">Talk Show</string>
++ <string id="19536"></string>
++ <string id="19537"></string>
++ <string id="19538"></string>
++ <string id="19539"></string>
++ <string id="19540"></string>
++ <string id="19541"></string>
++ <string id="19542"></string>
++ <string id="19543"></string>
++ <string id="19544"></string>
++ <string id="19545"></string>
++ <string id="19546"></string>
++ <string id="19547"></string>
++ <string id="19548">Sport</string>
++ <string id="19549">Sportveranstaltung</string>
++ <string id="19550">Sportmagazin</string>
++ <string id="19551">Fußball</string>
++ <string id="19552">Tennis/Squash</string>
++ <string id="19553">Team Sportarten</string>
++ <string id="19554">Athletik</string>
++ <string id="19555">Motorsport</string>
++ <string id="19556">Wassersport</string>
++ <string id="19557">Wintersport</string>
++ <string id="19558">Pferdesport</string>
++ <string id="19559">Kampfsport</string>
++ <string id="19560"></string>
++ <string id="19561"></string>
++ <string id="19562"></string>
++ <string id="19563"></string>
++ <string id="19564">Kinder- &amp; Jugendprogramm</string>
++ <string id="19565">Unterhaltungsprogramm für Vorschulkinder</string>
++ <string id="19566">Unterhaltungsprogramm für 6 bis 14 Jährige</string>
++ <string id="19567">Unterhaltungsprogramm für 10 bis 16 Jährige</string>
++ <string id="19568">Information/Educational/Schulprogramm</string>
++ <string id="19569">Zeichentrick/Puppen</string>
++ <string id="19570"></string>
++ <string id="19571"></string>
++ <string id="19572"></string>
++ <string id="19573"></string>
++ <string id="19574"></string>
++ <string id="19575"></string>
++ <string id="19576"></string>
++ <string id="19577"></string>
++ <string id="19578"></string>
++ <string id="19579"></string>
++ <string id="19580">Musik/Ballet/Tanzen</string>
++ <string id="19581">Rock/Pop</string>
++ <string id="19582">Serious/Klassische Musik</string>
++ <string id="19583">Volksmusik/Tradionelle Musik</string>
++ <string id="19584">Musical/Oper</string>
++ <string id="19585">Ballet</string>
++ <string id="19586"></string>
++ <string id="19587"></string>
++ <string id="19588"></string>
++ <string id="19589"></string>
++ <string id="19590"></string>
++ <string id="19591"></string>
++ <string id="19592"></string>
++ <string id="19593"></string>
++ <string id="19594"></string>
++ <string id="19595"></string>
++ <string id="19596">Kunst/Kultur</string>
++ <string id="19597">Performing Arts</string>
++ <string id="19598">Bildende Kunst</string>
++ <string id="19599">Religion</string>
++ <string id="19600">Volkskultur/Traditionelle Kunst</string>
++ <string id="19601">Literatur</string>
++ <string id="19602">Film/Kino</string>
++ <string id="19603">Experimental Film/Video</string>
++ <string id="19604">Rundfunk/Presse</string>
++ <string id="19605">Neue Medien</string>
++ <string id="19606">Kunst/Kultur Magazin</string>
++ <string id="19607">Mode</string>
++ <string id="19608"></string>
++ <string id="19609"></string>
++ <string id="19610"></string>
++ <string id="19611"></string>
++ <string id="19612">Soziales/Politik/Wirtschaft</string>
++ <string id="19613">Magazin/Report/Dokumentation</string>
++ <string id="19614">Wirtschaft- &amp; Sozialberatung</string>
++ <string id="19615">Bemerkenswerte Personen</string>
++ <string id="19616"></string>
++ <string id="19617"></string>
++ <string id="19618"></string>
++ <string id="19619"></string>
++ <string id="19620"></string>
++ <string id="19621"></string>
++ <string id="19622"></string>
++ <string id="19623"></string>
++ <string id="19624"></string>
++ <string id="19625"></string>
++ <string id="19626"></string>
++ <string id="19627"></string>
++ <string id="19628">Bildung/Wissenschaft/Fakten</string>
++ <string id="19629">Natur/Tiere/Umwelt</string>
++ <string id="19630">Technologie/Naturwissenschaften</string>
++ <string id="19631">Medizin/Physiologie/Psychologie</string>
++ <string id="19632">Ausland/Expeditionen</string>
++ <string id="19633">Sozial- &amp; Geisteswissenschaften</string>
++ <string id="19634">Weiterbildung</string>
++ <string id="19635">Sprachen</string>
++ <string id="19636"></string>
++ <string id="19637"></string>
++ <string id="19638"></string>
++ <string id="19639"></string>
++ <string id="19640"></string>
++ <string id="19641"></string>
++ <string id="19642"></string>
++ <string id="19643"></string>
++ <string id="19644">Freizeit/Hobbys</string>
++ <string id="19645">Tourismus/Reisen</string>
++ <string id="19646">Handwerk</string>
++ <string id="19647">Auto</string>
++ <string id="19648">Fitness &amp; Gesundheit</string>
++ <string id="19649">Kochen</string>
++ <string id="19650">Werbung/Einkaufen</string>
++ <string id="19651">Garten</string>
++ <string id="19652"></string>
++ <string id="19653"></string>
++ <string id="19654"></string>
++ <string id="19655"></string>
++ <string id="19656"></string>
++ <string id="19657"></string>
++ <string id="19658"></string>
++ <string id="19659"></string>
++ <string id="19660">Besondere Merkmale</string>
++ <string id="19661">Originalsprache</string>
++ <string id="19662">Schwarz/Weiß</string>
++ <string id="19663">Bisher unveröffentlicht</string>
++ <string id="19664">Livesendung</string>
++ <string id="19665"></string>
++ <string id="19666"></string>
++ <string id="19667"></string>
++ <string id="19668"></string>
++ <string id="19669"></string>
++ <string id="19670"></string>
++ <string id="19671"></string>
++ <string id="19672"></string>
++ <string id="19673"></string>
++ <string id="19674"></string>
++ <string id="19675"></string>
++ <string id="19676">Drama</string>
++ <string id="19677">Detektivfilm/Thriller</string>
++ <string id="19678">Abenteuer/Western/Krieg</string>
++ <string id="19679">Science Fiction/Fantasy/Horror</string>
++ <string id="19680">Komödie</string>
++ <string id="19681">Seifenoper/Melodram/Folklore</string>
++ <string id="19682">Romanze</string>
++ <string id="19683">Serie/Klassisch/Religion/Geschichte</string>
++ <string id="19684">Erwachsenenfilm</string>
++ <string id="19685"></string>
++ <string id="19686"></string>
++ <string id="19687"></string>
++ <string id="19688"></string>
++ <string id="19689"></string>
++ <string id="19690"></string>
++ <string id="19691"></string>
+
+ <string id="20000">Ordner für CD-Kopien</string>
+ <string id="20001">Externen DVD-Player verwenden</string>
+@@ -2351,6 +2788,8 @@
+ <string id="33081">Diese Datei ist gestapelt, wähle den Teil der abgespielt werden soll.</string>
+ <string id="33082">Skriptpfad</string>
+ <string id="33083">Benutzerdefinierten Skript Button aktivieren</string>
++ <string id="33100">Start fehlgeschlagen</string>
++
+ <string id="33200">Neue Verbindung erkannt</string>
+ <string id="34100">Lautsprecherkonfiguration</string>
+
+diff --git a/language/Greek/strings.xml b/language/Greek/strings.xml
+index a3c36a0..cd0e06d 100644
+--- a/language/Greek/strings.xml
++++ b/language/Greek/strings.xml
+@@ -1,8 +1,8 @@
+ <?xml version="1.0" encoding="utf-8" standalone="yes"?>
+-<!--$Revision$--> <!--Translator: Ydatografida / Email: ydatografida@gmail.com / Date of translation: 03/04/2012 / Last update (by CutSickAss): 13/03/2012 / Based on english strings version 1E+42-->
++<!--$Revision$--> <!-- Original translator: Ydatografida (ydatografida@gmail.com) / Last update (by CutSickAss): 02/04/2012 -->
+ <strings>
+ <string id="0">ΕφαÏμογές</string>
+- <string id="1">ΦωτογÏαφίες</string>
++ <string id="1">Εικόνες</string>
+ <string id="2">Μουσική</string>
+ <string id="3">Βίντεο</string>
+ <string id="4">Οδηγός τηλεόÏασης</string>
+@@ -82,7 +82,7 @@
+ <string id="105">Ταξ.: Μέγεθος</string>
+ <string id="106">Όχι</string>
+ <string id="107">Îαι</string>
+- <string id="108">ΠαÏουσίαση</string>
++ <string id="108">ΠαÏουσίαση διαφανειών</string>
+ <string id="109">ΔημιουÏγία εικονιδίων</string>
+ <string id="110">ΔημιουÏγία μικÏογÏαφιών</string>
+ <string id="111">ΣυντομεÏσεις</string>
+@@ -103,7 +103,7 @@
+ <string id="126">ΛειτουÏγία</string>
+ <string id="127">Αντικείμενα</string>
+ <string id="128">Γενικά</string>
+- <string id="129">ΠαÏουσίαση</string>
++ <string id="129">ΠαÏουσίαση διαφανειών</string>
+ <string id="130">ΠληÏοφοÏίες συστήματος</string>
+ <string id="131">Απεικόνιση</string>
+ <string id="132">Άλμπουμ</string>
+@@ -123,7 +123,7 @@
+ <string id="146">ΤÏπος:</string>
+ <string id="147">Στατική</string>
+ <string id="148">DHCP</string>
+- <string id="149">Φυσική διεÏθυνση</string>
++ <string id="149">ΔιεÏθυνση MAC</string>
+ <string id="150">ΔιεÏθυνση IP</string>
+ <string id="151">ΣÏνδεση:</string>
+ <string id="152">ΜονόδÏομη</string>
+@@ -136,19 +136,19 @@
+ <string id="159">Καμία σÏνδεση</string>
+ <string id="160">ΕλεÏθεÏα</string>
+ <string id="161">Μη διαθέσιμο</string>
+- <string id="162">Άνοιγμα μονάδας</string>
++ <string id="162">ΘÏÏα δίσκου ανοικτή</string>
+ <string id="163">Ανάγνωση</string>
+ <string id="164">Δεν υπάÏχει δίσκος</string>
+ <string id="165">ΥπάÏχει δίσκος</string>
+ <string id="166">Κέλυφος</string>
+
+ <string id="169">Ανάλυση</string>
+- <string id="170">ΠÏοσαÏμογή του ÏÏ…Î¸Î¼Î¿Ï Î±Î½Î±Î½Î­Ï‰ÏƒÎ·Ï‚</string>
++ <string id="170">ΠÏοσαÏμογή του ÏÏ…Î¸Î¼Î¿Ï Î±Î½Î±Î½Î­Ï‰ÏƒÎ·Ï‚ για να ταιÏιάζει με το βίντεο</string>
+
+ <string id="172">ΗμεÏομηνία κυκλοφοÏίας</string>
+ <string id="173">ΠÏοβολή των βίντεο με αναλογία 4:3 ως</string>
+
+- <string id="175">Διάθεση</string>
++ <string id="175">Τάσεις</string>
+ <string id="176">Στυλ</string>
+
+ <string id="179">ΤÏαγοÏδι</string>
+@@ -169,21 +169,21 @@
+ <string id="194">Αναζήτηση...</string>
+ <string id="195">Δεν βÏέθηκαν πληÏοφοÏίες!</string>
+ <string id="196">Επιλογή ταινίας:</string>
+- <string id="197">Αναζήτηση πληÏοφοÏιών %s</string>
++ <string id="197">ΕÏÏεση πληÏοφοÏιών %s</string>
+ <string id="198">ΦόÏτωση πληÏοφοÏιών ταινίας</string>
+ <string id="199">Διεπαφή ιστοÏ</string>
+
+- <string id="202">ΣÏνθημα</string>
++ <string id="202">Σλόγκαν</string>
+ <string id="203">ΠεÏίληψη πλοκής</string>
+
+ <string id="205">Ψήφοι</string>
+- <string id="206">Ρόλοι και ηθοποιοί</string>
++ <string id="206">Διανομή Ïόλων</string>
+ <string id="207">Πλοκή</string>
+ <string id="208">Εκκίνηση</string>
+ <string id="209">Επόμενο</string>
+ <string id="210">ΠÏοηγοÏμενο</string>
+- <string id="213">Βαθμονόμηση οθόνης…</string>
+- <string id="214">Βαθμονόμηση βίντεο…</string>
++ <string id="213">Βαθμονόμηση οθόνης...</string>
++ <string id="214">Βαθμονόμηση βίντεο...</string>
+ <string id="215">Απάλυνση εικόνας</string>
+ <string id="216">Ποσοστό μεγέθυνσης</string>
+ <string id="217">Λόγος εικονοστοιχείων</string>
+@@ -196,7 +196,7 @@
+ <string id="225">ΚατακόÏυφη μετατόπιση</string>
+ <string id="226">Δοκιμή μοτίβων...</string>
+ <string id="227">Αναζήτηση ονομάτων μουσικών κομματιών από το freedb.org</string>
+- <string id="228">Ανακάτεμα λίστας αναπαÏαγωγής κατα τη φόÏτωση</string>
++ <string id="228">Ανακάτεμα λίστας αναπαÏαγωγής κατά τη φόÏτωση</string>
+ <string id="229">ΧÏόνος ελάττωσης πεÏιστÏοφής (Spindown)</string>
+ <string id="230">ΦίλτÏα βίντεο</string>
+ <string id="231">Κανένα</string>
+@@ -205,22 +205,22 @@
+ <string id="234">ΑνισοτÏοπικό</string>
+ <string id="235">Quincunx</string>
+ <string id="236">Gaussian cubic</string>
+- <string id="237">Ελαχιστοποίηση</string>
++ <string id="237">ΣμίκÏυνση</string>
+ <string id="238">Μεγέθυνση</string>
+ <string id="239">ΕκκαθάÏιση λίστας αναπαÏαγωγής στη λήξη</string>
+- <string id="240">Κατάσταση Ï€Ïοβολής</string>
++ <string id="240">ΛειτουÏγία ΠÏοβολής</string>
+ <string id="241">ΠλήÏης οθόνη #%d</string>
+ <string id="242">Σε παÏάθυÏο</string>
+ <string id="243">Ανανέωση βαθμολογίας</string>
+ <string id="244">ΠλήÏης οθόνη</string>
+- <string id="245">Μέγεθος: (%i,%i)-&gt;(%i,%i) (Εστίαση x%2.2f) AR:%2.2f:1 (Εικονοστοιχεία: %2.2f:1) (ΚΜετατόπιση: %2.2f)</string>
++ <string id="245">Μέγεθος: (%i,%i)->(%i,%i) (Εστίαση x%2.2f) AR:%2.2f:1 (Εικονοστοιχεία: %2.2f:1) (ΚΜετατόπιση: %2.2f)</string>
+
+ <string id="247">Scripts</string>
+ <string id="248">Γλώσσα</string>
+ <string id="249">Μουσική</string>
+ <string id="250">Οπτικοποίηση</string>
+ <string id="251">Επιλογή καταλόγου Ï€ÏοοÏισμοÏ</string>
+- <string id="252">ΣτέÏεο έξοδος σ' όλα τα ηχεία</string>
++ <string id="252">ΣτέÏεο έξοδος σε όλα τα ηχεία</string>
+ <string id="253">ΑÏιθμός καναλιών</string>
+ <string id="254">- Δέκτης ικανός για αναπαÏαγωγή DTS</string>
+ <string id="255">CDDB</string>
+@@ -229,13 +229,13 @@
+ <string id="258">ΕνεÏγοποίηση ID3 ετικετών</string>
+ <string id="259">Άνοιγμα</string>
+ <string id="260">Shoutcast</string>
+- <string id="261">Αναμονή για την έναÏξη....</string>
++ <string id="261">Αναμονή για την έναÏξη...</string>
+ <string id="262">Εξαγωγές Scripts</string>
+- <string id="263">ΈγκÏιση ελέγχου του XBMC από το HTTP</string>
++ <string id="263">ΈγκÏιση ελέγχου του XBMC μέσω HTTP</string>
+ <string id="264">ΕγγÏαφή</string>
+ <string id="265">Διακοπή εγγÏαφής</string>
+ <string id="266">Ταξ.: Κομμάτι</string>
+- <string id="267">Ταξ.: Έτος</string>
++ <string id="267">Ταξ.: ΧÏόνος</string>
+ <string id="268">Ταξ.: Τίτλος</string>
+ <string id="269">Ταξ.: Καλλιτέχνης</string>
+ <string id="270">Ταξ.: Άλμπουμ</string>
+@@ -248,8 +248,8 @@
+ <string id="277">Μετακινήστε τη μπάÏα για να αλλάξει η θέση των υποτίτλων</string>
+ <string id="278">Αλλάξτε το σχήμα ώστε να είναι τέλειο τετÏάγωνο</string>
+ <string id="279">Αδυναμία φόÏτωσης των Ïυθμίσεων</string>
+- <string id="280">ΧÏησιμοποίηση Ï€Ïοεπιλεγμένων Ïυθμίσεων</string>
+- <string id="281">ΠαÏακαλώ, ελέγξτε τα αÏχεία .xml</string>
++ <string id="280">ΧÏήση Ï€Ïοεπιλεγμένων Ïυθμίσεων</string>
++ <string id="281">ΠαÏακαλώ ελέγξτε τα αÏχεία .XML</string>
+ <string id="282">Î’Ïέθηκαν %i αντικείμενα</string>
+ <string id="283">Αποτελέσματα αναζήτησης</string>
+ <string id="284">Δεν βÏέθηκαν αποτελέσματα</string>
+@@ -262,7 +262,7 @@
+ <string id="292">Ήχος</string>
+ <string id="293">Αναζήτηση για υπότιτλους</string>
+ <string id="294">ΔημιουÏγία</string>
+- <string id="296">Απαλοιφή</string>
++ <string id="296">ΕκκαθάÏιση</string>
+ <string id="297">ΚαθυστέÏηση ήχου</string>
+ <string id="298">Σελιδοδείκτες</string>
+ <string id="299">- Δέκτης ικανός για αναπαÏαγωγή AAC</string>
+@@ -272,7 +272,7 @@
+ <string id="303">ΚαθυστέÏηση</string>
+ <string id="304">Γλώσσα</string>
+ <string id="305">ΕνεÏγό/ή</string>
+- <string id="306">Μη-διαστÏωματωμένο</string>
++ <string id="306">Non-interleaved</string>
+
+ <string id="312">(0=αυτόματα)</string>
+ <string id="313">ΕκκαθάÏιση βάσης δεδομένων</string>
+@@ -303,7 +303,7 @@
+ <string id="338">Αναλογική</string>
+ <string id="339">Οπτική/Coax</string>
+ <string id="340">ΔιάφοÏοι καλλιτέχνες</string>
+- <string id="341">ΈναÏξη DVD</string>
++ <string id="341">ΈναÏξη δίσκου</string>
+ <string id="342">Ταινίες</string>
+ <string id="343">ΠÏοσαÏμογή δειγματοληψίας</string>
+ <string id="344">Ηθοποιοί</string>
+@@ -314,13 +314,13 @@
+ <string id="352">ΑμυδÏÏŒ φως</string>
+ <string id="353">ΜαÏÏη οθόνη</string>
+ <string id="354">Ίχνη πλέγματος</string>
+- <string id="355">Αναμονή ενεÏγοποίησης Ï€ÏοφÏλαξης οθόνης</string>
++ <string id="355">Αναμονή για Ï€ÏοφÏλαξη οθόνης</string>
+ <string id="356">ΛειτουÏγία Ï€ÏοφÏλαξης οθόνης</string>
+ <string id="357">ΛειτουÏγία χÏονοδιακόπτη τεÏματισμοÏ</string>
+ <string id="358">Όλα τα άλμπουμ</string>
+ <string id="359">ΠÏόσφατα άλμπουμ</string>
+ <string id="360">ΠÏοφÏλαξη οθόνης</string>
+- <string id="361">ΠαÏουσίαση</string>
++ <string id="361">ΠαÏουσίαση διαφανειών και των υποφακέλων</string>
+ <string id="362">Ποσοστό εξασθένισης της Ï€ÏοφÏλαξης οθόνης</string>
+ <string id="363">Ταξ.: ΑÏχείο</string>
+ <string id="364">- Δέκτης με ικανότητα αναπαÏαγωγής Dolby Digital (AC3)</string>
+@@ -331,8 +331,8 @@
+ <string id="369">Τίτλος</string>
+ <string id="370">Καταιγίδες</string>
+ <string id="371">ΔιάσπαÏτα</string>
+- <string id="372">Επί το πλείστον</string>
+- <string id="373">ΑίθÏιος</string>
++ <string id="372">ΚυÏίως</string>
++ <string id="373">Ηλιοφάνεια</string>
+ <string id="374">Îέφη</string>
+ <string id="375">Χιόνι</string>
+ <string id="376">Î’Ïοχή</string>
+@@ -344,7 +344,7 @@
+ <string id="382">ΣποÏαδικά</string>
+ <string id="383">Άνεμος</string>
+ <string id="384">Δυνατός</string>
+- <string id="385">Îεφελώδης</string>
++ <string id="385">Ήπιος</string>
+ <string id="386">ΑίθÏιος</string>
+ <string id="387">Îεφοσκεπής</string>
+ <string id="388">τις Ï€Ïώτες Ï€Ïωινές ÏŽÏες</string>
+@@ -354,7 +354,7 @@
+ <string id="392">Μεσ.</string>
+ <string id="393">Μεγ.</string>
+ <string id="394">Ομίχλη</string>
+- <string id="395">Καταχνιά</string>
++ <string id="395">ΞηÏά αχλή</string>
+ <string id="396">Επιλογή τοποθεσίας</string>
+ <string id="397">Ανανέωση χÏόνου</string>
+ <string id="398">Μονάδα θεÏμοκÏασίας</string>
+@@ -368,14 +368,14 @@
+ <string id="406">ΥγÏασία</string>
+
+ <string id="409">ΠÏοεπιλεγμένα</string>
+- <string id="410">ΠÏόσβαση στο Weather.com</string>
+- <string id="411">ΠληÏοφοÏίες καιÏοÏ:</string>
+- <string id="412">Αδυναμία λήξης δεδομένων καιÏοÏ</string>
++ <string id="410">ΠÏόσβαση στην υπηÏεσία καιÏοÏ</string>
++ <string id="411">Ανάκτηση καιÏÎ¿Ï Î³Î¹Î±:</string>
++ <string id="412">Αδυναμία λήψης δεδομένων καιÏοÏ</string>
+ <string id="413">ΧειÏοκίνητα</string>
+ <string id="414">Καμιά κÏιτική για το άλμπουμ</string>
+ <string id="415">Λήψη μικÏογÏαφίας...</string>
+ <string id="416">Μη διαθέσιμος πόÏος</string>
+- <string id="417">ΠÏοβολή: Εικόνες</string>
++ <string id="417">ΠÏοβολή: Μεγ. Εικονίδια</string>
+ <string id="418">Ελαχ.</string>
+ <string id="419">Μεγ.</string>
+ <string id="420">HDMI</string>
+@@ -386,7 +386,7 @@
+ <string id="425">Δεν βÏέθηκαν πληÏοφοÏίες για το άλμπουμ</string>
+ <string id="426">Δεν βÏέθηκαν πληÏοφοÏίες για το CD</string>
+ <string id="427">Δίσκος</string>
+- <string id="428">Εισαγωγή οÏÎ¸Î¿Ï CD/DVD</string>
++ <string id="428">Εισάγετε το σωστό CD/DVD</string>
+ <string id="429">ΠαÏακαλώ, εισάγετε τον ακόλουθο δίσκο:</string>
+ <string id="430">Ταξ.: DVD#</string>
+ <string id="431">Δεν υπάÏχει ελεÏθεÏη λανθάνουσα μνήμη</string>
+@@ -400,7 +400,7 @@
+ <string id="440">ΣκληÏός δίσκος</string>
+ <string id="441">UDF</string>
+ <string id="442">Τοπικό δίκτυο</string>
+- <string id="443">Internet</string>
++ <string id="443">Διαδίκτυο</string>
+ <string id="444">Βίντεο</string>
+ <string id="445">Ήχος</string>
+ <string id="446">DVD</string>
+@@ -422,9 +422,9 @@
+ <string id="463">Οπίσθιος φωτισμός</string>
+ <string id="464">Φωτεινότητα</string>
+ <string id="465">Αντίθεση</string>
+- <string id="466">Συντελεστής γάμα</string>
++ <string id="466">Συντελεστής γάμμα</string>
+ <string id="467">ΤÏπος</string>
+- <string id="468">Μετακίνηση της μπάÏας για την αλλαγή της θέσης των απεικονίσεων οθόνης (OSD)</string>
++ <string id="468">Μετακινήστε τη μπάÏα για αλλαγή της θέσης των απεικονίσεων οθόνης (OSD)</string>
+ <string id="469">Θέση απεικονίσεων οθόνης (OSD)</string>
+ <string id="470">Συντελεστές</string>
+ <string id="471">Τσιπάκι</string>
+@@ -440,10 +440,10 @@
+
+ <string id="485">ΔιαγÏαφή άλμπουμ</string>
+ <string id="486">Επανάληψη</string>
+- <string id="487">Επανάληψη του ιδίου</string>
+- <string id="488">Επανάληψη φακέλων</string>
++ <string id="487">Επανάληψη του ίδιου</string>
++ <string id="488">Επανάληψη φακέλου</string>
+ <string id="489">Αυτόματη αναπαÏαγωγή επόμενου κομματιοÏ</string>
+- <string id="491">- ΧÏήση Îœ. εικονιδίων</string>
++ <string id="491">- ΧÏήση Μεγ. εικονιδίων</string>
+ <string id="492">Μεγέθυνση υποτίτλων</string>
+ <string id="493">Ρυθμίσεις για Ï€ÏοχωÏημένους!</string>
+ <string id="494">ΕÏÏος στάθμης Î³ÎµÎ½Î¹ÎºÎ¿Ï Î®Ï‡Î¿Ï…</string>
+@@ -470,7 +470,7 @@
+ <string id="518">Εκκίνηση</string>
+ <string id="519">Εκκίνηση σε...</string>
+
+- <string id="521">Συλλογές</string>
++ <string id="521">Συνθέσεις</string>
+ <string id="522">ΑπομάκÏυνση πηγής</string>
+ <string id="523">Εναλλαγή πολυμέσων</string>
+ <string id="524">Επιλογή λίστας αναπαÏαγωγής</string>
+@@ -478,7 +478,7 @@
+ <string id="526">ΠÏοσθήκη στη λίστα αναπαÏαγωγής</string>
+ <string id="527">ΧειÏοκίνητη Ï€Ïοσθήκη στη συλλογή</string>
+ <string id="528">Εισάγετε τίτλο</string>
+- <string id="529">Σφάλμα: Διπλός τίτλος</string>
++ <string id="529">Σφάλμα: Διπλότυπος τίτλος</string>
+ <string id="530">Επιλέξτε είδος</string>
+ <string id="531">Îέο είδος</string>
+ <string id="532">ΧειÏοκίνητη Ï€Ïοσθήκη</string>
+@@ -487,24 +487,24 @@
+ <string id="535">Λίστα</string>
+ <string id="536">Εικονίδια</string>
+ <string id="537">Μεγάλη λίστα</string>
+- <string id="538">Εικόνες</string>
++ <string id="538">Μεγ. Εικονίδια</string>
+ <string id="539">ΕυÏεία</string>
+ <string id="540">Î Î¿Î»Ï ÎµÏ…Ïεία</string>
+- <string id="541">Εικόνες άλμπουμ</string>
+- <string id="542">Εικόνες DVD</string>
++ <string id="541">Εικον. άλμπουμ</string>
++ <string id="542">Εικον. DVD</string>
+ <string id="543">DVD</string>
+ <string id="544">ΠληÏοφοÏίες</string>
+ <string id="545">Συσκευή εξόδου ήχου</string>
+ <string id="546">Συσκευή εξόδου διέλευσης</string>
+- <string id="547">Δεν υπάÏχει βιογÏαφία γι΄ αυτόν τον καλλιτέχνη</string>
++ <string id="547">Δεν υπάÏχει βιογÏαφία γι' αυτόν τον καλλιτέχνη</string>
+ <string id="548">Υποβιβασμός πολυκάναλου ήχου σε στεÏεοφωνικό</string>
+
+- <string id="550">Κατάταξη: %s</string>
++ <string id="550">Ταξ.: %s</string>
+ <string id="551">Όνομα</string>
+ <string id="552">ΗμεÏομηνία</string>
+ <string id="553">Μέγεθος</string>
+ <string id="554">Κομμάτι</string>
+- <string id="555">ÎÏα</string>
++ <string id="555">ΧÏόνος</string>
+ <string id="556">Τίτλος</string>
+ <string id="557">Καλλιτέχνης</string>
+ <string id="558">Άλμπουμ</string>
+@@ -514,21 +514,21 @@
+ <string id="562">Έτος</string>
+ <string id="563">Αξιολόγηση</string>
+ <string id="564">ΤÏπος</string>
+- <string id="565">ΧÏησιμότητα</string>
+- <string id="566">Άλμπουμ καλλιτέχνη</string>
++ <string id="565">ΧÏήση</string>
++ <string id="566">Καλλιτέχνης Άλμπουμ</string>
+ <string id="567">ΑναπαÏάχθηκε</string>
+ <string id="568">Τελευταία αναπαÏαγωγή</string>
+ <string id="569">Σχόλια</string>
+- <string id="570">Ημ/νία Ï€ÏοσάÏτησης</string>
++ <string id="570">Ημ/νία Ï€Ïοσθήκης</string>
+ <string id="571">ΠÏοεπιλεγμένη</string>
+- <string id="572">ΕταιÏεία (studio)</string>
++ <string id="572">ΣτοÏντιο</string>
+ <string id="573">ΔιαδÏομή</string>
+ <string id="574">ΧώÏα</string>
+ <string id="575">Σε εξέλιξη</string>
+ <string id="576">ΑÏιθμός αναπαÏαγωγών</string>
+
+ <string id="580">ΣειÏά ταξινόμησης</string>
+- <string id="581">ΚÏιτήÏια ταξινόμησης</string>
++ <string id="581">Μέθοδος ταξινόμησης</string>
+ <string id="582">ΛειτουÏγία ΠÏοβολής</string>
+ <string id="583">Απομνημόνευση Ï€Ïοβολών διαφοÏετικών φακέλων</string>
+ <string id="584">ΑÏξουσα</string>
+@@ -541,30 +541,30 @@
+ <string id="591">Όχι</string>
+ <string id="592">Ένα</string>
+ <string id="593">Όλα</string>
+- <string id="594">Îαι</string>
++ <string id="594">Όχι</string>
+ <string id="595">Επανάληψη: Όχι</string>
+ <string id="596">Επανάληψη: Μία φοÏά</string>
+ <string id="597">Επανάληψη: Όλων</string>
+
+- <string id="600">ΕγγÏαφή CD ήχου</string>
++ <string id="600">ΑντιγÏαφή CD ήχου</string>
+ <string id="601">Μεσαία</string>
+ <string id="602">Κανονική</string>
+ <string id="603">ΑκÏαία</string>
+- <string id="604">ΣταθεÏός Ïυθμός</string>
+- <string id="605">ΕγγÏαφή...</string>
++ <string id="604">ΣταθεÏÏŒ bitrate</string>
++ <string id="605">ΑντιγÏαφή...</string>
+
+ <string id="607">ΠÏος:</string>
+- <string id="608">Αδυναμία εγγÏαφής CD ή κομματιοÏ</string>
+- <string id="609">Δεν οÏίσθηκε διαδÏομή εξαγωγής CDDA.</string>
+- <string id="610">ΕγγÏαφή κομματιοÏ</string>
++ <string id="608">Αδυναμία αντιγÏαφής CD ή κομματιοÏ</string>
++ <string id="609">Δεν οÏίσθηκε διαδÏομή αντιγÏαφής CDDA.</string>
++ <string id="610">ΑντιγÏαφή κομματιοÏ</string>
+ <string id="611">Εισαγωγή αÏιθμοÏ</string>
+ <string id="612">Bits/Sample</string>
+ <string id="613">Συχνότητα δειγματοληψίας</string>
+
+- <string id="620">CDs ήχου</string>
++ <string id="620">CD ήχου</string>
+ <string id="621">Κωδικοποιητής</string>
+ <string id="622">Ποιότητα</string>
+- <string id="623">Ρυθμός</string>
++ <string id="623">Bitrate</string>
+ <string id="624">ΣυμπεÏίληψη αÏÎ¹Î¸Î¼Î¿Ï ÎºÎ¿Î¼Î¼Î±Ï„Î¹Î¿Ï</string>
+ <string id="625">Όλα τα Ï„ÏαγοÏδια</string>
+ <string id="629">ΛειτουÏγία Ï€Ïοβολής</string>
+@@ -608,17 +608,17 @@
+
+ <string id="700">ΕκκαθάÏιση συλλογής</string>
+ <string id="701">ΑπομάκÏυνση παλαιών Ï„Ïαγουδιών από τη συλλογή</string>
+- <string id="702">Η διαδÏομή έχει σαÏωθεί Ï€ÏοηγοÏμενος</string>
++ <string id="702">Η διαδÏομή έχει σαÏωθεί παλαιότεÏα</string>
+ <string id="705">Δίκτυο</string>
+ <string id="706">- Διακομιστής</string>
+
+- <string id="708">ΧÏήση ενός διαμεσολαβητή HTTP για Ï€Ïόσβαση στο internet</string>
++ <string id="708">ΧÏήση διαμεσολαβητή HTTP για Ï€Ïόσβαση στο Διαδίκτυο</string>
+
+ <string id="711">ΠÏωτόκολλο Internet (IP)</string>
+- <string id="712">ΠÏοσδιοÏίσατε λάθος θÏÏα. Η τιμή κυμαίνεται Î¼ÎµÏ„Î±Î¾Ï 1 και 65535.</string>
++ <string id="712">ΟÏίστηκε λάθος θÏÏα. Η τιμή Ï€Ïέπει να είναι Î¼ÎµÏ„Î±Î¾Ï 1 και 65535.</string>
+ <string id="713">Διαμεσολαβητής HTTP</string>
+
+- <string id="715">- ΕκχώÏηση</string>
++ <string id="715">- Ανάθεση</string>
+ <string id="716">Αυτόματη (DHCP)</string>
+ <string id="717">ΧειÏοκίνητη (Στατική)</string>
+
+@@ -627,9 +627,9 @@
+ <string id="721">- ΠÏοεπιλεγμένη Ï€Ïλη</string>
+ <string id="722">- Διακομιστής DNS</string>
+ <string id="723">Αποθήκευση &amp; επανεκκίνηση</string>
+- <string id="724">ΠÏοσδιοÏίστηκε εσφαλμένη διεÏθυνση. Η τιμή Ï€Ïέπει να έχει τη μοÏφή AAA.BBB.CCC.DDD</string>
++ <string id="724">ΟÏίστηκε λάθος διεÏθυνση. ΠÏέπει να έχει τη μοÏφή AAA.BBB.CCC.DDD</string>
+ <string id="725">με τιμές Î¼ÎµÏ„Î±Î¾Ï 0 και 255.</string>
+- <string id="726">Οι αλλαγές δεν αποθηκεÏτηκαν. Συνέχιση χωÏίς αποθήκευση;</string>
++ <string id="726">Οι αλλαγές δεν αποθηκεÏτηκαν. Συνέχεια χωÏίς αποθήκευση;</string>
+ <string id="727">Διακομιστής ΙστοÏ</string>
+ <string id="728">Διακομιστής FTP</string>
+
+@@ -637,7 +637,7 @@
+
+ <string id="732">Αποθήκευση &amp; εφαÏμογή</string>
+ <string id="733">- Κωδικός Ï€Ïόσβασης</string>
+- <string id="734">ΧωÏίς κωδικό Ï€Ïόσβασης</string>
++ <string id="734">ΧωÏίς κωδικό</string>
+ <string id="735">- Κωδικοποίηση χαÏακτήÏων</string>
+ <string id="736">- Στυλ</string>
+ <string id="737">- ΧÏώμα</string>
+@@ -648,9 +648,9 @@
+ <string id="742">Λευκό</string>
+ <string id="743">ΚίτÏινο</string>
+ <string id="744">ΑÏχεία</string>
+- <string id="745">Δεν υπάÏχει πληÏοφοÏία γι΄ αυτή την Ï€Ïοβολή</string>
+- <string id="746">ΑπενεÏγοποιήστε την λειτουÏγία συλλογής</string>
+- <string id="747">Σφάλμα κατά την φόÏτωση της εικόνας</string>
++ <string id="745">Καμία πληÏοφοÏία για αυτήν την Ï€Ïοβολή</string>
++ <string id="746">ΑπενεÏγοποιήστε τη λειτουÏγία συλλογής</string>
++ <string id="747">Σφάλμα κατά τη φόÏτωση της εικόνας</string>
+ <string id="748">ΕπεξεÏγασία διαδÏομής</string>
+ <string id="749">KατοπτÏική εικόνα</string>
+ <string id="750">Είστε σίγουÏος;</string>
+@@ -665,7 +665,7 @@
+ <string id="760">ΚίτÏινο</string>
+ <string id="761">Λευκό</string>
+ <string id="762">Μπλε</string>
+- <string id="763">Φωτεινό Ï€Ïάσινο</string>
++ <string id="763">Έντονο Ï€Ïάσινο</string>
+ <string id="764">ΚιτÏινοπÏάσινο</string>
+ <string id="765">Κυανό</string>
+ <string id="766">Ανοικτό γκÏι</string>
+@@ -677,7 +677,7 @@
+ <string id="772">Έξοδος ήχου</string>
+ <string id="773">Αναζήτηση</string>
+ <string id="774">Φάκελος παÏουσίασης διαφανειών</string>
+- <string id="775">ΔιασÏνδεση</string>
++ <string id="775">ΔιασÏνδεση δικτÏου</string>
+ <string id="776">- Όνομα ασÏÏματου δικτÏου (ESSID)</string>
+ <string id="777">- Κωδικός Ï€Ïόσβασης ασÏÏματου δικτÏου</string>
+ <string id="778">- Ασφάλεια ασÏÏματου δικτÏου</string>
+@@ -686,33 +686,33 @@
+ <string id="781">WEP</string>
+ <string id="782">WPA</string>
+ <string id="783">WPA2</string>
+- <string id="784">ΕφαÏμογή των Ïυθμίσεων διασÏνδεσης δικτÏου. ΠαÏακαλώ, πεÏιμένετε.</string>
+- <string id="785">Επιτυχής εκκίνηση της διασÏνδεσης δικτÏου.</string>
+- <string id="786">Δεν ήταν επιτυχής η εκκίνηση της διασÏνδεσης δικτÏου.</string>
++ <string id="784">ΕφαÏμογή των Ïυθμίσεων διασÏνδεσης δικτÏου. ΠαÏακαλώ πεÏιμένετε.</string>
++ <string id="785">Επιτυχής επανεκκίνηση της διασÏνδεσης δικτÏου.</string>
++ <string id="786">Δεν ήταν επιτυχής η εκκίνηση της διασÏνδεσης δικτÏου.</string>
+ <string id="787">ΑπενεÏγοποίηση ΔιασÏνδεσης</string>
+ <string id="788">Η ΔιασÏνδεση δικτÏου απενεÏγοποιήθηκε επιτυχώς.</string>
+ <string id="789">Όνομα ασÏÏματου δικτÏου (ESSID)</string>
+
+- <string id="791">ΈγκÏιση σε Ï€ÏογÏάμματα απομακÏυσμένου ελέγχου να ελέγξουν το XBMC</string>
++ <string id="791">ΈγκÏιση σε Ï€ÏογÏάμματα του συστήματος να ελέγχουν το XBMC</string>
+ <string id="792">ΘÏÏα</string>
+- <string id="793">ΕÏÏος θÏÏας</string>
+- <string id="794">ΈγκÏιση σε Ï€ÏογÏάμματα άλλων υπολογιστών να ελέγξουν το XBMC</string>
++ <string id="793">ΕÏÏος θυÏών</string>
++ <string id="794">ΈγκÏιση σε Ï€ÏογÏάμματα άλλων συστημάτων να ελέγχουν το XBMC</string>
+ <string id="795">ΑÏχική καθυστέÏηση (ms)</string>
+ <string id="796">Συνεχής καθυστέÏηση (ms)</string>
+ <string id="797">Μέγιστος αÏιθμός πελατών</string>
+- <string id="798">ΠÏόσβαση στο internet</string>
++ <string id="798">ΠÏόσβαση στο Διαδίκτυο</string>
+
+- <string id="850">Εισαγωγή μη έγκυÏου εÏÏους τιμών θÏÏας</string>
+- <string id="851">Το έγκυÏο εÏÏος τιμών θÏÏας είναι 1-65535</string>
+- <string id="852">Το έγκυÏο εÏÏος τιμών θÏÏας είναι 1024-65535</string>
++ <string id="850">Εισαγωγή μη έγκυÏης τιμής θÏÏας</string>
++ <string id="851">Το έγκυÏο εÏÏος θυÏών είναι 1-65535</string>
++ <string id="852">Το έγκυÏο εÏÏος θυÏών είναι 1024-65535</string>
+
+ <string id="998">ΠÏοσθήκη Μουσικής...</string>
+ <string id="999">ΠÏοσθήκη Βίντεο...</string>
+ <string id="1000">- ΠÏοεπισκόπηση</string>
+ <string id="1001">Αδυναμία σÏνδεσης</string>
+- <string id="1002">Το ΧΒMC είναι ανίκανο να συνδεθεί στο δίκτυο.</string>
+- <string id="1003">Αυτό μποÏεί να συμβαίνει διότι το δίκτυο δεν είναι συνδεμένο.</string>
+- <string id="1004">Θέλετε οπωσδήποτε να το Ï€Ïοσθέσετε;</string>
++ <string id="1002">Το XBMC δεν μπόÏεσε να συνδεθεί στο δίκτυο.</string>
++ <string id="1003">Αυτό μποÏεί να συνέβη διότι το δίκτυο δεν είναι συνδεδεμένο.</string>
++ <string id="1004">Θέλετε παÏ' όλα αυτά να το Ï€Ïοσθέσετε;</string>
+
+ <string id="1006">ΔιεÏθυνση IP</string>
+ <string id="1007">ΠÏοσθήκη τοποθεσίας δικτÏου</string>
+@@ -724,13 +724,13 @@
+ <string id="1013">ΘÏÏα</string>
+ <string id="1014">Όνομα χÏήστη</string>
+ <string id="1015">Αναζήτηση Î´Î¹ÎºÏ„Ï…Î±ÎºÎ¿Ï Î´Î¹Î±ÎºÎ¿Î¼Î¹ÏƒÏ„Î®</string>
+- <string id="1016">Εισαγωγή της δικτυακής διεÏθυνσης για τον διακομιστή</string>
+- <string id="1017">Εισαγωγή της διαδÏομής στον διακομιστή</string>
++ <string id="1016">Εισαγωγή της δικτυακής διεÏθυνσης για το διακομιστή</string>
++ <string id="1017">Εισαγωγή της διαδÏομής στο διακομιστή</string>
+ <string id="1018">Εισαγωγή του αÏÎ¹Î¸Î¼Î¿Ï Î¸ÏÏας</string>
+ <string id="1019">Εισαγωγή του ονόματος χÏήστη</string>
+ <string id="1020">ΠÏοσθήκη πηγής %s</string>
+ <string id="1021">Εισαγωγή διαδÏομών ή αναζήτηση τοποθεσιών πολυμέσων.</string>
+- <string id="1022">Εισαγωγή ετικέτας για την πηγή πολυμέσων.</string>
++ <string id="1022">Εισαγωγή ονόματος για την πηγή πολυμέσων.</string>
+ <string id="1023">Αναζήτηση νέου κοινόχÏηστου πόÏου</string>
+ <string id="1024">Αναζήτηση</string>
+ <string id="1025">Δεν ήταν δυνατόν να ανακτηθοÏν οι πληÏοφοÏίες καταλόγου.</string>
+@@ -742,16 +742,16 @@
+ <string id="1031">Αναζήτηση φακέλου εικόνων</string>
+ <string id="1032">ΠÏοσθήκη τοποθεσίας δικτÏου...</string>
+ <string id="1033">Αναζήτηση αÏχείου</string>
+- <string id="1034">YπομενοÏ</string>
++ <string id="1034">ΥπομενοÏ</string>
+ <string id="1035">ΕνεÏγοποίηση πλήκτÏων υπομενοÏ</string>
+ <string id="1036">Αγαπημένα</string>
+- <string id="1037">ΠÏόσθετα βίντεο</string>
+- <string id="1038">ΠÏόσθετα μουσικής</string>
+- <string id="1039">ΠÏόσθετα εικόνας</string>
++ <string id="1037">ΠÏόσθετα Βίντεο</string>
++ <string id="1038">ΠÏόσθετα Μουσικής</string>
++ <string id="1039">ΠÏόσθετα Εικόνας</string>
+ <string id="1040">ΦόÏτωση φακέλου</string>
+ <string id="1041">Ανακτήθηκαν %i αντικείμενα</string>
+- <string id="1042">Ανακτήθηκαν %i από %i αντικείμενα</string>
+- <string id="1043">ΠÏόσθετες εφαÏμογές</string>
++ <string id="1042">Ανακτήθηκαν %i από τα %i αντικείμενα</string>
++ <string id="1043">ΠÏόσθετες ΕφαÏμογές</string>
+ <string id="1044">ΟÏισμός μικÏογÏαφίας plugin</string>
+ <string id="1045">Ρυθμίσεις Ï€Ïόσθετου</string>
+ <string id="1046">Σημεία Ï€Ïόσβασης</string>
+@@ -766,13 +766,13 @@
+ <string id="1203">ΠÏοεπιλεγμένο όνομα χÏήστη</string>
+ <string id="1204">ΠÏοεπιλεγμένος κωδικός Ï€Ïόσβασης</string>
+
+- <string id="1207">WINS διακομιστής</string>
++ <string id="1207">Διακομιστής WINS</string>
+ <string id="1208">ΠÏοσάÏτηση κοινόχÏηστων SMB</string>
+
+ <string id="1210">ΑπομάκÏυνση</string>
+ <string id="1211">Μουσική</string>
+ <string id="1212">Βίντεο</string>
+- <string id="1213">ΦωτογÏαφίες</string>
++ <string id="1213">Εικόνες</string>
+ <string id="1214">ΑÏχεία</string>
+ <string id="1215">Μουσική &amp; βίντεο </string>
+ <string id="1216">Μουσική &amp; εικόνες</string>
+@@ -798,12 +798,12 @@
+ <string id="1251">Αυτόματος εντοπισμός συστήματος</string>
+ <string id="1252">Ψευδώνυμο</string>
+
+- <string id="1254">Îα απαιτείται επιβεβαίωση για να συνδεθεί</string>
++ <string id="1254">Επιβεβαίωση σÏνδεσης</string>
+ <string id="1255">Αποστολή ονόματος και ÎºÏ‰Î´Î¹ÎºÎ¿Ï Ï€Ïόσβασης χÏήστη (FTP)</string>
+- <string id="1256">Διάστημα Î¼ÎµÏ„Î±Î»Î»Î¹ÎºÎ¿Ï Î¸Î¿ÏÏβου</string>
+- <string id="1257">Îα συνδεθείτε στο σÏστημα που εντοπίστηκε;</string>
++ <string id="1256">ΧÏονικό διάστημα ping</string>
++ <string id="1257">Επιθυμείτε να συνδεθείτε στο σÏστημα που εντοπίστηκε;</string>
+
+- <string id="1260">Αναγγελία αυτών των υπηÏεσιών σε άλλα συστήματα διαμέσου του Zeroconf</string>
++ <string id="1260">Αναγγελία αυτών των υπηÏεσιών σε άλλα συστήματα μέσω του Zeroconf</string>
+ <string id="1270">ΈγκÏιση στο XBMC να λαμβάνει πεÏιεχόμενο AirPlay</string>
+ <string id="1271">Όνομα συσκευής</string>
+ <string id="1272">- ΧÏήση Ï€Ïοστασίας με κωδικό</string>
+@@ -811,7 +811,7 @@
+ <string id="1300">Ειδική συσκευή ήχου</string>
+ <string id="1301">Ειδική συσκευή διέλευσης</string>
+
+- <string id="1396">Πυκνό</string>
++ <string id="1396">ΜετακινοÏμενο</string>
+ <string id="1397">και</string>
+ <string id="1398">Παγωνιά</string>
+ <string id="1399">Το βÏάδυ</string>
+@@ -826,7 +826,7 @@
+ <string id="1408">Πάγος</string>
+ <string id="1409">ΚÏÏσταλλοι</string>
+ <string id="1410">Άπνοια</string>
+- <string id="1411">και</string>
++ <string id="1411">με</string>
+ <string id="1412">θυελλώδης</string>
+ <string id="1413">ασθενής βÏοχή</string>
+ <string id="1414">Καταιγίδα</string>
+@@ -835,14 +835,14 @@
+ <string id="1417">Κόκκοι</string>
+ <string id="1418">Καταιγίδες (με κεÏαυνοÏÏ‚)</string>
+ <string id="1419">ΜπόÏες (με κεÏαυνοÏÏ‚)</string>
+- <string id="1420">Μέση</string>
++ <string id="1420">Ήπια</string>
+ <string id="1421">Î Î¿Î»Ï Ï…ÏˆÎ·Î»Î®</string>
+ <string id="1422">Θυελλώδης</string>
+- <string id="1423">Ομίχλη</string>
++ <string id="1423">ΥγÏά αχλή</string>
+
+ <!-- strings through to 1450 reserved for weather translation -->
+
+- <string id="1450">Σε κατάσταση Ïπνωσης κατά την αναμονή</string>
++ <string id="1450">ΑπενεÏγοποίηση οθόνης κατά την αναμονή</string>
+ <!-- strings through to 1470 reserved for power-saving -->
+
+ <string id="2050">ΔιάÏκεια</string>
+@@ -854,7 +854,7 @@
+
+ <string id="10000">ΑÏχική τοποθεσία</string>
+ <string id="10001">ΕφαÏμογές</string>
+- <string id="10002">ΦωτογÏαφίες</string>
++ <string id="10002">Εικόνες</string>
+ <string id="10003">ΔιαχείÏιση αÏχείων</string>
+ <string id="10004">Ρυθμίσεις</string>
+ <string id="10005">Μουσική</string>
+@@ -862,9 +862,9 @@
+ <string id="10007">ΠληÏοφοÏίες συστήματος</string>
+ <string id="10008">Ρυθμίσεις - Γενικά</string>
+ <string id="10009">Ρυθμίσεις - Οθόνη</string>
+- <string id="10010">Ρυθμίσεις - Αισθητικά - Βαθμονόμηση γÏÎ±Ï†Î¹ÎºÎ¿Ï Ï€ÎµÏιβάλλοντος (GUI)</string>
++ <string id="10010">Ρυθμίσεις - Εμφάνιση - Βαθμονόμηση γÏÎ±Ï†Î¹ÎºÎ¿Ï Ï€ÎµÏιβάλλοντος (GUI)</string>
+ <string id="10011">Ρυθμίσεις - Βίντεο - Βαθμονόμηση Οθόνης</string>
+- <string id="10012">Ρυθμίσεις - ΦωτογÏαφίες</string>
++ <string id="10012">Ρυθμίσεις - Εικόνες</string>
+ <string id="10013">Ρυθμίσεις - ΕφαÏμογές</string>
+ <string id="10014">Ρυθμίσεις - ΚαιÏός</string>
+ <string id="10015">Ρυθμίσεις - Μουσική</string>
+@@ -884,7 +884,7 @@
+ <string id="10101">ΠαÏάθυÏο διαδικασίας</string>
+
+ <string id="10210">Αναζήτηση για υπότιτλους...</string>
+- <string id="10211">ΠÏοσωÏινή αποθήκευση υπότιτλων...</string>
++ <string id="10211">ΠÏοσωÏινή αποθήκευση υποτίτλων...</string>
+ <string id="10212">τεÏματισμός</string>
+ <string id="10213">αποθήκευση</string>
+ <string id="10214">Άνοιγμα Ïοής</string>
+@@ -892,26 +892,26 @@
+ <string id="10500">Μουσική/Λίστα αναπαÏαγωγής</string>
+ <string id="10501">Μουσική/ΑÏχεία</string>
+ <string id="10502">Μουσική/Συλλογή</string>
+- <string id="10503">ΕπεξεÏγαστής λίστας αναπαÏαγωγής</string>
++ <string id="10503">ΕπεξεÏγασία λίστας αναπαÏαγωγής</string>
+ <string id="10504">100 κοÏυφαία Ï„ÏαγοÏδια</string>
+ <string id="10505">100 κοÏυφαία άλμπουμ</string>
+ <string id="10506">ΕφαÏμογές</string>
+ <string id="10507">Ρυθμίσεις</string>
+ <string id="10508">ΠÏόγνωση καιÏοÏ</string>
+ <string id="10509">Δικτυακό παιχνίδι</string>
+- <string id="10510">Καταλήξεις</string>
++ <string id="10510">Επεκτάσεις</string>
+ <string id="10511">ΠληÏοφοÏίες συστήματος</string>
+
+ <string id="10516">Μουσική - Συλλογή</string>
+- <string id="10517">Μουσική - Εκτελείται Ï„ÏŽÏα</string>
++ <string id="10517">ΤώÏα Εκτελείται - Μουσική</string>
+
+- <string id="10522">Βίντεο - Εκτελείται Ï„ÏŽÏα</string>
+- <string id="10523">ΠληÏοφοÏίες</string>
++ <string id="10522">ΤώÏα Εκτελείται - Βίντεο</string>
++ <string id="10523">ΠληÏοφοÏίες άλμπουμ</string>
+ <string id="10524">ΠληÏοφοÏίες ταινίας</string>
+
+ <string id="12000">ΠαÏάθυÏο επιλογής</string>
+ <string id="12001">Μουσική/ΠληÏοφοÏίες</string>
+- <string id="12002">ΠαÏάθυÏο OK</string>
++ <string id="12002">ΠαÏάθυÏο 'Επιλογή'</string>
+ <string id="12003">Βίντεο/ΠληÏοφοÏίες</string>
+ <string id="12004">Scripts/ΠληÏοφοÏίες</string>
+ <string id="12005">Βίντεο πλήÏους οθόνης</string>
+@@ -923,7 +923,7 @@
+ <string id="12011">ΕπιστÏοφή στα βίντεο</string>
+
+ <string id="12021">Εκκίνηση από την αÏχή</string>
+- <string id="12022">Επανεκκίνηση από %s</string>
++ <string id="12022">Συνέχεια από %s</string>
+
+ <string id="12310">0</string>
+ <string id="12311">1</string>
+@@ -936,28 +936,28 @@
+ <string id="12318">8</string>
+ <string id="12319">9</string>
+ <string id="12320">c</string>
+- <string id="12321">Εντάξει</string>
++ <string id="12321">Επιλογή</string>
+ <string id="12322">*</string>
+- <string id="12325">Κλειδωμένος! Εισαγωγή κωδικοÏ...</string>
++ <string id="12325">Κλειδώθηκε! Εισαγωγή κωδικοÏ...</string>
+ <string id="12326">Εισαγωγή ÎºÏ‰Î´Î¹ÎºÎ¿Ï Ï€Ïόσβασης</string>
+ <string id="12327">Εισαγωγή κεντÏÎ¹ÎºÎ¿Ï ÎºÏ‰Î´Î¹ÎºÎ¿Ï</string>
+ <string id="12328">Εισάγετε κωδικό ξεκλειδώματος</string>
+ <string id="12329">ή πιέστε C για ακÏÏωση</string>
+- <string id="12330">Εισάγετε συνδυασμό πλήκτÏων και</string>
+- <string id="12331">πιέστε Εντάξει, ή ΕπιστÏοφή για ακÏÏωση</string>
+- <string id="12332">Τοποθέτηση κλειδώματος</string>
++ <string id="12330">Εισάγετε συνδυασμό πλήκτÏων (combo) και</string>
++ <string id="12331">πιέστε 'Επιλογή', ή 'ΕπιστÏοφή' για ακÏÏωση</string>
++ <string id="12332">ΟÏισμός κλειδώματος</string>
+ <string id="12333">Ξεκλείδωμα</string>
+ <string id="12334">Επανατοποθέτηση κλειδώματος</string>
+ <string id="12335">ΑπομάκÏυνση κλειδώματος</string>
+ <string id="12337">ΑÏιθμητικός κωδικός Ï€Ïόσβασης</string>
+- <string id="12338">Συνδυασμός πλήκτÏων του μοχλοÏ</string>
++ <string id="12338">Συνδυασμός πλήκτÏων (combo) του μοχλοÏ</string>
+ <string id="12339">Κωδικός Ï€Ïόσβασης πλήÏους κειμένου</string>
+ <string id="12340">Εισαγωγή νέου ÎºÏ‰Î´Î¹ÎºÎ¿Ï Ï€Ïόσβασης</string>
+ <string id="12341">Επανεισαγωγή νέου ÎºÏ‰Î´Î¹ÎºÎ¿Ï Ï€Ïόσβασης</string>
+- <string id="12342">Λάθος κωδικός Ï€Ïόσβασης.</string>
++ <string id="12342">Λάθος κωδικός Ï€Ïόσβασης,</string>
+ <string id="12343">δοκιμές έμειναν </string>
+ <string id="12344">Ο κωδικός Ï€Ïόσβασης δεν ταιÏιάζει.</string>
+- <string id="12345">Δεν επιτÏάπηκε η Ï€Ïόσβαση</string>
++ <string id="12345">Δεν επιτÏέπεται η Ï€Ïόσβαση</string>
+ <string id="12346">Οι δοκιμές εισαγωγής ÎºÏ‰Î´Î¹ÎºÎ¿Ï Ï€Ïόσβασης εξαντλήθηκαν.</string>
+ <string id="12347">Το σÏστημα Ï„ÏŽÏα θα απενεÏγοποιηθεί.</string>
+ <string id="12348">Το αντικείμενο κλειδώθηκε</string>
+@@ -968,15 +968,15 @@
+ <string id="12360">ΚεντÏικό κλείδωμα</string>
+ <string id="12362">ΑπενεÏγοποίηση συστήματος όταν εξαντληθοÏν οι Ï€Ïοσπάθειες εισαγωγής</string>
+ <string id="12367">Ο κεντÏικός κωδικός δεν είναι έγκυÏος</string>
+- <string id="12368">ΠαÏακαλώ, εισάγετε έναν έγκυÏο κεντÏικό κωδικό</string>
++ <string id="12368">ΠαÏακαλώ εισάγετε έναν έγκυÏο κεντÏικό κωδικό</string>
+ <string id="12373">Ρυθμίσεις &amp; διαχείÏιση αÏχείων</string>
+ <string id="12376">ΟÏισμός ως Ï€Ïοεπιλογή για όλες τις ταινίες</string>
+ <string id="12377">ΔιαγÏάφει όλες τις Ï€ÏοηγοÏμενες αποθηκευμένες τιμές</string>
+ <string id="12378">ΧÏονικό διάστημα Ï€Ïοβολής κάθε εικόνας</string>
+ <string id="12379">Εφέ Ï€ÏοσαÏμογής αναλογιών σήματος (pan &amp; zoom)</string>
+
+- <string id="12383">Κανονικό Ïολόι</string>
+- <string id="12384">Ψηφιακό Ïολόι</string>
++ <string id="12383">Ρολόι 12 ωÏών</string>
++ <string id="12384">Ρολόι 24 ωÏών</string>
+ <string id="12385">ΜέÏα/Μήνας</string>
+ <string id="12386">Μήνας/ΜέÏα</string>
+
+@@ -1002,16 +1002,16 @@
+ <string id="13008">ΛειτουÏγία τεÏματισμοÏ</string>
+ <string id="13009">Έξοδος</string>
+ <string id="13010">ΑδÏανοποίηση</string>
+- <string id="13011">Ύπνωση</string>
++ <string id="13011">Αναστολή</string>
+ <string id="13012">Έξοδος</string>
+ <string id="13013">Επανεκκίνηση Συστήματος</string>
+ <string id="13014">Ελαχιστοποίηση</string>
+ <string id="13015">ΕνέÏγεια πλήκτÏου λειτουÏγίας</string>
+ <string id="13016">ΑπενεÏγοποίηση συστήματος</string>
+
+- <string id="13020">Είναι ενεÏγή άλλη συνεδÏία (ίσως μέσω ssh);</string>
+- <string id="13021">ΠÏοσάÏτηση αναιÏοÏμενου δίσκου</string>
+- <string id="13022">Επισφαλής αφαίÏεση συσκευής</string>
++ <string id="13020">Είναι ενεÏγή άλλη συνεδÏία, ίσως μέσω ssh;</string>
++ <string id="13021">ΠÏοσαÏτήθηκε αφαιÏοÏμενος δίσκος</string>
++ <string id="13022">Μη ασφαλής αφαίÏεση συσκευής</string>
+ <string id="13023">Η συσκευή αφαιÏέθηκε επιτυχώς</string>
+ <string id="13024">Συνδέθηκε χειÏιστήÏιο</string>
+ <string id="13025">Αποσυνδέθηκε χειÏιστήÏιο</string>
+@@ -1034,25 +1034,25 @@
+ <string id="13114">ΕνεÏγό για πεÏιεχόμενο SD</string>
+ <string id="13115">Πάντα ενεÏγό</string>
+
+- <string id="13116">Μέθοδος ανακλιμακοθέτησης</string>
+- <string id="13117">Δικυβική</string>
++ <string id="13116">Μέθοδος Upscaling</string>
++ <string id="13117">Bicubic</string>
+ <string id="13118">Lanczos</string>
+ <string id="13119">Sinc</string>
+ <string id="13120">VDPAU</string>
+- <string id="13121">VDPAU HQ ανακλιμακοθέτηση</string>
+- <string id="13122">VDPAU ΜετατÏοπή στάθμης χÏώματος</string>
++ <string id="13121">VDPAU HQ Upscaling επίπεδο</string>
++ <string id="13122">VDPAU ΜετατÏοπή χÏώματος επιπέδου στοÏντιο</string>
+
+- <string id="13130">Κενό σε άλλες απεικονίσεις</string>
++ <string id="13130">Κενό σε άλλες οθόνες</string>
+ <string id="13131">ΑνενεÏγή</string>
+- <string id="13132">Κενές απεικονίσεις</string>
++ <string id="13132">Κενές οθόνες</string>
+
+ <string id="13140">Έχουν ανιχνευτεί ενεÏγές συνδέσεις!</string>
+- <string id="13141">Εάν συνεχίσετε, δεν θα είναι δυνατός πλέον ο έλεγχος του XBMC.</string>
+- <string id="13142">Îα διακοπεί η λειτουÏγία του ΑπομακÏυσμένου διακομιστή;</string>
++ <string id="13141">Αν συνεχίσετε, μποÏεί να χάσετε τον έλεγχο του XBMC.</string>
++ <string id="13142">Îα διακοπεί η λειτουÏγία του απομακÏυσμένου διακομιστή;</string>
+
+- <string id="13144">Αλλαγή κατάστασης συσκευής (Apple Remote);</string>
+- <string id="13145">Εάν χÏησιμοποιείτε Ï„ÏŽÏα τη συσκευή (Apple Remote) για να</string>
+- <string id="13146">ελέγξετε το XBMC, η αλλαγή της ÏÏθμισης αυτής θα επηÏεάσει</string>
++ <string id="13144">Αλλαγή λειτουÏγίας συσκευής Apple Remote;</string>
++ <string id="13145">Εάν χÏησιμοποιείτε Ï„ÏŽÏα τη συσκευή Apple Remote για να</string>
++ <string id="13146">ελέγχετε το XBMC, η αλλαγή αυτής της ÏÏθμισης θα επηÏεάσει</string>
+ <string id="13147">την ικανότητα ελέγχου του. Θέλετε να συνεχίσετε;</string>
+
+ <string id="13159">Μάσκα ΥποδικτÏου</string>
+@@ -1063,8 +1063,8 @@
+ <string id="13170">Ποτέ</string>
+ <string id="13171">Αμέσως</string>
+ <string id="13172">Μετά από %i δευτ.</string>
+- <string id="13173">ΗμεÏομηνία εγκατάστασης</string>
+- <string id="13174">Πλήθος πεÏιστÏοφών σε ισχÏ</string>
++ <string id="13173">ΗμεÏομηνία εγκατάστασης:</string>
++ <string id="13174">Πλήθος πεÏιστÏοφών σε ισχÏ:</string>
+
+ <string id="13200">ΠÏοφίλ</string>
+ <string id="13201">ΔιαγÏαφή Ï€Ïοφίλ '%s';</string>
+@@ -1077,20 +1077,20 @@
+ <string id="13209">ΔιάÏκεια ÎºÎ¿Ï…Î´Î¿Ï…Î½Î¹ÏƒÎ¼Î¿Ï (σε λεπτά)</string>
+ <string id="13210">Εκκίνηση ξυπνητηÏÎ¹Î¿Ï ÏƒÎµ %im</string>
+ <string id="13211">ΣυναγεÏμός!</string>
+- <string id="13212">ΑκυÏώθηκε %im%is απέμειναν</string>
+- <string id="13213">%2.0fm</string>
+- <string id="13214">%2.0fs</string>
++ <string id="13212">ΑκυÏώθηκε με %im%is να απομένουν</string>
++ <string id="13213">%2.0fm</string> <!--minutes (left from countdown)-->
++ <string id="13214">%2.0fs</string> <!--seconds (left from countdown)-->
+
+- <string id="13249">Αναζήτηση υποτίτλων στα RARs</string>
++ <string id="13249">Αναζήτηση υποτίτλων σε RAR</string>
+ <string id="13250">Αναζήτηση για υπότιτλο...</string>
+ <string id="13251">Μετακίνηση αντικειμένου</string>
+ <string id="13252">Μετακίνηση αντικειμένου εδώ</string>
+ <string id="13253">ΑκÏÏωση μετακίνησης</string>
+
+- <string id="13270">Υλικό</string>
+- <string id="13271">ΤαχÏτητα CPU</string>
++ <string id="13270">Υλικό:</string>
++ <string id="13271">ΧÏήση CPU:</string>
+
+- <string id="13274">Συνδέθηκε, αλλά η διεÏθυνση DNS δεν είναι διαθέσιμη.</string>
++ <string id="13274">Συνδέθηκε, αλλά δεν υπάÏχει διαθέσιμη DNS.</string>
+ <string id="13275">ΣκληÏός δίσκος</string>
+ <string id="13276">DVD-ROM</string>
+ <string id="13277">Μέσα αποθήκευσης</string>
+@@ -1099,28 +1099,28 @@
+ <string id="13280">Βίντεο</string>
+ <string id="13281">Υλικό</string>
+
+- <string id="13283">ΛειτουÏγικό σÏστημα</string>
+- <string id="13284">ΤαχÏτητα CPU</string>
++ <string id="13283">ΛειτουÏγικό σÏστημα:</string>
++ <string id="13284">ΤαχÏτητα CPU:</string>
+
+ <string id="13286">Κωδικοποιητής βίντεο:</string>
+- <string id="13287">Ανάλυση οθόνης</string>
++ <string id="13287">Ανάλυση οθόνης:</string>
+
+- <string id="13292">Καλώδιο A/V</string>
++ <string id="13292">Καλώδιο A/V:</string>
+
+- <string id="13294">ΠεÏιοχή DVD</string>
+- <string id="13295">Internet</string>
++ <string id="13294">ΠεÏιοχή DVD:</string>
++ <string id="13295">Διαδίκτυο:</string>
+ <string id="13296">Συνδέθηκε</string>
+ <string id="13297">Δε συνδέθηκε. Ελέγξτε τις Ïυθμίσεις δικτÏου.</string>
+
+- <string id="13299">ΘεÏμοκÏασία στόχος</string>
++ <string id="13299">Επιθυμητή θεÏμοκÏασία</string>
+ <string id="13300">ΤαχÏτητα ανεμιστήÏα</string>
+ <string id="13301">Αυτόματος έλεγχος θεÏμοκÏασίας</string>
+- <string id="13302">ΠÏοτεÏαιότητα στην ταχÏτητα του ανεμιστήÏα</string>
++ <string id="13302">ΠαÏάκαμψη ταχÏτητας ανεμιστήÏα</string>
+ <string id="13303">- ΓÏαμματοσειÏές</string>
+ <string id="13304">ΕνεÏγοποίηση αμφίδÏομης κατεÏθυνσης</string>
+- <string id="13305">Εμφάνιση Ïοών τίτλων ειδήσεων (RSS)</string>
+- <string id="13306">Εμφάνιση πεÏιεχομένων φακέλου</string>
+- <string id="13307">ΠÏότυπο ονομασίας κομματιοÏ</string>
++ <string id="13305">Εμφάνιση Ïοής τίτλων ειδήσεων (RSS)</string>
++ <string id="13306">Εμφάνιση πεÏιεχομένων αÏÏ‡Î¹ÎºÎ¿Ï Ï†Î±ÎºÎ­Î»Î¿Ï…</string>
++ <string id="13307">ΠÏότυπο ονομασίας κομματιών</string>
+ <string id="13308">Επιθυμείτε επανεκκίνηση του συστήματος</string>
+ <string id="13309">και όχι μόνο του XBMC;</string>
+ <string id="13310">Εφέ μεγέθυνσης</string>
+@@ -1128,23 +1128,23 @@
+ <string id="13312">Ελαχιστοποίηση μαÏÏης μπάÏας</string>
+ <string id="13313">Επανεκκίνηση</string>
+ <string id="13314">Βαθμιαία εξασθένιση Î¼ÎµÏ„Î±Î¾Ï Ï„Ï‰Î½ Ï„Ïαγουδιών</string>
+- <string id="13315">ΕπαναδημιουÏγία μικÏογÏαφίας</string>
+- <string id="13316">Επαναλαμβανόμενη μικÏογÏαφία</string>
+- <string id="13317">ΠÏοβολή παÏουσίασης</string>
+- <string id="13318">Επαναλαμβανόμενη παÏουσίαση</string>
++ <string id="13315">ΕπαναδημιουÏγία μικÏογÏαφιών</string>
++ <string id="13316">ΜικÏογÏαφίες και σε υποφακέλους</string>
++ <string id="13317">ΠαÏουσίαση διαφανειών</string>
++ <string id="13318">ΠαÏουσίαση διαφανειών και των υποφακέλων</string>
+ <string id="13319">Τυχαία διάταξη</string>
+ <string id="13320">ΣτεÏεοφωνικά</string>
+ <string id="13321">ΑÏιστεÏÏŒ μόνο</string>
+- <string id="13322">Δεξιό μόνο</string>
++ <string id="13322">Δεξί μόνο</string>
+ <string id="13323">ΕνεÏγοποίηση υποστήÏιξης karaoke</string>
+- <string id="13324">Διαφάνεια στο υπόβαθÏÏŒ</string>
+- <string id="13325">Διαφάνεια σε Ï€Ïώτο πλάνο</string>
++ <string id="13324">Διαφάνεια υποβάθÏου</string>
++ <string id="13325">Διαφάνεια Ï€Ïοσκήνιου</string>
+ <string id="13326">A/V καθυστέÏηση</string>
+ <string id="13327">Karaoke</string>
+ <string id="13328">%s δεν βÏέθηκε</string>
+ <string id="13329">Σφάλμα κατά το άνοιγμα %s</string>
+ <string id="13330">Αδυναμία φόÏτωσης %s</string>
+- <string id="13331">Σφάλμα: Έλλειψη μνήμης.</string>
++ <string id="13331">Σφάλμα: Έλλειψη μνήμης</string>
+ <string id="13332">Μετακίνηση πάνω</string>
+ <string id="13333">Μετακίνηση κάτω</string>
+ <string id="13334">ΕπεξεÏγασία ετικέτας</string>
+@@ -1156,19 +1156,19 @@
+ <string id="13342">ΠοÏτοκαλί</string>
+ <string id="13343">Κόκκινο</string>
+ <string id="13344">ΚÏκλος</string>
+- <string id="13345">Σβησμένη η λυχνία κατά την αναπαÏαγωγή</string>
++ <string id="13345">ΑπενεÏγοποίηση λυχνίας (LED) κατά την αναπαÏαγωγή</string>
+ <string id="13346">ΠληÏοφοÏίες ταινίας</string>
+ <string id="13347">Κομμάτι σε αναμονή</string>
+- <string id="13348">Αναζήτηση IMDb...</string>
++ <string id="13348">Αναζήτηση στο IMDb...</string>
+ <string id="13349">ΈÏευνα για νέο πεÏιεχόμενο</string>
+ <string id="13350">ΤώÏα εκτελείται...</string>
+ <string id="13351">ΠληÏοφοÏίες άλμπουμ</string>
+ <string id="13352">Ανίχνευση αντικειμένου στη συλλογή</string>
+ <string id="13353">Διακοπή αναζήτησης</string>
+ <string id="13354">Μέθοδος απόδοσης</string>
+- <string id="13355">Χαμηλής ποιότητας (Pixel Shader)</string>
++ <string id="13355">Χαμηλής ποιότητας σκίαση pixel</string>
+ <string id="13356">ΕπικαλÏψεις υλικοÏ</string>
+- <string id="13357">Υψηλής ποιότητας (Pixel Shader)</string>
++ <string id="13357">Υψηλής ποιότητας σκίαση pixel</string>
+ <string id="13358">ΑναπαÏαγωγή αντικειμένου</string>
+ <string id="13359">ΟÏισμός μικÏογÏαφίας καλλιτέχνη</string>
+ <string id="13360">Αυτόματη δημιουÏγία μικÏογÏαφιών</string>
+@@ -1179,15 +1179,15 @@
+ <string id="13377">ΠÏοεπιλεγμένη κατάσταση Ï€Ïοβολής</string>
+ <string id="13378">ΠÏοεπιλεγμένη φωτεινότητα</string>
+ <string id="13379">ΠÏοεπιλεγμένη αντίθεση</string>
+- <string id="13380">ΠÏοεπιλεγμένος συντελεστής γάμα</string>
++ <string id="13380">ΠÏοεπιλεγμένος συντελεστής γάμμα</string>
+ <string id="13381">Συνέχιση βίντεο</string>
+ <string id="13382">Μάσκα φωνής - ΘÏÏα 1</string>
+ <string id="13383">Μάσκα φωνής - ΘÏÏα 2</string>
+ <string id="13384">Μάσκα φωνής - ΘÏÏα 3</string>
+ <string id="13385">Μάσκα φωνής - ΘÏÏα 4</string>
+- <string id="13386">Αναζήτηση βασισμένη στην χÏήση χÏόνου</string>
++ <string id="13386">Αναζήτηση βάσει χÏόνου</string>
+ <string id="13387">ΠÏότυπο ονομασίας ÎºÎ¿Î¼Î¼Î±Ï„Î¹Î¿Ï (δεξιά πλευÏά)</string>
+- <string id="13388">ΠÏοκαθοÏισμένα</string>
++ <string id="13388">ΠÏοκαθοÏισμένο</string>
+ <string id="13389">Δεν υπάÏχουν διαθέσιμα Ï€ÏοκαθοÏισμένα&#10;γι' αυτή την οπτικοποίηση</string>
+ <string id="13390">Δεν υπάÏχουν διαθέσιμες Ïυθμίσεις&#10;γι' αυτή την οπτικοποίηση</string>
+ <string id="13391">Εξαγωγή/Εισαγωγή</string>
+@@ -1198,70 +1198,70 @@
+ <string id="13396">Ρυθμίσεις ήχου και υποτίτλων</string>
+ <string id="13397">ΕνεÏγοποίηση υποτίτλων</string>
+ <string id="13398">ΣυντομεÏσεις</string>
+- <string id="13399">Αγνόησε το άÏθÏο "The" κατά την ταξινόμηση</string>
+- <string id="13400">Βαθμιαία εξασθένιση Î¼ÎµÏ„Î±Î¾Ï Ï„Ï‰Î½ Ï„Ïαγουδιών του ίδιου άλμπουμ</string>
++ <string id="13399">Αγνόησε τα άÏθÏα κατά την ταξινόμηση (Ï€.χ. το "The")</string>
++ <string id="13400">Βαθμιαία εξασθένιση Î¼ÎµÏ„Î±Î¾Ï Ï„Ïαγουδιών του ίδιου άλμπουμ</string>
+ <string id="13401">Αναζήτηση για %s</string>
+ <string id="13402">Εμφάνιση της θέσης του κομματιοÏ</string>
+ <string id="13403">Απαλοιφή Ï€Ïοεπιλογών</string>
+ <string id="13404">Συνέχεια</string>
+- <string id="13405">ΜικÏογÏαφία</string>
++ <string id="13405">Λήψη μικÏογÏαφίας</string>
+ <string id="13406">ΠληÏοφοÏίες εικόνας</string>
+ <string id="13407">%s Ï€ÏοκαθοÏισμένα</string>
+ <string id="13408">(Αξιολόγηση IMDb)</string>
+ <string id="13409">250 κοÏυφαία</string>
+- <string id="13410">Συντονισμός στο Last.FM</string>
++ <string id="13410">Συντονισμός στο Last.fm</string>
+ <string id="13411">Ελάχιστη ταχÏτητα ανεμιστήÏα</string>
+ <string id="13412">ΑναπαÏαγωγή από εδώ</string>
+ <string id="13413">Λήψη</string>
+- <string id="13414">Ενσωμάτωση καλλιτεχνών που εμφανίζονται μόνο στις συνθέσεις</string>
++ <string id="13414">ΣυμπεÏίληψη καλλιτεχνών που εμφανίζονται μόνο σε συνθέσεις</string>
+ <string id="13415">Μέθοδος απόδοσης</string>
+ <string id="13416">Αυτόματη ανίχνευση</string>
+- <string id="13417">Στοιχειώδης (ARB)</string>
+- <string id="13418">Εξειδικευμένη (GLSL)</string>
++ <string id="13417">Στοιχειώδεις σκιαστές (ARB)</string>
++ <string id="13418">ΠÏοηγμένοι σκιαστές (GLSL)</string>
+ <string id="13419">Λογισμικό</string>
+ <string id="13420">Ασφαλής κατάÏγηση</string>
+ <string id="13421">VDPAU</string>
+- <string id="13422">ΈναÏξη παÏουσίασης διαφανειών εδώ</string>
+- <string id="13423">Απομνημόνευση αυτής της διαδÏομής</string>
+- <string id="13424">ΧÏήση των Ï€ÏοσωÏινά αποθηκευμένων αντικειμένων</string>
+- <string id="13425">Αποδοχή επιτάχυνσης Ï…Î»Î¹ÎºÎ¿Ï (VDPAU)</string>
+- <string id="13426">Αποδοχή επιτάχυνσης Ï…Î»Î¹ÎºÎ¿Ï (VAAPI)</string>
+- <string id="13427">Αποδοχή επιτάχυνσης Ï…Î»Î¹ÎºÎ¿Ï (DXVA2)</string>
+- <string id="13428">Αποδοχή επιτάχυνσης Ï…Î»Î¹ÎºÎ¿Ï (CrystalHD)</string>
+- <string id="13429">Αποδοχή επιτάχυνσης Ï…Î»Î¹ÎºÎ¿Ï (VDADecoder)</string>
+- <string id="13430">Αποδοχή επιτάχυνσης Ï…Î»Î¹ÎºÎ¿Ï (OpenMax)</string>
+- <string id="13431">Σκιάσεις εικονοστοιχείου</string>
++ <string id="13422">ΠαÏουσίαση διαφανειών από εδώ</string>
++ <string id="13423">Απομνημόνευση για αυτή τη διαδÏομή</string>
++ <string id="13424">ΧÏήση αντικειμένων Ï€ÏοσωÏινής μνήμης pixel</string>
++ <string id="13425">ΈγκÏιση επιτάχυνσης Ï…Î»Î¹ÎºÎ¿Ï (VDPAU)</string>
++ <string id="13426">ΈγκÏιση επιτάχυνσης Ï…Î»Î¹ÎºÎ¿Ï (VAAPI)</string>
++ <string id="13427">ΈγκÏιση επιτάχυνσης Ï…Î»Î¹ÎºÎ¿Ï (DXVA2)</string>
++ <string id="13428">ΈγκÏιση επιτάχυνσης Ï…Î»Î¹ÎºÎ¿Ï (CrystalHD)</string>
++ <string id="13429">ΈγκÏιση επιτάχυνσης Ï…Î»Î¹ÎºÎ¿Ï (VDADecoder)</string>
++ <string id="13430">ΈγκÏιση επιτάχυνσης Ï…Î»Î¹ÎºÎ¿Ï (OpenMax)</string>
++ <string id="13431">Σκιάσεις Pixel</string>
+ <string id="13432">ΈγκÏιση επιτάχυνσης Ï…Î»Î¹ÎºÎ¿Ï (VideoToolbox)</string>
+
+ <string id="13500">Μέθοδος συγχÏÎ¿Î½Î¹ÏƒÎ¼Î¿Ï A/V</string>
+ <string id="13501">ΧÏονισμός ήχου</string>
+ <string id="13502">ΧÏονισµός βίντεο (εις βάÏος του ήχου)</string>
+- <string id="13503">ΧÏονισμός βίντεο (εν νέου δειγματοληψία ήχου)</string>
++ <string id="13503">ΧÏονισμός βίντεο (εκ νέου δειγματοληψία ήχου)</string>
+ <string id="13504">Μέγιστο ποσοστό επανάληψης δειγματοληψίας (%)</string>
+ <string id="13505">Ποιότητα επανάληψης δειγματοληψίας</string>
+ <string id="13506">Χαμηλή (γÏήγοÏη)</string>
+ <string id="13507">Μεσαία</string>
+ <string id="13508">Υψηλή</string>
+ <string id="13509">ΠÏαγματικά υψηλή (αÏγή!)</string>
+- <string id="13510">ΣυγχÏονισμός αναπαÏαγωγής ήχου κατά τη Ï€Ïοβολή</string>
++ <string id="13510">ΣυγχÏονισμός αναπαÏαγωγής ήχου με οθόνη</string>
+
+- <string id="13550">ΠαÏση κατά τη διάÏκεια ανανέωσης του ÏÏ…Î¸Î¼Î¿Ï Î±Î»Î»Î±Î³Î®Ï‚</string>
++ <string id="13550">ΠαÏση κατά την αλλαγή του ÏÏ…Î¸Î¼Î¿Ï Î±Î½Î±Î½Î­Ï‰ÏƒÎ·Ï‚</string>
+ <string id="13551">ΑνενεÏγό</string>
+ <string id="13552">%.1f ΔευτεÏόλεπτο</string>
+ <string id="13553">%.1f ΔευτεÏόλεπτα</string>
+
+ <string id="13600">Apple Remote</string>
+
+- <string id="13602">ΈγκÏιση έναÏξης του XBMC μέσου απομακÏυσμένου ελέγχου</string>
+- <string id="13603">Ακολουθία χÏόνου καθυστέÏησης</string>
++ <string id="13602">ΈγκÏιση έναÏξης του XBMC μέσω του χειÏιστηÏίου</string>
++ <string id="13603">ΧÏόνος καθυστέÏησης ακολουθίας</string>
+
+ <string id="13610">ΑνενεÏγή</string>
+ <string id="13611">Τυπική</string>
+ <string id="13612">ΤηλεχειÏιστήÏιο γενικής χÏήσης</string>
+- <string id="13613">ΤηλεχειÏιστήÏιο γενικής χÏήσης (Harmony)</string>
++ <string id="13613">ΤηλεχειÏιστήÏιο Multi (Harmony)</string>
+
+- <string id="13620">Σφάλμα συσκευής (Apple Remote)</string>
+- <string id="13621">Αδυναμία υποστήÏιξης συσκευής (Apple Remote).</string>
++ <string id="13620">Σφάλμα συσκευής Apple Remote</string>
++ <string id="13621">Αδυναμία υποστήÏιξης συσκευής Apple Remote.</string>
+
+ <string id="14000">Στοίβαγμα</string>
+ <string id="14001">ΣκόÏπισμα</string>
+@@ -1272,13 +1272,13 @@
+ <string id="14007">Αποτυχία λήψης αÏχείου λίστας αναπαÏαγωγής</string>
+
+ <string id="14009">Κατάλογος παιχνιδιών</string>
+- <string id="14010">Αυτόματη εναλλαγή με βάση τις μικÏογÏαφίες</string>
++ <string id="14010">Αυτόματη εναλλαγή σε μικÏογÏαφίες βάσει</string>
+ <string id="14011">ΕνεÏγοποίηση αυτόματης εναλλαγής στην Ï€Ïοβολή μικÏογÏαφιών</string>
+ <string id="14012">- ΧÏήση μεγάλων εικονιδίων</string>
+- <string id="14013">- Εναλλαγή βάση</string>
++ <string id="14013">- Εναλλαγή βάσει</string>
+ <string id="14014">- Ποσοστό</string>
+ <string id="14015">Καθόλου αÏχεία και τουλάχιστον μία μικÏογÏαφία</string>
+- <string id="14016">Τουλάχιστον ένα αÏχείο και μια μικÏογÏαφία</string>
++ <string id="14016">Τουλάχιστον ένα αÏχείο και μία μικÏογÏαφία</string>
+ <string id="14017">Ποσοστό μικÏογÏαφιών</string>
+ <string id="14018">Επιλογές Ï€Ïοβολής</string>
+ <string id="14019">Αλλαγή ÎºÏ‰Î´Î¹ÎºÎ¿Ï 1ης πεÏιοχής</string>
+@@ -1286,22 +1286,22 @@
+ <string id="14021">Αλλαγή ÎºÏ‰Î´Î¹ÎºÎ¿Ï 3ης πεÏιοχής</string>
+ <string id="14022">Συλλογή</string>
+ <string id="14023">ΧωÏίς τηλεόÏαση</string>
+- <string id="14024">Εισαγωγή της κοντινότεÏης μεγάλης πόλης ή ταχυδÏÎ¿Î¼Î¹ÎºÎ¿Ï ÎºÏ‰Î´Î¹ÎºÎ¿Ï (US)</string>
++ <string id="14024">Εισαγωγή της κοντινότεÏης μεγάλης πόλης</string>
+ <string id="14025">Συνολική λανθάνουσα μνήμη - ΣκληÏός δίσκος</string>
+- <string id="14026">Λανθάνουσα μνήμη βίντεο - DVDRom</string>
++ <string id="14026">Λανθάνουσα μνήμη βίντεο - DVD-ROM</string>
+ <string id="14027">- Τοπικό δίκτυο</string>
+- <string id="14028">- Internet</string>
+- <string id="14030">Λανθάνουσα μνήμη ήχου - DVDRom</string>
++ <string id="14028">- Διαδίκτυο</string>
++ <string id="14030">Λανθάνουσα μνήμη ήχου - DVD-ROM</string>
+ <string id="14031">- Τοπικό δίκτυο</string>
+- <string id="14032">- Internet</string>
+- <string id="14034">Λανθάνουσα μνήμη DVD - DVDRom</string>
++ <string id="14032">- Διαδίκτυο</string>
++ <string id="14034">Λανθάνουσα μνήμη DVD - DVD-ROM</string>
+ <string id="14035">- Τοπικό δίκτυο</string>
+ <string id="14036">ΥπηÏεσίες</string>
+
+ <string id="14038">Οι Ïυθμίσεις του δικτÏου άλλαξαν</string>
+ <string id="14039">Το XBMC Ï€Ïέπει να επανεκκινηθεί για να αλλάξουν</string>
+ <string id="14040">οι Ïυθμίσεις δικτÏου. Îα γίνει επανεκκίνηση;</string>
+- <string id="14041">ΣÏνδεση Internet με πεÏιοÏισμένο εÏÏος</string>
++ <string id="14041">ΣÏνδεση ΔιαδικτÏου με πεÏιοÏισμένη ταχÏτητα</string>
+
+ <string id="14043">- ΤεÏματισμός κατά την αναπαÏαγωγή</string>
+ <string id="14044">%i λεπτ.</string>
+@@ -1311,27 +1311,27 @@
+ <string id="14048">%i kbps</string>
+ <string id="14049">%i kb</string>
+ <string id="14050">%i.0 dB</string>
+- <string id="14051">ΔιαμόÏφωση ÎÏας</string>
+- <string id="14052">ΔιαμόÏφωση Ημ/νίας</string>
++ <string id="14051">ΜοÏφή ÎÏας</string>
++ <string id="14052">ΜοÏφή Ημ/νίας</string>
+ <string id="14053">ΦίλτÏα γÏÎ±Ï†Î¹ÎºÎ¿Ï Ï€ÎµÏιβάλλοντος (GUI)</string>
+
+ <string id="14055">ΣάÏωση στο παÏασκήνιο</string>
+ <string id="14056">Διακοπή σάÏωσης</string>
+- <string id="14057">Δεν είναι εφικτό κατά τη σάÏωση πληÏοφοÏιών αÏχείων</string>
++ <string id="14057">Δεν είναι εφικτό κατά τη σάÏωση πληÏοφοÏιών πολυμέσων</string>
+ <string id="14058">Εφέ Film Grain</string>
+ <string id="14059">ΈÏευνα στους κοινόχÏηστους πόÏους για μικÏογÏαφίες</string>
+- <string id="14060">Άγνωστος Ï„Ïπος λανθάνουσας μνήμης - Internet</string>
++ <string id="14060">Άγνωστος Ï„Ïπος λανθάνουσας μνήμης - Διαδίκτυο</string>
+ <string id="14061">Αυτόματο/η</string>
+ <string id="14062">Εισάγετε όνομα χÏήστη για</string>
+- <string id="14063">ΗμεÏομηνία &amp; ÎÏα</string>
+- <string id="14064">ΟÏισμός ημεÏομηνίας</string>
++ <string id="14063">ΗμεÏομηνία &amp; ÏŽÏα</string>
++ <string id="14064">ΟÏισμός ημ/νίας</string>
+ <string id="14065">ΟÏισμός ÏŽÏας</string>
+- <string id="14066">Εισάγετε την ÏŽÏα σε ψηφιακή μοÏφή</string>
+- <string id="14067">Εισάγετε την ημ/νία στο ΗΗ/MM/ΕΤΟΣ</string>
++ <string id="14066">Εισάγετε την ÏŽÏα σε 24ωÏη μοÏφή (ΩΩ:ΛΛ)</string>
++ <string id="14067">Εισάγετε την ημ/νία σε μοÏφή ΗΗ/MM/ΕΤΟΣ</string>
+ <string id="14068">Εισάγετε την διεÏθυνση ΙP</string>
+ <string id="14069">ΕφαÏμογή αυτών των Ïυθμίσεων Ï„ÏŽÏα;</string>
+ <string id="14070">ΕφαÏμογή Ïυθμίσεων Ï„ÏŽÏα</string>
+- <string id="14071">ΕπιτÏεπτή η μετονομασία και η διαγÏαφή</string>
++ <string id="14071">ΕπιτÏεπτή η μετονομασία και η διαγÏαφή αÏχείων</string>
+
+ <string id="14074">ΟÏισμός ζώνης ÏŽÏας</string>
+ <string id="14075">ΕνεÏγοποίηση θεÏινής ÏŽÏας</string>
+@@ -1344,10 +1344,10 @@
+ <string id="14082">Εμφάνιση πληÏοφοÏιών εικόνας (EXIF)</string>
+ <string id="14083">ΧÏήση του μεγιστοποιημένου παÏαθÏÏου αντί της πλήÏους οθόνης</string>
+ <string id="14084">Επιλεγμένα Ï„ÏαγοÏδια στη λίστα αναμονής</string>
+- <string id="14085">Αυτόματη αναπαÏαγωγή CDs ήχου</string>
++ <string id="14085">Αυτόματη αναπαÏαγωγή CD ήχου</string>
+ <string id="14086">ΑναπαÏαγωγή</string>
+- <string id="14087">DVDs</string>
+- <string id="14088">Αυτόματη αναπαÏαγωγή DVDs βίντεο</string>
++ <string id="14087">DVD</string>
++ <string id="14088">Αυτόματη αναπαÏαγωγή DVD</string>
+ <string id="14089">ΧÏήση γÏαμματοσειÏάς στους υπότιτλους</string>
+ <string id="14090">Διεθνοποίηση</string>
+ <string id="14091">Κωδικοποίηση χαÏακτήÏων</string>
+@@ -1372,94 +1372,94 @@
+
+ <string id="15107">Aποθήκευση...</string>
+ <string id="15108">Ήχοι πλοήγησης</string>
+- <string id="15109">ΠÏοεπιλεγμένο κέλυφος</string>
++ <string id="15109">ΠÏοεπιλογή κελÏφους</string>
+ <string id="15111">- Θέμα</string>
+ <string id="15112">ΠÏοεπιλεγμένο Θέμα</string>
+
+- <string id="15200">Last.FM</string>
+- <string id="15201">Αποστολή Ï„Ïαγουδιών στο Last.FM</string>
+- <string id="15202">Όνομα χÏήστη Last.FM</string>
+- <string id="15203">Κωδικός Ï€Ïόσβασης Last.FM</string>
++ <string id="15200">Last.fm</string>
++ <string id="15201">Αποστολή Ï„Ïαγουδιών στο Last.fm</string>
++ <string id="15202">Όνομα χÏήστη Last.fm</string>
++ <string id="15203">Κωδικός Ï€Ïόσβασης Last.fm</string>
+ <string id="15204">ΑδÏνατη η χειÏαψία: σε αδÏάνεια...</string>
+- <string id="15205">ΠαÏακαλώ, αναβαθμίστε το XBMC</string>
+- <string id="15206">Απέτυχε η εξακÏίβωση. Ελέγξτε το όνομα χÏήστη και τον κωδικό Ï€Ïόσβασης</string>
++ <string id="15205">ΠαÏακαλώ αναβαθμίστε το XBMC</string>
++ <string id="15206">Απέτυχε η εξακÏίβωση: Ελέγξτε το όνομα χÏήστη και τον κωδικό Ï€Ïόσβασης</string>
+ <string id="15207">Συνδεμένο</string>
+ <string id="15208">Αποσυνδεμένο</string>
+- <string id="15209">Αποστολή διαστήματος %i</string>
++ <string id="15209">Διάστημα αποστολής %i</string>
+ <string id="15210">%i Ï„ÏαγοÏδια στη λανθάνουσα μνήμη</string>
+ <string id="15211">Αποστολή...</string>
+ <string id="15212">Αποστολή σε %i δευτ.</string>
+- <string id="15213">ΔουλεÏει χÏησιμοποιώντας...</string>
++ <string id="15213">Άνοιγμα με...</string>
+ <string id="15214">ΧÏήση συγχÏÎ¿Î½Î¹ÏƒÎ¼Î¿Ï Smoothed A/V</string>
+ <string id="15215">ΑπόκÏυψη ονομάτων αÏχείων στην Ï€Ïοβολή μικÏογÏαφιών</string>
+- <string id="15216">Εκτελείτε σε Party Mode</string>
+- <string id="15217">Υποβολή Ï„Ïαγουδιών στο Libre.fm</string>
+- <string id="15218">Όνομα χÏήστη (Libre.fm)</string>
+- <string id="15219">Κωδικός Ï€Ïόσβασης (Libre.fm)</string>
++ <string id="15216">Εκτέλεση σε Party Mode</string>
++ <string id="15217">Αποστολή Ï„Ïαγουδιών στο Libre.fm</string>
++ <string id="15218">Όνομα χÏήστη Libre.fm</string>
++ <string id="15219">Κωδικός Ï€Ïόσβασης Libre.fm</string>
+ <string id="15220">Libre.fm</string>
+- <string id="15221">Υποβολή αιτήματος Ï„ÏαγουδιοÏ</string>
++ <string id="15221">Υποβολή Ï„ÏαγουδιοÏ</string>
+
+ <string id="15250">Αποστολή Last.fm radio στο Last.fm</string>
+- <string id="15251">ΣÏνδεση με Last.FM...</string>
++ <string id="15251">ΣÏνδεση με Last.fm...</string>
+ <string id="15252">Επιλογή σταθμοÏ...</string>
+ <string id="15253">Αναζήτηση παÏόμοιων καλλιτεχνών...</string>
+ <string id="15254">Αναζήτηση παÏόμοιων ετικετών...</string>
+ <string id="15255">Το Ï€Ïοφίλ σας (%name%)</string>
+- <string id="15256">Γενικές κοÏυφαίες ετικέτες</string>
++ <string id="15256">Γενικά κοÏυφαίες ετικέτες</string>
+ <string id="15257">ΚοÏυφαίοι καλλιτέχνες για την ετικέτα %name%</string>
+ <string id="15258">ΚοÏυφαία άλμπουμ για την ετικέτα %name%</string>
+ <string id="15259">ΚοÏυφαία κομμάτια για την ετικέτα %name%</string>
+- <string id="15260">ΑκÏόαση της ετικέτας %name% στο Last.FM radio</string>
++ <string id="15260">ΑκÏόαση της ετικέτας %name% στο Last.fm radio</string>
+ <string id="15261">ΠαÏόμοιοι καλλιτέχνες όπως ο %name%</string>
+- <string id="15262">%name% κοÏυφαία άλμπουμ</string>
+- <string id="15263">%name% κοÏυφαία κομμάτια</string>
+- <string id="15264">%name% κοÏυφαίες ετικέτες</string>
++ <string id="15262">ΚοÏυφαία άλμπουμ %name%</string>
++ <string id="15263">ΚοÏυφαία κομμάτια %name%</string>
++ <string id="15264">ΚοÏυφαίες ετικέτες %name%</string>
+ <string id="15265">Οι μεγαλÏτεÏοι θαυμαστές του %name%</string>
+- <string id="15266">ΑκÏόαση θαυμαστών του %name% στο Last.FM radio</string>
+- <string id="15267">ΑκÏόαση παÏόμοιων καλλιτεχνών με τον %name% στο Last.FM radio</string>
++ <string id="15266">ΑκÏόαση θαυμαστών του %name% στο Last.fm radio</string>
++ <string id="15267">ΑκÏόαση καλλιτεχνών παÏόμοιων με τον %name% στο Last.fm radio</string>
+ <string id="15268">Οι καλÏτεÏοι καλλιτέχνες του χÏήστη %name%</string>
+ <string id="15269">Τα καλÏτεÏα άλμπουμ του χÏήστη %name%</string>
+ <string id="15270">Τα καλÏτεÏα κομμάτια του χÏήστη %name%</string>
+ <string id="15271">Φίλοι του χÏήστη %name%</string>
+ <string id="15272">Γείτονες του χÏήστη %name%</string>
+- <string id="15273">Εβδομαδιαίο chart καλλιτέχνη του %name%</string>
+- <string id="15274">Εβδομαδιαίο chart άλμπουμ του %name%</string>
+- <string id="15275">Εβδομαδιαίο chart ÎºÎ¿Î¼Î¼Î±Ï„Î¹Î¿Ï Ï„Î¿Ï… %name%</string>
+- <string id="15276">ΑκÏόαση των γειτόνων του %name% στο Last.FM radio</string>
+- <string id="15277">ΑκÏόαση των Ï€Ïοσωπικών του %name% στο Last.FM radio</string>
+- <string id="15278">ΑκÏόαση των %name%'s mix Last.fm radio</string>
+- <string id="15279">Ανάκτηση λίστας από Last.FM...</string>
+- <string id="15280">ΑδÏνατη η ανάκτηση λίστας από Last.FM...</string>
+- <string id="15281">Εισάγετε ένα όνομα καλλιτέχνη</string>
+- <string id="15282">Εισάγετε μία ετικέτα για να βÏείτε τα παÏόμοια</string>
++ <string id="15273">Εβδομαδιαίο chart του καλλιτέχνη %name%</string>
++ <string id="15274">Εβδομαδιαίο chart του άλμπουμ %name%</string>
++ <string id="15275">Εβδομαδιαίο chart του ÎºÎ¿Î¼Î¼Î±Ï„Î¹Î¿Ï %name%</string>
++ <string id="15276">ΑκÏόαση του Last.fm radio των γειτόνων του %name%</string>
++ <string id="15277">ΑκÏόαση του Ï€ÏÎ¿ÏƒÏ‰Ï€Î¹ÎºÎ¿Ï Last.fm radio του %name%</string>
++ <string id="15278">ΑκÏόαση της μίξης Last.fm radio του %name%</string>
++ <string id="15279">Ανάκτηση λίστας από Last.fm...</string>
++ <string id="15280">ΑδÏνατη η ανάκτηση λίστας από Last.fm...</string>
++ <string id="15281">Εισάγετε ένα όνομα καλλιτέχνη για να βÏείτε παÏόμοια</string>
++ <string id="15282">Εισάγετε μία ετικέτα για να βÏείτε παÏόμοιες</string>
+ <string id="15283">Κομμάτια που ακοÏστηκαν Ï€Ïόσφατα από τον %name%</string>
+- <string id="15284">ΑκÏόαση των Ï€Ïοτιμήσεων του %name% στο Last.FM radio</string>
++ <string id="15284">ΑκÏόαση των Ï€Ïοτιμήσεων του %name% στο Last.fm radio</string>
+ <string id="15285">ΚοÏυφαίες ετικέτες για τον χÏήστη %name%</string>
+
+- <string id="15287">Îα Ï€Ïοστεθεί το παÏόν κομμάτι στα αγαπημένα σας;</string>
++ <string id="15287">Îα Ï€Ïοστεθεί το παÏόν κομμάτι στα αγαπημένα σας;</string>
+ <string id="15288">Îα αποκλειστεί το παÏόν κομμάτι;</string>
+ <string id="15289">ΠÏοσθήκη στα αγαπημένα σας κομμάτια: '%s'.</string>
+- <string id="15290">Δεν Ï€Ïοστέθηκε το '%s' στα Αγαπημένα σας.</string>
++ <string id="15290">Δεν μποÏεί να Ï€Ïοστεθεί το '%s' στα αγαπημένα σας.</string>
+ <string id="15291">Αποκλεισμένα: '%s'.</string>
+- <string id="15292">Îα μην αποκλειστεί '%s'.</string>
++ <string id="15292">Δεν μποÏεί να αποκλειστεί το '%s'.</string>
+ <string id="15293">ΠÏόσφατα αγαπημένα κομμάτια από %name%</string>
+- <string id="15294">ΠÏόσφατες αναγγελίες από %name%</string>
+- <string id="15295">ΑφαιÏέθηκε από τα αγαπημένα σας</string>
+- <string id="15296">Αποδεκτό</string>
+- <string id="15297">Îα αφαιÏεθεί το παÏόν κομμάτι από τα αγαπημένα σας;</string>
+- <string id="15298">Îα μην αποκλειστεί το παÏόν κομμάτι;</string>
+-
+- <string id="15300">H διαδÏομή δεν βÏέθηκε</string>
+- <string id="15301">Απέτυχε η σÏνδεση με τον διακομιστή δικτÏου</string>
++ <string id="15294">ΠÏόσφατα αποκλεισμένα κομμάτια από %name%</string>
++ <string id="15295">ΑφαίÏεση από τα αγαπημένα σας</string>
++ <string id="15296">ΆÏση αποκλεισμοÏ</string>
++ <string id="15297">Îα αφαιÏεθεί το παÏόν κομμάτι από τα αγαπημένα σας;</string>
++ <string id="15298">Îα αÏθεί ο αποκλεισμός για αυτό το κομμάτι;</string>
++
++ <string id="15300">Η διαδÏομή δε βÏέθηκε</string>
++ <string id="15301">Απέτυχε η σÏνδεση με το διακομιστή δικτÏου</string>
+ <string id="15302">Δεν βÏέθηκαν διακομιστές</string>
+- <string id="15303">Δεν βÏέθηκαν ομάδες εÏγασίας</string>
++ <string id="15303">Δεν βÏέθηκε η ομάδα εÏγασίας</string>
+
+ <string id="15310">Άνοιγμα πηγής multi-path</string>
+ <string id="15311">ΔιαδÏομή:</string>
+
+ <string id="16000">Γενικά</string>
+
+- <string id="16002">ΈÏευνα στο Internet</string>
++ <string id="16002">ΈÏευνα στο Διαδίκτυο</string>
+ <string id="16003">ΑναπαÏαγωγή</string>
+ <string id="16004">ΑναπαÏαγωγή πολυμέσων από το δίσκο</string>
+
+@@ -1472,27 +1472,27 @@
+ <string id="16014">Εισάγετε όνομα φακέλου</string>
+ <string id="16015">Εισάγετε κατάλογο</string>
+ <string id="16016">Διαθέσιμες επιλογές: %A, %T, %N, %B, %D, %G, %Y, %F, %S</string>
+- <string id="16017">Εισαγωγή συμβολοσειÏάς αναζήτησης</string>
++ <string id="16017">Εισαγωγή κειμένου αναζήτησης</string>
+ <string id="16018">Καμία</string>
+ <string id="16019">Αυτόματη επιλογή</string>
+ <string id="16020">Απόπλεξη</string>
+- <string id="16021">Επιλεκτική</string>
+- <string id="16022">Επιλεκτική (αντιστÏοφή)</string>
+- <string id="16023">Μέθοδος πεÏιπλεγμένου χειÏισμοÏ</string>
++ <string id="16021">Bob</string>
++ <string id="16022">Bob (αντιστÏοφή)</string>
++ <string id="16023"></string>
+ <string id="16024">ΑκÏÏωση...</string>
+ <string id="16025">Εισάγετε το όνομα του καλλιτέχνη</string>
+ <string id="16026">Η αναπαÏαγωγή απέτυχε</string>
+- <string id="16027">Αποτυχία αναπαÏαγωγής ενός ή πεÏισσότεÏων αντικείμενων.</string>
++ <string id="16027">Αποτυχία αναπαÏαγωγής ενός ή πεÏισσότεÏων αντικειμένων.</string>
+ <string id="16028">Εισαγωγή τιμής</string>
+- <string id="16029">Για πεÏισσότεÏες λεπτομέÏειες ελέγξτε το αÏχείο συμβάντων</string>
+- <string id="16030">Το Party Mode ως μέθοδος αποÏÏίφθηκε.</string>
+- <string id="16031">Δεν υπάÏχουν Ï„ÏαγοÏδια στη συλλογή που να ταιÏιάζουν.</string>
++ <string id="16029">Για λεπτομέÏειες ελέγξτε το αÏχείο καταγÏαφής.</string>
++ <string id="16030">Το Party Mode αποÏÏίφθηκε.</string>
++ <string id="16031">Δεν υπάÏχουν παÏόμοια Ï„ÏαγοÏδια στη συλλογή.</string>
+ <string id="16032">Δεν είναι δυνατός ο συγχÏονισμός με τη βάση δεδομένων.</string>
+ <string id="16033">Απέτυχε το άνοιγμα της βάσης δεδομένων.</string>
+ <string id="16034">Απέτυχε η λήψη Ï„Ïαγουδιών από τη βάση δεδομένων.</string>
+- <string id="16035">Λίστα αναπαÏαγωγής σε Party Îœode</string>
++ <string id="16035">Λίστα αναπαÏαγωγής σε Party Mode</string>
+ <string id="16036">Απόπλεξη (κατά το ήμισυ)</string>
+- <string id="16037">Απόπλεξη βίντεο</string>
++ <string id="16037">Απόπλεξη βίντεο (Deinterlace)</string>
+ <string id="16038">Μέθοδος απόπλεξης</string>
+ <string id="16039">ΑνενεÏγή</string>
+ <string id="16040">Αυτόματη</string>
+@@ -1515,14 +1515,14 @@
+
+ <string id="16300">Μέθοδος Ï€ÏοσαÏμογής ανάλυσης βίντεο</string>
+ <string id="16301">ΕγγÏτεÏη Ï€Ïοσέγγιση</string>
+- <string id="16302">ΔιγÏαμμική</string>
+- <string id="16303">Δικυβική</string>
++ <string id="16302">Bilinear</string>
++ <string id="16303">Bicubic</string>
+ <string id="16304">Lanczos2</string>
+ <string id="16305">Lanczos3</string>
+ <string id="16306">Sinc8</string>
+- <string id="16307">Δικυβική (με λογισμικό)</string>
+- <string id="16308">Lanczos (με λογισμικό)</string>
+- <string id="16309">Sinc (με λογισμικό)</string>
++ <string id="16307">Bicubic (λογισμικό)</string>
++ <string id="16308">Lanczos (λογισμικό)</string>
++ <string id="16309">Sinc (λογισμικό)</string>
+ <string id="16310">ΧÏονική</string>
+ <string id="16311">ΧÏονική/ΧωÏική</string>
+ <string id="16312">(VDPAU) Μείωση θοÏÏβου</string>
+@@ -1549,7 +1549,7 @@
+ <string id="20001">ΧÏήση εξωτεÏÎ¹ÎºÎ¿Ï Î±Î½Î±Ï€Î±Ïαγωγέα DVD</string>
+ <string id="20002">ΕξωτεÏικός αναπαÏαγωγέας DVD</string>
+ <string id="20003">Φάκελος trainers</string>
+- <string id="20004">Φάκελος στιγμιότυπου</string>
++ <string id="20004">Φάκελος στιγμιότυπων</string>
+
+ <string id="20006">Φάκελος λίστας αναπαÏαγωγής</string>
+ <string id="20007">ΕγγÏαφές</string>
+@@ -1569,63 +1569,63 @@
+ <!-- string id 20022 will always be set to an empty string (LocalizeStrings.cpp)-->
+ <string id="20022"></string>
+ <string id="20023">Διένεξη</string>
+- <string id="20024">ΣάÏωση καινοÏÏγιων</string>
++ <string id="20024">ΣάÏωση νέων</string>
+ <string id="20025">ΣάÏωση όλων</string>
+ <string id="20026">ΠεÏιοχή</string>
+
+ <!-- string id's 20027 thru 20034 are reserved for temperature strings (LocalizeStrings.cpp)-->
+
+- <string id="20037">Συνοπτικές</string>
+- <string id="20038">Κλείδωμα της μουσικής</string>
+- <string id="20039">Κλείδωμα των βίντεο</string>
+- <string id="20040">Κλείδωμα των φωτογÏαφιών</string>
+- <string id="20041">Κλείδωμα εφαÏμογών και scripts windows</string>
++ <string id="20037">Συνοπτικά</string>
++ <string id="20038">Κλείδωμα παÏαθÏÏου μουσικής</string>
++ <string id="20039">Κλείδωμα παÏαθÏÏου βίντεο</string>
++ <string id="20040">Κλείδωμα παÏαθÏÏου εικόνων</string>
++ <string id="20041">Κλείδωμα παÏαθÏÏου εφαÏμογών &amp; script</string>
+ <string id="20042">Κλείδωμα της διαχείÏισης αÏχείων</string>
+ <string id="20043">Κλείδωμα των Ïυθμίσεων</string>
+ <string id="20044">Îέα έναÏξη</string>
+ <string id="20045">Είσοδος στη γενική διαχείÏιση</string>
+ <string id="20046">Έξοδος από τη γενική διαχείÏιση</string>
+ <string id="20047">ΔημιουÏγία Ï€Ïοφίλ '%s';</string>
+- <string id="20048">ΈναÏξη με καινοÏÏγιες Ïυθμίσεις</string>
++ <string id="20048">ΈναÏξη με νέες Ïυθμίσεις</string>
+ <string id="20049">Η καλÏτεÏη δυνατή</string>
+ <string id="20050">Αυτόματη εναλλαγή Î¼ÎµÏ„Î±Î¾Ï 16:9 και 4:3</string>
+ <string id="20051">ΜεταχείÏιση στοιβαγμένων αÏχείων σαν ένα</string>
+ <string id="20052">ΠÏοσοχή</string>
+- <string id="20053">ΑÏιστεÏή γενική διαχείÏιση</string>
+- <string id="20054">ΚαταχωÏημένη γενική διαχείÏιση</string>
++ <string id="20053">ΑποχώÏηση από τη γεν. διαχείÏιση</string>
++ <string id="20054">Εισαγωγή στη γεν. διαχείÏιση</string>
+ <string id="20055">ΜικÏογÏαφία Allmusic.com</string>
+
+ <string id="20057">ΑπομάκÏυνση μικÏογÏαφίας</string>
+- <string id="20058">ΠÏοσθέστε Ï€Ïοφίλ...</string>
+- <string id="20059">ΕÏώτημα για όλα τα άλμπουμ</string>
++ <string id="20058">ΠÏοσθήκη Ï€Ïοφίλ...</string>
++ <string id="20059">ΕÏÏεση πληÏοφοÏιών για όλα τα άλμπουμ</string>
+ <string id="20060">ΠληÏοφοÏίες πολυμέσων</string>
+ <string id="20061">ΞεχωÏιστές</string>
+- <string id="20062">ΚοινόχÏηστα με Ï€Ïοεπιλεγμένα</string>
+- <string id="20063">ΚοινόχÏηστα με Ï€Ïοεπιλεγμένα (ανάγνωση μόνο)</string>
+- <string id="20064">ΑντιγÏαφή Ï€Ïοεπιλεγμένων</string>
++ <string id="20062">ΚοινόχÏηστα με Ï€Ïοεπιλογές</string>
++ <string id="20063">ΚοινόχÏηστα με Ï€Ïοεπιλογές (ανάγνωση μόνο)</string>
++ <string id="20064">ΑντιγÏαφή Ï€Ïοεπιλογών</string>
+ <string id="20065">Εικόνα Ï€Ïοφίλ</string>
+- <string id="20066">Επιλογές κλειδώματος</string>
++ <string id="20066">ΠÏοτιμήσεις κλειδώματος</string>
+ <string id="20067">ΕπεξεÏγασία Ï€Ïοφίλ</string>
+ <string id="20068">Κλείδωμα Ï€Ïοφίλ</string>
+ <string id="20069">ΑδÏνατη η δημιουÏγία φακέλου</string>
+- <string id="20070">Φάκελος Ï€Ïοφίλ</string>
++ <string id="20070">Κατάλογος Ï€Ïοφίλ</string>
+ <string id="20071">ΈναÏξη με νέες πηγές πολυμέσων</string>
+ <string id="20072">ΣιγουÏευτείτε ότι ο επιλεγμένος φάκελος μποÏεί να εγγÏαφεί</string>
+- <string id="20073">και ότι το όνομα του είναι έγκυÏο</string>
+- <string id="20074">Αξιολόγηση MPAA</string>
++ <string id="20073">και ότι το νέο όνομα είναι έγκυÏο</string>
++ <string id="20074">Αξιολόγ. MPAA</string>
+ <string id="20075">Εισαγωγή ÎºÏ‰Î´Î¹ÎºÎ¿Ï ÎºÎµÎ½Ï„ÏÎ¹ÎºÎ¿Ï ÎºÎ»ÎµÎ¹Î´ÏŽÎ¼Î±Ï„Î¿Ï‚</string>
+ <string id="20076">Αίτημα για κωδικό κεντÏÎ¹ÎºÎ¿Ï ÎºÎ»ÎµÎ¹Î´ÏŽÎ¼Î±Ï„Î¿Ï‚ στην εκκίνηση</string>
+ <string id="20077">Ρυθμίσεις κελÏφους</string>
+ <string id="20078">- δεν έχει οÏιστεί σÏνδεση -</string>
+- <string id="20079">ΕνεÏγοποίηση εφέ κίνησης κελÏφους</string>
+- <string id="20080">ΑνενεÏγές οι Ροές Τίτλων Ειδήσεων κατά την ακÏόαση Ï„Ïαγουδιών</string>
++ <string id="20079">ΕνεÏγοποίηση εφέ κίνησης</string>
++ <string id="20080">ΑπενεÏγοποίηση της Ροής Τίτλων Ειδήσεων (RSS) κατά την ακÏόαση μουσικής</string>
+ <string id="20081">ΕνεÏγοποίηση πλήκτÏων συντόμευσης</string>
+ <string id="20082">Εμφάνιση των εφαÏμογών στο κεντÏικό μενοÏ</string>
+ <string id="20083">Εμφάνιση πληÏοφοÏιών για τη μουσική</string>
+ <string id="20084">Εμφάνιση πληÏοφοÏιών για τον καιÏÏŒ</string>
+ <string id="20085">Εμφάνιση πληÏοφοÏιών για το σÏστημα</string>
+- <string id="20086">Εμφάνιση διαθέσιμου χώÏου στους C: E: F:</string>
+- <string id="20087">Εμφάνιση διαθέσιμου χώÏου στους E: F: G:</string>
++ <string id="20086">Εμφάνιση διαθέσιμου χώÏου στα C: E: F:</string>
++ <string id="20087">Εμφάνιση διαθέσιμου χώÏου στα E: F: G:</string>
+ <string id="20088">ΠληÏοφοÏίες καιÏοÏ</string>
+ <string id="20089">ΕλεÏθεÏος χώÏος</string>
+ <string id="20090">Εισάγετε όνομα κοινόχÏηστου πόÏου</string>
+@@ -1636,47 +1636,47 @@
+ <string id="20095">Εισάγετε κωδικό κλειδώματος Ï€Ïοφίλ</string>
+ <string id="20096">Οθόνη σÏνδεσης</string>
+ <string id="20097">Λήψη πληÏοφοÏιών άλμπουμ</string>
+- <string id="20098">Λαμβάνει πληÏοφοÏίες για το άλμπουμ</string>
+- <string id="20099">Αδυναμία εγγÏαφής του CD ή του ÎºÎ¿Î¼Î¼Î±Ï„Î¹Î¿Ï ÎºÎ±Ï„Î¬ την αναπαÏαγωγή δίσκου</string>
+- <string id="20100">ΚεντÏικό κλείδωμα και επιλογές</string>
+- <string id="20101">Εισαγωγή ÎºÏ‰Î´Î¹ÎºÎ¿Ï Î³Î¹Î± την ενεÏγοποίηση της γενικής διαχείÏισης</string>
+- <string id="20102">ή αντιγÏαφή του ÎºÏ‰Î´Î¹ÎºÎ¿Ï ÏŒÏ€Ï‰Ï‚ αυτός έχει Ï€ÏοκαθοÏιστεί;</string>
++ <string id="20098">Λαμβάνονται πληÏοφοÏίες για το άλμπουμ</string>
++ <string id="20099">Αδυναμία αντιγÏαφής του CD ή του ÎºÎ¿Î¼Î¼Î±Ï„Î¹Î¿Ï ÎºÎ±Ï„Î¬ την αναπαÏαγωγή του CD</string>
++ <string id="20100">Κωδικός κεντÏÎ¹ÎºÎ¿Ï ÎºÎ»ÎµÎ¹Î´ÏŽÎ¼Î±Ï„Î¿Ï‚ και επιλογές</string>
++ <string id="20101">Ο κωδικός κεντÏÎ¹ÎºÎ¿Ï ÎºÎ»ÎµÎ¹Î´ÏŽÎ¼Î±Ï„Î¿Ï‚ να ενεÏγοποιεί πάντα τη γεν. διαχείÏιση</string>
++ <string id="20102">ή να αντιγÏάφεται ο Ï€ÏοκαθοÏισμένος κωδικός;</string>
+ <string id="20103">Αποθήκευση αλλαγών στο Ï€Ïοφίλ;</string>
+ <string id="20104">Î’Ïέθηκαν παλιές Ïυθμίσεις.</string>
+ <string id="20105">Θέλετε να τις χÏησιμοποιήσετε;</string>
+ <string id="20106">Î’Ïέθηκαν παλιές πηγές πολυμέσων.</string>
+- <string id="20107">ΧωÏισμένο (κλειδωμένο)</string>
++ <string id="20107">ΞεχωÏιστές (κλειδωμένο)</string>
+ <string id="20108">Root</string>
+ <string id="20109">- Μεγέθυνση</string>
+ <string id="20110">Ρυθμίσεις UPnP</string>
+ <string id="20111">Αυτόματη έναÏξη πελάτη UPnP</string>
+ <string id="20112">Τελευταία σÏνδεση: %s</string>
+- <string id="20113">Ποτέ δεν συνδέθηκε</string>
++ <string id="20113">Ποτέ δε συνδέθηκε</string>
+ <string id="20114">ΠÏοφίλ %i / %i</string>
+- <string id="20115">Όνομα χÏήστη / Επιλέξτε ένα Ï€Ïοφίλ</string>
++ <string id="20115">ΣÏνδεση χÏήστη / Επιλέξτε ένα Ï€Ïοφίλ</string>
+ <string id="20116">ΧÏήση κλειδώματος στην οθόνη σÏνδεσης</string>
+ <string id="20117">Λάθος κωδικός κλειδώματος.</string>
+- <string id="20118">Απαιτεί να ενεÏγοποιήσετε το κεντÏικό κλείδωμα</string>
+- <string id="20119">Θέλετε να το Ïυθμίσετε Ï„ÏŽÏα;</string>
++ <string id="20118">Απαιτείται οÏισμός του κεντÏÎ¹ÎºÎ¿Ï ÎºÎ»ÎµÎ¹Î´ÏŽÎ¼Î±Ï„Î¿Ï‚.</string>
++ <string id="20119">Θέλετε να το οÏίσετε Ï„ÏŽÏα;</string>
+ <string id="20120">ΦόÏτωση πληÏοφοÏιών εφαÏμογής</string>
+ <string id="20121">Party on!</string>
+ <string id="20122">Αληθές</string>
+- <string id="20123">ΑναμιγνÏοντας τα ποτά</string>
+- <string id="20124">Γεμίζοντας τα ποτήÏια</string>
++ <string id="20123">ΑναμιγνÏονται τα ποτά</string>
++ <string id="20124">Γεμίζουν τα ποτήÏια</string>
+ <string id="20125">Συνδεμένος σαν</string>
+ <string id="20126">ΑποσÏνδεση</string>
+- <string id="20128">Πήγαινε στο root</string>
+- <string id="20129">Ύφανση</string>
+- <string id="20130">Ύφανση (αντιστÏοφή)</string>
+- <string id="20131">Συνδυασμός</string>
++ <string id="20128">Μετάβαση σε root</string>
++ <string id="20129">Weave</string>
++ <string id="20130">Weave (αντιστÏοφή)</string>
++ <string id="20131">Blend</string>
+ <string id="20132">Επανεκκίνηση βίντεο</string>
+ <string id="20133">ΕπεξεÏγασία τοποθεσίας δικτÏου</string>
+ <string id="20134">ΑπομάκÏυνση τοποθεσίας δικτÏου</string>
+- <string id="20135">Θέλετε να σαÏώσετε τον φάκελο;</string>
++ <string id="20135">Θέλετε να σαÏώσετε το φάκελο;</string>
+ <string id="20136">Μονάδα μνήμης</string>
+ <string id="20137">ΠÏοσάÏτηση μονάδας μνήμης</string>
+ <string id="20138">ΑδÏνατη η Ï€ÏοσάÏτηση μονάδας μνήμης</string>
+- <string id="20139">Στη θÏÏα %i ,υποδοχή %i</string>
++ <string id="20139">Στη θÏÏα %i, υποδοχή %i</string>
+ <string id="20140">Κλείδωμα Ï€ÏοφÏλαξης οθόνης</string>
+ <string id="20141">ΟÏισμός σε</string>
+ <string id="20142">Όνομα χÏήστη</string>
+@@ -1691,7 +1691,7 @@
+ <string id="20151">ΑκÏÏωση χÏονοδιακόπτη τεÏματισμοÏ</string>
+ <string id="20152">Κλείδωμα Ïυθμίσεων για %s</string>
+ <string id="20153">Αναζήτηση...</string>
+- <string id="20154">Συνολικές πληÏοφοÏίες</string>
++ <string id="20154">Συνοπτικές πληÏοφοÏίες</string>
+ <string id="20155">ΠληÏοφοÏίες Î±Ï€Î¿Î¸Î·ÎºÎµÏ…Ï„Î¹ÎºÎ¿Ï Ï‡ÏŽÏου</string>
+ <string id="20156">ΠληÏοφοÏίες σκληÏÎ¿Ï Î´Î¯ÏƒÎºÎ¿Ï…</string>
+ <string id="20157">ΠληÏοφοÏίες DVD-ROM</string>
+@@ -1701,7 +1701,7 @@
+ <string id="20161">Συνολικά</string>
+ <string id="20162">ΧÏησιμοποιημένα</string>
+ <string id="20163">από τα</string>
+- <string id="20164">Δεν υποστηÏίζει κλείδωμα</string>
++ <string id="20164">Δεν υποστηÏίζεται κλείδωμα</string>
+ <string id="20165">Ξεκλείδωτος</string>
+ <string id="20166">Κλειδωμένος</string>
+ <string id="20167">Παγωμένος</string>
+@@ -1711,7 +1711,7 @@
+ <string id="20171">Δίκτυο Windows (SMB)</string>
+ <string id="20172">Διακομιστής XBMSP</string>
+ <string id="20173">Διακομιστής FTP</string>
+- <string id="20174">ΜοίÏασμα μουσικής iTunes (DAAP)</string>
++ <string id="20174">ΔιαμοίÏαση μουσικής iTunes (DAAP)</string>
+ <string id="20175">Διακομιστής UPnP</string>
+ <string id="20176">Εμφάνιση πληÏοφοÏιών για το βίντεο</string>
+ <string id="20177">Τέλος</string>
+@@ -1721,27 +1721,27 @@
+ <string id="20181">ΔιαγÏαφή</string>
+ <string id="20182">Κενό</string>
+ <string id="20183">ΕπαναφόÏτωση κελÏφους</string>
+- <string id="20184">ΠεÏιστÏοφή εικόνων που χÏησιμοποιοÏν τις πληÏοφοÏίες EXIF</string>
++ <string id="20184">ΠεÏιστÏοφή εικόνων που χÏησιμοποιοÏν πληÏοφοÏίες EXIF</string>
+ <string id="20185">ΧÏήση στυλ αφισών για τις τηλεοπτικές σειÏές</string>
+ <string id="20186">Απασχολημένο</string>
+
+- <string id="20189">ΕνεÏγοποίηση αυτόματης κÏλισης στην επισκόπηση πλοκής &amp; κÏιτικής</string>
++ <string id="20189">ΕνεÏγοποίηση αυτόματης κÏλισης για πλοκή &amp; κÏιτική</string>
+ <string id="20190">ΠÏοσαÏμογή</string>
+ <string id="20191">ΕνεÏγοποίηση καταγÏαφής σφαλμάτων (debug log)</string>
+ <string id="20192">Λήψη Ï€Ïόσθετων πληÏοφοÏιών κατά τις ενημεÏώσεις συλλογής</string>
+ <string id="20193">ΠÏοκαθοÏισμένη υπηÏεσία πληÏοφοÏιών άλμπουμ</string>
+ <string id="20194">ΠÏοκαθοÏισμένη υπηÏεσία πληÏοφοÏιών καλλιτέχνη</string>
+- <string id="20195">Αλλαγή καταγÏαφέα</string>
++ <string id="20195">Αλλαγή scraper</string>
+ <string id="20196">Εξαγωγή μουσικής συλλογής</string>
+ <string id="20197">Εισαγωγή μουσικής συλλογής</string>
+ <string id="20198">Δεν βÏέθηκε ο καλλιτέχνης!</string>
+- <string id="20199">Αποτυχία λήψης πληÏοφοÏιών για τον καλλιτέχνη</string>
++ <string id="20199">Αποτυχία λήψης πληÏοφοÏιών καλλιτέχνη</string>
+
+ <!-- string id's 20200 thru 20211 are reserved for speedstrings (LocalizeStrings.cpp)-->
+
+ <string id="20250">Party on! (βίντεο)</string>
+- <string id="20251">ΑναμιγνÏοντας τα ποτά (βίντεο)</string>
+- <string id="20252">Γεμίζοντας τα ποτήÏια (βίντεο)</string>
++ <string id="20251">ΑναμιγνÏονται τα ποτά (βίντεο)</string>
++ <string id="20252">Γεμίζουν τα ποτήÏια (βίντεο)</string>
+ <string id="20253">Διακομιστής WebDAV (HTTP)</string>
+ <string id="20254">Διακομιστής WebDAV (HTTPS)</string>
+ <string id="20255">ΠÏώτη σÏνδεση, επεξεÏγαστείτε το Ï€Ïοφίλ σας</string>
+@@ -1759,15 +1759,15 @@
+ <string id="20304">Ροή τίτλων ειδήσεων (RSS)</string>
+
+ <string id="20307">ΔευτεÏεÏον DNS</string>
+- <string id="20308">DHCP διακομιστή:</string>
++ <string id="20308">Διακομιστής DHCP:</string>
+ <string id="20309">ΔημιουÏγία νέου φακέλου</string>
+- <string id="20310">Σκίαση LCD κατά την αναπαÏαγωγή</string>
++ <string id="20310">ΑμαÏÏωση LCD κατά την αναπαÏαγωγή</string>
+ <string id="20311">Άγνωστο ή πάνω στην μητÏική (Ï€ÏοστατεÏεται)</string>
+- <string id="20312">Σκίαση LCD σε παÏση</string>
++ <string id="20312">ΑμαÏÏωση LCD στην παÏση</string>
+
+ <string id="20314">Βίντεο - Συλλογή</string>
+
+- <string id="20316">Ταξ. κατά: ID</string>
++ <string id="20316">Ταξ.: ID</string>
+
+ <string id="20324">ΑναπαÏαγωγή κομματιοÏ...</string>
+ <string id="20325">ΕπαναφοÏά βαθμονόμησης</string>
+@@ -1780,18 +1780,18 @@
+ <string id="20332">ΧÏήση φακέλων ή ονομάτων αÏχείων στις αναζητήσεις;</string>
+ <string id="20333">ΟÏισμός πεÏιεχομένου</string>
+ <string id="20334">Φάκελος</string>
+- <string id="20335">Έλεγχος πεÏιεχομένου κατ' επανάληψη;</string>
++ <string id="20335">Έλεγχος πεÏιεχομένου και των υποφακέλων;</string>
+ <string id="20336">Ξεκλείδωμα πηγών</string>
+ <string id="20337">Ηθοποιός</string>
+ <string id="20338">Ταινία</string>
+ <string id="20339">Σκηνοθεσία</string>
+- <string id="20340">Θέλετε να απομακÏÏνετε όλα τα αντικείμενα που βÏίσκονται</string>
+- <string id="20341">σ΄ αυτή την διαδÏομή από τη συλλογή του XBMC;</string>
++ <string id="20340">Θέλετε να απομακÏÏνετε όλα τα αντικείμενα</string>
++ <string id="20341">αυτής της διαδÏομής από τη συλλογή του XBMC;</string>
+ <string id="20342">Ταινίες</string>
+ <string id="20343">Τηλεοπτικές σειÏές</string>
+ <string id="20344">Αυτός ο φάκελος πεÏιέχει</string>
+ <string id="20345">Εκτέλεση αυτόματης σάÏωσης</string>
+- <string id="20346">ΣάÏωση κατ' επανάληψη</string>
++ <string id="20346">ΣάÏωση και των υποφακέλων</string>
+ <string id="20347">ως</string>
+ <string id="20348">Σκηνοθέτες</string>
+ <string id="20349">Δεν βÏέθηκαν αÏχεία βίντεο σε αυτή τη διαδÏομή!</string>
+@@ -1824,13 +1824,13 @@
+ <string id="20376">ΠÏωτότυπος τίτλος</string>
+ <string id="20377">Ανανέωση πληÏοφοÏιών τηλεοπτικής σειÏάς</string>
+ <string id="20378">Ανανέωση πληÏοφοÏιών για όλα τα επεισόδια;</string>
+- <string id="20379">Ο επιλεγμένος φάκελος πεÏιέχει μία ενιαία τηλεοπτική σειÏά</string>
++ <string id="20379">Ο επιλεγμένος φάκελος πεÏιέχει μόνο μία τηλεοπτική σειÏά</string>
+ <string id="20380">ΕξαίÏεση του επιλεγμένου φακέλου από τυχόν ελέγχους</string>
+ <string id="20381">ΙδιαίτεÏα στοιχεία</string>
+- <string id="20382">Αυτόματη απόσπαση μικÏογÏαφιών κÏκλου</string>
+- <string id="20383">Ο Επιλεγμένος φάκελος πεÏιέχει ένα ενιαίο βίντεο</string>
+- <string id="20384">ΣÏνδεσμος στην τηλεοπτική σειÏά</string>
+- <string id="20385">ΑφαίÏεση συνδέσμου από την τηλεοπτική σειÏά</string>
++ <string id="20382">Αυτόματη λήψη μικÏογÏαφιών κÏκλων</string>
++ <string id="20383">Ο επιλεγμένος φάκελος πεÏιέχει μόνο ένα βίντεο</string>
++ <string id="20384">ΣÏνδεσμος σε τηλεοπτική σειÏά</string>
++ <string id="20385">ΑφαίÏεση συνδέσμου από τηλεοπτική σειÏά</string>
+ <string id="20386">ΠÏόσφατα εισαχθείσες ταινίες</string>
+ <string id="20387">ΠÏόσφατα εισαχθέντα επεισόδια</string>
+ <string id="20388">ΣτοÏντιο</string>
+@@ -1839,25 +1839,25 @@
+ <string id="20391">Μουσικό βίντεο</string>
+ <string id="20392">ΑφαίÏεση Î¼Î¿Ï…ÏƒÎ¹ÎºÎ¿Ï Î²Î¯Î½Ï„ÎµÎ¿ από τη συλλογή</string>
+ <string id="20393">ΠληÏοφοÏίες Î¼Î¿Ï…ÏƒÎ¹ÎºÎ¿Ï Î²Î¯Î½Ï„ÎµÎ¿</string>
+- <string id="20394">Λαμβάνονται πληÏοφοÏίες για το μουσικό βίντεο</string>
+- <string id="20395">ΣυÏÏαμμένα</string>
++ <string id="20394">ΦόÏτωση πληÏοφοÏιών για το μουσικό βίντεο</string>
++ <string id="20395">Ανάμικτα</string>
+ <string id="20396">Άλμπουμ καλλιτέχνη</string>
+ <string id="20397">Άλμπουμ</string>
+ <string id="20398">ΑναπαÏαγωγή Ï„ÏαγουδιοÏ</string>
+ <string id="20399">Μουσικά βίντεο από άλμπουμ</string>
+ <string id="20400">Μουσικά βίντεο ανά καλλιτέχνη</string>
+ <string id="20401">ΑναπαÏαγωγή Î¼Î¿Ï…ÏƒÎ¹ÎºÎ¿Ï Î²Î¯Î½Ï„ÎµÎ¿</string>
+- <string id="20402">Λήψη μικÏογÏαφιών Î·Î¸Î¿Ï€Î¿Î¹Î¿Ï ÎºÎ±Ï„Î¬ τη Ï€Ïοσθήκη στη συλλογή</string>
++ <string id="20402">Λήψη μικÏογÏαφιών Î·Î¸Î¿Ï€Î¿Î¹Î¿Ï ÎºÎ±Ï„Î¬ την Ï€Ïοσθήκη στη συλλογή</string>
+ <string id="20403">ΟÏισμός μικÏογÏαφίας ηθοποιοÏ</string>
+
+ <string id="20405">ΑφαίÏεση σελιδοδείκτη επεισοδίου</string>
+ <string id="20406">ΟÏισμός σελιδοδείκτη επεισοδίου</string>
+- <string id="20407">Ρυθμίσεις καταγÏαφέα</string>
++ <string id="20407">Ρυθμίσεις scraper</string>
+ <string id="20408">Λήψη πληÏοφοÏιών για το μουσικό βίντεο</string>
+ <string id="20409">Λήψη πληÏοφοÏιών για την τηλεοπτική σειÏά</string>
+ <string id="20410">Διαφημιστικό</string>
+- <string id="20411">Ισοπέδωση</string>
+- <string id="20412">Ισοπέδωση τηλεοπτικών σειÏών</string>
++ <string id="20411">Ενιαία λίστα (Flatten)</string>
++ <string id="20412">Ενιαία λίστα επεισοδίων σειÏάς (Flatten)</string>
+ <string id="20413">Λήψη Fanart</string>
+ <string id="20414">ΠÏοβολή Fanart στις συλλογές βίντεο και μουσικής</string>
+ <string id="20415">Έλεγχος για νέα πεÏιεχόμενα</string>
+@@ -1871,7 +1871,7 @@
+ <string id="20422">Πάντα</string>
+ <string id="20423">Έχει διαφημιστικό</string>
+ <string id="20424">Ψευδές</string>
+- <string id="20425">ΠαÏουσίαση Fanart</string>
++ <string id="20425">ΠαÏουσίαση διαφανειών Fanart</string>
+ <string id="20426">Εξαγωγή σε ένα αÏχείο ή σε πολλαπλά</string>
+ <string id="20427">αÏχεία ανά εγγÏαφή;</string>
+ <string id="20428">Ένα αÏχείο</string>
+@@ -1881,9 +1881,9 @@
+ <string id="20432">ΕξαίÏεση διαδÏομής από τις ενημεÏώσεις συλλογής</string>
+ <string id="20433">Εξαγωγή μικÏογÏαφιών και πληÏοφοÏιών</string>
+ <string id="20434">Ομάδες Ταινιών</string>
+- <string id="20435">ΟÏισμός θέσης μικÏογÏαφίας ταινίας</string>
+- <string id="20436">Εξαγωγή μικÏογÏαφιών καλλιτέχνη;</string>
+- <string id="20437">Επιλεγμένο fanart</string>
++ <string id="20435">ΟÏισμός μικÏογÏαφίας ομάδας</string>
++ <string id="20436">Εξαγωγή μικÏογÏαφιών ηθοποιοÏ;</string>
++ <string id="20437">Επιλογή fanart</string>
+ <string id="20438">Τοπικό fanart</string>
+ <string id="20439">ΧωÏίς fanart</string>
+ <string id="20440">ΤÏέχον fanart</string>
+@@ -1893,7 +1893,7 @@
+ <string id="20444">για όλα τα στοιχεία αυτής της διαδÏομής;</string>
+ <string id="20445">Fanart</string>
+ <string id="20446">Î’Ïέθηκαν τοπικά αποθηκευμένες πληÏοφοÏίες.</string>
+- <string id="20447">Îα αγνοηθοÏν και να ανανεωθοÏν από το internet;</string>
++ <string id="20447">Îα αγνοηθοÏν και να ανανεωθοÏν από το διαδίκτυο;</string>
+ <string id="20448">Αδυναμία λήψης πληÏοφοÏιών</string>
+ <string id="20449">Αδυναμία σÏνδεσης με τον απομακÏυσμένο διακομιστή</string>
+ <string id="20450">Επιθυμείτε να συνεχίσετε την αναζήτηση;</string>
+@@ -1919,7 +1919,7 @@
+ <!-- up to 21355 is reserved for the TuxBox Client!! !-->
+
+ <string id="21359">ΠÏοσθήκη κοινόχÏηστου πολυμέσου...</string>
+- <string id="21360">ΔιαμοιÏασμός κοινόχÏηστων συλλογών μουσικής και βίντεο διαμέσου UPnP</string>
++ <string id="21360">ΔιαμοιÏασμός συλλογών μουσικής και βίντεο μέσω UPnP</string>
+
+ <string id="21364">ΕπεξεÏγασία κοινόχÏηστων πολυμέσων</string>
+ <string id="21365">ΑπομάκÏυνση κοινόχÏηστων πολυμέσων</string>
+@@ -1927,15 +1927,15 @@
+ <string id="21367">Ταινία &amp; εναλλακτικός φάκελος υποτίτλων</string>
+ <string id="21368">ΠαÏάκαμψη γÏαμματοσειÏών υποτίτλων ASS/SSA</string>
+
+- <string id="21369">ΕνεÏγοποίηση Ï€Î¿Î½Ï„Î¹ÎºÎ¹Î¿Ï ÎºÎ±Î¹ υποστήÏιξης Οθόνης Αφής</string>
+- <string id="21370">ΕνεÏγοποίηση ήχων πλοήγησης κατά την εκτέλεση πολυμέσων</string>
++ <string id="21369">ΕνεÏγοποίηση Ï€Î¿Î½Ï„Î¹ÎºÎ¹Î¿Ï ÎºÎ±Î¹ υποστήÏιξης οθόνης αφής</string>
++ <string id="21370">ΕνεÏγοποίηση ήχων πλοήγησης κατά την αναπαÏαγωγή</string>
+ <string id="21371">ΜικÏογÏαφία</string>
+ <string id="21372">ΠεÏιοχή αναπαÏαγωγέα DVD</string>
+ <string id="21373">Έξοδος βίντεο</string>
+ <string id="21374">Αναλογία οθόνης</string>
+ <string id="21375">Κανονική</string>
+ <string id="21376">Letterbox</string>
+- <string id="21377">ΕυÏεία Ï€Ïοβολή</string>
++ <string id="21377">ΕυÏεία οθόνη</string>
+ <string id="21378">ΕνεÏγοποίηση 480p</string>
+ <string id="21379">ΕνεÏγοποίηση 720p</string>
+ <string id="21380">ΕνεÏγοποίηση 1080i</string>
+@@ -1947,15 +1947,15 @@
+ <string id="21386">ΔιαχείÏιση στάθμης θοÏÏβου</string>
+ <string id="21387">ΘοÏυβώδες</string>
+ <string id="21388">ΑθόÏυβο</string>
+- <string id="21389">ΕνεÏγοποίηση Ï€ÏοσαÏμοσμένου υπόβαθÏου</string>
++ <string id="21389">ΕνεÏγοποίηση Ï€ÏοσαÏμοσμένου υποβάθÏου</string>
+ <string id="21390">ΔιαχείÏιση παÏοχής ενέÏγειας</string>
+ <string id="21391">Υψηλή Ï„Ïοφοδοσία</string>
+ <string id="21392">Χαμηλή Ï„Ïοφοδοσία</string>
+ <string id="21393">Υψηλή αναμονή</string>
+ <string id="21394">Χαμηλή αναμονή</string>
+- <string id="21395">Αδυναμία δημιουÏγίας Ï€ÏοσωÏινών αÏχείων μεγαλÏτεÏων των 4 gb</string>
++ <string id="21395">Αδυναμία δημιουÏγίας Ï€ÏοσωÏινών αÏχείων μεγαλÏτεÏων από 4GB</string>
+ <string id="21396">Κεφάλαιο</string>
+- <string id="21397">Υψηλή ποιότητα (Pixel Shader V2)</string>
++ <string id="21397">Υψηλή ποιότητα (Σκίαση pixel v2)</string>
+ <string id="21398">ΕνεÏγοποίηση λίστας αναπαÏαγωγής κατά την εκκίνηση</string>
+ <string id="21399">ΕνεÏγοποίηση εφέ παλινδÏομικής κίνησης</string>
+ <string id="21400">πεÏιέχει</string>
+@@ -1970,36 +1970,36 @@
+ <string id="21409">Ï€Ïιν</string>
+ <string id="21410">στο τελευταίο</string>
+ <string id="21411">όχι στο τελευταίο</string>
+- <string id="21412">ΚαταγÏαφείς</string>
+- <string id="21413">ΠÏοεπιλεγμένος καταγÏαφέας ταινίας</string>
+- <string id="21414">ΠÏοεπιλεγμένος καταγÏαφέας τηλεοπτικής σειÏάς</string>
+- <string id="21415">ΠÏοεπιλεγμένος καταγÏαφέας Î¼Î¿Ï…ÏƒÎ¹ÎºÎ¿Ï Î²Î¯Î½Ï„ÎµÎ¿</string>
+- <string id="21416">ΕνεÏγοποίηση επαναφοÏάς (βάση της γλώσσας του καταγÏαφέα)</string>
++ <string id="21412">Scraper</string>
++ <string id="21413">ΠÏοεπιλεγμένο scraper ταινιών</string>
++ <string id="21414">ΠÏοεπιλεγμένο scraper τηλεοπτικών σειÏών</string>
++ <string id="21415">ΠÏοεπιλεγμένο scraper μουσικών βίντεο</string>
++ <string id="21416">ΕνεÏγοποίηση εναλλακτικής βάσει της γλώσσας του scraper</string>
+ <string id="21417">- Ρυθμίσεις</string>
+ <string id="21418">ΠολÏγλωσσο</string>
+- <string id="21419">Δεν υπάÏχει διαθέσιμος καταγÏαφέας</string>
+- <string id="21420">Τιμές για αντιστοίχιση</string>
+- <string id="21421">ÎŒÏοι έξυπνης λίστας αναπαÏαγωγής</string>
+- <string id="21422">ΚÏιτήÏια αντιστοίχισης αντικειμένων</string>
+- <string id="21423">Îέοι κανόνες...</string>
+- <string id="21424">Αντικείμενα με πολλές αντιστοιχίες</string>
+- <string id="21425">με όλους τους κανόνες</string>
+- <string id="21426">με έναν ή πεÏισσότεÏους κανόνες</string>
++ <string id="21419">Δεν υπάÏχει διαθέσιμο scraper</string>
++ <string id="21420">Τιμή για αντιστοίχιση</string>
++ <string id="21421">Κανόνας έξυπνης λίστας αναπαÏαγωγής</string>
++ <string id="21422">Αντιστοίχιση αντικειμένων όταν</string>
++ <string id="21423">Îέος κανόνας...</string>
++ <string id="21424">Τα αντικείμενα Ï€Ïέπει να πληÏοÏν</string>
++ <string id="21425">όλους τους κανόνες</string>
++ <string id="21426">έναν ή πεÏισσότεÏους κανόνες</string>
+ <string id="21427">ΑÏιθμητικό ÏŒÏιο</string>
+ <string id="21428">ΧωÏίς πεÏιοÏισμό</string>
+- <string id="21429">Συσχετιζόμενο στοιχείο</string>
++ <string id="21429">ΣειÏά κατά</string>
+ <string id="21430">αÏξουσα</string>
+ <string id="21431">φθίνουσα</string>
+ <string id="21432">ΕπεξεÏγασία έξυπνης λίστας αναπαÏαγωγής</string>
+ <string id="21433">Όνομα λίστας αναπαÏαγωγής</string>
+- <string id="21434">ΕÏÏεση αντιστοιχίας αντικειμένων</string>
++ <string id="21434">ΕÏÏεση αντικειμένων για τα οποία</string>
+ <string id="21435">ΕπεξεÏγασία</string>
+ <string id="21436">%i αντικείμενα</string>
+ <string id="21437">Îέα έξυπνη λίστα αναπαÏαγωγής...</string>
+ <string id="21438">%c Δίσκος</string>
+- <string id="21439">ΕπεξεÏγασία ÏŒÏων Party Mode</string>
++ <string id="21439">ΕπεξεÏγασία κανόνων Party Mode</string>
+ <string id="21440">ΑÏχικός φάκελος</string>
+- <string id="21441">Πλήθος θεαμάτων</string>
++ <string id="21441">Πλήθος Ï€Ïοβληθέντων</string>
+ <string id="21442">Τίτλος επεισοδίου</string>
+ <string id="21443">Ανάλυση βίντεο</string>
+ <string id="21444">Κανάλια ήχου</string>
+@@ -2009,13 +2009,13 @@
+ <string id="21448">Γλώσσα υποτίτλων</string>
+ <string id="21449">Το τηλεχειÏιστήÏιο στέλνει εντολές πληκτÏολογίου</string>
+ <string id="21450">- ΕπεξεÏγασία</string>
+- <string id="21451">Απαιτείται σÏνδεση στο Internet.</string>
+- <string id="21452">Λήψη πεÏισσότεÏων...</string>
++ <string id="21451">Απαιτείται σÏνδεση στο Διαδίκτυο.</string>
++ <string id="21452">Λήψη ΠεÏισσότεÏων...</string>
+ <string id="21453">ΣÏστημα αÏχείων root</string>
+ <string id="21454">Λανθάνουσα μνήμη πλήÏης</string>
+ <string id="21455">Η λανθάνουσα μνήμη γέμισε Ï€Ïιν φθάσει το απαιτοÏμενο μέγεθος για συνεχόμενη αναπαÏαγωγή</string>
+
+- <string id="21460">Θέση υπότιτλου</string>
++ <string id="21460">Θέση υποτίτλου</string>
+ <string id="21461">ΣταθεÏή</string>
+ <string id="21462">Κάτω πλευÏά του βίντεο</string>
+ <string id="21463">Κάτω από το βίντεο</string>
+@@ -2026,13 +2026,13 @@
+ <string id="21801">ΔιαδÏομή αÏχείου</string>
+ <string id="21802">Μέγεθος αÏχείου</string>
+ <string id="21803">ΗμεÏομηνία αÏχείου</string>
+- <string id="21804">ΕυÏετήÏιο διαφάνειας</string>
++ <string id="21804">Θέση διαφάνειας</string>
+ <string id="21805">Ανάλυση</string>
+ <string id="21806">Σχόλιο</string>
+- <string id="21807">Απόδοση</string>
++ <string id="21807">ΈγχÏωμη/ΑσπÏόμαυÏη</string>
+ <string id="21808">ΕπεξεÏγασία JPEG</string>
+
+- <string id="21820">ΗμεÏομηνία λήψης</string>
++ <string id="21820">ΗμεÏομηνία/ÎÏα</string>
+ <string id="21821">ΠεÏιγÏαφή</string>
+ <string id="21822">Κατασκευαστής κάμεÏας</string>
+ <string id="21823">Μοντέλο κάμεÏας</string>
+@@ -2052,8 +2052,8 @@
+ <string id="21837">Ευαισθησία ISO</string>
+ <string id="21838">Ψηφιακή μεγέθυνση</string>
+ <string id="21839">Πλάτος CCD</string>
+- <string id="21840">GPS γεωγÏαφικό πλάτος</string>
+- <string id="21841">GPS γεωγÏαφικό μήκος</string>
++ <string id="21840">GPS γεωγÏ. πλάτος</string>
++ <string id="21841">GPS γεωγÏ. μήκος</string>
+ <string id="21842">GPS υψόμετÏο</string>
+ <string id="21843">ΠÏοσανατολισμός</string>
+
+@@ -2062,7 +2062,7 @@
+ <string id="21862">Λεζάντα</string>
+ <string id="21863">ΔημιουÏγός</string>
+ <string id="21864">Επικεφαλίδα</string>
+- <string id="21865">Εξειδικευμένες πληÏοφοÏίες</string>
++ <string id="21865">Ειδικές πληÏοφοÏίες</string>
+ <string id="21866">ΚατηγοÏία</string>
+ <string id="21867">ΠÏοέλευση</string>
+ <string id="21868">Τίτλος</string>
+@@ -2071,17 +2071,17 @@
+ <string id="21871">Σημειώσεις πνευματικής ιδιοκτησίας</string>
+ <string id="21872">Όνομα αντικειμένου</string>
+ <string id="21873">Πόλη</string>
+- <string id="21874">ΕπαÏχεία</string>
++ <string id="21874">Πολιτεία</string>
+ <string id="21875">ΧώÏα</string>
+ <string id="21876">Îομοθετικές πληÏοφοÏίες</string>
+ <string id="21877">ΗμεÏομηνία δημιουÏγίας</string>
+ <string id="21878">Πνευματικά δικαιώματα</string>
+ <string id="21879">Κωδικός χώÏας</string>
+ <string id="21880">ΥπηÏεσία πληÏοφόÏησης</string>
+- <string id="21881">ΈγκÏιση στους ελεγκτές UPnP να ελέγχουν το XBMC</string>
+- <string id="21882">ΑπόπειÏα αποφυγής οδηγιών Ï€Ïιν από το Î¼ÎµÎ½Î¿Ï DVD</string>
++ <string id="21881">ΈγκÏιση ελέγχου του XBMC μέσω UPnP</string>
++ <string id="21882">ΑπόπειÏα αποφυγής οδηγιών Ï€Ïιν το Î¼ÎµÎ½Î¿Ï DVD</string>
+ <string id="21883">Αποθηκευμένη μουσική</string>
+- <string id="21884">ΕÏώτημα για όλους τους καλλιτέχνες</string>
++ <string id="21884">ΕÏÏεση πληÏοφοÏιών για όλους τους καλλιτέχνες</string>
+ <string id="21885">Λήψη πληÏοφοÏιών για το άλμπουμ</string>
+ <string id="21886">Λήψη πληÏοφοÏιών για τον καλλιτέχνη</string>
+ <string id="21887">ΒιογÏαφικό</string>
+@@ -2090,11 +2090,11 @@
+ <string id="21890">Επιλογή καλλιτέxνη</string>
+ <string id="21891">ΠληÏοφοÏίες καλλιτέχνη</string>
+ <string id="21892">ÎŒÏγανα</string>
+- <string id="21893">Γέννηση</string>
++ <string id="21893">Γεννήθηκε</string>
+ <string id="21894">ΔημιουÏγία</string>
+ <string id="21895">Θεματολόγιο</string>
+- <string id="21896">Διάλυση</string>
+- <string id="21897">Θάνατος</string>
++ <string id="21896">ΔιαλÏθηκε</string>
++ <string id="21897">Πέθανε</string>
+ <string id="21898">Έτη ενεÏγής σταδιοδÏομίας</string>
+ <string id="21899">Ετικέτα</string>
+ <string id="21900">Γέννηση/ΔημιουÏγία</string>
+@@ -2116,14 +2116,14 @@
+ <string id="22011">ΘεÏμοκÏασία CPU:</string>
+ <string id="22012">Συνολική μνήμη</string>
+ <string id="22013">Δεδομένα Ï€Ïοφίλ</string>
+- <string id="22014">ΧÏήση 'ΑμυδÏÎ¿Ï Ï†Ï‰Ï„ÏŒÏ‚' κατά την παÏση της αναπαÏαγωγής βίντεο</string>
++ <string id="22014">ΧÏήση 'ΑμυδÏÎ¿Ï Ï†Ï‰Ï„ÏŒÏ‚' κατά την παÏση αναπαÏαγωγής βίντεο</string>
+ <string id="22015">Όλες οι εγγÏαφές</string>
+ <string id="22016">Aνά τίτλο</string>
+ <string id="22017">Ανά ομάδα</string>
+ <string id="22018">Δικτυακά κανάλια</string>
+ <string id="22019">ΕγγÏαφές ανά τίτλο</string>
+ <string id="22020">Οδηγός</string>
+- <string id="22021">ΕπιτÏεπόμενο σφάλμα στην αναλογία οθόνης για να ελαχιστοποιηθοÏν οι μαÏÏες μπάÏες</string>
++ <string id="22021">Σφάλμα αναλογίας οθόνης για μείωση μαÏÏων μπαÏών</string>
+ <string id="22022">Εμφάνιση αÏχείων βίντεο στις λίστες</string>
+ <string id="22023">Κατασκευαστής DirectX:</string>
+ <string id="22024">Έκδοση Direct3D:</string>
+@@ -2159,9 +2159,9 @@
+
+ <!-- strings 23100 thru 23150 reserved for external player -->
+ <string id="23100">ΕνεÏγός εξωτεÏικός αναπαÏαγωγέας</string>
+- <string id="23101">Πιέστε το 'Εντάξει' για να τεÏματίσετε τον αναπαÏαγωγέα</string>
++ <string id="23101">Πιέστε 'Επιλογή' για να τεÏματίσετε την αναπαÏαγωγή</string>
+
+- <string id="23104">Πιέστε το 'Εντάξει' όταν η αναπαÏαγωγή ολοκληÏωθεί</string>
++ <string id="23104">Πιέστε 'Επιλογή' όταν η αναπαÏαγωγή ολοκληÏωθεί</string>
+
+ <!-- strings 24000 thru 24299 reserved for the Add-ons framework -->
+ <string id="24000">ΠÏόσθετο</string>
+@@ -2203,10 +2203,10 @@
+ <string id="24041">Εγκατάσταση από αÏχείο zip</string>
+ <string id="24042">Λήψη %i%%</string>
+ <string id="24043">Διαθέσιμες ενημεÏώσεις</string>
+- <string id="24044">Δεν πληÏοÏνται οι ΕξαÏτήσεις</string>
+- <string id="24045">Το ΠÏόσθετο δεν έχει τη σωστή δομή</string>
+- <string id="24046">%s χÏησιμοποιείται από τα εγκατεστημένα ΠÏόσθετα</string>
+- <string id="24047">Αδυναμία απεγκατάστασης Î±Ï…Ï„Î¿Ï Ï„Î¿Ï… ΠÏόσθετου</string>
++ <string id="24044">Δεν πληÏοÏνται οι εξαÏτήσεις</string>
++ <string id="24045">Το Ï€Ïόσθετο δεν έχει τη σωστή δομή</string>
++ <string id="24046">Το %s χÏησιμοποιείται από τα εγκατεστημένα Ï€Ïόσθετα</string>
++ <string id="24047">Αδυναμία απεγκατάστασης Î±Ï…Ï„Î¿Ï Ï„Î¿Ï… Ï€Ïόσθετου</string>
+ <string id="24048">ΕπαναφοÏά</string>
+
+ <string id="24050">Διαθέσιμα Ï€Ïόσθετα</string>
+@@ -2232,7 +2232,7 @@
+ <string id="24073">Δεν ήταν δυνατή η σÏνδεση</string>
+ <string id="24074">Απαιτείται επανεκκίνηση</string>
+ <string id="24075">ΑπενεÏγοποίηση</string>
+- <string id="24076">Απαιτείται ΠÏόσθετο</string>
++ <string id="24076">Απαιτείται Ï€Ïόσθετο</string>
+ <string id="24080">Îέα Ï€Ïοσπάθεια επανασÏνδεσης;</string>
+ <string id="24089">Επανεκκινήσεις Ï€Ïόσθετου</string>
+ <string id="24090">Κλείδωμα διαχειÏιστή Ï€Ïόσθετων</string>
+@@ -2243,12 +2243,12 @@
+ <string id="24097">Επιθυμείτε να απενεÏγοποιηθεί από το σÏστημά σας;</string>
+ <string id="24098">ΚατεστÏαμμένο</string>
+ <string id="24099">Επιθυμείτε να εφαÏμοστεί αυτό το κέλυφος;</string>
+- <string id="24100">Για να χÏησιμοποιήσετε αυτή τη λειτουÏγία Ï€Ïέπει να κατεβάσετε ένα ΠÏόσθετο:</string>
+- <string id="24101">Επιθυμείτε να κατεβάσετε αυτό το ΠÏόσθετο;</string>
++ <string id="24100">Για να χÏησιμοποιήσετε αυτή τη λειτουÏγία Ï€Ïέπει να κατεβάσετε ένα Ï€Ïόσθετο:</string>
++ <string id="24101">Επιθυμείτε να κατεβάσετε αυτό το Ï€Ïόσθετο;</string>
+ <string id="25000">Ειδοποιήσεις</string>
+
+ <!-- strings 29800 thru 29998 reserved strings used only in the default Project Mayhem III skin and not c++ code -->
+- <string id="29800">ΛειτουÏγία συλλογής</string>
++ <string id="29800">ΛειτουÏγία Συλλογής</string>
+ <string id="29801">Διάταξη πληκτÏολογίου QWERTY</string>
+ <string id="29802">Σε λειτουÏγία η διέλευση ήχου</string>
+
+@@ -2259,8 +2259,8 @@
+ <string id="33001">Ποιότητα διαφημιστικοÏ</string>
+ <string id="33002">Ροή πολυμέσων</string>
+ <string id="33003">Λήψη</string>
+- <string id="33004">Λήψη και εκτέλεση</string>
+- <string id="33005">Λήψη και αποθήκευση</string>
++ <string id="33004">Λήψη &amp; εκτέλεση</string>
++ <string id="33005">Λήψη &amp; αποθήκευση</string>
+ <string id="33006">ΣήμεÏα</string>
+ <string id="33007">ΑÏÏιο</string>
+ <string id="33008">Αποθήκευση</string>
+@@ -2271,33 +2271,33 @@
+ <string id="33013">Εκτεταμένος</string>
+ <string id="33014">ΟÏισμός αναπαÏαγωγέα DVD αντί του κανονικοÏ</string>
+ <string id="33015">Επιβεβαίωση λήψης Ï€Ïιν την αναπαÏαγωγή του βίντεο</string>
+- <string id="33016">Clips</string>
+- <string id="33017">Επανεκκίνηση plugin για την ενεÏγοποίηση του</string>
+- <string id="33018">ΣήμεÏα το βÏάδυ</string>
+- <string id="33019">ΑÏÏιο το βÏάδυ</string>
++ <string id="33016">Clip</string>
++ <string id="33017">Επανεκκίνηση plugin για την ενεÏγοποίησή του</string>
++ <string id="33018">Απόψε</string>
++ <string id="33019">ΑÏÏιο βÏάδυ</string>
+ <string id="33020">Kατάσταση καιÏοÏ</string>
+- <string id="33021">Î’Ïοχόπτωση</string>
++ <string id="33021">Υετός</string>
+ <string id="33022">Î’Ïοχή</string>
+ <string id="33023">ΥγÏασία</string>
+ <string id="33024">Αίσθηση</string>
+ <string id="33025">ΠαÏατηÏοÏμενη</string>
+- <string id="33026">Εκφυγή από το κανονικό</string>
++ <string id="33026">ΠαÏέκκλιση από το κανονικό</string>
+ <string id="33027">Ανατολή ηλίου</string>
+ <string id="33028">ΔÏση ηλίου</string>
+ <string id="33029">ΛεπτομέÏειες</string>
+- <string id="33030">Εξέλιξη</string>
+- <string id="33031">Ροή εξώφυλλων</string>
++ <string id="33030">ΠÏόγνωση</string>
++ <string id="33031">Ροή εξωφÏλλων</string>
+ <string id="33032">ΜετάφÏαση κειμένου</string>
+ <string id="33033">ΚατηγοÏία λίστας χαÏτών %s</string>
+- <string id="33034">ΤÏιήμεÏη</string>
+- <string id="33035">ΧάÏτες καιÏοÏ</string>
++ <string id="33034">36ωÏο</string>
++ <string id="33035">ΧάÏτες</string>
+ <string id="33036">ΩÏιαία</string>
+ <string id="33037">ΣαββατοκÏÏιακο</string>
+ <string id="33038">%sη ημέÏα</string>
+ <string id="33049">ΠÏοειδοποίηση</string>
+ <string id="33050">ΠÏοειδοποιήσεις</string>
+ <string id="33051">Επιλογή</string>
+- <string id="33052">ΠÏόγνωση</string>
++ <string id="33052">Έλεγχος</string>
+ <string id="33053">ΡÏθμιση</string>
+ <string id="33054">ΚÏκλοι</string>
+ <string id="33055">Εκτέλεση</string>
+@@ -2319,9 +2319,9 @@
+ <string id="33072">Σημειώσεις έκδοσης</string>
+ <string id="33073">Αλλαγές έκδοσης</string>
+ <string id="33074">Για να εκτελεστεί η έκδοση %s απαιτείται</string>
+- <string id="33075">η %s αναθεωÏημένη έκδοση του XBMC ή και νεώτεÏη.</string>
+- <string id="33076">ΠαÏακαλώ, αναβαθμίστε άμεσα το XBMC.</string>
+- <string id="33077">Δεν βÏέθηκαν δεδομένα!</string>
++ <string id="33075">η %s αναθεωÏημένη έκδοση του XBMC ή νεώτεÏη.</string>
++ <string id="33076">ΠαÏακαλώ αναβαθμίστε το XBMC.</string>
++ <string id="33077">Δε βÏέθηκαν δεδομένα!</string>
+ <string id="33078">Επόμενη σελίδα</string>
+ <string id="33079">Αγαπητό</string>
+ <string id="33080">Απεχθές</string>
+@@ -2330,7 +2330,7 @@
+ <string id="33083">ΕνεÏγοποίηση Ï€ÏοσαÏμοσμένου πλήκτÏου script</string>
+
+ <string id="33100">Αποτυχία εκκίνησης</string>
+- <string id="33101">Διακομιστής web</string>
++ <string id="33101">Διακομιστής ιστοÏ</string>
+ <string id="33102">Διακομιστής Συμβάντων</string>
+ <string id="33103">ΑπομακÏυσμένος Διακομιστής Επικοινωνίας</string>
+
+@@ -2361,11 +2361,11 @@
+ <string id="34202">Αδυναμία εÏÏεσης Ï€ÏοηγοÏμενου αντικειμένου για αναπαÏαγωγή</string>
+
+ <string id="34300">Αποτυχία εκκίνησης του zeroconf</string>
+- <string id="34301">Είναι εγκατεστημένη η ΥπηÏεσία Bonjour της Apple; Δείτε το αÏχείο καταγÏαφής για πεÏισσότεÏες πληÏοφοÏίες.</string>
++ <string id="34301">Είναι εγκατεστημένη η υπηÏεσία Bonjour της Apple; Δείτε το αÏχείο καταγÏαφής για πεÏισσότεÏες πληÏοφοÏίες.</string>
+
+ <string id="34400">Απόδοση Βίντεο</string>
+- <string id="34401">Αποτυχία αÏχικοποίησης φίλτÏων/scalers βίντεο, επαναφοÏά σε διγÏαμμική Ï€ÏοσαÏμογή ανάλυσης</string>
+- <string id="34402">Αποτυχία αÏχικοποίησης της συσκευής ήχου</string>
++ <string id="34401">Αποτυχία χÏήσης φίλτÏων/scalers βίντεο, επαναφοÏά σε bilinear Ï€ÏοσαÏμογή ανάλυσης</string>
++ <string id="34402">Αποτυχία έναÏξης της συσκευής ήχου</string>
+ <string id="34403">Ελέγξτε τις Ïυθμίσεις ήχου</string>
+
+ <string id="35000">ΠεÏιφεÏειακά</string>
+@@ -2374,9 +2374,9 @@
+ <string id="35002">Γενικός Ï€ÏοσαÏμογέας δικτÏου</string>
+ <string id="35003">Γενικός δίσκος</string>
+ <string id="35004">Δεν υπάÏχουν διαθέσιμες Ïυθμίσεις&#10;για αυτό το πεÏιφεÏειακό.</string>
+- <string id="35005">Η νέα συσκευή έχει Ïυθμιστεί</string>
+- <string id="35006">Η συσκευή έχει αφαιÏεθεί</string>
+- <string id="35007">Το Keymap να χÏησιμοποιηθεί για τη συγκεκÏιμένη συσκευή</string>
++ <string id="35005">Η νέα συσκευή Ïυθμίστηκε</string>
++ <string id="35006">Η συσκευή αφαιÏέθηκε</string>
++ <string id="35007">Keymap για χÏήση με αυτή τη συσκευή</string>
+ <string id="35008">ΕνεÏγοποιημένο Keymap</string>
+
+ <string id="35500">Τοποθεσία</string>
+@@ -2389,19 +2389,19 @@
+ <string id="36001">Pulse-Eight Nyxboard</string>
+ <string id="36002">Μετάβαση στο πληκτÏολόγιο εντολών</string>
+ <string id="36003">ΕνεÏγοποίηση διακόπτη εντολών</string>
+- <string id="36004">Πιέστε το "user" ως πλήκτÏο εντολών</string>
++ <string id="36004">Πιέστε το "user" πλήκτÏο εντολών</string>
+ <string id="36005">ΕνεÏγοποίηση διακόπτη εντολών</string>
+ <string id="36006">Αδυναμία εκκίνησης του Ï€ÏοσαÏμογέα</string>
+ <string id="36007">ΕνεÏγοποίηση της τηλεόÏασης κατά την εκκίνηση του XBMC</string>
+- <string id="36008">ΑπενεÏγοποίηση συσκευών κατά τη διακοπή λειτουÏγίας του XBMC</string>
++ <string id="36008">ΑπενεÏγοποίηση συσκευών κατά τον τεÏματισμό του XBMC</string>
+ <string id="36009">Συσκευές σε κατάσταση αναμονής κατά την Ï€ÏοφÏλαξη οθόνης</string>
+ <string id="36010"></string>
+- <string id="36011">Δεν ήταν δυνατή η ανίχνευση της θÏÏας CEC. Ρυθμίστε την χειÏοκίνητα.</string>
+- <string id="36012">Δεν ήταν δυνατή η ανίχνευση του Ï€ÏοσαÏμογέα CEC.</string>
+- <string id="36013">Μη υποστηÏιζόμενη έκδοση διεπαφής libcec. %d είναι μεγαλÏτεÏη από την έκδοση που το XBMC υποστηÏίζει (%d)</string>
+- <string id="36014">Βάλτε αυτόν τον υπολογιστή σε κατάσταση αναμονής όταν η τηλεόÏαση είναι απενεÏγοποιημένη</string>
++ <string id="36011">Δεν εντοπίστηκε η θÏÏα CEC. Ρυθμίστε την χειÏοκίνητα.</string>
++ <string id="36012">Δεν εντοπίστηκε ο Ï€ÏοσαÏμογέας CEC.</string>
++ <string id="36013">Μη υποστηÏιζόμενη έκδοση libCEC. Η %d είναι μεγαλÏτεÏη από την έκδοση που το XBMC υποστηÏίζει (%d)</string>
++ <string id="36014">Ο υπολογιστής σε κατάσταση αναμονής όταν κλείσει η τηλεόÏαση</string>
+ <string id="36015">ΑÏιθμός θÏÏας HDMI</string>
+ <string id="36016">Συνδεδεμένη</string> <!-- max. 13 characters -->
+- <string id="36017">O Ï€ÏοσαÏμογέας εντοπίσθηκε, αλλά η libcec δεν είναι διαθέσιμη</string>
++ <string id="36017">O Ï€ÏοσαÏμογέας εντοπίσθηκε, αλλά η libCEC δεν είναι διαθέσιμη</string>
+ <string id="36018">ΧÏήση των Ïυθμίσεων γλώσσας της τηλεόÏασης</string>
+ </strings>
+diff --git a/language/Japanese/strings.xml b/language/Japanese/strings.xml
+index 46e3282..d3126e0 100644
+--- a/language/Japanese/strings.xml
++++ b/language/Japanese/strings.xml
+@@ -1,10 +1,9 @@
+ <?xml version="1.0" encoding="utf-8" standalone="yes"?>
+ <!--Language file translated with Team XBMC Translator-->
+-<!--Translator: kyouhei-->
+-<!--Email: Twitter: @kyouhei-->
+-<!--Date of translation: 01/29/2011-->
++<!--Translator: Kohji 'Shaolin' MATSUBAYASHI (kohji405mi16)-->
++<!--Email: shaolin@vinelinux.org-->
++<!--Date of translation: 03/31/2012-->
+ <!--$Revision$-->
+-<!--Based on english strings version 35208-->
+ <strings>
+ <string id="0">プログラム</string>
+ <string id="1">ピクãƒãƒ£</string>
+@@ -15,7 +14,8 @@
+ <string id="6">XBMC SVN</string>
+ <string id="7">ファイルマãƒãƒ¼ã‚¸ãƒ£ãƒ¼</string>
+ <string id="8">天気予報</string>
+- <string id="9">xbmc media center</string>
++ <string id="9">XBMC メディアセンター</string>
++
+ <string id="11">月曜日</string>
+ <string id="12">ç«æ›œæ—¥</string>
+ <string id="13">水曜日</string>
+@@ -23,6 +23,7 @@
+ <string id="15">金曜日</string>
+ <string id="16">土曜日</string>
+ <string id="17">日曜日</string>
++
+ <string id="21">1月</string>
+ <string id="22">2月</string>
+ <string id="23">3月</string>
+@@ -35,9 +36,49 @@
+ <string id="30">10月</string>
+ <string id="31">11月</string>
+ <string id="32">12月</string>
++
++ <string id="41">月</string>
++ <string id="42">ç«</string>
++ <string id="43">æ°´</string>
++ <string id="44">木</string>
++ <string id="45">金</string>
++ <string id="46">土</string>
++ <string id="47">æ—¥</string>
++
++ <string id="51">1月</string>
++ <string id="52">2月</string>
++ <string id="53">3月</string>
++ <string id="54">4月</string>
++ <string id="55">5月</string>
++ <string id="56">6月</string>
++ <string id="57">7月</string>
++ <string id="58">8月</string>
++ <string id="59">9月</string>
++ <string id="60">10月</string>
++ <string id="61">11月</string>
++ <string id="62">12月</string>
++
++ <string id="71">北</string>
++ <string id="72">北北æ±</string>
++ <string id="73">北æ±</string>
++ <string id="74">æ±åŒ—æ±</string>
++ <string id="75">æ±</string>
++ <string id="76">æ±å—æ±</string>
++ <string id="77">å—æ±</string>
++ <string id="78">å—å—æ±</string>
++ <string id="79">å—</string>
++ <string id="80">å—å—西</string>
++ <string id="81">å—西</string>
++ <string id="82">西å—西</string>
++ <string id="83">西</string>
++ <string id="84">西北西</string>
++ <string id="85">北西</string>
++ <string id="86">北北西</string>
++ <string id="87">ä¸å®š</string>
++
+ <string id="98">表示: 自動</string>
+- <string id="99">表示: 自動(大)</string>
+- <string id="100">表示: アイコン(å°)</string>
++ <string id="99">表示: 自動 (大)</string>
++ <string id="100">表示: アイコン (å°)</string>
+ <string id="101">表示: リスト</string>
+ <string id="102">スキャン</string>
+ <string id="103">並ã¹æ›¿ãˆ: åå‰</string>
+@@ -50,17 +91,19 @@
+ <string id="110">サムãƒã‚¤ãƒ«(複数)作æˆ</string>
+ <string id="111">ショートカット</string>
+ <string id="112">一時åœæ­¢</string>
++ <string id="113">更新失敗</string>
++ <string id="114">インストール失敗</string>
+ <string id="115">コピー</string>
+ <string id="116">移動</string>
+ <string id="117">削除</string>
+ <string id="118">åå‰ã®å¤‰æ›´</string>
+- <string id="119">æ–°ã—ã„フォルダー</string>
++ <string id="119">æ–°è¦ãƒ•ã‚©ãƒ«ãƒ€ãƒ¼</string>
+ <string id="120">コピーã®ç¢ºèª</string>
+ <string id="121">移動ã®ç¢ºèª</string>
+ <string id="122">削除ã®ç¢ºèª</string>
+- <string id="123">コピーã—ã¾ã™ã‹ï¼Ÿ</string>
+- <string id="124">移動ã—ã¾ã™ã‹ï¼Ÿ</string>
+- <string id="125">削除ã—ã¾ã™ã‹ï¼Ÿ</string>
++ <string id="123">コピーã—ã¾ã™ã‹?</string>
++ <string id="124">移動ã—ã¾ã™ã‹?</string>
++ <string id="125">削除ã—ã¾ã™ã‹?</string>
+ <string id="126">ステータス</string>
+ <string id="127">オブジェクト</string>
+ <string id="128">一般</string>
+@@ -84,8 +127,8 @@
+ <string id="146">タイプ:</string>
+ <string id="147">固定</string>
+ <string id="148">DHCP</string>
+- <string id="149">MACアドレス</string>
+- <string id="150">IPアドレス</string>
++ <string id="149">MAC アドレス</string>
++ <string id="150">IP アドレス</string>
+ <string id="151">リンク: </string>
+ <string id="152">åŠäºŒé‡</string>
+ <string id="153">全二é‡</string>
+@@ -97,92 +140,110 @@
+ <string id="159">リンクã—ã¦ã„ã¾ã›ã‚“</string>
+ <string id="160">空ã</string>
+ <string id="161">利用ä¸å¯</string>
+- <string id="162">トレーã¯é–‹ã„ã¦ã„ã¾ã™</string>
++ <string id="162">トレーãŒé–‹ã„ã¦ã„ã¾ã™</string>
+ <string id="163">読ã¿è¾¼ã¿ä¸­...</string>
+ <string id="164">ディスク無ã—</string>
+ <string id="165">ディスク有り</string>
+ <string id="166">スキン</string>
+- <string id="169">映åƒæ–¹å¼ãƒ»ã‚µã‚¤ã‚º</string>
+- <string id="170">
+- </string>
++
++ <string id="169">ç”»é¢è§£åƒåº¦</string>
++ <string id="170">ビデオã«åˆã‚ã›ã¦ãƒªãƒ•ãƒ¬ãƒƒã‚·ãƒ¥ãƒ¬ãƒ¼ãƒˆã‚’調整 </string>
++
+ <string id="172">発売日</string>
+- <string id="175">トーン</string>
++ <string id="173">アスペクト比 4:3 ã®ãƒ“デオã®å†ç”Ÿ</string>
++
++ <string id="175">ムード</string>
+ <string id="176">スタイル</string>
++
+ <string id="179">曲</string>
+ <string id="180">é•·ã•</string>
+ <string id="181">アルãƒãƒ ã®é¸æŠž</string>
+ <string id="182">トラック</string>
+- <string id="183">批評</string>
++ <string id="183">レビュー</string>
+ <string id="184">æ›´æ–°</string>
+ <string id="185">アルãƒãƒ ã®æ¤œç´¢</string>
+ <string id="186">OK</string>
+- <string id="187">アルãƒãƒ ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ï¼</string>
++ <string id="187">アルãƒãƒ ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“!</string>
+ <string id="188">ã™ã¹ã¦é¸æŠž</string>
+- <string id="189">メディア情報ã®ã‚¹ã‚­ãƒ£ãƒ³</string>
++ <string id="189">メディア情報スキャン中</string>
+ <string id="190">ä¿å­˜</string>
+ <string id="191">シャッフル</string>
+- <string id="192">削除</string>
++ <string id="192">クリア</string>
+ <string id="193">スキャン</string>
+ <string id="194">検索中...</string>
+- <string id="196">ビデオã®é¸æŠž:</string>
+- <string id="198">ビデオã®è©³ç´°ã‚’読込中...</string>
++ <string id="195">情報ãŒè¦‹ä»˜ã‹ã‚Šã¾ã›ã‚“!</string>
++ <string id="196">ムービーã®é¸æŠž:</string>
++ <string id="197">%s ã®æƒ…報をå•ã„åˆã‚ã›ä¸­</string>
++ <string id="198">ムービーã®è©³ç´°ã‚’読込中...</string>
++ <string id="199">Web インターフェイス</string>
++
+ <string id="202">公開時コピー</string>
+- <string id="203">概è¦</string>
+- <string id="205">投票çµæžœ</string>
++ <string id="203">ã‚らã™ã˜</string>
++
++ <string id="205">評価</string>
+ <string id="206">出演者</string>
+ <string id="207">ストーリー</string>
+ <string id="208">å†ç”Ÿ</string>
+ <string id="209">次ã¸</string>
+ <string id="210">å‰ã¸</string>
+- <string id="213">UI設定</string>
+- <string id="214">ç”»é¢ã®è£œæ­£</string>
++ <string id="213">UI ã®è¨­å®š...</string>
++ <string id="214">ç”»é¢è£œæ­£...</string>
+ <string id="215">ソフト化</string>
+ <string id="216">拡大</string>
+ <string id="217">ピクセル比</string>
+- <string id="218">DVDドライブ</string>
++ <string id="218">DVD ドライブ</string>
+ <string id="219">ディスクを入れã¦ä¸‹ã•ã„</string>
+ <string id="220">リモート共有</string>
+ <string id="221">ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã«æŽ¥ç¶šã•ã‚Œã¦ã„ã¾ã›ã‚“</string>
+- <string id="222">中止</string>
++ <string id="222">キャンセル</string>
+ <string id="224">表示時間</string>
+- <string id="226">テストパターン</string>
++ <string id="225">表示ä½ç½®ã®ä¸Šä¸‹å¾®èª¿æ•´</string>
++ <string id="226">テストパターン...</string>
++ <string id="227">オーディオ CD トラックåã‚’ freedb.org ã§æ¤œç´¢</string>
+ <string id="228">プレイリストã®ãƒ©ãƒ³ãƒ€ãƒ å†ç”Ÿ</string>
+ <string id="229">HDDåœæ­¢ã¾ã§ã®æ™‚é–“(分)</string>
+ <string id="230">ビデオフィルター</string>
+ <string id="231">ç„¡ã—</string>
+ <string id="232">ãƒã‚¤ãƒ³ãƒˆãƒ•ã‚£ãƒ«ã‚¿</string>
+- <string id="233">ãƒã‚¤ãƒªãƒ‹ã‚¢è£œé–“(シャープ)</string>
+- <string id="234">異方性フィルタリï¾ï½¸ï¾ž(ノーï¾ï¾™)</string>
+- <string id="235">クインカンクス(ソフト)</string>
+- <string id="236">ガウスキュービック(ï¾ï¾žï¾˜-ソフト)</string>
+- <string id="237">縮å°ï¾Œï½¨ï¾™ï¾€ï¾˜ï¾ï½¸ï¾ž</string>
+- <string id="238">拡大フィルタリï¾ï½¸ï¾ž</string>
+- <string id="239">終了時 プレイリストを消去</string>
++ <string id="233">ãƒã‚¤ãƒªãƒ‹ã‚¢è£œé–“(シャープ)</string>
++ <string id="234">異方性フィルタリング(ノーマル)</string>
++ <string id="235">クインカンクス(ソフト)</string>
++ <string id="236">ガウスキュービック(ベリーソフト)</string>
++ <string id="237">縮å°ãƒ•ã‚£ãƒ«ã‚¿ãƒªãƒ³ã‚°</string>
++ <string id="238">拡大フィルタリング</string>
++ <string id="239">終了時ã«ãƒ—レイリストを消去</string>
++ <string id="240">表示モード</string>
++ <string id="241">フルスクリーン #%d</string>
++ <string id="242">ウィンドウ化</string>
++ <string id="243">リフレッシュレート</string>
++ <string id="244">フルスクリーン</string>
++ <string id="245">Sizing: (%i,%i)->(%i,%i) (Zoom x%2.2f) AR:%2.2f:1 (Pixels: %2.2f:1) (VShift: %2.2f)</string>
++
+ <string id="247">スクリプト</string>
+ <string id="248">言語</string>
+- <string id="249">音楽</string>
++ <string id="249">ミュージック</string>
+ <string id="250">視覚エフェクト</string>
+ <string id="251">ディレクトリã®é¸æŠž</string>
+- <string id="252">ã™ã¹ã¦ã®ã‚¹ãƒ”ーカーã¸å‡ºåŠ›</string>
++ <string id="252">ステレオ音声を全スピーカーã‹ã‚‰å‡ºåŠ›</string>
+ <string id="253">ãƒãƒ£ãƒ³ãƒãƒ«æ•°</string>
+- <string id="254">- DTSレシーãƒãƒ¼ã‚’有効化</string>
++ <string id="254">- DTS 対応レシーãƒãƒ¼</string>
+ <string id="255">CDDB</string>
+- <string id="256">CDDB情報をフリーサーãƒãƒ¼ã‹ã‚‰æ¤œç´¢</string>
++ <string id="256">CDDB 情報å–得中</string>
+ <string id="257">エラー</string>
+ <string id="258">タグを読ã¿è¾¼ã‚€</string>
+ <string id="259">開始</string>
+ <string id="260">SHOUTcast</string>
+ <string id="261">ãŠå¾…ã¡ä¸‹ã•ã„...</string>
+ <string id="262">スクリプト出力</string>
+- <string id="263">Webサーãƒãƒ¼ã‚’有効化</string>
++ <string id="263">HTTP 経由ã§ã® XBMC ã®åˆ¶å¾¡ã‚’有効ã«</string>
+ <string id="264">録音</string>
+- <string id="265">録音ã®åœæ­¢</string>
+- <string id="266">並ã¹æ›¿ãˆ: トラック</string>
++ <string id="265">録音åœæ­¢</string>
++ <string id="266">並ã¹æ›¿ãˆ: トラック番å·</string>
+ <string id="267">並ã¹æ›¿ãˆ: 時間</string>
+- <string id="268">並ã¹æ›¿ãˆ: タイトル</string>
+- <string id="269">並ã¹æ›¿ãˆ: アーティスト</string>
+- <string id="270">並ã¹æ›¿ãˆ: アルãƒãƒ </string>
+- <string id="271">トップ100</string>
++ <string id="268">並ã¹æ›¿ãˆ: タイトルå</string>
++ <string id="269">並ã¹æ›¿ãˆ: アーティストå</string>
++ <string id="270">並ã¹æ›¿ãˆ: アルãƒãƒ å</string>
++ <string id="271">トップ 100</string>
+ <string id="272">å·¦å´ä¸Šéƒ¨æž ã®è£œæ­£</string>
+ <string id="273">å³å´ä¸‹éƒ¨æž ã®è£œæ­£</string>
+ <string id="274">字幕ã®ä½ç½®</string>
+@@ -192,30 +253,37 @@
+ <string id="278">正方形ã«ãªã‚‹ã‚ˆã†ã«èª¿æ•´ã—ã¦ä¸‹ã•ã„</string>
+ <string id="279">設定ãŒèª­ã¿è¾¼ã‚ã¾ã›ã‚“</string>
+ <string id="280">設定ã®åˆæœŸåŒ–</string>
+- <string id="281">.xmlファイルをãƒã‚§ãƒƒã‚¯ã—ã¦ä¸‹ã•ã„</string>
+- <string id="282">%iアイテムを発見ã—ã¾ã—ãŸ</string>
++ <string id="281">.xml ファイルをãƒã‚§ãƒƒã‚¯ã—ã¦ä¸‹ã•ã„</string>
++ <string id="282">%i 個見ã¤ã‹ã‚Šã¾ã—ãŸ</string>
+ <string id="283">検索çµæžœ</string>
+ <string id="284">見ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ</string>
++
+ <string id="287">字幕</string>
+ <string id="288">フォント</string>
+ <string id="289">- サイズ:</string>
++ <string id="290">ダイナミックレンジã®åœ§ç¸®</string>
+ <string id="291">ビデオ</string>
+ <string id="292">オーディオ</string>
+- <string id="293">字幕をå‚ç…§ã™ã‚‹</string>
++ <string id="293">字幕をå‚ç…§</string>
+ <string id="294">ブックマークã®ä½œæˆ</string>
+ <string id="296">ブックマークã®å‰Šé™¤</string>
+ <string id="297">音声オフセット</string>
+- <string id="298">Bookmarks</string>
++ <string id="298">ブックマーク</string>
++ <string id="299">- AAC 対応レシーãƒãƒ¼</string>
++ <string id="300">- MP1 対応レシーãƒãƒ¼</string>
++ <string id="301">- MP2 対応レシーãƒãƒ¼</string>
++ <string id="302">- MP3 対応レシーãƒãƒ¼</string>
+ <string id="303">é…延</string>
+ <string id="304">言語</string>
+ <string id="305">有効</string>
+ <string id="306">éžã‚¤ãƒ³ã‚¿ãƒ¼ãƒªãƒ¼ãƒ–</string>
++
+ <string id="312">(0=自動)</string>
+- <string id="313">データベースを更新ã—ã¾ã™</string>
++ <string id="313">データベースを更新中</string>
+ <string id="314">準備中...</string>
+ <string id="315">データベースエラー</string>
+ <string id="316">曲ã®æ¤œç´¢ä¸­...</string>
+- <string id="317">データベースを更新ã—ã¾ã—ãŸ</string>
++ <string id="317">データベースã®æ›´æ–°ãŒå®Œäº†ã—ã¾ã—ãŸ</string>
+ <string id="318">曲ã®æ›´æ–°ä¸­...</string>
+ <string id="319">曲を更新ã§ãã¾ã›ã‚“</string>
+ <string id="320">アーティストã®æ›´æ–°ä¸­...</string>
+@@ -232,18 +300,19 @@
+ <string id="331">データベースã®åœ§ç¸®ä¸­...</string>
+ <string id="332">データベースを圧縮ã§ãã¾ã›ã‚“</string>
+ <string id="333">ライブラリを消去ã—ã¾ã™ã‹ï¼Ÿ</string>
+- <string id="334">データベースã®æ›´æ–°</string>
++ <string id="334">ライブラリã®æ¶ˆåŽ»...</string>
+ <string id="335">実行</string>
+ <string id="336">フレームレートã®èª¿æ•´</string>
+ <string id="337">オーディオ出力</string>
+ <string id="338">アナログ</string>
+- <string id="339">デジタル</string>
+- <string id="340">色々ãªã‚¢ãƒ¼ãƒ†ã‚£ã‚¹ãƒˆ</string>
+- <string id="341">Discã®å†ç”Ÿ</string>
+- <string id="342">映画</string>
++ <string id="339">デジタル(å…‰/åŒè»¸)</string>
++ <string id="340">VA/オムニãƒã‚¹</string>
++ <string id="341">ディスクã®å†ç”Ÿ</string>
++ <string id="342">ムービー</string>
+ <string id="343">フレームレートã®èª¿æ•´</string>
+ <string id="344">俳優</string>
+ <string id="345">公開年</string>
++ <string id="346">ダウンミックス時ã«éŸ³é‡ãƒ–ースト</string>
+ <string id="350">プログラム</string>
+ <string id="351">OFF</string>
+ <string id="352">è–„</string>
+@@ -251,14 +320,14 @@
+ <string id="354">マトリックス</string>
+ <string id="355">スクリーンセーãƒãƒ¼èµ·å‹•æ™‚é–“</string>
+ <string id="356">スクリーンセーãƒãƒ¼ãƒ¢ãƒ¼ãƒ‰</string>
+- <string id="357">シャットダウï¾ã¾ã§ã®æ™‚é–“</string>
++ <string id="357">シャットダウンタイマー</string>
+ <string id="358">ã™ã¹ã¦ã®ã‚¢ãƒ«ãƒãƒ </string>
+ <string id="359">最近追加ã•ã‚ŒãŸã‚¢ãƒ«ãƒãƒ </string>
+ <string id="360">スクリーンセーãƒãƒ¼</string>
+- <string id="361">R.スライドショー</string>
+- <string id="362">スクリーンセーãƒãƒ¼ã®ï¾Œï½ªï½°ï¾„゙レï¾ï¾žï¾™(%)</string>
+- <string id="363">並ã¹æ›¿ãˆ: ファイル</string>
+- <string id="364">- AC3レシーãƒãƒ¼ã‚’有効化</string>
++ <string id="361">ランダムスライドショー</string>
++ <string id="362">スクリーンセーãƒãƒ¼ã®æš—ã•(%)</string>
++ <string id="363">並ã¹æ›¿ãˆ: ファイルå</string>
++ <string id="364">- Dolby Digital (AC3) 対応レシーãƒãƒ¼</string>
+ <string id="365">並ã¹æ›¿ãˆ: åå‰</string>
+ <string id="366">並ã¹æ›¿ãˆ: 公開年</string>
+ <string id="367">並ã¹æ›¿ãˆ: 評価</string>
+@@ -301,7 +370,7 @@
+ <string id="404">風</string>
+ <string id="405">çµéœ²ç‚¹</string>
+ <string id="406">湿度</string>
+- <string id="409">Defaults</string>
++ <string id="409">デフォルト</string>
+ <string id="410">天気予報サービスã«æŽ¥ç¶šä¸­</string>
+ <string id="411">天気をå–得中:</string>
+ <string id="412">天気をå–å¾—ã§ãã¾ã›ã‚“</string>
+@@ -310,18 +379,24 @@
+ <string id="415">サムãƒã‚¤ãƒ«ã®å–得中...</string>
+ <string id="416">利用ã§ãã¾ã›ã‚“</string>
+ <string id="417">表示: アイコン(大)</string>
++ <string id="418">最低気温</string>
++ <string id="419">最高気温</string>
++ <string id="420">HDMI</string>
++
+ <string id="422">アルãƒãƒ æƒ…å ±ã®å‰Šé™¤</string>
+- <string id="423">CDDB情報ã®å‰Šé™¤</string>
++ <string id="423">CDDB 情報ã®å‰Šé™¤</string>
+ <string id="424">é¸æŠž</string>
+ <string id="425">アルãƒãƒ æƒ…å ±ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“</string>
+- <string id="426">CDDB情報ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“</string>
++ <string id="426">CDDB 情報ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“</string>
+ <string id="427">ディスク</string>
+- <string id="428">æ­£ã—ã„CD/DVDを入れã¦ä¸‹ã•ã„</string>
+- <string id="429">次ã®CD/DVDを挿入ã—ã¦ä¸‹ã•ã„</string>
++ <string id="428">æ­£ã—ã„ CD/DVD を入れã¦ä¸‹ã•ã„</string>
++ <string id="429">次㮠CD/DVD を挿入ã—ã¦ä¸‹ã•ã„:</string>
+ <string id="430">並ã¹æ›¿ãˆ: DVD#</string>
+ <string id="431">キャッシュã—ãªã„</string>
+- <string id="432">ライブラリã‹ã‚‰æ˜ ç”»ã‚’削除</string>
+- <string id="433">Really remove '%s'?</string>
++ <string id="432">ライブラリã‹ã‚‰ãƒ ãƒ¼ãƒ“ーを削除</string>
++ <string id="433">本当㫠'%s' を削除ã—ã¾ã™ã‹?</string>
++ <string id="434">%s ã®é¢¨ %i %s</string> <!--From <wind dir.> at <speed> <unit>-->
++
+ <string id="437">リムーãƒãƒ–ルディスク</string>
+ <string id="438">ファイルを開ã</string>
+ <string id="439">キャッシュ</string>
+@@ -352,31 +427,32 @@
+ <string id="465">コントラスト</string>
+ <string id="466">ガンマ</string>
+ <string id="467">タイプ</string>
+- <string id="468">OSDä½ç½®ã®èª¿ç¯€</string>
+- <string id="469">OSDã®ä½ç½®</string>
++ <string id="468">OSD ä½ç½®ã®èª¿ç¯€</string>
++ <string id="469">OSD ã®ä½ç½®</string>
+ <string id="470">クレジット</string>
+ <string id="471">MODãƒãƒƒãƒ—</string>
+- <string id="474">OFF</string>
+- <string id="475">音楽ã®ã¿</string>
+- <string id="476">音楽 &amp; ビデオ</string>
++ <string id="474">オフ</string>
++ <string id="475">ミュージックã®ã¿</string>
++ <string id="476">ミュージック &amp; ビデオ</string>
+ <string id="477">プレイリストをロードã§ãã¾ã›ã‚“</string>
+ <string id="478">OSD</string>
+- <string id="479">スキン&言語</string>
++ <string id="479">スキン &amp; 言語</string>
+ <string id="480">外観</string>
+- <string id="481">オーディオ設定</string>
+- <string id="482">XBMCã«ã¤ã„ã¦</string>
++ <string id="481">オーディオ設定</string>
++ <string id="482">XBMC ã«ã¤ã„ã¦</string>
++
+ <string id="485">アルãƒãƒ ã®å‰Šé™¤</string>
+ <string id="486">リピート</string>
+ <string id="487">個別リピート</string>
+ <string id="488">フォルダーã”ã¨ã®ãƒªãƒ”ート</string>
+ <string id="489">次ã®ã‚¢ã‚¤ãƒ†ãƒ ã‚’自動å†ç”Ÿ</string>
+- <string id="491">- アイコン大ã®ä½¿ç”¨</string>
++ <string id="491">- アイコン(大)ã®ä½¿ç”¨</string>
+ <string id="492">字幕ã®å¤§ãã•</string>
+- <string id="493">高度ãªè¨­å®š (エキスパート専用)</string>
++ <string id="493">高度ãªè¨­å®š (エキスパート専用!)</string>
+ <string id="494">全体ã®éŸ³é‡(dB)</string>
+- <string id="495">ビデオをGUIã®è§£åƒåº¦ã«åˆã‚ã›ã‚‹</string>
++ <string id="495">ビデオを GUI ã®è§£åƒåº¦ã«åˆã‚ã›ã‚‹</string>
+ <string id="496">設定</string>
+- <string id="497">æ‹¡å¼µå­ã‚’éš ã™</string>
++ <string id="497">æ‹¡å¼µå­ã‚’表示ã™ã‚‹</string>
+ <string id="498">並ã¹æ›¿ãˆ: タイプ</string>
+ <string id="499">オンラインサービスã«æŽ¥ç¶šã§ãã¾ã›ã‚“</string>
+ <string id="500">アルãƒãƒ æƒ…å ±ã®å–å¾—ã«å¤±æ•—ã—ã¾ã—ãŸ</string>
+@@ -389,12 +465,14 @@
+ <string id="510">視覚エフェクトを有効化</string>
+ <string id="511">ビデオモードを切り替ãˆã‚’有効化</string>
+ <string id="512">èµ·å‹•ç”»é¢</string>
+- <string id="513">メニュー画é¢</string>
++ <string id="513">ホーム画é¢</string>
+ <string id="514">手動設定</string>
+ <string id="515">ジャンル</string>
++
+ <string id="517">最近å†ç”Ÿã•ã‚ŒãŸã‚¢ãƒ«ãƒãƒ </string>
+ <string id="518">実行</string>
+ <string id="519">Launch in...</string>
++
+ <string id="521">コンピレーション</string>
+ <string id="522">ã“ã®ã‚½ãƒ¼ã‚¹ã‚’削除</string>
+ <string id="523">メディア切り替ãˆ</string>
+@@ -403,25 +481,26 @@
+ <string id="526">プレイリストã«è¿½åŠ </string>
+ <string id="527">手動ã§ãƒ©ã‚¤ãƒ–ラリã«è¿½åŠ </string>
+ <string id="528">タイトルを入力</string>
+- <string id="529">Error: Duplicate title</string>
++ <string id="529">エラー: タイトルãŒé‡è¤‡ã—ã¦ã„ã¾ã™</string>
+ <string id="530">ジャンルをé¸æŠž</string>
+- <string id="531">New Genre</string>
+- <string id="532">Manual Addition</string>
+- <string id="533">Enter Genre</string>
++ <string id="531">æ–°è¦ã‚¸ãƒ£ãƒ³ãƒ«</string>
++ <string id="532">手動ã§è¿½åŠ </string>
++ <string id="533">ジャンルåを入力</string>
+ <string id="534">表示: %s</string>
+- <string id="535">List</string>
++ <string id="535">リスト</string>
+ <string id="536">アイコン</string>
+- <string id="537">Big List</string>
+- <string id="538">Big Icons</string>
+- <string id="539">Wide</string>
+- <string id="540">Big Wide</string>
+- <string id="541">Album Icons</string>
+- <string id="542">DVD Icons</string>
++ <string id="537">リスト(大)</string>
++ <string id="538">アイコン(大)</string>
++ <string id="539">ワイド</string>
++ <string id="540">超ワイド</string>
++ <string id="541">アルãƒãƒ ã‚¢ã‚¤ã‚³ãƒ³</string>
++ <string id="542">DVD アイコン</string>
+ <string id="543">DVD</string>
+- <string id="544">Media Info</string>
++ <string id="544">メディア情報</string>
+ <string id="545">オーディオ出力デãƒã‚¤ã‚¹</string>
+- <string id="546">Passthrough Output Device</string>
+- <string id="547">No biography for this artist</string>
++ <string id="546">パススルー出力デãƒã‚¤ã‚¹</string>
++ <string id="547">ã“ã®ã‚¢ãƒ¼ãƒ†ã‚£ã‚¹ãƒˆã®ãƒã‚¤ã‚ªã‚°ãƒ©ãƒ•ã‚£ãƒ¼ãŒã‚ã‚Šã¾ã›ã‚“</string>
++
+ <string id="550">並ã¹æ›¿ãˆ: %s</string>
+ <string id="551">åå‰</string>
+ <string id="552">日付</string>
+@@ -436,26 +515,28 @@
+ <string id="561">ファイル</string>
+ <string id="562">公開年</string>
+ <string id="563">評価</string>
+- <string id="564">Type</string>
+- <string id="565">Usage</string>
+- <string id="566">Album Artist</string>
++ <string id="564">タイプ</string>
++ <string id="565">回数</string>
++ <string id="566">アルãƒãƒ ã‚¢ãƒ¼ãƒ†ã‚£ã‚¹ãƒˆ</string>
+ <string id="567">å†ç”Ÿå›žæ•°</string>
+ <string id="568">最後ã«å†ç”Ÿã—ãŸæ—¥</string>
+- <string id="569">Comment</string>
++ <string id="569">コメント</string>
+ <string id="570">追加日</string>
+- <string id="571">Default</string>
++ <string id="571">ã§ãƒ•ã‚©ãƒ«ãƒˆ</string>
+ <string id="572">スタジオ</string>
+ <string id="573">パス</string>
+ <string id="575">処ç†ä¸­</string>
++ <string id="576">å†ç”Ÿå›žæ•°</string>
++
+ <string id="580">並ã¹æ›¿ãˆæ–¹å‘</string>
+ <string id="581">並ã¹æ›¿ãˆæ–¹æ³•</string>
+ <string id="582">表示モード</string>
+- <string id="583">Remember views for different folders</string>
++ <string id="583">ä»–ã®ãƒ•ã‚©ãƒ«ãƒ€ã§ã‚‚ã“ã®ãƒ“ューを使用</string>
+ <string id="584">昇順</string>
+ <string id="585">é™é †</string>
+ <string id="586">プレイリストを編集</string>
+ <string id="587">フィルター</string>
+- <string id="588">Cancel Party Mode</string>
++ <string id="588">パーティーモードをキャンセル</string>
+ <string id="589">パーティーモード</string>
+ <string id="590">ランダム</string>
+ <string id="591">オフ</string>
+@@ -465,18 +546,23 @@
+ <string id="595">リピート: オフ</string>
+ <string id="596">リピート: 1項目</string>
+ <string id="597">リピート: ã™ã¹ã¦</string>
+- <string id="600">CD録音</string>
+- <string id="601">ミディアム(低音質)</string>
+- <string id="602">スタンダード(中音質)</string>
+- <string id="603">エクストリーム(高音質)</string>
++
++ <string id="600">オーディオ CD ã®èª­ã¿è¾¼ã¿</string>
++ <string id="601">ミディアム (低音質)</string>
++ <string id="602">スタンダード (中音質)</string>
++ <string id="603">エクストリーム (高音質)</string>
+ <string id="604">固定ビットレート</string>
+ <string id="605">リッピング中...</string>
++
+ <string id="607">ä¿å­˜å…ˆ:</string>
+- <string id="608">CD or Trackを録音ã§ãã¾ã›ã‚“</string>
+- <string id="609">CDDARipパスãŒè¨­å®šã•ã‚Œã¦ã„ã¾ã›ã‚“</string>
+- <string id="610">Rip Audio Track</string>
+- <string id="611">Enter number</string>
+- <string id="620">CDリッピング</string>
++ <string id="608">CD ã¾ãŸã¯æ¥½æ›²ã‚’読ã¿è¾¼ã‚ã¾ã›ã‚“ã§ã—ãŸ</string>
++ <string id="609">CDDARip パスãŒè¨­å®šã•ã‚Œã¦ã„ã¾ã›ã‚“</string>
++ <string id="610">オーディオトラックを読ã¿è¾¼ã‚€</string>
++ <string id="611">番å·ã‚’入力</string>
++ <string id="612">ビットレート/サンプルレート</string>
++ <string id="613">サンプリング周波数</string>
++
++ <string id="620">オーディオ CD</string>
+ <string id="621">エンコーダー</string>
+ <string id="622">å“質</string>
+ <string id="623">ビットレート</string>
+@@ -494,11 +580,11 @@
+ <string id="638">モード</string>
+ <string id="639">トラックレベル使用</string>
+ <string id="640">アルãƒãƒ ãƒ¬ãƒ™ãƒ«ä½¿ç”¨</string>
+- <string id="641">プリアï¾ï¾Œï¾Ÿï¾šï¾ï¾žï¾™ - å¹³å‡åŒ–ã—ãŸï¾Œï½§ï½²ï¾™ã‚’基準</string>
+- <string id="642">プリアï¾ï¾Œï¾Ÿï¾šï¾ï¾žï¾™ - å¹³å‡åŒ–ファイルを基準ã«ã—ãªã„</string>
+- <string id="643">å¹³å‡åŒ–ã—ãŸï¾Œï½§ï½²ï¾™ã®ï½¸ï¾˜ï½¯ï¾‹ï¾Ÿï¾ï½¸ï¾žã‚’é¿ã‘ã‚‹</string>
+- <string id="644">クロップ ブラック バー</string>
+- <string id="645">大ããªï¾Œï½§ï½²ï¾™ã‚’解å‡ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚続ã‘ã¾ã™ã‹?</string>
++ <string id="641">プリアンプレベル - å¹³å‡åŒ–ã—ãŸãƒ•ã‚¡ã‚¤ãƒ«ã‚’基準</string>
++ <string id="642">プリアンプレベル - å¹³å‡åŒ–ファイルを基準ã«ã—ãªã„</string>
++ <string id="643">å¹³å‡åŒ–ã—ãŸãƒ•ã‚¡ã‚¤ãƒ«ã®ã‚¯ãƒªãƒƒãƒ”ングをé¿ã‘ã‚‹</string>
++ <string id="644">黒帯部分をトリミングã™ã‚‹</string>
++ <string id="645">大ããªãƒ•ã‚¡ã‚¤ãƒ«ã‚’解å‡ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚続ã‘ã¾ã™ã‹?</string>
+ <string id="646">ライブラリã‹ã‚‰å‰Šé™¤</string>
+ <string id="647">ビデオライブラリをエクスãƒãƒ¼ãƒˆ</string>
+ <string id="648">ビデオライブラリをインãƒãƒ¼ãƒˆ</string>
+@@ -507,42 +593,52 @@
+ <string id="651">ライブラリをå‚ç…§ã™ã‚‹</string>
+ <string id="652">製作年</string>
+ <string id="653">ライブラリを更新</string>
+- <string id="654">Show Debug Info</string>
+- <string id="655">Browse for Executable</string>
+- <string id="656">プレイリストをå‚ç…§ã™ã‚‹</string>
+- <string id="657">フォルダーをå‚ç…§ã™ã‚‹</string>
++ <string id="654">デãƒãƒƒã‚°æƒ…報を表示</string>
++ <string id="655">実行ファイルをå‚ç…§</string>
++ <string id="656">プレイリストをå‚ç…§</string>
++ <string id="657">フォルダーをå‚ç…§</string>
+ <string id="658">楽曲情報</string>
+- <string id="660">Volume Amplification</string>
+- <string id="661">Choose Export Folder</string>
++ <string id="659">ノンリニアズーム</string>
++
++ <string id="660">ボリューム増幅</string>
++ <string id="661">エクスãƒãƒ¼ãƒˆãƒ•ã‚©ãƒ«ãƒ€ã®é¸æŠž</string>
+ <string id="662">ã“ã®ãƒ•ã‚¡ã‚¤ãƒ«ã¯å­˜åœ¨ã—ã¾ã›ã‚“。</string>
+- <string id="663">ライブラリã‹ã‚‰å‰Šé™¤ã—ã¾ã™ã‹ï¼Ÿ</string>
++ <string id="663">ライブラリã‹ã‚‰å‰Šé™¤ã—ã¾ã™ã‹?</string>
+ <string id="664">スクリプトをå‚ç…§ã™ã‚‹</string>
++ <string id="665">圧縮レベル</string>
++
+ <string id="700">ライブラリã®æ•´ç†</string>
+ <string id="701">ライブラリã‹ã‚‰å¤ã„曲を削除ã—ã¾ã™</string>
+ <string id="702">ã“ã®ãƒ‘スã¯ä»¥å‰ã«ã‚¹ã‚­ãƒ£ãƒ³ã—ã¾ã—ãŸ</string>
+ <string id="705">ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯</string>
+- <string id="706">- HTTPプロキシ ホスト</string>
+- <string id="708">HTTPプロキシを使用</string>
+- <string id="711">インターãƒãƒƒãƒˆ プロトコル(IP)</string>
+- <string id="712">無効ãªãƒãƒ¼ãƒˆã§ã™ã€‚1ã‹ã‚‰65535ã®é–“ã§è¨­å®šã—ã¦ä¸‹ã•ã„</string>
+- <string id="713">HTTPプロキシ</string>
+- <string id="715">接続方å¼</string>
++ <string id="706">- サーãƒ</string>
++
++ <string id="708">HTTP プロキシを使用</string>
++
++ <string id="711">インターãƒãƒƒãƒˆ プロトコル (IP)</string>
++ <string id="712">無効ãªãƒãƒ¼ãƒˆã§ã™ã€‚1ã‹ã‚‰65535ã®é–“ã§è¨­å®šã—ã¦ä¸‹ã•ã„。</string>
++ <string id="713">HTTP プロキシ</string>
++
++ <string id="715">- 接続方å¼</string>
+ <string id="716">自動 (DHCP)</string>
+ <string id="717">手動 (固定)</string>
++
+ <string id="719">- IPアドレス</string>
+ <string id="720">- ãƒãƒƒãƒˆãƒžã‚¹ã‚¯</string>
+ <string id="721">- デフォルト ゲートウェイ</string>
+ <string id="722">- DNSサーãƒãƒ¼</string>
+ <string id="723">設定ã®ä¿å­˜ &amp; å†èµ·å‹•</string>
+- <string id="724">無効ãªï½±ï¾„゙レスã§ã™ã€‚AAA.BBB.CCC.DDDã®æ§˜ã«è¨˜è¿°ã—ã¦ä¸‹ã•ã„</string>
+- <string id="725">0ã‹ã‚‰255ã®é–“ã‹ã‚‰é¸ã‚“ã§ä¸‹ã•ã„</string>
+- <string id="726">変更ã¯ä¿å­˜ã•ã‚Œã¾ã›ã‚“ 本当ã«ã‚ˆã‚ã—ã„ã§ã™ã‹ï¼Ÿ</string>
+- <string id="727">Webサーãƒãƒ¼</string>
+- <string id="728">FTPサーãƒãƒ¼</string>
++ <string id="724">無効ãªã‚¢ãƒ‰ãƒ¬ã‚¹ã§ã™ã€‚AAA.BBB.CCC.DDD ã®ã‚ˆã†ã«è¨˜è¿°ã—ã¦ãã ã•ã„。</string>
++ <string id="725">0 ã‹ã‚‰ 255 ã®é–“ã‹ã‚‰é¸ã‚“ã§ä¸‹ã•ã„</string>
++ <string id="726">変更ã¯ä¿å­˜ã•ã‚Œã¾ã›ã‚“ 本当ã«ã‚ˆã‚ã—ã„ã§ã™ã‹?</string>
++ <string id="727">Web サーãƒãƒ¼</string>
++ <string id="728">FTP サーãƒãƒ¼</string>
++
+ <string id="730">- ãƒãƒ¼ãƒˆ</string>
++
+ <string id="732">設定ã®ä¿å­˜</string>
+ <string id="733">- パスワード</string>
+- <string id="734">No Pass</string>
++ <string id="734">パスワードãªã—</string>
+ <string id="735">- 文字コード</string>
+ <string id="736">- スタイル:</string>
+ <string id="737">- カラー:</string>
+@@ -555,15 +651,17 @@
+ <string id="744">ファイル</string>
+ <string id="745">スキャン情報ã¯ã‚ã‚Šã¾ã›ã‚“ã§ã—ãŸ</string>
+ <string id="746">ライブラリモードを終了ã—ã¦ä¸‹ã•ã„</string>
+- <string id="747">イメージロードã§ãã¾ã›ã‚“</string>
++ <string id="747">イメージã®èª­ã¿è¾¼ã¿ã«å¤±æ•—</string>
+ <string id="748">パスã®ç·¨é›†</string>
+ <string id="749">Mirror Image</string>
+- <string id="750">よã‚ã—ã„ã§ã™ã‹ï¼Ÿ</string>
+- <string id="751">Removing Source</string>
++ <string id="750">よã‚ã—ã„ã§ã™ã‹?</string>
++ <string id="751">ソースã®å‰Šé™¤</string>
++
+ <string id="754">プログラムリンクã®è¿½åŠ </string>
+ <string id="755">プログラムパスã®ç·¨é›†</string>
+ <string id="756">プログラムåã®ç·¨é›†</string>
+ <string id="757">パスã®éšŽå±¤ã®ç·¨é›†</string>
++
+ <string id="759">表示: リスト(大)</string>
+ <string id="760">黄色</string>
+ <string id="761">白色</string>
+@@ -573,106 +671,138 @@
+ <string id="765">é’緑色</string>
+ <string id="766">Reserved</string>
+ <string id="767">Reserved</string>
++ <!-- strings 768 and 769 reserved for more subtitle colors -->
++
+ <string id="770">エラー %i: 利用ä¸å¯</string>
+ <string id="772">オーディオãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢</string>
+ <string id="773">シーク中</string>
+ <string id="774">スライドショーフォルダー</string>
+ <string id="775">ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚¤ãƒ³ã‚¿ãƒ¼ãƒ•ã‚§ãƒ¼ã‚¹</string>
+- <string id="776">-ワイヤレスãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯å(SSID)</string>
++ <string id="776">- ワイヤレスãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯å(ESSID)</string>
++ <string id="777">- ワイヤレスパスワード</string>
++ <string id="778">- ワイヤレスセキュリティ</string>
+ <string id="779">ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚¤ãƒ³ã‚¿ãƒ¼ãƒ•ã‚§ãƒ¼ã‚¹è¨­å®šã‚’ä¿å­˜ã—ã¦é©ç”¨</string>
+ <string id="784">ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚¤ãƒ³ã‚¿ãƒ¼ãƒ•ã‚§ãƒ¼ã‚¹è¨­å®šã‚’é©ç”¨ã—ã¦ã„ã¾ã™ã€‚ãŠå¾…ã¡ä¸‹ã•ã„。</string>
+- <string id="791">システム上ã®ãƒ—ログラムã«ã‚ˆã‚‹XBMCã®åˆ¶å¾¡ã‚’許å¯</string>
++ <string id="791">システム上ã®ãƒ—ログラムã«ã‚ˆã‚‹ XBMC ã®åˆ¶å¾¡ã‚’許å¯</string>
+ <string id="792">ãƒãƒ¼ãƒˆ</string>
+- <string id="793">ãƒãƒ¼ãƒˆãƒ¬ãƒ³ã‚¸</string>
+- <string id="794">別ã®ã‚·ã‚¹ãƒ†ãƒ ä¸Šã®ãƒ—ログラムã«ã‚ˆã‚‹XBMCã®åˆ¶å¾¡ã‚’許å¯</string>
++ <string id="793">ãƒãƒ¼ãƒˆç•ªå·ã®ç¯„囲</string>
++ <string id="794">別ã®ã‚·ã‚¹ãƒ†ãƒ ä¸Šã®ãƒ—ログラムã«ã‚ˆã‚‹ XBMC ã®åˆ¶å¾¡ã‚’許å¯</string>
+ <string id="795">Initial Repeat Delay (ms)</string>
+ <string id="796">Continuous Repeat Delay (ms)</string>
+ <string id="797">Maximum Number of Clients</string>
+ <string id="798">インターãƒãƒƒãƒˆæŽ¥ç¶š</string>
++
+ <string id="850">誤ã£ãŸãƒãƒ¼ãƒˆç•ªå·ãŒå…¥åŠ›ã•ã‚Œã¾ã—ãŸ</string>
++ <string id="851">ãƒãƒ¼ãƒˆç•ªå·ã¯ 1-65535 ã®é–“ã‹ã‚‰é¸ã‚“ã§ä¸‹ã•ã„</string>
++ <string id="852">ãƒãƒ¼ãƒˆç•ªå·ã¯ 1024-65535 ã®é–“ã‹ã‚‰é¸ã‚“ã§ä¸‹ã•ã„</string>
++
++ <string id="998">ミュージックを追加...</string>
++ <string id="999">ビデオを追加...</string>
+ <string id="1000">- プレビュー</string>
+ <string id="1001">接続ã§ãã¾ã›ã‚“</string>
+- <string id="1002">XBMC was unable to connect to the network location.</string>
+- <string id="1003">This could be due to the network not being connected.</string>
+- <string id="1004">Would you like to add it anyway?</string>
+- <string id="1006">IP Address</string>
++ <string id="1002">XBMC ã¯ã“ã®ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ä¸Šã®å ´æ‰€ã«æŽ¥ç¶šã§ãã¾ã›ã‚“ã§ã—ãŸã€‚</string>
++ <string id="1003">ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã«ç¹‹ãŒã£ã¦ã„ãªã„å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚</string>
++ <string id="1004">ãã‚Œã§ã‚‚追加ã—ã¾ã™ã‹?</string>
++
++ <string id="1006">IP アドレス</string>
+ <string id="1007">ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ä¸Šã®å ´æ‰€ã‚’追加</string>
+- <string id="1008">Protocol</string>
+- <string id="1009">Server Address</string>
+- <string id="1010">Server Name</string>
+- <string id="1011">Remote Path</string>
+- <string id="1012">Shared Folder</string>
++ <string id="1008">プロトコル</string>
++ <string id="1009">サーãƒã®ã‚¢ãƒ‰ãƒ¬ã‚¹</string>
++ <string id="1010">サーãƒå</string>
++ <string id="1011">リモートパス</string>
++ <string id="1012">共有フォルダ</string>
+ <string id="1013">ãƒãƒ¼ãƒˆ</string>
+ <string id="1014">ユーザーå</string>
+- <string id="1015">ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚µãƒ¼ãƒã‚’å‚ç…§ã™ã‚‹</string>
+- <string id="1016">Enter the network address of the server</string>
+- <string id="1017">Enter the path on the server</string>
+- <string id="1018">Enter the port number</string>
+- <string id="1019">Enter the username</string>
+- <string id="1020">Add %s Source</string>
+- <string id="1021">メディアロケーションã®ãƒ‘スを入力ã™ã‚‹ã‹ã€å‚ç…§ã—ã¦ãã ã•ã„。</string>
+- <string id="1022">メディアソースã®åå‰ã‚’入力ã—ã¦ãã ã•ã„。</string>
+- <string id="1023">Browse for new share</string>
++ <string id="1015">ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚µãƒ¼ãƒã‚’å‚ç…§</string>
++ <string id="1016">サーãƒã®ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚¢ãƒ‰ãƒ¬ã‚¹ã‚’入力</string>
++ <string id="1017">サーãƒä¸Šã®ãƒ‘スを入力</string>
++ <string id="1018">ãƒãƒ¼ãƒˆç•ªå·ã‚’入力</string>
++ <string id="1019">ユーザåを入力</string>
++ <string id="1020">%s ソースã®è¿½åŠ </string>
++ <string id="1021">メディアã®ãƒ‘スをå‚ç…§ã™ã‚‹ã‹å…¥åŠ›ã—ã¦ãã ã•ã„。</string>
++ <string id="1022">ソースã®åå‰ã‚’入力ã—ã¦ãã ã•ã„。</string>
++ <string id="1023">æ–°ã—ã„共有ã®å‚ç…§</string>
+ <string id="1024">å‚ç…§</string>
+- <string id="1025">Could not retrieve directory information.</string>
+- <string id="1026">ソース追加</string>
++ <string id="1025">フォルダ情報ã®å–å¾—ã«å¤±æ•—</string>
++ <string id="1026">ソースを追加</string>
+ <string id="1027">ソースを編集</string>
+- <string id="1028">Edit %s Source</string>
+- <string id="1029">Enter the new label</string>
+- <string id="1030">イメージをå‚ç…§ã™ã‚‹</string>
+- <string id="1031">イメージフォルダーをå‚ç…§ã™ã‚‹</string>
++ <string id="1028">%s ソースを編集</string>
++ <string id="1029">æ–°ã—ã„ラベルを入力</string>
++ <string id="1030">イメージをå‚ç…§</string>
++ <string id="1031">イメージフォルダをå‚ç…§</string>
+ <string id="1032">ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ä¸Šã®å ´æ‰€ã‚’追加...</string>
+- <string id="1033">ファイルをå‚ç…§ã™ã‚‹</string>
+- <string id="1034">Submenu</string>
+- <string id="1035">Enable Submenu Buttons</string>
++ <string id="1033">ファイルをå‚ç…§</string>
++ <string id="1034">サブメニュー</string>
++ <string id="1035">サブメニューボタンを有効ã«ã™ã‚‹</string>
+ <string id="1036">ãŠæ°—ã«å…¥ã‚Š</string>
+ <string id="1037">ビデオ アドオン</string>
+ <string id="1038">ミュージック アドオン</string>
+ <string id="1039">ピクãƒãƒ£ アドオン</string>
+- <string id="1040">Loading directory</string>
+- <string id="1041">Retrieved %i items</string>
+- <string id="1042">Retrieved %i of %i items</string>
++ <string id="1040">フォルダã®èª­ã¿è¾¼ã¿ä¸­</string>
++ <string id="1041">%i アイテムをå–å¾—</string>
++ <string id="1042">%i アイテム (å…¨ %i アイテム中) ã‚’å–å¾—</string>
+ <string id="1043">プログラム アドオン</string>
+ <string id="1044">プラグインサムãƒã‚¤ãƒ«æŒ‡å®š</string>
+ <string id="1045">アドオン設定</string>
++ <string id="1046">アクセスãƒã‚¤ãƒ³ãƒˆ</string>
++ <string id="1047">ãã®ä»–...</string>
++ <string id="1048">- ユーザå</string>
++ <string id="1049">スクリプト設定</string>
+ <string id="1050">シングル</string>
++ <string id="1051">Web ã®ã‚¢ãƒ‰ãƒ¬ã‚¹ã‚’入力</string>
++
+ <string id="1200">SMB シェア</string>
+ <string id="1202">SMB ワークグループ</string>
+ <string id="1203">SMB ユーザーå</string>
+- <string id="1204">SMB パスワード</string>
+- <string id="1207">SMB WINSサーãƒãƒ¼</string>
+- <string id="1208">Mount SMB Shares</string>
++ <string id="1204">SMB パスワード</string>
++
++ <string id="1207">WINSサーãƒãƒ¼</string>
++ <string id="1208">SMB 共有ã®ãƒžã‚¦ãƒ³ãƒˆ</string>
++
+ <string id="1210">削除</string>
+ <string id="1211">ミュージック</string>
+ <string id="1212">ビデオ</string>
+ <string id="1213">ピクãƒãƒ£</string>
+ <string id="1214">ファイル</string>
+- <string id="1215">ï¾ï½­ï½°ï½¼ï¾žï½¯ï½¸ &amp; ビデオ</string>
+- <string id="1216">ï¾ï½­ï½°ï½¼ï¾žï½¯ï½¸ &amp; ピクï¾ï½¬ï½°</string>
+- <string id="1217">ï¾ï½­ï½°ï½¼ï¾žï½¯ï½¸ &amp; ファイル</string>
+- <string id="1218">ビデオ &amp; ピクï¾ï½¬ï½°</string>
+- <string id="1219">ビデオ &amp; ファイル</string>
+- <string id="1220">ピクï¾ï½¬ï½° &amp; ファイル</string>
+- <string id="1221">ï¾ï½­ï½°ï½¼ï¾žï½¯ï½¸ &amp; ビデオ &amp; ピクï¾ï½¬ï½°</string>
+- <string id="1222">ï¾ï½­ï½°ï½¼ï¾žï½¯ï½¸ &amp; ビデオ &amp; ピクï¾ï½¬ï½° &amp; ファイル</string>
++ <string id="1215">ミュージック &amp; ビデオ</string>
++ <string id="1216">ミュージック &amp; ピクãƒãƒ£ãƒ¼</string>
++ <string id="1217">ミュージック &amp; ファイル</string>
++ <string id="1218">ビデオ &amp; ピクãƒãƒ£ãƒ¼</string>
++ <string id="1219">ビデオ &amp; ファイル</string>
++ <string id="1220">ピクãƒãƒ£ãƒ¼ &amp; ファイル</string>
++ <string id="1221">ミュージック &amp; ビデオ &amp; ピクãƒãƒ£ãƒ¼</string>
++ <string id="1222">ミュージック &amp; ビデオ &amp; ピクãƒãƒ£ãƒ¼ &amp; ファイル</string>
+ <string id="1223">無効</string>
+- <string id="1226">ファイル &amp; ï¾ï½­ï½°ï½¼ï¾žï½¯ï½¸ &amp; ビデオ</string>
+- <string id="1227">ファイル &amp; ピクï¾ï½¬ï½° &amp; ï¾ï½­ï½°ï½¼ï¾žï½¯ï½¸</string>
+- <string id="1228">ファイル &amp; ピクï¾ï½¬ï½° &amp; ビデオ</string>
+- <string id="1229">ï¾ï½­ï½°ï½¼ï¾žï½¯ï½¸ &amp; プログラム</string>
+- <string id="1230">ビデオ &amp; プログラム</string>
+- <string id="1231">ピクï¾ï½¬ï½° &amp; プログラム</string>
+- <string id="1232">ï¾ï½­ï½°ï½¼ï¾žï½¯ï½¸ &amp; ビデオ &amp; ピクï¾ï½¬ï½° &amp; プログラム</string>
+- <string id="1233">プログラム &amp; ビデオ &amp; ï¾ï½­ï½°ï½¼ï¾žï½¯ï½¸</string>
+- <string id="1234">プログラム &amp; ピクï¾ï½¬ï½° &amp; ï¾ï½­ï½°ï½¼ï¾žï½¯ï½¸</string>
+- <string id="1235">プログラム &amp; ピクï¾ï½¬ï½° &amp; ビデオ</string>
++ <string id="1226">ファイル &amp; ミュージック &amp; ビデオ</string>
++ <string id="1227">ファイル &amp; ピクãƒãƒ£ãƒ¼ &amp; ミュージック</string>
++ <string id="1228">ファイル &amp; ピクãƒãƒ£ãƒ¼ &amp; ビデオ</string>
++ <string id="1229">ミュージック &amp; プログラム</string>
++ <string id="1230">ビデオ &amp; プログラム</string>
++ <string id="1231">ピクãƒãƒ£ãƒ¼ &amp; プログラム</string>
++ <string id="1232">ミュージック &amp; ビデオ &amp; ピクãƒãƒ£ãƒ¼ &amp; プログラム</string>
++ <string id="1233">プログラム &amp; ビデオ &amp; ミュージック</string>
++ <string id="1234">プログラム &amp; ピクãƒãƒ£ãƒ¼ &amp; ミュージック</string>
++ <string id="1235">プログラム &amp; ピクãƒãƒ£ãƒ¼ &amp; ビデオ</string>
++
+ <string id="1250">自動検出</string>
+- <string id="1251">system 自動検出</string>
++ <string id="1251">システムã«ã‚ˆã‚Šè‡ªå‹•æ¤œå‡º</string>
+ <string id="1252">ニックãƒãƒ¼ãƒ </string>
+- <string id="1254">Ask to Connect</string>
+- <string id="1255">FTPユーザー・パスワードをé€ã‚‹</string>
+- <string id="1256">PINGã‚’ã‹ã‘ã‚‹é–“éš”</string>
+- <string id="1257">Would you like to connect to the Autodetected system?</string>
++
++ <string id="1254">接続を確èªã™ã‚‹</string>
++ <string id="1255">FTP ユーザー・パスワードをé€ã‚‹</string>
++ <string id="1256">PING ã‚’ã‹ã‘ã‚‹é–“éš”</string>
++ <string id="1257">自動検出ã•ã‚ŒãŸã‚·ã‚¹ãƒ†ãƒ ã«æŽ¥ç¶šã—ã¾ã™ã‹?</string>
++
++ <string id="1259">Zeroconf</string>
++ <string id="1260">本サービスを Zeroconf 経由ã§ã‚¢ãƒŠã‚¦ãƒ³ã‚¹ã™ã‚‹</string>
++ <string id="1270">XBMC ã® AirPlay コンテンツå—信を許å¯ã™ã‚‹</string>
++ <string id="1271">デãƒã‚¤ã‚¹å</string>
++ <string id="1272">- パスワードä¿è­·ã‚’使ã†</string>
++ <string id="1273">AirPlay</string>
++
++ <string id="1300">カスタムオーディオデãƒã‚¤ã‚¹</string>
++ <string id="1301">カスタムパススルーデãƒã‚¤ã‚¹</string>
++
+ <string id="1396">漂ã†</string>
+ <string id="1397">and</string>
+ <string id="1398">å‡ã£ãŸ</string>
+@@ -690,23 +820,41 @@
+ <string id="1410">ãŠã ã‚„ã‹</string>
+ <string id="1411">with</string>
+ <string id="1412">windy</string>
+- <string id="1413">drizzle</string>
+- <string id="1414">T-Storm</string>
+- <string id="1415">Drizzle</string>
+- <string id="1416">Foggy</string>
++ <string id="1413">å°é›¨</string>
++ <string id="1414">雷雨</string>
++ <string id="1415">å°é›¨</string>
++ <string id="1416">霧</string>
++ <string id="1417">Grains</string>
++ <string id="1418">雷・暴風</string>
++ <string id="1419">雷・大雨</string>
++ <string id="1420">Moderate</string>
++ <string id="1421">Very High</string>
++ <string id="1422">Windy</string>
++ <string id="1423">Mist</string>
++
++ <!-- strings through to 1450 reserved for weather translation -->
++
++ <string id="1450">システムãŒå³ã®æ™‚間以上アイドル状態ãŒç¶šãã¨ç”»é¢ã‚’スリープ</string>
++ <!-- strings through to 1470 reserved for power-saving -->
++
+ <string id="2050">上映時間</string>
+- <string id="4501">LCD Type</string>
++
++ <string id="2100">スクリプトãŒå¤±æ•—! : %s</string>
++ <string id="2101">æ–°ã—ã„ãƒãƒ¼ã‚¸ãƒ§ãƒ³ãŒå¿…è¦ã§ã™ - ログをå‚ç…§</string>
++
++ <string id="4501">LCD/VFDを有効ã«ã™ã‚‹</string>
++
+ <string id="10000">ホーム</string>
+ <string id="10001">プログラム</string>
+ <string id="10002">ピクãƒãƒ£ãƒ¼</string>
+- <string id="10003">ファイル</string>
++ <string id="10003">ファイルマãƒãƒ¼ã‚¸ãƒ£ãƒ¼</string>
+ <string id="10004">設定</string>
+ <string id="10005">ミュージック</string>
+ <string id="10006">ビデオ</string>
+ <string id="10007">システム情報</string>
+ <string id="10008">設定 - 一般</string>
+ <string id="10009">設定 - ç”»é¢</string>
+- <string id="10010">設定 - 外観 - GUI設定</string>
++ <string id="10010">設定 - 外観 - GUI 設定</string>
+ <string id="10011">設定 - ç”»é¢ - スクリーン設定</string>
+ <string id="10012">設定 - ピクãƒãƒ£ãƒ¼</string>
+ <string id="10013">設定 - プログラム</string>
+@@ -714,23 +862,29 @@
+ <string id="10015">設定 - ミュージック</string>
+ <string id="10016">設定 - システム</string>
+ <string id="10017">設定 - ビデオ</string>
+- <string id="10018">設定 - ネットワーク</string>
++ <string id="10018">設定 - ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯</string>
+ <string id="10019">設定 - 外観</string>
+ <string id="10020">スクリプト</string>
+ <string id="10021">Webブラウザ</string>
++ <string id="10025">ビデオ</string>
+ <string id="10028">ビデオ/プレイリスト</string>
++ <string id="10029">ログイン画é¢</string>
+ <string id="10034">設定 - プロファイル</string>
++ <string id="10040">アドオンブラウザー</string>
++
+ <string id="10100">ã¯ã„/ã„ã„㈠ダイアログ</string>
+ <string id="10101">進行ダイアログ</string>
+- <string id="10210">字幕を検索ã—ã¦ã„ã¾ã™...</string>
+- <string id="10211">Caching subtitles...</string>
+- <string id="10212">terminating</string>
++
++ <string id="10210">字幕を検索中...</string>
++ <string id="10211">字幕をキャッシュ中...</string>
++ <string id="10212">終了中</string>
+ <string id="10213">ãƒãƒƒãƒ•ã‚¡ä¸­</string>
+- <string id="10214">Opening stream</string>
++ <string id="10214">ストリームを開ã„ã¦ã„ã„ã¾ã™</string>
++
+ <string id="10500">ミュージック/プレイリスト</string>
+ <string id="10501">ミュージック/ファイルリスト</string>
+ <string id="10502">ミュージック/ライブラリ</string>
+- <string id="10503">Playlist Editor</string>
++ <string id="10503">プレイリストエディター</string>
+ <string id="10504">Top 100 曲</string>
+ <string id="10505">Top 100 アルãƒãƒ </string>
+ <string id="10506">プログラム</string>
+@@ -739,24 +893,30 @@
+ <string id="10509">ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ãƒ»ã‚²ãƒ¼ãƒ </string>
+ <string id="10510">エクステンション</string>
+ <string id="10511">システム情報</string>
++
+ <string id="10516">ミュージック - ライブラリ</string>
+- <string id="10517">ミュージック - å†ç”Ÿä¸­</string>
+- <string id="10522">ビデオ - å†ç”Ÿä¸­</string>
++ <string id="10517">ミュージック - ç¾åœ¨å†ç”Ÿä¸­</string>
++
++ <string id="10522">ビデオ - ç¾åœ¨å†ç”Ÿä¸­</string>
+ <string id="10523">アルãƒãƒ æƒ…å ±</string>
+ <string id="10524">ムービー情報</string>
++
+ <string id="12000">ダイアログã®é¸æŠž</string>
+ <string id="12001">ミュージック/情報</string>
+- <string id="12002">ダイアログOK</string>
++ <string id="12002">ダイアログ OK</string>
+ <string id="12003">ビデオ/情報</string>
+ <string id="12004">スクリプト/情報</string>
+ <string id="12005">フルスクリーンビデオ</string>
+ <string id="12006">オーディオ視覚エフェクト</string>
++
+ <string id="12008">ファイルスタッキング ダイアログ</string>
+- <string id="12009">Rebuild index...</string>
+- <string id="12010">Return to Music Window</string>
+- <string id="12011">Return to Videos Window</string>
++ <string id="12009">インデクスã®å†æ§‹ç¯‰...</string>
++ <string id="12010">ミュージックã®ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ã«æˆ»ã‚‹</string>
++ <string id="12011">ビデオã®ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ã«æˆ»ã‚‹</string>
++
+ <string id="12021">最åˆã‹ã‚‰å†ç”Ÿ</string>
+ <string id="12022">%s ã‹ã‚‰å†é–‹</string>
++
+ <string id="12310">0</string>
+ <string id="12311">1</string>
+ <string id="12312">2</string>
+@@ -770,13 +930,13 @@
+ <string id="12320">c</string>
+ <string id="12321">ok</string>
+ <string id="12322">*</string>
+- <string id="12325">フォルダーロック</string>
++ <string id="12325">ロック中ï¼ã‚³ãƒ¼ãƒ‰ã‚’入力ã—ã¦ãã ã•ã„</string>
+ <string id="12326">パスワード入力</string>
+ <string id="12327">マスターコード入力</string>
+ <string id="12328">アンロックコード入力</string>
+ <string id="12329">C を押ã™ã¨ã‚­ãƒ£ãƒ³ã‚»ãƒ«</string>
+ <string id="12330">ボタンパスワードを入力ã—</string>
+- <string id="12331">Startを押ã—ã¦ãã ã•ã„。Backã§ã‚­ãƒ£ãƒ³ã‚»ãƒ«</string>
++ <string id="12331">Start を押ã—ã¦ãã ã•ã„。Back ã§ã‚­ãƒ£ãƒ³ã‚»ãƒ«</string>
+ <string id="12332">セット ロック</string>
+ <string id="12333">アンロック</string>
+ <string id="12334">リセット ロック</string>
+@@ -790,56 +950,87 @@
+ <string id="12343">リトライ åœæ­¢</string>
+ <string id="12344">パスワードãŒåˆã„ã¾ã›ã‚“</string>
+ <string id="12345">アクセスãŒå¦å®šã•ã‚Œã¾ã™</string>
+- <string id="12346">パスワードリトライã®é™ç•Œã‚’超ãˆã¾ã—ãŸ</string>
+- <string id="12347">Systemã‚’åœæ­¢ã—ã¾ã™</string>
++ <string id="12346">パスワードリトライ回数ã®é™ç•Œã‚’超ãˆã¾ã—ãŸ</string>
++ <string id="12347">システムをåœæ­¢ã—ã¾ã™</string>
+ <string id="12348">アイテム ロック</string>
+ <string id="12353">シェアロックãŒå¾©æ´»ã—ã¾ã™</string>
+ <string id="12356">ロック変更</string>
+ <string id="12357">Source Lock</string>
+- <string id="12358">パスワードãŒæœªè¨˜å…¥ã§ã™ å†ã³è©¦ã¿ã¦ãã ã•ã„</string>
++ <string id="12358">パスワードãŒæœªè¨˜å…¥ã§ã™ å†ã³è©¦ã¿ã¦ãã ã•ã„</string>
+ <string id="12360">マスターロック</string>
+- <string id="12362">Shutdown system if Master Lock retries exceeded</string>
++ <string id="12362">パスワードリトライ回数ã®é™ç•Œã‚’超ãˆãŸã‚‰ã‚·ã‚¹ãƒ†ãƒ ã‚’シャットダウンã™ã‚‹</string>
+ <string id="12367">マスターコードãŒæœ‰åŠ¹ã§ã¯ã‚ã‚Šã¾ã›ã‚“</string>
+- <string id="12368">æ­£ã—ã„ï¾ï½½ï¾€ï½° コードを入力ã—ã¦ä¸‹ã•ã„</string>
+- <string id="12373">Settings &amp; Filemanager</string>
+- <string id="12376">Set as default for all Movies</string>
+- <string id="12377">This will reset any previously saved values</string>
++ <string id="12368">æ­£ã—ã„マスターコードを入力ã—ã¦ä¸‹ã•ã„</string>
++ <string id="12373">設定 &amp; ファイルマãƒãƒ¼ã‚¸ãƒ£ãƒ¼</string>
++ <string id="12376">全ビデオå†ç”Ÿã®ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆå€¤ã«è¨­å®š</string>
++ <string id="12377">éŽåŽ»ã®è¨­å®šå€¤ã¯ãƒªã‚»ãƒƒãƒˆã•ã‚Œã¾ã™</string>
+ <string id="12378">一枚ã®è¡¨ç¤ºæ™‚é–“</string>
+ <string id="12379">パン/ズームエフェクトã®ä½¿ç”¨</string>
+- <string id="12383">12 hour clock</string>
+- <string id="12384">24 hour clock</string>
+- <string id="12385">Day/Month</string>
+- <string id="12386">Month/Day</string>
++
++ <string id="12383">12時間時計</string>
++ <string id="12384">24時間時計</string>
++ <string id="12385">日/月</string>
++ <string id="12386">月/日</string>
++
+ <string id="12390">システム稼動時間</string>
+ <string id="12391">分</string>
+ <string id="12392">時間</string>
+ <string id="12393">æ—¥</string>
+ <string id="12394">åˆè¨ˆç¨¼åƒæ™‚é–“</string>
++ <string id="12395">ãƒãƒƒãƒ†ãƒªãƒ¼ãƒ¬ãƒ™ãƒ«</string>
++
+ <string id="12600">天気予報</string>
++
+ <string id="12900">スクリーンセーãƒãƒ¼</string>
+ <string id="12901">フルスクリーンOSD</string>
++
+ <string id="13000">システム</string>
+- <string id="13001">HDDã®åœæ­¢</string>
++ <string id="13001">HDD ã®åœæ­¢</string>
+ <string id="13002">ビデオã®ã¿</string>
+- <string id="13003">- é…延</string>
+- <string id="13004">- 最å°ï¾Œï½§ï½²ï¾™å†ç”Ÿæ™‚é–“</string>
++ <string id="13003">- åœæ­¢ã¾ã§ã®é…延時間</string>
++ <string id="13004">- ã“ã®æ™‚間より長ã„å†ç”Ÿã¯é…延時間後ã«åœæ­¢</string>
+ <string id="13005">シャットダウン</string>
+- <string id="13008">Default Shutdown mode</string>
+- <string id="13009">Quit</string>
+- <string id="13010">Hibernate</string>
+- <string id="13011">Suspend</string>
+- <string id="13012">Exit</string>
++
++ <string id="13008">シャットダウン機能</string>
++ <string id="13009">終了</string>
++ <string id="13010">ãƒã‚¤ãƒãƒãƒ¼ãƒˆ</string>
++ <string id="13011">サスペンド</string>
++ <string id="13012">終了</string>
+ <string id="13013">å†èµ·å‹•</string>
+ <string id="13014">最å°åŒ–</string>
+- <string id="13021">リムーãƒãƒ–ルHDDをマウントã—ã¾ã—ãŸ</string>
++ <string id="13015">é›»æºãƒœã‚¿ãƒ³ã‚’押ã—ãŸéš›ã®å‹•ä½œ</string>
++ <string id="13016">システム終了・電æºã‚’切る</string>
++
++ <string id="13020">別ã®ã‚»ãƒƒã‚·ãƒ§ãƒ³ãŒå‹•ä½œä¸­ã§ã™ï¼ˆssh 経由ã‹ã‚‚?)</string>
++ <string id="13021">リムーãƒãƒ–ル HDD をマウントã—ã¾ã—ãŸ</string>
+ <string id="13022">安全ã§ãªã„デãƒã‚¤ã‚¹ã®å–り外ã—</string>
+ <string id="13023">デãƒã‚¤ã‚¹ãŒæ­£ã—ã外ã•ã‚Œã¾ã—ãŸ</string>
++ <string id="13024">ジョイスティックãŒæŽ¥ç¶šã•ã‚Œã¾ã—ãŸ</string>
++ <string id="13025">ジョイスティックãŒå¤–ã•ã‚Œã¾ã—ãŸ</string>
++
++ <string id="13050">ãƒãƒƒãƒ†ãƒªæ®‹é‡ãŒå°‘ãªããªã£ã¦ã„ã¾ã™</string>
++
+ <string id="13100">フリッカーフィルタ</string>
+- <string id="13105">Vertical Blank Sync</string>
+- <string id="13106">Disabled</string>
+- <string id="13107">Enabled During Video Playback</string>
+- <string id="13108">Always Enabled</string>
+- <string id="13111">ã“ã®è§£åƒåº¦ã‚’ä¿æŒã—ã¾ã™ã‹ï¼Ÿ</string>
++ <string id="13101">ドライãƒã«ä»»ã›ã‚‹ (å†èµ·å‹•ãŒå¿…è¦)</string>
++ <string id="13105">Vertical Blank ã®åŒæœŸæ–¹æ³•</string>
++ <string id="13106">無効</string>
++ <string id="13107">ビデオå†ç”Ÿä¸­ã®ã¿æœ‰åŠ¹</string>
++ <string id="13108">常ã«æœ‰åŠ¹</string>
++ <string id="13111">ã“ã®è§£åƒåº¦ã‚’ä¿æŒã—ã¾ã™ã‹?</string>
++
++ <string id="13112">高å“質アップコンãƒãƒ¼ãƒˆ</string>
++ <string id="13113">無効</string>
++ <string id="13114">SD 画質ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã«ã®ã¿æœ‰åŠ¹</string>
++ <string id="13115">常ã«æœ‰åŠ¹</string>
++
++ <string id="13116">アップコンãƒãƒ¼ãƒˆã®æ–¹å¼</string>
++ <string id="13117">Bicubic</string>
++ <string id="13118">Lanczos</string>
++ <string id="13119">Sinc</string>
++ <string id="13120">VDPAU</string>
++ <string id="13121">VDPAU HQ Upscaling level</string>
++ <string id="13122">VDPAU Studio level color conversion</string>
++
+ <string id="13140">Active Connections Detected!</string>
+ <string id="13141">If you proceed, you might not be able to control XBMC</string>
+ <string id="13142">any longer. Are you sure you to stop the Event Server?</string>
+@@ -847,75 +1038,74 @@
+ <string id="13145">If you are currently using the Apple Remote to control</string>
+ <string id="13146">XBMC, changing this setting might affect your ability</string>
+ <string id="13147">to continue controlling it. Do you want to proceed?</string>
+- <string id="13150">HDD Key:</string>
+- <string id="13151">HDD Temp:</string>
+- <string id="13152">DVD Model:</string>
+- <string id="13153">DVD Firmware:</string>
+- <string id="13154">HDD Model:</string>
+- <string id="13155">HDD Serial:</string>
+- <string id="13156">HDD Firmware:</string>
+- <string id="13157">HDD Password:</string>
+- <string id="13158">HDD Lock State:</string>
+ <string id="13159">サブãƒãƒƒãƒˆãƒžã‚¹ã‚¯</string>
+ <string id="13160">デフォルトゲートウェイ</string>
+- <string id="13161">プライマリDNS</string>
+- <string id="13162">Initialize failed</string>
+- <string id="13163">GamePad</string>
+- <string id="13164">Keyboard</string>
+- <string id="13165">Mouse</string>
+- <string id="13166">Head/MicroPhone</string>
+- <string id="13167">MemoryStick</string>
+- <string id="13168">IR-Remote</string>
+- <string id="13169">Controller Port</string>
++ <string id="13161">プライマリ DNS</string>
++ <string id="13162">åˆæœŸåŒ–ã«å¤±æ•—</string>
++
+ <string id="13170">Never</string>
+ <string id="13171">Immediately</string>
+ <string id="13172">After %i secs</string>
+ <string id="13173">HDD Install Date:</string>
+ <string id="13174">HDD Power Cycle Count:</string>
++
+ <string id="13200">プロファイル</string>
+- <string id="13201">プロフィール '%s'を削除ã—ã¾ã™ã‹ï¼Ÿ</string>
+- <string id="13204">最後ã«ï¾›ï½°ï¾„゙ã—ãŸï¾Œï¾Ÿï¾›ï¾Œï½¨ï½°ï¾™:</string>
++ <string id="13201">プロファイル '%s' を削除ã—ã¾ã™ã‹?</string>
++
++ <string id="13204">最後ã«ãƒ­ãƒ¼ãƒ‰ã—ãŸãƒ—ロファイル:</string>
+ <string id="13205">ä¸æ˜Ž</string>
+ <string id="13206">上書ã</string>
++
+ <string id="13208">アラーム クロック</string>
+ <string id="13209">アラーム クロック 間隔 (分)</string>
+- <string id="13210">%im 分後ã«ã‚¢ãƒ©ãƒ¼ãƒ é–‹å§‹</string>
++ <string id="13210">%i 分後ã«ã‚¢ãƒ©ãƒ¼ãƒ ãŒä½œå‹•ã—ã¾ã™</string>
+ <string id="13211">アラーム!</string>
+- <string id="13212">%im%is ã§ã‚­ãƒ£ãƒ³ã‚»ãƒ«</string>
++ <string id="13212">残り%i分%i秒ã§ã‚­ãƒ£ãƒ³ã‚»ãƒ«ã•ã‚Œã¾ã—ãŸ</string>
++ <string id="13213">%2.0f分</string> <!--minutes (left from countdown)-->
++ <string id="13214">%2.0f秒</string> <!--seconds (left from countdown)-->
++
+ <string id="13249">RARファイル内ã‹ã‚‰å­—幕を探ã™</string>
+ <string id="13250">字幕をå‚ç…§ã™ã‚‹...</string>
+- <string id="13251">Move Item</string>
+- <string id="13252">Move Item Here</string>
+- <string id="13253">Cancel Move</string>
+- <string id="13270">Hardware:</string>
+- <string id="13271">CPU Usage:</string>
+- <string id="13275">Hard Disk</string>
++ <string id="13251">アイテムã®ç§»å‹•</string>
++ <string id="13252">アイテムをã“ã“ã«ç§»å‹•</string>
++ <string id="13253">移動ã®ã‚­ãƒ£ãƒ³ã‚»ãƒ«</string>
++
++ <string id="13270">ãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢:</string>
++ <string id="13271">CPU 使用率:</string>
++
++ <string id="13274">接続ã—ã¾ã—ãŸãŒã€DNS ãŒã‚ã‚Šã¾ã›ã‚“。</string>
++ <string id="13275">ãƒãƒ¼ãƒ‰ãƒ‡ã‚£ã‚¹ã‚¯</string>
+ <string id="13276">DVD-ROM</string>
+ <string id="13277">ストレージ</string>
+- <string id="13278">Default</string>
++ <string id="13278">デフォルト</string>
+ <string id="13279">ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯</string>
+ <string id="13280">ビデオ</string>
+ <string id="13281">ãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢</string>
++
+ <string id="13283">オペレーティング システム:</string>
+- <string id="13284">CPU速度:</string>
++ <string id="13284">CPU 速度:</string>
++
+ <string id="13286">ビデオエンコーダー:</string>
+ <string id="13287">ç”»é¢è§£åƒåº¦:</string>
+- <string id="13292">A/V Cable:</string>
+- <string id="13294">DVD 地域:</string>
++
++ <string id="13292">A/V ケーブル:</string>
++
++ <string id="13294">DVD リージョン(地域):</string>
+ <string id="13295">インターãƒãƒƒãƒˆ:</string>
+ <string id="13296">接続済ã¿</string>
+ <string id="13297">接続ã•ã‚Œã¦ã„ã¾ã›ã‚“。ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯è¨­å®šã‚’確èªã—ã¦ãã ã•ã„。</string>
+- <string id="13299">- 目標温度</string>
+- <string id="13300">- ファンスピード</string>
++
++ <string id="13299">目標温度</string>
++ <string id="13300">ファンスピード</string>
+ <string id="13301">自動温度コントロール</string>
+- <string id="13302">ファï¾ï½½ï¾‹ï¾Ÿï½°ï¾„゙ コントロール</string>
++ <string id="13302">ファンスピード制御</string>
+ <string id="13303">- フォント</string>
+- <string id="13304">FlippingåŒæ–¹å‘ストリングスを有効化</string>
+- <string id="13305">RSSフィードを有効化</string>
+- <string id="13306">親フォルダーã®é …目を隠ã™</string>
++ <string id="13304">Flipping åŒæ–¹å‘ストリングスを有効化</string>
++ <string id="13305">RSS フィードを有効化</string>
++ <string id="13306">親フォルダーã®é …目を表示ã™ã‚‹</string>
+ <string id="13307">トラック命åテンプレート</string>
+- <string id="13308">Systemをリブートã—ã¾ã™ã‹?</string>
+- <string id="13309">XBMCをリスタートã—ã¾ã™ã‹?</string>
++ <string id="13308">システムをå†èµ·å‹•ã—ã¾ã™ã‹?</string>
++ <string id="13309">XBMC ã®å†èµ·å‹•ã§ã¯ã‚ã‚Šã¾ã›ã‚“</string>
+ <string id="13310">ズーム エフェクト</string>
+ <string id="13311">フロート エフェクト</string>
+ <string id="13312">ブラックãƒãƒ¼ 縮å°</string>
+@@ -929,29 +1119,30 @@
+ <string id="13320">ステレオ</string>
+ <string id="13321">å·¦ã®ã¿</string>
+ <string id="13322">å³ã®ã¿</string>
+- <string id="13323">CD+Gå†ç”Ÿæœ‰åŠ¹åŒ–</string>
++ <string id="13323">CD+G å†ç”Ÿæœ‰åŠ¹åŒ–</string>
+ <string id="13324">ãƒãƒƒã‚¯ã‚°ãƒ©ã‚¦ãƒ³ãƒ‰é€æ˜Ž</string>
+ <string id="13325">フォアグラウンドé€æ˜Ž</string>
+ <string id="13326">A/V é…延</string>
+ <string id="13327">カラオケ</string>
+ <string id="13328">%s ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“</string>
+- <string id="13329">オープン エラー %s</string>
+- <string id="13330">ロードã§ãã¾ã›ã‚“ %s</string>
++ <string id="13329">%s ã‚’é–‹ãéš›ã«ã‚¨ãƒ©ãƒ¼</string>
++ <string id="13330">ï¼…ãŒãƒ­ãƒ¼ãƒ‰ã§ãã¾ã›ã‚“</string>
+ <string id="13331">エラー: メモリーä¸è¶³</string>
+ <string id="13332">上ã¸</string>
+ <string id="13333">下ã¸</string>
+ <string id="13334">ラベル編集</string>
+- <string id="13335">è¦å®šå€¤ä½œæˆ</string>
++ <string id="13335">デフォルトã«è¨­å®š</string>
+ <string id="13336">削除ボタン</string>
++
+ <string id="13340">ç¾çŠ¶</string>
+ <string id="13341">ç·‘</string>
+ <string id="13342">æ©™</string>
+ <string id="13343">赤</string>
+ <string id="13344">サイクル</string>
+- <string id="13345">å†ç”Ÿã§LEDã®ON/OFFã‚’ã™ã‚‹</string>
++ <string id="13345">å†ç”Ÿã§LED ã® ON/OFF ã‚’ã™ã‚‹</string>
+ <string id="13346">映画情報</string>
+ <string id="13347">キュー項目</string>
+- <string id="13348">IMDbを検索...</string>
++ <string id="13348">IMDb を検索...</string>
+ <string id="13349">æ–°ã—ã„コンテンツをスキャン</string>
+ <string id="13350">プレイ中...</string>
+ <string id="13351">アルãƒãƒ æƒ…å ±</string>
+@@ -965,20 +1156,21 @@
+ <string id="13359">アーティストサムãƒã‚¤ãƒ«æŒ‡å®š</string>
+ <string id="13360">サムãƒã‚¤ãƒ«ã‚’自動ã§ä½œæˆ</string>
+ <string id="13361">音声有効化</string>
++
+ <string id="13375">デãƒã‚¤ã‚¹æœ‰åŠ¹åŒ–</string>
+ <string id="13376">ボリューム</string>
+ <string id="13377">デフォルト 表示モード</string>
+ <string id="13378">デフォルト è¼åº¦</string>
+ <string id="13379">デフォルト コントラスト</string>
+ <string id="13380">デフォルト ガンマ</string>
+- <string id="13381">åœæ­¢å±¥æ­´ã‹ã‚‰å†ç”Ÿ</string>
++ <string id="13381">最後ã«åœæ­¢ã—ãŸä½ç½®ã‹ã‚‰ãƒ“デオをå†ç”Ÿ</string>
+ <string id="13382">Voice Mask - Port 1</string>
+ <string id="13383">Voice Mask - Port 2</string>
+ <string id="13384">Voice Mask - Port 3</string>
+ <string id="13385">Voice Mask - Port 4</string>
+ <string id="13386">タイムベースシーク使用</string>
+ <string id="13387">トラックå 詳細情報 表示</string>
+- <string id="13388">Preset</string>
++ <string id="13388">プリセット</string>
+ <string id="13389">プリセットãŒæœ‰åŠ¹ãªè¦–覚エフェクトã§ã¯ã‚ã‚Šã¾ã›ã‚“</string>
+ <string id="13390">設定ãŒæœ‰åŠ¹ãªè¦–覚エフェクトã§ã¯ã‚ã‚Šã¾ã›ã‚“</string>
+ <string id="13391">イジェクト/ロード</string>
+@@ -989,41 +1181,71 @@
+ <string id="13396">オーディオ・字幕 設定</string>
+ <string id="13397">字幕有効化</string>
+ <string id="13398">ショートカット</string>
+- <string id="13399">冠詞(theãªã©)を並ã¹æ›¿ãˆå¯¾è±¡ã‹ã‚‰å¤–ã™</string>
++ <string id="13399">冠詞 (the ãªã©) を無視ã—ã¦ä¸¦ã¹æ›¿ãˆã‚’è¡Œã†</string>
+ <string id="13400">アルãƒãƒ å†…クロスフェード</string>
+- <string id="13401">%sã‚’å‚ç…§ã™ã‚‹</string>
++ <string id="13401">%s ã‚’å‚ç…§</string>
+ <string id="13402">Show track position</string>
+ <string id="13403">デフォルトをクリア</string>
+- <string id="13404">Resume</string>
++ <string id="13404">å†é–‹</string>
+ <string id="13405">サムãƒã‚¤ãƒ«ã‚’å–å¾—</string>
+ <string id="13406">ç”»åƒæƒ…å ±</string>
+- <string id="13407">%s Presets</string>
+- <string id="13408">(IMDb User Rating)</string>
++ <string id="13407">%s プリセット</string>
++ <string id="13408">(IMDb ユーザã«ã‚ˆã‚‹è©•ä¾¡)</string>
+ <string id="13409">Top 250</string>
+- <string id="13410">Tune in on Last.FM</string>
+- <string id="13411">Minimum fanspeed</string>
+- <string id="13413">Downloading</string>
++ <string id="13410">Tune in on Last.fm</string>
++ <string id="13411">最低ファン速度</string>
++ <string id="13413">ダウンロード中</string>
+ <string id="13414">コンピレーションã®ã¿ã«ç™»å ´ã™ã‚‹ã‚¢ãƒ¼ãƒ†ã‚£ã‚¹ãƒˆã‚’å«ã‚ã‚‹</string>
++ <string id="13415">レンダリング方法</string>
++ <string id="13416">自動検出</string>
++ <string id="13417">ベーシック (ARB)</string>
++ <string id="13418">アドãƒãƒ³ã‚¹ãƒˆ (GLSL)</string>
++ <string id="13419">ソフトウェア</string>
++ <string id="13420">安全ã«å‰Šé™¤</string>
++ <string id="13421">VDPAU</string>
++ <string id="13422">ã“ã“ã‹ã‚‰ã‚¹ãƒ©ã‚¤ãƒ‰ã‚·ãƒ§ãƒ¼ã‚’開始</string>
++ <string id="13423">ã“ã®ãƒ‘ス用ã«è¨˜æ†¶</string>
++ <string id="13424">ピクセルãƒãƒƒãƒ•ã‚¡ãƒ¼ã‚ªãƒ–ジェクトを使用</string>
+ <string id="13425">ãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢ ã‚¢ã‚¯ã‚»ãƒ©ãƒ¬ãƒ¼ã‚¿ã‚’è¨±å¯ (VDPAU)</string>
+ <string id="13426">ãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢ ã‚¢ã‚¯ã‚»ãƒ©ãƒ¬ãƒ¼ã‚¿ã‚’è¨±å¯ (VAAPI)</string>
+ <string id="13427">ãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢ ã‚¢ã‚¯ã‚»ãƒ©ãƒ¬ãƒ¼ã‚¿ã‚’è¨±å¯ (DXVA2)</string>
+ <string id="13428">ãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢ ã‚¢ã‚¯ã‚»ãƒ©ãƒ¬ãƒ¼ã‚¿ã‚’è¨±å¯ (CrystalHD)</string>
+ <string id="13429">ãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢ ã‚¢ã‚¯ã‚»ãƒ©ãƒ¬ãƒ¼ã‚¿ã‚’è¨±å¯ (VDADecoder)</string>
+ <string id="13430">ãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢ ã‚¢ã‚¯ã‚»ãƒ©ãƒ¬ãƒ¼ã‚¿ã‚’è¨±å¯ (OpenMax)</string>
++ <string id="13431">ピクセル シェーダー</string>
++ <string id="13432">ãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢ ã‚¢ã‚¯ã‚»ãƒ©ãƒ¬ãƒ¼ã‚¿ã‚’è¨±å¯ (VideoToolbox)</string>
++
++ <string id="13500">A/V åŒæœŸæ–¹æ³•</string>
++ <string id="13501">オーディオクロック</string>
++ <string id="13502">ビデオクロック (オーディオå´ã‚’ã‚ã‚ã›ã‚‹)</string>
++ <string id="13503">ビデオクロック (オーディオをå†ã‚µãƒ³ãƒ—リングã™ã‚‹)</string>
++ <string id="13504">最大å†ã‚µãƒ³ãƒ—ãƒªãƒ³ã‚°é‡ (%)</string>
++ <string id="13505">å†ã‚µãƒ³ãƒ—リングå“質</string>
++ <string id="13506">低 (高速)</string>
++ <string id="13507">中</string>
++ <string id="13508">高</string>
++ <string id="13509">最高 (é…ã„!)</string>
++ <string id="13510">ディスプレイåŒæœŸå‘¨æ³¢æ•°ã«åˆã‚ã›ã¦å†ç”Ÿ</string>
++
+ <string id="13600">Apple Remote</string>
+- <string id="13603">Sequence Delay Time</string>
+- <string id="13610">Disabled</string>
+- <string id="13611">Standard</string>
+- <string id="13612">Universal Remote</string>
+- <string id="13620">Apple Remote Error</string>
+- <string id="13621">Apple Remote support could be enabled.</string>
++
++ <string id="13602">リモコンã‹ã‚‰ã® XBMC ã®èµ·å‹•ã‚’許å¯</string>
++ <string id="13603">Sequence delay time</string>
++
++ <string id="13610">無効</string>
++ <string id="13611">標準</string>
++ <string id="13612">汎用リモコン</string>
++ <string id="13620">Apple Remote エラー</string>
++ <string id="13621">Apple Remote サãƒãƒ¼ãƒˆã‚’有効ã«ã§ãã¾ã›ã‚“。</string>
++
+ <string id="14000">スタック</string>
+ <string id="14001">スタック解除</string>
+- <string id="14003">プレイリストをå–得中...</string>
++ <string id="14003">プレイリストファイルã®å–得中...</string>
+ <string id="14004">ストリームリストをå–得中...</string>
+ <string id="14005">ストリームリストを解æžä¸­...</string>
+- <string id="14006">ストリームリストをå–å¾—ã§ãã¾ã›ã‚“</string>
+- <string id="14007">プレイリストをå–å¾—ã§ãã¾ã›ã‚“</string>
++ <string id="14006">ストリームリストã®å–å¾—ã«å¤±æ•—</string>
++ <string id="14007">プレイリストファイルã®å–å¾—ã«å¤±æ•—</string>
++
+ <string id="14009">ゲームディレクトリ</string>
+ <string id="14010">サムãƒã‚¤ãƒ«è¡¨ç¤ºã®è‡ªå‹•åˆ‡æ›</string>
+ <string id="14011">サムãƒã‚¤ãƒ«è¡¨ç¤ºã®è‡ªå‹•åˆ‡æ›ã‚’有効化</string>
+@@ -1037,57 +1259,68 @@
+ <string id="14021">エリアコード 3</string>
+ <string id="14022">ライブラリ</string>
+ <string id="14023">No TV</string>
+- <string id="14024">最も近ãã®å¤§éƒ½å¸‚å゙を入力</string>
+- <string id="14025">ビデオ/オーディオ/DVD キャッシュ - HDD</string>
+- <string id="14026">ビデオ キャッシュ - DVDRom</string>
+- <string id="14027">- ローカルãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯</string>
++ <string id="14024">最も近ãã®å¤§éƒ½å¸‚åを入力</string>
++ <string id="14025">ビデオ/オーディオ/DVD キャッシュ - HDD</string>
++ <string id="14026">ビデオ キャッシュ - DVD-ROM</string>
++ <string id="14027">- LAN</string>
+ <string id="14028">- インターãƒãƒƒãƒˆ</string>
+- <string id="14030">オーディオ キャッシュ - DVDRom</string>
+- <string id="14031">オーディオ キャッシュ - Local Network</string>
+- <string id="14032">オーディオ キャッシュ - Internet</string>
+- <string id="14034">DVD キャッシュ - DVDRom</string>
+- <string id="14035">DVD キャッシュ - Local Network</string>
++ <string id="14030">オーディオ キャッシュ - DVD-ROM</string>
++ <string id="14031">オーディオ キャッシュ - LAN</string>
++ <string id="14032">オーディオ キャッシュ - インターãƒãƒƒãƒˆ</string>
++ <string id="14034">DVD キャッシュ - DVD-ROM</string>
++ <string id="14035">DVD キャッシュ - LAN</string>
+ <string id="14036">サーãƒãƒ¼</string>
++
+ <string id="14038">ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯è¨­å®šãŒå¤‰æ›´ã•ã‚Œã¾ã—ãŸ</string>
+- <string id="14039">設定をå映ã™ã‚‹ç‚ºã«å†å§‹å‹•ãŒå¿…è¦ã§ã™</string>
+- <string id="14040">ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯è¨­å®š 今å†å§‹å‹•ã—ã¾ã™ã‹ï¼Ÿ</string>
++ <string id="14039">ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯è¨­å®šã®å¤‰æ›´ã‚’å映ã™ã‚‹ã«ã¯ </string>
++ <string id="14040">å†èµ·å‹•ãŒå¿…è¦ã§ã™ã€‚ 今ã™ãå†å§‹å‹•ã—ã¾ã™ã‹ï¼Ÿ</string>
++ <string id="14041">インターãƒãƒƒãƒˆæŽ¥ç¶šã®å¸¯åŸŸåˆ¶é™</string>
++
+ <string id="14043">- プレー中ã§ã‚‚シャットダウン</string>
+ <string id="14044">%i 分</string>
+ <string id="14045">%i 秒</string>
+- <string id="14046">%i ms</string>
++ <string id="14046">%i ミリ秒</string>
+ <string id="14047">%i %%</string>
+ <string id="14048">%i kbps</string>
+ <string id="14049">%i kb</string>
+ <string id="14050">%i.0 dB</string>
+- <string id="14051">Time Format</string>
+- <string id="14052">Date Format</string>
+- <string id="14053">GUIフィルター</string>
++ <string id="14051">時刻ã®ãƒ•ã‚©ãƒ¼ãƒžãƒƒãƒˆ</string>
++ <string id="14052">日付ã®ãƒ•ã‚©ãƒ¼ãƒžãƒƒãƒˆ</string>
++ <string id="14053">GUI フィルター</string>
++
+ <string id="14055">ãƒãƒƒã‚¯ã‚°ãƒ©ã‚¦ãƒ³ãƒ‰ã‚¹ã‚­ãƒ£ãƒ³ã®ä½¿ç”¨</string>
+ <string id="14056">スキャンåœæ­¢</string>
+ <string id="14057">メディア情報スキャン中ã¯ä¸å¯èƒ½</string>
+ <string id="14058">フィルム粒å­ã‚¨ãƒ•ã‚§ã‚¯ãƒˆ</string>
+- <string id="14059">Look for thumbs on remote shares</string>
+- <string id="14060">ä¸æ˜Žãªï½·ï½¬ï½¯ï½¼ï½­ - Internet</string>
++ <string id="14059">リモート共有上ã§ã‚µãƒ ãƒãƒ¼ãƒ«ã‚’使ã†</string>
++ <string id="14060">ä¸æ˜Žãªã‚­ãƒ£ãƒƒã‚·ãƒ¥ - インターãƒãƒƒãƒˆ</string>
+ <string id="14061">自動</string>
+ <string id="14062">ユーザーå入力</string>
+ <string id="14063">日付 &amp; 時間</string>
+- <string id="14064">日付 設定</string>
+- <string id="14065">時間 設定</string>
++ <string id="14064">日付ã®è¨­å®š</string>
++ <string id="14065">時刻ã®è¨­å®š</string>
+ <string id="14066">24時間表示 時:分 ã§å…¥åŠ›</string>
+ <string id="14067">日付 æ—¥/月/å¹´ ã§å…¥åŠ›</string>
+- <string id="14068">IPアドレス入力</string>
++ <string id="14068">IP アドレスを入力</string>
+ <string id="14069">ã“ã®è¨­å®šã‚’é©ç”¨ã—ã¾ã™ã‹?</string>
+ <string id="14070">設定é©ç”¨</string>
+ <string id="14071">ファイルå変更・削除 許å¯</string>
+- <string id="14074">タイムゾーン</string>
++
++ <string id="14074">タイムゾーン設定</string>
+ <string id="14075">サマータイム有効化</string>
+ <string id="14076">ãŠæ°—ã«å…¥ã‚Šã«è¿½åŠ </string>
+ <string id="14077">ãŠæ°—ã«å…¥ã‚Šã‹ã‚‰å‰Šé™¤</string>
+- <string id="14078">スキンカラー</string>
++ <string id="14078">- スキンカラー</string>
++ <string id="14079">タイムゾーン(国)</string>
+ <string id="14080">タイムゾーン</string>
+ <string id="14081">ファイルリスト</string>
+- <string id="14082">EXIF情報を表示</string>
++ <string id="14082">EXIF 情報を表示</string>
++ <string id="14083">フルスクリーンウィンドウã®ä½¿ç”¨ (「フルスクリーンã€ã§ã¯ãªã)</string>
++ <string id="14084">é¸æŠžã—ãŸæ›²ã‚’キューã«å…¥ã‚Œã‚‹</string>
++ <string id="14085">オーディオ CD を自動的ã«å†ç”Ÿã™ã‚‹</string>
+ <string id="14086">å†ç”Ÿ</string>
++ <string id="14087">DVD</string>
++ <string id="14088">DVD を自動的ã«å†ç”Ÿã™ã‚‹</string>
+ <string id="14089">字幕ã«ä½¿ç”¨ã™ã‚‹ãƒ•ã‚©ãƒ³ãƒˆ</string>
+ <string id="14090">インターナショナル</string>
+ <string id="14091">文字セット</string>
+@@ -1095,28 +1328,34 @@
+ <string id="14093">セキュリティ</string>
+ <string id="14094">入力デãƒã‚¤ã‚¹</string>
+ <string id="14095">çœé›»åŠ›</string>
+- <string id="15015">Remove</string>
+- <string id="15016">Games</string>
++
++ <string id="15015">削除</string>
++ <string id="15016">ゲーム</string>
++
+ <string id="15019">追加</string>
+- <string id="15052">Password</string>
++
++ <string id="15052">パスワード</string>
++
+ <string id="15100">ライブラリ</string>
+ <string id="15101">データベース</string>
+ <string id="15102">* ã™ã¹ã¦ã®ã‚¢ãƒ«ãƒãƒ </string>
+ <string id="15103">* ã™ã¹ã¦ã®ã‚¢ãƒ¼ãƒ†ã‚£ã‚¹ãƒˆ</string>
+ <string id="15104">* ã™ã¹ã¦ã®æ›²</string>
+ <string id="15105">* ã™ã¹ã¦ã®ã‚¸ãƒ£ãƒ³ãƒ«</string>
++
+ <string id="15107">ãƒãƒƒãƒ•ã‚¡ãƒªãƒ³ã‚°...</string>
+ <string id="15108">ナビゲーションサウンド</string>
+ <string id="15109">スキン デフォルト</string>
+- <string id="15111">スキンテーマ</string>
+- <string id="15112">Default Theme</string>
+- <string id="15200">Last.FM</string>
+- <string id="15201">Submit songs to Last.FM</string>
+- <string id="15202">Last.FMユーザーå</string>
+- <string id="15203">Last.FMパスワード</string>
++ <string id="15111">- テーマ</string>
++ <string id="15112">デフォルトテーマ</string>
++
++ <string id="15200">Last.fm</string>
++ <string id="15201">Last.fm ã«æ›²ã‚’é€ä¿¡ã™ã‚‹</string>
++ <string id="15202">Last.fm ユーザå</string>
++ <string id="15203">Last.fm パスワード</string>
+ <string id="15204">æ“作ä¸å¯: åœæ­¢ä¸­...</string>
+- <string id="15205">XBMCã‚’æ›´æ–°ã—ã¦ãã ã•ã„</string>
+- <string id="15206">Bad authorisation: Check username and password</string>
++ <string id="15205">XBMC ã‚’æ›´æ–°ã—ã¦ãã ã•ã„</string>
++ <string id="15206">èªè¨¼ã«å¤±æ•—: ユーザーåã¨ãƒ‘スワードを確èªã—ã¦ãã ã•ã„</string>
+ <string id="15207">接続ã—ã¾ã—ãŸ</string>
+ <string id="15208">接続ã§ãã¾ã›ã‚“</string>
+ <string id="15209">é€ä¿¡é–“éš” %i</string>
+@@ -1125,21 +1364,77 @@
+ <string id="15212">é€ä¿¡ %i 秒</string>
+ <string id="15213">...を使用ã—ã¦å†ç”Ÿ</string>
+ <string id="15214">Use Smoothed A/V Synchronisation</string>
+- <string id="15215">Hide File Names in Thumbs View</string>
+- <string id="15216">Play in Party Mode</string>
+- <string id="15218">Libre.fmユーザーå</string>
+- <string id="15219">Libre.fmパスワード</string>
++ <string id="15215">サムãƒãƒ¼ãƒ«ãƒ“ューã§ã¯ãƒ•ã‚¡ã‚¤ãƒ«åã‚’éš ã™</string>
++ <string id="15216">パーティーモードã§å†ç”Ÿ</string>
++ <string id="15218">Libre.fm ユーザーå</string>
++ <string id="15219">Libre.fm パスワード</string>
++ <string id="15220">Libre.fm</string>
+ <string id="15221">楽曲é€ä¿¡</string>
++
++ <string id="15250">Last.fm ラジオを Last.fm ã«é€ä¿¡</string>
++ <string id="15251">Last.fm ã«æŽ¥ç¶šä¸­...</string>
++ <string id="15252">ラジオ局をé¸æŠžä¸­...</string>
++ <string id="15253">類似アーティストを検索...</string>
++ <string id="15254">類似タグを検索...</string>
++ <string id="15255">ã‚ãªãŸã®ãƒ—ロフィール (%name%)</string>
++ <string id="15256">人気ã®ã‚¿ã‚°</string>
++ <string id="15257">ã‚¿ã‚° %name% ã§äººæ°—ã®ã‚¢ãƒ¼ãƒ†ã‚£ã‚¹ãƒˆ</string>
++ <string id="15258">ã‚¿ã‚° %name% ã§äººæ°—ã®ã‚¢ãƒ«ãƒãƒ </string>
++ <string id="15259">ã‚¿ã‚° %name% ã§äººæ°—ã®ãƒˆãƒ©ãƒƒã‚¯</string>
++ <string id="15260">ã‚¿ã‚° %name% ã® Last.fm ラジオをè´ã</string>
++ <string id="15261">%name% ã®é¡žä¼¼ã‚¢ãƒ¼ãƒ†ã‚£ã‚¹ãƒˆ</string>
++ <string id="15262">%name% ã®ãƒˆãƒƒãƒ—アルãƒãƒ </string>
++ <string id="15263">%name% ã®ãƒˆãƒƒãƒ—トラック</string>
++ <string id="15264">%name% ã®ãƒˆãƒƒãƒ—ã‚¿ã‚°</string>
++ <string id="15265">%name% ã®ãƒˆãƒƒãƒ—ファン</string>
++ <string id="15266">%name% ã®ãƒ•ã‚¡ãƒ³ã® Last.fm ラジオをè´ã</string>
++ <string id="15267">%name% ã®é¡žä¼¼ã‚¢ãƒ¼ãƒ†ã‚£ã‚¹ãƒˆã® Last.fm ラジオをè´ã</string>
++ <string id="15268">ユーザー %name% ã®ãƒ™ã‚¹ãƒˆã‚¢ãƒ¼ãƒ†ã‚£ã‚¹ãƒˆ</string>
++ <string id="15269">ユーザー %name% ã®ãƒ™ã‚¹ãƒˆã‚¢ãƒ«ãƒãƒ </string>
++ <string id="15270">ユーザー %name% ã®ãƒ™ã‚¹ãƒˆãƒˆãƒ©ãƒƒã‚¯</string>
++ <string id="15271">ユーザー %name% ã®å‹ã ã¡</string>
++ <string id="15272">ユーザー %name% ã®ã”近所ã•ã‚“</string>
++ <string id="15273">%name% ã®é€±åˆŠã‚¢ãƒ¼ãƒ†ã‚£ã‚¹ãƒˆãƒãƒ£ãƒ¼ãƒˆ</string>
++ <string id="15274">%name% ã®é€±åˆŠã‚¢ãƒ«ãƒãƒ ãƒãƒ£ãƒ¼ãƒˆ</string>
++ <string id="15275">%name% ã®é€±åˆŠãƒˆãƒ©ãƒƒã‚¯ãƒãƒ£ãƒ¼ãƒˆ</string>
++ <string id="15276">%name% ã®ã”近所ã•ã‚“ã® Last.fm ラジオをè´ã</string>
++ <string id="15277">%name% ã® Last.fm ラジオをè´ã</string>
++ <string id="15278">%name% ã® mix Last.fm ラジオをè´ã</string>
++ <string id="15279">Last.fm ã‹ã‚‰ãƒªã‚¹ãƒˆã‚’å–得中...</string>
++ <string id="15280">Last.fm ã‹ã‚‰ãƒªã‚¹ãƒˆå–å¾—ã«å¤±æ•—...</string>
+ <string id="15281">アーティストåを入力ã—関連アイテム検索</string>
+ <string id="15282">ã‚¿ã‚°åを入力ã—類似アイテム検索</string>
+- <string id="15300">パスãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ or 無効ã§ã™</string>
++ <string id="15283">%name% ãŒæœ€è¿‘è´ã„ãŸãƒˆãƒ©ãƒƒã‚¯</string>
++ <string id="15284">%name% ãŠã™ã™ã‚ã® Last.fm ラジオ</string>
++ <string id="15285">ユーザー %name% ã®ãƒˆãƒƒãƒ—ã‚¿ã‚°</string>
++
++ <string id="15287">ç¾åœ¨å†ç”Ÿä¸­ã®ãƒˆãƒ©ãƒƒã‚¯ã‚’ Love トラックã«è¿½åŠ ã—ã¾ã™ã‹?</string>
++ <string id="15288">ç¾åœ¨å†ç”Ÿä¸­ã®ãƒˆãƒ©ãƒƒã‚¯ã‚’ Last.fm ライブラリã‹ã‚‰å‰Šé™¤ã—ã¾ã™ã‹?</string>
++ <string id="15289">Love トラックã«è¿½åŠ : '%s'</string>
++ <string id="15290">'%s' ã‚’ Love トラックã«è¿½åŠ ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚</string>
++ <string id="15291">Last.fm ライブラリã‹ã‚‰å‰Šé™¤: '%s'.</string>
++ <string id="15292">'%s' ã‚’ Last.fm ライブラリã‹ã‚‰å‰Šé™¤ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚</string>
++ <string id="15293">%name% ãŒæœ€è¿‘ Love トラックã«ã—ãŸæ›²</string>
++ <string id="15294">%name% ãŒæœ€è¿‘ Last.fm ライブラリã‹ã‚‰å‰Šé™¤ã—ãŸæ›²</string>
++ <string id="15295">Love トラックã‹ã‚‰å‰Šé™¤</string>
++ <string id="15296">ライブラリã«æˆ»ã™</string>
++ <string id="15297">ã“ã®ãƒˆãƒ©ãƒƒã‚¯ã‚’ Love トラックã‹ã‚‰å‰Šé™¤ã—ã¾ã™ã‹?</string>
++ <string id="15298">ã“ã®ãƒˆãƒ©ãƒƒã‚¯ã‚’ライブラリã«æˆ»ã—ã¾ã™ã‹?</string>
++
++ <string id="15300">パスãŒè¦‹ã¤ã‹ã‚‰ãªã„ã‹ã€ãƒ‘スãŒç„¡åŠ¹ã§ã™</string>
+ <string id="15301">サーãƒãƒ¼ã«æŽ¥ç¶šã§ãã¾ã›ã‚“</string>
+ <string id="15302">サーãƒãƒ¼ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“</string>
+ <string id="15303">ワークグループãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“</string>
++
++ <string id="15310">Opening multi-path source</string>
++ <string id="15311">パス:</string>
++
+ <string id="16000">一般</string>
++
+ <string id="16002">インターãƒãƒƒãƒˆæ¤œç´¢</string>
+ <string id="16003">プレイヤー</string>
+- <string id="16004">Play media from disc</string>
++ <string id="16004">ディスクã‹ã‚‰ãƒ¡ãƒ‡ã‚£ã‚¢å†ç”Ÿ</string>
++
+ <string id="16008">æ–°ã—ã„タイトルを入力</string>
+ <string id="16009">å‹•ç”»åを入力</string>
+ <string id="16010">プロファイルåを入力</string>
+@@ -1148,68 +1443,236 @@
+ <string id="16013">æ–°ã—ã„ファイルåを入力</string>
+ <string id="16014">フォルダーåを入力</string>
+ <string id="16015">ディレクトリを入力</string>
+- <string id="16016">使用å¯èƒ½ï½µï¾Œï¾Ÿï½¼ï½®ï¾: %A, %T, %N, %B, %D, %G, %Y, %F</string>
++ <string id="16016">使用å¯èƒ½ã‚ªãƒ—ション: %A, %T, %N, %B, %D, %G, %Y, %F</string>
+ <string id="16017">検索文字列を入力</string>
+- <string id="16018">None</string>
++ <string id="16018">ãªã—</string>
+ <string id="16019">自動é¸æŠž</string>
+ <string id="16020">インターレース解除</string>
+- <string id="16021">ODDフィールド表示</string>
+- <string id="16022">EVENフィールド表示</string>
+- <string id="16023">インターレース処ç†</string>
++ <string id="16021">ODD フィールド表示</string>
++ <string id="16022">EVEN フィールド表示</string>
++ <string id="16023"></string>
+ <string id="16024">キャンセル中...</string>
+ <string id="16025">アーティストåを入力</string>
+ <string id="16026">プレイãƒãƒƒã‚¯ã«å¤±æ•—ã—ã¾ã—ãŸ</string>
+- <string id="16027">1ã¤ä»¥ä¸Šã®ã‚¢ã‚¤ãƒ†ãƒ ã®å†ç”Ÿã«å¤±æ•—ã—ã¾ã—ãŸ</string>
++ <string id="16027">1ã¤ä»¥ä¸Šã®ã‚¢ã‚¤ãƒ†ãƒ ã®å†ç”Ÿã«å¤±æ•—ã—ã¾ã—ãŸã€‚</string>
++ <string id="16028">値を入力</string>
++ <string id="16029">詳ã—ãã¯ãƒ­ã‚°ãƒ•ã‚¡ã‚¤ãƒ«ã‚’å‚ç…§ã—ã¦ä¸‹ã•ã„。</string>
++ <string id="16030">パーティーモードを終了ã—ã¾ã—ãŸã€‚</string>
++ <string id="16031">ライブラリã«ã¯ä¸€è‡´ã™ã‚‹æ›²ã¯ã‚ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚</string>
++ <string id="16032">データベースをåˆæœŸåŒ–ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚</string>
++ <string id="16033">データベースを開ã‘ã¾ã›ã‚“ã§ã—ãŸã€‚</string>
++ <string id="16034">データベースã‹ã‚‰æ›²ã‚’å–å¾—ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚</string>
++ <string id="16035">パーティモードプレイリスト</string>
++ <string id="16036">インターレース解除 (Half)</string>
++ <string id="16037">ビデオã®ã‚¤ãƒ³ã‚¿ãƒ¼ãƒ¬ãƒ¼ã‚¹è§£é™¤</string>
++ <string id="16038">インターレース解除方法</string>
++ <string id="16039">オフ</string>
++ <string id="16040">自動</string>
++ <string id="16041">オン</string>
++
+ <string id="16100">ã™ã¹ã¦ã®ãƒ“デオ</string>
+ <string id="16101">未視è´</string>
+ <string id="16102">視è´æ¸ˆã¿</string>
+ <string id="16103">視è´æ¸ˆã¿ã«ã™ã‚‹</string>
+ <string id="16104">未視è´ã«ã™ã‚‹</string>
+ <string id="16105">タイトルを編集</string>
+- <string id="20001">外部DVDプレイヤを使用</string>
+- <string id="20002">外部DVDプレイヤ指定</string>
++
++ <string id="16200">処ç†ãŒä¸­æ–­ã•ã‚Œã¾ã—ãŸ</string>
++ <string id="16201">コピーã«å¤±æ•—</string>
++ <string id="16202">1ã¤ä»¥ä¸Šã®ãƒ•ã‚¡ã‚¤ãƒ«ã®ã‚³ãƒ”ーã«å¤±æ•—ã—ã¾ã—ãŸ</string>
++ <string id="16203">移動ã«å¤±æ•—</string>
++ <string id="16204">1ã¤ä»¥ä¸Šã®ãƒ•ã‚¡ã‚¤ãƒ«ã®ç§»å‹•ã«å¤±æ•—ã—ã¾ã—ãŸ</string>
++ <string id="16205">削除ã«å¤±æ•—</string>
++ <string id="16206">1ã¤ä»¥ä¸Šã®ãƒ•ã‚¡ã‚¤ãƒ«ã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸ</string>
++
++ <string id="16300">ビデオã®ãƒªã‚µã‚¤ã‚ºæ–¹å¼</string>
++ <string id="16301">ニアレストãƒã‚¤ãƒãƒ¼</string>
++ <string id="16302">ãƒã‚¤ãƒªãƒ‹ã‚¢</string>
++ <string id="16303">ãƒã‚¤ã‚­ãƒ¥ãƒ¼ãƒ“ック</string>
++ <string id="16304">Lanczos2</string>
++ <string id="16305">Lanczos3</string>
++ <string id="16306">Sinc8</string>
++ <string id="16307">ãƒã‚¤ã‚­ãƒ¥ãƒ¼ãƒ“ック (ソフトウェア)</string>
++ <string id="16308">Lanczos (software)</string>
++ <string id="16309">Sinc (software)</string>
++ <string id="16310">Temporal</string>
++ <string id="16311">Temporal/Spatial</string>
++ <string id="16312">(VDPAU) ノイズリダクション</string>
++ <string id="16313">(VDPAU) シャープãƒã‚¹</string>
++ <string id="16314">逆テレシム(30fps => 24fps)</string>
++ <string id="16315">Lanczos3 最é©åŒ–</string>
++ <string id="16316">自動</string>
++ <string id="16317">Temporal (Half)</string>
++ <string id="16318">Temporal/Spatial (Half)</string>
++ <string id="16319">DXVA</string>
++ <string id="16320">DXVA Bob</string>
++ <string id="16321">DXVA Best</string>
++ <string id="16322">Spline36</string>
++ <string id="16323">Spline36 optimized</string>
++ <string id="16324">Software Blend</string>
++
++ <string id="16400">後処ç†</string>
++
++ <string id="17500">ディスプレイスリープタイムアウト</string>
++
++ <string id="19000">ãƒãƒ£ãƒ³ãƒãƒ«åˆ‡ã‚Šæ›¿ãˆ</string>
++
++
++ <string id="20000">ä¿å­˜ã•ã‚ŒãŸãƒŸãƒ¥ãƒ¼ã‚¸ãƒƒã‚¯ã®ãƒ•ã‚©ãƒ«ãƒ€ãƒ¼</string>
++ <string id="20001">外部 DVD プレーヤーを使用</string>
++ <string id="20002">外部 DVD プレーヤーã®æŒ‡å®š</string>
+ <string id="20004">スクリーンショットフォルダー設定</string>
+- <string id="20006">プレイリストフォルダー設定</string>
++
++ <string id="20006">プレイリストフォルダー</string>
++ <string id="20007">録音</string>
++ <string id="20008">スクリーンショット</string>
++ <string id="20009">XBMC を使用</string>
++
+ <string id="20011">ミュージックプレイリスト</string>
+ <string id="20012">ビデオプレイリスト</string>
++ <string id="20013">ゲームを起動ã—ã¾ã™ã‹?</string>
+ <string id="20014">並ã¹æ›¿ãˆ: プレイリスト</string>
+ <string id="20015">サーãƒä¸Šã®ã‚µãƒ ãƒã‚¤ãƒ«</string>
+ <string id="20016">ç¾åœ¨ã®ã‚µãƒ ãƒã‚¤ãƒ«</string>
+ <string id="20017">ローカルサムãƒã‚¤ãƒ«</string>
+ <string id="20018">サムãƒã‚¤ãƒ«ãªã—</string>
+ <string id="20019">サムãƒã‚¤ãƒ«ã‚’é¸æŠž</string>
+- <string id="20022">
+- </string>
++
++ <!-- string id 20022 will always be set to an empty string (LocalizeStrings.cpp)-->
++ <string id="20022"></string>
++ <string id="20023">コンフリクト</string>
++ <string id="20024">æ–°è¦ã‚¹ã‚­ãƒ£ãƒ³</string>
++ <string id="20025">å…¨ã¦ã‚¹ã‚­ãƒ£ãƒ³</string>
+ <string id="20026">地域</string>
++
++ <!-- string id's 20027 thru 20034 are reserved for temperature strings (LocalizeStrings.cpp)-->
++
+ <string id="20037">概è¦</string>
++ <string id="20038">ミュージックウィンドウをロック</string>
++ <string id="20039">ビデオウィンドウをロック</string>
++ <string id="20040">ピクãƒãƒ£ãƒ¼ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ã‚’ロック</string>
++ <string id="20041">プログラム &amp; スクリプトウィンドウをロック</string>
++ <string id="20042">ファイルマãƒãƒ¼ã‚¸ãƒ£ãƒ¼ã‚’ロック</string>
++ <string id="20043">ロックã®è¨­å®š</string>
++ <string id="20044">フレッシュ起動</string>
++ <string id="20045">マスターモードã«å…¥ã‚‹</string>
++ <string id="20046">マスターモードã‹ã‚‰æŠœã‘ã‚‹</string>
++ <string id="20047">プロファイル '%s' を作æˆã—ã¾ã™ã‹?</string>
++ <string id="20048">全設定をåˆæœŸåŒ–ã—ã¦èµ·å‹•ã™ã‚‹</string>
++ <string id="20049">Best available</string>
++ <string id="20050">16x9 㨠4x3 を自動切り替ãˆ</string>
++ <string id="20051">Treat stacked files as single file</string>
++ <string id="20052">注æ„</string>
++ <string id="20053">マスターモードã‹ã‚‰æŠœã‘ã¾ã—ãŸ</string>
++ <string id="20054">マスターモードã«å…¥ã‚Šã¾ã—ãŸ</string>
++ <string id="20055">Allmusic.com サムãƒãƒ¼ãƒ«</string>
++
++ <string id="20057">サムãƒãƒ¼ãƒ«ã®å‰Šé™¤</string>
++ <string id="20058">プロファイルã®è¿½åŠ ...</string>
++ <string id="20059">全アルãƒãƒ ã®æƒ…報をå–å¾—</string>
++ <string id="20060">メディア情報</string>
++ <string id="20061">Separate</string>
++ <string id="20062">Shares with default</string>
++ <string id="20063">Shares with default (read only)</string>
++ <string id="20064">Copy default</string>
++ <string id="20065">プロファイル画åƒ</string>
++ <string id="20066">プロファイルをロック</string>
++ <string id="20067">プロファイルã®ç·¨é›†</string>
++ <string id="20068">プロファイルã®ãƒ­ãƒƒã‚¯</string>
++ <string id="20069">フォルダã®ä½œæˆã«å¤±æ•—</string>
++ <string id="20070">プロファイルフォルダー</string>
++ <string id="20071">メディアソース設定をåˆæœŸåŒ–ã—ã¦èµ·å‹•ã™ã‚‹</string>
++ <string id="20072">é¸æŠžã—ãŸãƒ•ã‚©ãƒ«ãƒ€ãƒ¼ãŒæ›¸ãè¾¼ã¿å¯èƒ½ã§ã‚ã‚‹ã“ã¨ã€</string>
++ <string id="20073">æ–°è¦ãƒ•ã‚©ãƒ«ãƒ€åãŒæœ‰åŠ¹ã§ã‚ã‚‹ã“ã¨ã‚’確èªã—ã¦ãã ã•ã„。</string>
+ <string id="20074">MAPP評価</string>
++ <string id="20075">マスターロックコードã®å…¥åŠ›</string>
++ <string id="20076">起動時ã«ãƒžã‚¹ã‚¿ãƒ¼ãƒ­ãƒƒã‚¯ã‚³ãƒ¼ãƒ‰ã‚’入力ã•ã›ã‚‹</string>
+ <string id="20077">スキン設定</string>
+ <string id="20078">- 未設定 -</string>
+- <string id="20079">アニメーション表示有効化</string>
+- <string id="20080">音楽å†ç”Ÿä¸­ã¯RSSã‚’åœæ­¢</string>
+- <string id="20081">ショートカットボタン有効化</string>
+- <string id="20109">スキンを拡大・縮å°</string>
++ <string id="20079">アニメーション表示を有効ã«ã™ã‚‹</string>
++ <string id="20080">音楽å†ç”Ÿä¸­ã¯ RSS ã‚’åœæ­¢</string>
++ <string id="20081">ショートカットボタンを有効ã«ã™ã‚‹</string>
++ <string id="20082">メインメニューã«ã€Œãƒ—ログラムã€ã‚’表示</string>
++ <string id="20083">ミュージック情報を表示</string>
++ <string id="20084">天気予報情報を表示</string>
++ <string id="20085">システム情報を表示</string>
++ <string id="20086">Show available disc space C: E: F:</string>
++ <string id="20087">Show available disc space E: F: G:</string>
++ <string id="20088">天気予報情報</string>
++ <string id="20089">Drive space free</string>
++ <string id="20090">Enter the name of an existing share</string>
++ <string id="20091">Lock code</string>
++ <string id="20092">プロファイルã®èª­ã¿è¾¼ã¿</string>
++ <string id="20093">プロファイルå</string>
++ <string id="20094">メディアソース</string>
++ <string id="20095">プロファイルã®ãƒ­ãƒƒã‚¯ã‚³ãƒ¼ãƒ‰ã‚’入力</string>
++ <string id="20096">ログイン画é¢</string>
++ <string id="20097">アルãƒãƒ æƒ…å ±ã®å–得中</string>
++ <string id="20098">アルãƒãƒ ã®æƒ…報をå–得中</string>
++ <string id="20099">CD å†ç”Ÿä¸­ã« CD やトラックã®èª­ã¿è¾¼ã¿ã¯ã§ãã¾ã›ã‚“</string>
++ <string id="20100">マスターロックコードã¨è¨­å®š</string>
++ <string id="20101">Entering master lock code always enables master mode</string>
++ <string id="20102">ãã‚Œã¨ã‚‚デフォルト値ã‹ã‚‰ã‚³ãƒ”ーã—ã¾ã™ã‹?</string>
++ <string id="20103">プロファイルã«å¤‰æ›´å†…容をä¿å­˜ã—ã¾ã™ã‹?</string>
++ <string id="20104">å¤ã„設定ãŒè¦‹ã¤ã‹ã‚Šã¾ã—ãŸã€‚</string>
++ <string id="20105">ãã®å¤ã„設定を使ã„ã¾ã™ã‹?</string>
++ <string id="20106">å¤ã„メディアソースãŒè¦‹ã¤ã‹ã‚Šã¾ã—ãŸã€‚</string>
++ <string id="20107">Separate (locked)</string>
++ <string id="20108">Root</string>
++ <string id="20109">- スキンã®æ‹¡å¤§ãƒ»ç¸®å°</string>
++ <string id="20109">- Zoom</string>
++ <string id="20110">UPnP 設定</string>
++ <string id="20111">UPnP クライアントã®è‡ªå‹•èµ·å‹•</string>
++ <string id="20112">最終ログイン: %s</string>
++ <string id="20113">ログイン履歴ãªã—</string>
++ <string id="20114">プロファイル %i / %i</string>
++ <string id="20115">ユーザーログイン / プロファイルをé¸æŠž</string>
++ <string id="20116">ログインãŒé¢ã§ãƒ­ãƒƒã‚¯ã‚’使用</string>
++ <string id="20117">ロックコードãŒä¸æ­£ã§ã™ã€‚</string>
++ <string id="20118">マスターロックã®è¨­å®šãŒå¿…è¦ã§ã™ã€‚</string>
++ <string id="20119">ã„ã¾ã“ã“ã§è¨­å®šã—ã¾ã™ã‹?</string>
++ <string id="20120">プログラム情報ã®èª­ã¿è¾¼ã¿ä¸­</string>
+ <string id="20121">パーティー開始ï¼</string>
++ <string id="20122">True</string>
++ <string id="20123">Mixing drinks</string>
++ <string id="20124">Filling glasses</string>
++ <string id="20125">ログイン済: </string>
++ <string id="20126">ログオフ</string>
+ <string id="20128">一番上ã®éšŽå±¤ã¸</string>
++ <string id="20129">Weave</string>
++ <string id="20130">Weave (inverted)</string>
+ <string id="20131">Blend</string>
++ <string id="20132">ビデオをå†èµ·å‹•</string>
+ <string id="20133">ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ä¸Šã®å ´æ‰€ã‚’編集</string>
+ <string id="20134">ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ä¸Šã®å ´æ‰€ã‚’削除</string>
+- <string id="20144">シャットダウンタイマ</string>
++ <string id="20135">フォルダをスキャンã—ã¾ã™ã‹?</string>
++ <string id="20136">メモリユニット</string>
++ <string id="20137">メモリユニットãŒãƒžã‚¦ãƒ³ãƒˆã•ã‚Œã¾ã—ãŸ</string>
++ <string id="20138">メモリユニットをマウントã§ãã¾ã›ã‚“ã§ã—ãŸ</string>
++ <string id="20139">ãƒãƒ¼ãƒˆ %i, スロット %i</string>
++ <string id="20140">スクリーンセーãƒãƒ¼ã‚’ロック</string>
++ <string id="20141">Set</string>
++ <string id="20142">ユーザーå</string>
++ <string id="20143">パスワードを設定: </string>
++ <string id="20144">シャットダウンタイマー</string>
+ <string id="20145">シャットダウンã¾ã§ã®æ™‚é–“ (å˜ä½:分)</string>
+- <string id="20146">タイマスタート, %i分後ã«ã‚·ãƒ£ãƒƒãƒˆãƒ€ã‚¦ãƒ³</string>
++ <string id="20146">タイマースタート, %i分後ã«ã‚·ãƒ£ãƒƒãƒˆãƒ€ã‚¦ãƒ³</string>
+ <string id="20147">30分後ã«ã‚·ãƒ£ãƒƒãƒˆãƒ€ã‚¦ãƒ³</string>
+ <string id="20148">1時間後ã«ã‚·ãƒ£ãƒƒãƒˆãƒ€ã‚¦ãƒ³</string>
+ <string id="20149">2時間後ã«ã‚·ãƒ£ãƒƒãƒˆãƒ€ã‚¦ãƒ³</string>
+- <string id="20150">シャットダウンタイマを設定</string>
+- <string id="20151">シャットダウンタイマをキャンセル</string>
++ <string id="20150">シャットダウンタイマーを設定</string>
++ <string id="20151">シャットダウンタイマーをキャンセル</string>
++ <string id="20152">%s ã®è¨­å®šã‚’ロック</string>
+ <string id="20153">å‚ç…§...</string>
+ <string id="20154">概è¦æƒ…å ±</string>
+ <string id="20155">ストレージ情報</string>
++ <string id="20156">ãƒãƒ¼ãƒ‰ãƒ‡ã‚£ã‚¹ã‚¯æƒ…å ±</string>
++ <string id="20157">DVD-ROM 情報</string>
+ <string id="20158">ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯æƒ…å ±</string>
+ <string id="20159">ビデオ情報</string>
+ <string id="20160">ãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢æƒ…å ±</string>
+- <string id="20161">Total</string>
+- <string id="20162">Used</string>
++ <string id="20161">åˆè¨ˆ</string>
++ <string id="20162">使用済</string>
+ <string id="20163">of</string>
+ <string id="20164">Locking Not Supported</string>
+ <string id="20165">Not Locked</string>
+@@ -1219,63 +1682,98 @@
+ <string id="20169">Week</string>
+ <string id="20170">Line</string>
+ <string id="20171">Windowsãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ (SMB)</string>
+- <string id="20172">XBMSP Server</string>
+- <string id="20173">FTP Server</string>
+- <string id="20174">iTunes music share (DAAP)</string>
+- <string id="20175">UPnP Server</string>
+- <string id="20176">Show Video Info</string>
+- <string id="20177">Done</string>
++ <string id="20172">XBMSP サーãƒãƒ¼</string>
++ <string id="20173">FTP サーãƒãƒ¼</string>
++ <string id="20174">iTunes ミュージック共有 (DAAP)</string>
++ <string id="20175">UPnP サーãƒãƒ¼</string>
++ <string id="20176">ビデオ情報を表示</string>
++ <string id="20177">完了</string>
+ <string id="20178">Shift</string>
+ <string id="20179">Caps Lock</string>
+- <string id="20180">Symbols</string>
++ <string id="20180">記å·</string>
+ <string id="20181">Backspace</string>
+ <string id="20182">Space</string>
+ <string id="20183">スキンをå†ãƒ­ãƒ¼ãƒ‰</string>
+- <string id="20184">EXIF情報ã«ã‚ˆã‚‹ç”»åƒã®å‘ã補正</string>
++ <string id="20184">EXIF 情報ã«ã‚ˆã‚‹ç”»åƒã®å‘ã補正</string>
+ <string id="20185">Use Poster View Styles for TV Shows</string>
+ <string id="20186">ãŠå¾…ã¡ä¸‹ã•ã„...</string>
+- <string id="20189">ã‚らã™ã˜ã¨æ‰¹è©•ã§è‡ªå‹•ã‚¹ã‚¯ãƒ­ãƒ¼ãƒ«ã‚’有効化</string>
+- <string id="20190">Custom</string>
+- <string id="20191">Enable debug logging</string>
++ <string id="20187">UPnP</string>
++
++ <string id="20189">ã‚らã™ã˜ã¨ãƒ¬ãƒ“ューã§è‡ªå‹•ã‚¹ã‚¯ãƒ­ãƒ¼ãƒ«ã‚’有効化</string>
++ <string id="20190">カスタム</string>
++ <string id="20191">デãƒãƒƒã‚°ãƒ­ã‚°å‡ºåŠ›ã‚’有効ã«ã™ã‚‹</string>
++ <string id="20192">更新時ã«è¿½åŠ æƒ…報をダウンロードã™ã‚‹</string>
++ <string id="20193">アルãƒãƒ æƒ…å ±å–得用デフォルトサービス</string>
++ <string id="20194">アーティスト情報å–得用デフォルトサービス</string>
+ <string id="20195">スクレーパーを変更</string>
+ <string id="20196">ミュージックライブラリをエクスãƒãƒ¼ãƒˆ</string>
+ <string id="20197">ミュージックライブラリをインãƒãƒ¼ãƒˆ</string>
++ <string id="20198">アーティストãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ!</string>
+ <string id="20199">アーティスト情報ã®å–å¾—ã«å¤±æ•—ã—ã¾ã—ãŸ</string>
++
++ <!-- string id's 20200 thru 20211 are reserved for speedstrings (LocalizeStrings.cpp)-->
++
+ <string id="20250">ãƒ‘ãƒ¼ãƒ†ã‚£ãƒ¼é–‹å§‹ï¼ (ビデオ)</string>
+- <string id="20302">
+- </string>
+- <string id="20303">
+- </string>
+- <string id="20304">
+- </string>
+- <string id="20307">セカンダリDNS</string>
+- <string id="20308">DHCPサーãƒ:</string>
++ <string id="20251">Mixing drinks (videos)</string>
++ <string id="20252">Filling glasses (videos)</string>
++ <string id="20253">WebDAV サーãƒãƒ¼ (HTTP)</string>
++ <string id="20254">WebDAV サーãƒãƒ¼ (HTTPS)</string>
++ <string id="20255">åˆå›žãƒ­ã‚°ã‚¤ãƒ³ã€ãƒ—ロファイルを編集ã—ã¦ä¸‹ã•ã„</string>
++ <string id="20256">HTS Tvheadend クライアント</string>
++ <string id="20257">VDR Streamdev クライアント</string>
++ <string id="20258">MythTV クライアント</string>
++ <string id="20259">ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ãƒ•ã‚¡ã‚¤ãƒ«ã‚·ã‚¹ãƒ†ãƒ  (NFS)</string>
++ <string id="20260">セキュアシェル (SSH/SFTP)</string>
++ <string id="20261">Apple ファイル共有プロトコル (AFP)</string>
++
++ <string id="20300">Web サーãƒãƒ¼ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒª (HTTP)</string>
++ <string id="20301">Web サーãƒãƒ¼ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒª (HTTPS)</string>
++ <string id="20302">フォルダã«æ›¸ãè¾¼ã¿ã§ãã¾ã›ã‚“:</string>
++ <string id="20303">スキップã—ã¦ç¶šã‘ã¾ã™ã‹?</string>
++ <string id="20304">RSS フィード</string>
++
++ <string id="20307">セカンダリ DNS</string>
++ <string id="20308">DHCP サーãƒ:</string>
++ <string id="20309">æ–°è¦ãƒ•ã‚©ãƒ«ãƒ€ãƒ¼ä½œæˆ</string>
++ <string id="20310">å†ç”Ÿä¸­ã¯ LCD ã‚’æš—ãã™ã‚‹</string>
++ <string id="20311">ä¸æ˜Žã€ã¾ãŸã¯ã‚ªãƒ³ãƒœãƒ¼ãƒ‰ (protected)</string>
++ <string id="20312">一時åœæ­¢ä¸­ã« LCD ã‚’æš—ãã™ã‚‹</string>
++
+ <string id="20314">ビデオ - ライブラリ</string>
++
+ <string id="20316">並ã¹æ›¿ãˆ: ID</string>
++
++ <string id="20324">Play part...</string>
++ <string id="20325">設定ã®ãƒªã‚»ãƒƒãƒˆ</string>
++ <string id="20326">%s ã®è¨­å®šå€¤ã‚’リセットã—ã¦ã™ã¹ã¦</string>
++ <string id="20327">デフォルト値ã«æˆ»ã—ã¾ã™ã€‚</string>
++ <string id="20328">Browse for destination</string>
++ <string id="20329">Movies are in separate folders that match the movie title</string>
++ <string id="20330">フォルダåも検索対象ã«ã™ã‚‹</string>
+ <string id="20331">ファイル</string>
+- <string id="20332">Use file or folder names in lookups?</string>
++ <string id="20332">ファイルå・フォルダåを検索対象ã«ã—ã¾ã™ã‹?</string>
+ <string id="20333">コンテンツを設定</string>
+ <string id="20334">フォルダー</string>
+- <string id="20335">Look for content recursively?</string>
+- <string id="20336">Unlock Sources</string>
+- <string id="20337">Actor</string>
+- <string id="20338">Movie</string>
++ <string id="20335">コンテンツをå†å¸°çš„ã«æŽ¢ç´¢ã—ã¾ã™ã‹?</string>
++ <string id="20336">ソースã®ãƒ­ãƒƒã‚¯è§£é™¤</string>
++ <string id="20337">俳優</string>
++ <string id="20338">映画</string>
+ <string id="20339">監ç£</string>
+ <string id="20340">Do you want to remove all items within</string>
+ <string id="20342">映画</string>
+ <string id="20343">テレビ番組</string>
+ <string id="20344">ã“ã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªãŒå«ã‚€ã®ã¯</string>
+- <string id="20345">Run Automated Scan</string>
++ <string id="20345">自動スキャンを実行</string>
+ <string id="20346">å†å¸°çš„スキャン</string>
+ <string id="20347">as</string>
+ <string id="20348">監ç£</string>
+- <string id="20349">No video files found in this path!</string>
++ <string id="20349">ã“ã®ãƒ‘スã«ã¯ãƒ“デオファイルãŒã‚ã‚Šã¾ã›ã‚“ã§ã—ãŸ!</string>
+ <string id="20350">votes</string>
+ <string id="20351">テレビ番組情報</string>
+ <string id="20352">エピソード情報</string>
+ <string id="20353">テレビ番組ã®è©³ç´°ã‚’ロード中</string>
+- <string id="20354">Fetching Episode Guide</string>
+- <string id="20355">Loading Info For Episodes In Directory</string>
++ <string id="20354">エピソードガイドå–得中</string>
++ <string id="20355">ディレクトリ内ã®ã‚¨ãƒ”ソード情報を読ã¿è¾¼ã¿ä¸­</string>
+ <string id="20356">テレビ番組をé¸æŠž:</string>
+ <string id="20357">テレビ番組åを入力</string>
+ <string id="20358">シーズン %i</string>
+@@ -1285,24 +1783,24 @@
+ <string id="20362">エピソードをライブラリã‹ã‚‰å‰Šé™¤</string>
+ <string id="20363">テレビ番組をライブラリã‹ã‚‰å‰Šé™¤</string>
+ <string id="20364">テレビ番組</string>
+- <string id="20365">Episode Plot</string>
++ <string id="20365">エピソードã®ã‚らã™ã˜</string>
+ <string id="20366">* 全シーズン</string>
+ <string id="20367">視è´æ¸ˆã¿ã‚’除外</string>
+ <string id="20368">Prod Code</string>
+ <string id="20369">未視è´ã‚¢ã‚¤ãƒ†ãƒ ã®ã‚らã™ã˜ã‚’表示ã™ã‚‹</string>
+- <string id="20370">* Hidden to prevent spoilers *</string>
++ <string id="20370">* ã‚らã™ã˜éžè¡¨ç¤º *</string>
+ <string id="20371">シーズンサムãƒã‚¤ãƒ«æŒ‡å®š</string>
+- <string id="20372">Season Image</string>
++ <string id="20372">シーズンイメージ</string>
+ <string id="20373">シーズン</string>
+ <string id="20374">映画情報をå–得中</string>
+ <string id="20375">Unassign Content</string>
+ <string id="20377">テレビ番組情報を更新</string>
+ <string id="20378">ã™ã¹ã¦ã®ã‚¨ãƒ”ソード情報を更新ã—ã¾ã™ã‹ï¼Ÿ</string>
+ <string id="20379">é¸æŠžã—ãŸãƒ•ã‚©ãƒ«ãƒ€ãƒ¼ã«ã¯ãƒ†ãƒ¬ãƒ“番組1種類ã®ã¿å«ã¾ã‚Œã‚‹</string>
+- <string id="20380">Exclude selected folder from scans</string>
+- <string id="20381">Specials</string>
++ <string id="20380">é¸æŠžã—ãŸãƒ•ã‚©ãƒ«ãƒ€ãƒ¼ã‚’スキャン対象ã‹ã‚‰é™¤å¤–ã™ã‚‹</string>
++ <string id="20381">スペシャル</string>
+ <string id="20382">シーズンサムãƒã‚¤ãƒ«ã®è‡ªå‹•å–å¾—</string>
+- <string id="20383">Selected folder contains a single Video</string>
++ <string id="20383">é¸æŠžã—ãŸãƒ•ã‚©ãƒ«ãƒ€ã«ã¯ãƒ“デオãŒ1ã¤ã ã‘ã‚ã‚Šã¾ã™</string>
+ <string id="20384">テレビ番組ã¨ãƒªãƒ³ã‚¯ã‚’設定</string>
+ <string id="20385">テレビ番組ã¨ã®ãƒªãƒ³ã‚¯ã‚’解除</string>
+ <string id="20386">最近追加ã•ã‚ŒãŸæ˜ ç”»</string>
+@@ -1315,74 +1813,106 @@
+ <string id="20393">音楽ビデオ情報</string>
+ <string id="20394">音楽ビデオ情報をå–得中</string>
+ <string id="20395">Mixed</string>
+- <string id="20396">Go To Albums by Artist</string>
+- <string id="20397">Go To Album</string>
+- <string id="20398">Play Song</string>
+- <string id="20399">Go To Music Videos from Album</string>
+- <string id="20400">Go To Music Videos by Artist</string>
+- <string id="20401">Play Music Video</string>
++ <string id="20396">アーティスト別アルãƒãƒ ã¸</string>
++ <string id="20397">アルãƒãƒ ã¸</string>
++ <string id="20398">曲をå†ç”Ÿ</string>
++ <string id="20399">アルãƒãƒ ã®ãƒŸãƒ¥ãƒ¼ã‚¸ãƒƒã‚¯ãƒ“デオã¸</string>
++ <string id="20400">アーティストã®ãƒŸãƒ¥ãƒ¼ã‚¸ãƒƒã‚¯ãƒ“デオã¸</string>
++ <string id="20401">ミュージックビデオをå†ç”Ÿ</string>
+ <string id="20402">ライブラリ追加時ã«ä¿³å„ªã®ã‚µãƒ ãƒã‚¤ãƒ«ã‚’å–å¾—ã™ã‚‹</string>
+ <string id="20403">俳優ã®ã‚µãƒ ãƒã‚¤ãƒ«ã‚’指定</string>
+- <string id="20405">Remove Episode Bookmark</string>
+- <string id="20406">Set Episode Bookmark</string>
++
++ <string id="20405">エピソードブックマークを削除</string>
++ <string id="20406">エピソードブックマークを設定</string>
+ <string id="20407">スクレーパー設定</string>
+ <string id="20408">音楽ビデオ情報をå–得中</string>
+ <string id="20409">テレビ番組情報をå–得中</string>
+ <string id="20410">予告</string>
+ <string id="20411">フラット化</string>
+ <string id="20412">テレビ番組ã®ãƒ•ãƒ©ãƒƒãƒˆåŒ–</string>
+- <string id="20413">Fanartã‚’å–å¾—</string>
+- <string id="20414">ビデオåŠã³éŸ³æ¥½ãƒ©ã‚¤ãƒ–ラリã§Fanartを表示ã™ã‚‹</string>
++ <string id="20413">Fanart ã‚’å–å¾—</string>
++ <string id="20414">ビデオåŠã³éŸ³æ¥½ãƒ©ã‚¤ãƒ–ラリ㧠Fanart を表示ã™ã‚‹</string>
+ <string id="20415">æ–°ã—ã„コンテンツをスキャン中</string>
+ <string id="20416">åˆå›žæ”¾æ˜ </string>
+ <string id="20417">脚本家</string>
+ <string id="20418">Clean File and Folder Names</string>
+- <string id="20420">Never</string>
++ <string id="20419">Replace file names with library titles</string>
++
++ <string id="20420">ã—ãªã„</string>
+ <string id="20421">1シーズンã®ã¿ã®å ´åˆ</string>
+ <string id="20422">常ã«</string>
+- <string id="20423">Has Trailer</string>
++ <string id="20423">予告編ã‚ã‚Š</string>
+ <string id="20424">False</string>
+- <string id="20425">Fanart Slideshow</string>
++ <string id="20425">Fanart スライドショー</string>
+ <string id="20426">Export to a single file or separate</string>
+ <string id="20427">files per entry?</string>
+ <string id="20428">Single file</string>
+ <string id="20429">Separate</string>
+ <string id="20430">Export thumbs and fanart?</string>
+- <string id="20431">Overwrite old files?</string>
++ <string id="20431">å¤ã„ファイルを上書ãã—ã¾ã™ã‹?</string>
+ <string id="20432">ライブラリã®æ›´æ–°ã‹ã‚‰é™¤å¤–</string>
+- <string id="20437">Fanartã‚’é¸æŠž</string>
++ <string id="20437">Fanart ã‚’é¸æŠž</string>
++ <string id="20438">ローカル Fanart</string>
++ <string id="20439">Fanart ãªã—</string>
++ <string id="20440">ç¾åœ¨ã® Fanart</string>
++ <string id="20441">リモート Fanart</string>
++ <string id="20442">コンテンツを変更</string>
++ <string id="20443">Do you want to refresh info for all</string>
++ <string id="20444">items within this path?</string>
++ <string id="20445">Fanart</string>
++ <string id="20446">Locally stored information found.</string>
++ <string id="20447">Ignore and refresh from internet?</string>
+ <string id="20448">情報をå–å¾—ã§ãã¾ã›ã‚“ã§ã—ãŸ</string>
++ <string id="20449">Unable to connect to remote server</string>
++ <string id="20450">Would you like to continue scanning?</string>
++ <string id="20451">Countries</string>
++ <string id="20452">episode</string>
++ <string id="20453">episodes</string>
++ <string id="20454">Listener</string>
++ <string id="20455">Listeners</string>
++ <string id="20456">Set movieset fanart</string>
++ <string id="20457">Movie set</string>
++ <string id="20458">Group movies in sets</string>
++ <!-- up to 21329 is reserved for the video db !! !-->
++
++
+ <string id="21330">éš ã—ファイル/フォルダーを表示</string>
+- <string id="21331">TuxBox Client</string>
++
++ <string id="21331">TuxBox クライアント</string>
+ <string id="21332">WARNING: Target TuxBox device is in Recording-Mode!</string>
+ <string id="21333">The Stream will be Stopped!</string>
+ <string id="21334">Zap to Channel: %s Failed!</string>
+ <string id="21335">Are you sure to start the stream?</string>
+ <string id="21336">Connecting to: %s</string>
+ <string id="21337">TuxBox Device</string>
+- <string id="21359">Add Media Share...</string>
+- <string id="21360">UPnPサーãƒæœ‰åŠ¹åŒ–</string>
+- <string id="21364">Edit Media Share</string>
+- <string id="21365">Remove Media Share</string>
++ <!-- up to 21355 is reserved for the TuxBox Client!! !-->
++
++ <string id="21359">メディア共有を追加...</string>
++ <string id="21360">ビデオ/ミュージックライブラリを UPnP 経由ã§å…±æœ‰</string>
++
++ <string id="21364">メディア共有を編集</string>
++ <string id="21365">メディア共有を削除</string>
+ <string id="21366">字幕フォルダー設定</string>
+ <string id="21367">Movie &amp; Alternate Subtitle Directory</string>
+- <string id="21369">マウス有効化</string>
++ <string id="21368">Override ASS/SSA subtitles fonts</string>
++
++ <string id="21369">マウス・タッãƒã‚¹ã‚¯ãƒªãƒ¼ãƒ³ã®æœ‰åŠ¹åŒ–</string>
+ <string id="21370">å†ç”Ÿæ™‚もナビゲーションサウンドを有効化</string>
+- <string id="21371">Thumbnail</string>
+- <string id="21372">Forced DVD Player Region</string>
++ <string id="21371">サムãƒãƒ¼ãƒ«</string>
++ <string id="21372">DVD Player リージョンã®å¼·åˆ¶æŒ‡å®š</string>
+ <string id="21373">ビデオ出力</string>
+ <string id="21374">アスペクト比</string>
+- <string id="21375">Normal</string>
+- <string id="21376">Letterbox</string>
+- <string id="21377">Widescreen</string>
+- <string id="21378">480p有効化</string>
+- <string id="21379">720p有効化</string>
+- <string id="21380">1080i有効化</string>
+- <string id="21381">Enter Name of New Playlist</string>
++ <string id="21375">ノーマル</string>
++ <string id="21376">レターボックス</string>
++ <string id="21377">ワイドスクリーン</string>
++ <string id="21378">480p 有効化</string>
++ <string id="21379">720p 有効化</string>
++ <string id="21380">1080i 有効化</string>
++ <string id="21381">æ–°è¦ãƒ—レイリストåを入力</string>
+ <string id="21382">ソース追加ボタンを無効</string>
+ <string id="21383">スクロールãƒãƒ¼è¡¨ç¤º</string>
+ <string id="21384">視è´æ¸ˆã¿é™¤å¤–ボタンをビデオライブラリã«è¿½åŠ </string>
+- <string id="21385">Open</string>
++ <string id="21385">é–‹ã</string>
+ <string id="21386">Acoustic Management Level</string>
+ <string id="21387">Fast</string>
+ <string id="21388">Quiet</string>
+@@ -1392,11 +1922,11 @@
+ <string id="21392">Low Power</string>
+ <string id="21393">High Standby</string>
+ <string id="21394">Low Standby</string>
+- <string id="21395">Unable to cache files bigger than 4 gb</string>
+- <string id="21396">Chapter</string>
++ <string id="21395">4GB 以上ã®ãƒ•ã‚¡ã‚¤ãƒ«ã¯ã‚­ãƒ£ãƒƒã‚·ãƒ¥ã§ãã¾ã›ã‚“</string>
++ <string id="21396">ãƒãƒ£ãƒ—ター</string>
+ <string id="21397">High Quality Pixel Shader V2</string>
+ <string id="21398">起動時後プレイリストをå†ç”Ÿ</string>
+- <string id="21399">Tweenアニメーション有効化</string>
++ <string id="21399">Tween アニメーション有効化</string>
+ <string id="21400">contains</string>
+ <string id="21401">does not contain</string>
+ <string id="21402">is</string>
+@@ -1423,64 +1953,84 @@
+ <string id="21426">one or more of the rules</string>
+ <string id="21427">Limit to</string>
+ <string id="21428">No Limit</string>
+- <string id="21429">Order by</string>
+- <string id="21430">ascending</string>
+- <string id="21431">descending</string>
+- <string id="21432">Edit Smart Playlist</string>
+- <string id="21433">Name of the playlist</string>
++ <string id="21429">並ã¹æ›¿ãˆ</string>
++ <string id="21430">昇順</string>
++ <string id="21431">é™é †</string>
++ <string id="21432">スマートプレイリストã®ç·¨é›†</string>
++ <string id="21433">プレイリストå</string>
+ <string id="21434">Find items where</string>
+- <string id="21435">Edit</string>
+- <string id="21436">%i items</string>
+- <string id="21437">New Smart Playlist...</string>
+- <string id="21438">%c Drive</string>
+- <string id="21439">Edit Party Mode Rules</string>
+- <string id="21441">Watched count</string>
+- <string id="21442">Episode Title</string>
+- <string id="21800">File Name</string>
+- <string id="21801">File Path</string>
+- <string id="21802">File Size</string>
+- <string id="21803">File Date/Time</string>
++ <string id="21435">編集</string>
++ <string id="21436">%i アイテム</string>
++ <string id="21437">æ–°è¦ã‚¹ãƒžãƒ¼ãƒˆãƒ—レイリスト...</string>
++ <string id="21438">%c ドライブ</string>
++ <string id="21439">パーティモードルールã®ç·¨é›†</string>
++ <string id="21441">試è´å›žæ•°</string>
++ <string id="21442">エピソードタイトル</string>
++ <string id="21443">ビデオã®è§£åƒåº¦</string>
++ <string id="21444">オーディオãƒãƒ£ãƒ³ãƒãƒ«</string>
++ <string id="21445">ビデオコーデック</string>
++ <string id="21446">オーディオコーデック</string>
++ <string id="21447">音声言語</string>
++ <string id="21448">字幕言語</string>
++ <string id="21449">リモコンã«ã‚ˆã‚‹ã‚­ãƒ¼ãƒœãƒ¼ãƒ‰å…¥åŠ›</string>
++ <string id="21450">- 編集</string>
++ <string id="21451">インターãƒãƒƒãƒˆæŽ¥ç¶šãŒå¿…è¦ã§ã™ã€‚</string>
++ <string id="21452">Get More...</string>
++ <string id="21453">Root filesystem</string>
++ <string id="21454">Cache full</string>
++ <string id="21455">Cache filled before reaching required amount for continous playback</string>
++
++ <string id="21460">字幕ã®å ´æ‰€</string>
++ <string id="21461">Fixed</string>
++ <string id="21462">Bottom of video</string>
++ <string id="21463">Below video</string>
++ <string id="21464">Top of video</string>
++ <string id="21465">Above video</string>
++ <string id="21800">ファイルå</string>
++ <string id="21801">ファイルã®ãƒ‘ス</string>
++ <string id="21802">ファイルサイズ</string>
++ <string id="21803">ファイルã®æ—¥ä»˜</string>
+ <string id="21804">Slide Index</string>
+- <string id="21805">Resolution</string>
+- <string id="21806">Comment</string>
+- <string id="21807">Colour/bw</string>
+- <string id="21808">Jpeg Process</string>
+- <string id="21820">Date/Time</string>
+- <string id="21821">Description</string>
+- <string id="21822">Camera Make</string>
+- <string id="21823">Camera Model</string>
+- <string id="21824">Exif Comment</string>
+- <string id="21825">Firmware</string>
+- <string id="21826">Aperture</string>
+- <string id="21827">Focal Length</string>
+- <string id="21828">Focus Distance</string>
+- <string id="21829">Exposure</string>
+- <string id="21830">Exposure Time</string>
+- <string id="21831">Exposure Bias</string>
+- <string id="21832">Exposure Mode</string>
+- <string id="21833">Flash Used</string>
+- <string id="21834">Whitebalance</string>
+- <string id="21835">Light Source</string>
+- <string id="21836">Metering Mode</string>
++ <string id="21805">解åƒåº¦</string>
++ <string id="21806">コメント</string>
++ <string id="21807">カラー/白黒</string>
++ <string id="21808">JPEG 処ç†</string>
++ <string id="21820">日時</string>
++ <string id="21821">説明</string>
++ <string id="21822">カメラã®ãƒ¡ãƒ¼ã‚«ãƒ¼</string>
++ <string id="21823">カメラã®ãƒ¢ãƒ‡ãƒ«å</string>
++ <string id="21824">EXIF コメント</string>
++ <string id="21825">ファームウェア</string>
++ <string id="21826">絞り</string>
++ <string id="21827">焦点è·é›¢</string>
++ <string id="21828">ピントä½ç½®</string>
++ <string id="21829">露出</string>
++ <string id="21830">露出時間</string>
++ <string id="21831">露出ãƒã‚¤ã‚¢ã‚¹</string>
++ <string id="21832">露出モード</string>
++ <string id="21833">フラッシュã®æœ‰ç„¡</string>
++ <string id="21834">ホワイトãƒãƒ©ãƒ³ã‚¹</string>
++ <string id="21835">å…‰æº</string>
++ <string id="21836">測光方å¼</string>
+ <string id="21837">ISO</string>
+- <string id="21838">Digital Zoom</string>
+- <string id="21839">CCD Width</string>
+- <string id="21840">GPS Latitude</string>
+- <string id="21841">GPS Longitude</string>
+- <string id="21842">GPS Altitude</string>
+- <string id="21843">Orientation</string>
+- <string id="21860">Supplemental Categories</string>
+- <string id="21861">Keywords</string>
+- <string id="21862">Caption</string>
+- <string id="21863">Author</string>
+- <string id="21864">Headline</string>
++ <string id="21838">ディジタルズーム</string>
++ <string id="21839">CCD ã®æ¨ªå¹…</string>
++ <string id="21840">GPS 緯度</string>
++ <string id="21841">GPS 経度</string>
++ <string id="21842">GPS 高度</string>
++ <string id="21843">写真ã®å‘ã</string>
++ <string id="21860">追加カテゴリ</string>
++ <string id="21861">キーワード</string>
++ <string id="21862">キャプション</string>
++ <string id="21863">作者</string>
++ <string id="21864">ヘッドライン</string>
+ <string id="21865">Special Instructions</string>
+- <string id="21866">Category</string>
++ <string id="21866">カテゴリー</string>
+ <string id="21867">Byline</string>
+ <string id="21868">Byline Title</string>
+- <string id="21869">Credit</string>
+- <string id="21870">Source</string>
+- <string id="21871">Copyright Notice</string>
++ <string id="21869">クレジット</string>
++ <string id="21870">ソース</string>
++ <string id="21871">著作権情報</string>
+ <string id="21872">Object Name</string>
+ <string id="21873">City</string>
+ <string id="21874">State</string>
+@@ -1490,59 +2040,340 @@
+ <string id="21878">Copyright Flag</string>
+ <string id="21879">Country Code</string>
+ <string id="21880">Reference Service</string>
+- <string id="21881">Enable UPnP Renderer</string>
+- <string id="21882">Attempt to skip introduction before DVD Menu</string>
+- <string id="21883">Ripped Audio CDs</string>
+- <string id="21884">Query Info For All Artists</string>
++ <string id="21881">UPnP 経由ã§ã®XBMC ã®æ“作を許å¯</string>
++ <string id="21882">DVD メニューよりå‰ã®ã‚¤ãƒ³ãƒˆãƒ­éƒ¨åˆ†ã‚’スキップ (å¯èƒ½ãªå ´åˆ)</string>
++ <string id="21883">Saved music</string>
++ <string id="21884">全アーティストã®æƒ…報を検索</string>
+ <string id="21885">アルãƒãƒ æƒ…報をå–得中</string>
+ <string id="21886">アーティスト情報をå–得中</string>
+- <string id="21887">Biography</string>
+- <string id="21888">Discography</string>
+- <string id="21889">Searching Artist</string>
+- <string id="21890">Select Artist</string>
++ <string id="21887">ãƒã‚¤ã‚ªã‚°ãƒ©ãƒ•ã‚£ãƒ¼</string>
++ <string id="21888">ディスコグラフィー</string>
++ <string id="21889">アーティストã®æ¤œç´¢</string>
++ <string id="21890">アーティストをé¸æŠž</string>
+ <string id="21891">アーティスト情報</string>
++ <string id="21892">楽器</string>
++ <string id="21893">生ã¾ã‚Œ</string>
++ <string id="21894">çµæˆ</string>
++ <string id="21895">テーマ</string>
++ <string id="21896">解散</string>
++ <string id="21897">死去</string>
++ <string id="21898">アクティブãªæœŸé–“</string>
++ <string id="21899">レーベル</string>
++ <string id="21900">生誕/çµæˆ</string>
++
++ <!-- strings 21900 thru 21999 reserved for slideshow info -->
++
+ <string id="22000">開始時ã«ãƒ©ã‚¤ãƒ–ラリを更新</string>
+ <string id="22001">ライブラリã®æ›´æ–°çŠ¶æ³ã‚’表示ã—ãªã„</string>
+ <string id="22002">- DNSサフィックス</string>
++
++ <string id="22003">%2.3fs</string>
+ <string id="22004">Delayed by: %2.3fs</string>
+ <string id="22005">Ahead by: %2.3fs</string>
+ <string id="22006">字幕オフセット</string>
+- <string id="22007">OpenGLベンダー:</string>
+- <string id="22008">OpenGLレンダラー:</string>
+- <string id="22009">OpenGLãƒãƒ¼ã‚¸ãƒ§ãƒ³:</string>
+- <string id="22010">GPU温度:</string>
+- <string id="22011">CPU温度:</string>
++ <string id="22007">OpenGL ベンダー:</string>
++ <string id="22008">OpenGL レンダラー:</string>
++ <string id="22009">OpenGL ãƒãƒ¼ã‚¸ãƒ§ãƒ³:</string>
++ <string id="22010">GPU 温度:</string>
++ <string id="22011">CPU 温度:</string>
+ <string id="22012">åˆè¨ˆãƒ¡ãƒ¢ãƒªãƒ¼</string>
+- <string id="22013">Profile Data</string>
++ <string id="22013">プロファイルデータ</string>
++ <string id="22014">ビデオå†ç”Ÿä¸­ã«ä¸€æ™‚åœæ­¢ã—ãŸã‚‰ç”»é¢ã‚’æš—ãã™ã‚‹</string>
++ <string id="22015">全録音</string>
++ <string id="22016">タイトル順</string>
++ <string id="22017">グループ順</string>
++ <string id="22018">ライブãƒãƒ£ãƒ³ãƒãƒ«</string>
++ <string id="22019">タイトル順録音</string>
++ <string id="22020">ガイド</string>
++ <string id="22021">Allowed error in aspect ratio to minimize black bars</string>
++ <string id="22022">Show video files in listings</string>
++ <string id="22023">DirectX ベンダー:</string>
++ <string id="22024">Direct3D ãƒãƒ¼ã‚¸ãƒ§ãƒ³:</string>
++
++ <!-- strings 22030 thru 22060 reserved for karaoke -->
++ <string id="22030">フォント</string>
++ <string id="22031">- サイズ</string>
++ <string id="22032">- 色</string>
++ <string id="22033">- 文字セット</string>
++ <string id="22034">カラオケタイトルをエクスãƒãƒ¼ãƒˆ (HTML)</string>
++ <string id="22035">カラオケタイトルをエクスãƒãƒ¼ãƒˆ (CSV)</string>
++ <string id="22036">カラオケタイトルã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ...</string>
++ <string id="22037">Show song selector automatically</string>
++ <string id="22038">カラオケタイトルã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ...</string>
++ <string id="22039">Enter song number</string>
++ <string id="22040">白/緑</string>
++ <string id="22041">白/赤</string>
++ <string id="22042">白/é’</string>
++ <string id="22043">黒/白</string>
++
+ <string id="22079">デフォルトã®å‹•ä½œ</string>
++ <string id="22080">é¸æŠž</string>
++ <string id="22081">情報を表示</string>
+ <string id="22082">ã•ã‚‰ã«...</string>
++ <string id="22083">å…¨ã¦å†ç”Ÿ</string>
++
++ <string id="23049">Teletext not available</string>
++ <string id="23050">Activate Teletext</string>
++ <string id="23051">Part %i</string>
++ <string id="23052">Buffering %i bytes</string>
++ <string id="23053">Stopping</string>
++ <string id="23054">Running</string>
++
++ <!-- strings 23100 thru 23150 reserved for external player -->
++ <string id="23100">External Player Active</string>
++ <string id="23101">Click OK to terminate the player</string>
++
++ <string id="23104">Click OK when playback has ended</string>
++
+ <string id="24000">アドオン</string>
+ <string id="24001">アドオン</string>
+ <string id="24002">アドオン設定</string>
+ <string id="24003">アドオン情報</string>
++
++ <string id="24005">メディアソース</string>
++ <string id="24007">ムービー情報</string>
+ <string id="24008">スクリーンセーãƒãƒ¼</string>
++ <string id="24009">スクリプト</string>
++ <string id="24010">視覚エフェクト</string>
+ <string id="24011">アドオン レãƒã‚¸ãƒˆãƒª</string>
++ <string id="24011">字幕</string>
++ <string id="24013">歌詞</string>
++ <string id="24014">TV 情報</string>
++ <string id="24015">ミュージックビデオ情報</string>
++ <string id="24016">アルãƒãƒ æƒ…å ±</string>
++ <string id="24017">アーティスト情報</string>
++ <string id="24018">サービス</string>
++
++ <string id="24020">設定</string>
++ <string id="24021">無効</string>
++ <string id="24022">有効</string>
+ <string id="24023">アドオン無効</string>
+- <string id="24030">設定ã§ããªã„アドオン</string>
++ <string id="24027">天気</string>
++ <string id="24028">Weather.com (標準)</string>
++ <string id="24030">ã“ã®ã‚¢ãƒ‰ã‚ªãƒ³ã¯è¨­å®šã§ãã¾ã›ã‚“</string>
++ <string id="24031">設定ã®èª­ã¿è¾¼ã¿ã«å¤±æ•—</string>
+ <string id="24032">ã™ã¹ã¦ã®ã‚¢ãƒ‰ã‚ªãƒ³</string>
+ <string id="24033">アドオンå–å¾—</string>
++ <string id="24034">アップデートã®ç¢ºèª</string>
++ <string id="24035">強制的ã«ãƒªãƒ•ãƒ¬ãƒƒã‚·ãƒ¥</string>
++ <string id="24036">変更履歴</string>
++ <string id="24037">アンインストール</string>
++ <string id="24038">インストール</string>
+ <string id="24039">無効ãªã‚¢ãƒ‰ã‚ªãƒ³</string>
+- <string id="24041">zipファイルã‹ã‚‰ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«</string>
++ <string id="24040">(ç¾åœ¨ã®è¨­å®šã‚’クリア)</string>
++ <string id="24041">zip ファイルã‹ã‚‰ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«</string>
+ <string id="24042">ダウンロード中 %i%%</string>
+- <string id="24050">有効ãªã‚¢ãƒ‰ã‚ªãƒ³</string>
+- <string id="24059">ã“ã®ã‚¢ãƒ‰ã‚ªãƒ³ã‚’有効化ã—ã¾ã™ã‹ï¼Ÿ</string>
+- <string id="24060">ã“ã®ã‚¢ãƒ‰ã‚ªãƒ³ã‚’無効化ã—ã¾ã™ã‹ï¼Ÿ</string>
+- <string id="24061">アドオンã®ã‚¢ãƒƒãƒ—デートãŒå¯èƒ½ï¼</string>
++ <string id="24043">利用å¯èƒ½ãªã‚¢ãƒƒãƒ—デート</string>
++ <string id="24044">ä¾å­˜é–¢ä¿‚ãŒæº€ãŸã•ã‚Œã¾ã›ã‚“</string>
++ <string id="24045">アドオンãŒæ­£ã—ã„構造ã«ãªã£ã¦ã„ã¾ã›ã‚“</string>
++ <string id="24046">%s ã¯ä»¥ä¸‹ã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«æ¸ˆã®ã‚¢ãƒ‰ã‚ªãƒ³ã«ä½¿ã‚ã‚Œã¦ã„ã¾ã™</string>
++ <string id="24047">ã“ã®ã‚¢ãƒ‰ã‚ªãƒ³ã¯ã‚¢ãƒ³ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã§ãã¾ã›ã‚“</string>
++ <string id="24048">ロールãƒãƒƒã‚¯</string>
++
++ <string id="24050">利用å¯èƒ½ãªã‚¢ãƒ‰ã‚ªãƒ³</string>
++ <string id="24051">ãƒãƒ¼ã‚¸ãƒ§ãƒ³:</string>
++ <string id="24052">注æ„書ã</string>
++ <string id="24053">ライセンス:</string>
++ <string id="24054">変更履歴</string>
++ <string id="24059">ã“ã®ã‚¢ãƒ‰ã‚ªãƒ³ã‚’有効ã«ã—ã¾ã™ã‹ï¼Ÿ</string>
++ <string id="24060">ã“ã®ã‚¢ãƒ‰ã‚ªãƒ³ã‚’無効ã«ã—ã¾ã™ã‹ï¼Ÿ</string>
++ <string id="24061">アドオンã®ã‚¢ãƒƒãƒ—デートãŒã‚ã‚Šã¾ã™!</string>
+ <string id="24062">有効ãªã‚¢ãƒ‰ã‚ªãƒ³</string>
+ <string id="24063">自動更新</string>
+- <string id="24064">アドオン有効</string>
+- <string id="24065">アドオン更新</string>
+- <string id="24066">アドオンã®ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã‚’キャンセル?</string>
++ <string id="24064">アドオンãŒæœ‰åŠ¹ã«ãªã‚Šã¾ã—ãŸ</string>
++ <string id="24065">アドオンãŒæ›´æ–°ã•ã‚Œã¾ã—ãŸ</string>
++ <string id="24066">アドオンã®ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã‚’キャンセルã—ã¾ã™ã‹?</string>
+ <string id="24067">ç¾åœ¨ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ä¸­ã®ã‚¢ãƒ‰ã‚ªãƒ³</string>
++ <string id="24068">アップデートå¯èƒ½</string>
++ <string id="24069">アップデート</string>
++
++ <string id="24070">アドオンを読ã¿è¾¼ã‚ã¾ã›ã‚“ã§ã—ãŸã€‚</string>
++ <string id="24071">原因ä¸æ˜Žã®ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚</string>
++ <string id="24072">設定ãŒå¿…è¦</string>
++ <string id="24073">接続ä¸å¯</string>
++ <string id="24074">å†èµ·å‹•ãŒå¿…è¦</string>
++ <string id="24075">無効化</string>
++ <string id="24076">å¿…è¦ãªã‚¢ãƒ‰ã‚ªãƒ³</string>
++ <string id="24080">最接続ã—ã¾ã™ã‹?</string>
++ <string id="24089">アドオンã®å†èµ·å‹•</string>
++ <string id="24090">アドオンマãƒãƒ¼ã‚¸ãƒ£ã‚’ロック</string>
++
++ <string id="24094">(ç¾åœ¨)</string>
++ <string id="24095">(ブラックリスト)</string>
++ <string id="24096">Add-on has been marked as broken in repository.</string>
++ <string id="24097">ã“ã®ã‚·ã‚¹ãƒ†ãƒ ä¸Šã§ç„¡åŠ¹ã«ã—ã¾ã™ã‹?</string>
++ <string id="24098">Broken</string>
++ <string id="24099">ã“ã®ã‚¹ã‚­ãƒ³ã«åˆ‡ã‚Šæ›¿ãˆã¾ã™ã‹?</string>
++ <string id="24100">ã“ã®æ©Ÿèƒ½ã‚’使ã†ã«ã¯ä»¥ä¸‹ã®ã‚¢ãƒ‰ã‚ªãƒ³ã®ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ãŒå¿…è¦ã§ã™:</string>
++ <string id="24101">ã“ã®ã‚¢ãƒ‰ã‚ªãƒ³ã‚’ダウンロードã—ã¾ã™ã‹?</string>
+ <string id="25000">通知</string>
++
++ <!-- strings 29800 thru 29998 reserved strings used only in the default Project Mayhem III skin and not c++ code -->
+ <string id="29800">ライブラリモード</string>
+ <string id="29801">QWERTYキーボード</string>
+ <string id="29802">パススルーオーディオ使用中</string>
++
++ <!-- strings 30000 thru 30999 reserved for plugins and plugin settings -->
++ <!-- strings 31000 thru 31999 reserved for skins -->
++ <!-- strings 32000 thru 32999 reserved for scripts -->
++ <!-- strings 33000 thru 33999 reserved for common strings used in addons -->
++ <string id="33001">予告編ã®å“質</string>
++ <string id="33002">ストリーム</string>
++ <string id="33003">ダウンロード</string>
++ <string id="33004">ダウンロードã—ã¦å†ç”Ÿ</string>
++ <string id="33005">ダウンロードã—ã¦ä¿å­˜</string>
++ <string id="33006">今日</string>
++ <string id="33007">明日</string>
++ <string id="33008">Saving</string>
++ <string id="33009">Copying</string>
++ <string id="33010">ダウンロードフォルダã®æŒ‡å®š</string>
++ <string id="33011">Search duration</string>
++ <string id="33012">Short</string>
++ <string id="33013">Long</string>
++ <string id="33014">通常ã®ãƒ—レーヤーã®ä»£ã‚ã‚Šã« DVD プレーヤーを使用</string>
++ <string id="33015">ビデオå†ç”Ÿå‰ã«ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã™ã‚‹ã‹ç¢ºèªã™ã‚‹</string>
++ <string id="33016">クリップ</string>
++ <string id="33017">プラグインをå†èµ·å‹•ã—ã¦æœ‰åŠ¹ã«ã™ã‚‹</string>
++ <string id="33018">今夜</string>
++ <string id="33019">明日ã®å¤œ</string>
++ <string id="33020">Condition</string>
++ <string id="33021">é™é›¨é‡</string>
++ <string id="33022">é™é›¨é‡</string>
++ <string id="33023">Humid</string>
++ <string id="33024">Feels</string>
++ <string id="33025">Observed</string>
++ <string id="33026">Departure from normal</string>
++ <string id="33027">Sunrise</string>
++ <string id="33028">日没</string>
++ <string id="33029">Details</string>
++ <string id="33030">Outlook</string>
++ <string id="33031">ã‚«ãƒãƒ¼ãƒ•ãƒ­ãƒ¼</string>
++ <string id="33032">テキストを翻訳</string>
++ <string id="33033">Map list %s category</string>
++ <string id="33034">36 Hour</string>
++ <string id="33035">マップ</string>
++ <string id="33036">時間ã”ã¨</string>
++ <string id="33037">週末</string>
++ <string id="33038">%sæ—¥</string>
++ <string id="33049">アラート</string>
++ <string id="33050">アラート</string>
++ <string id="33051">é¸æŠžã™ã‚‹: </string>
++ <string id="33052">ãƒã‚§ãƒƒã‚¯: </string>
++ <string id="33053">設定ã™ã‚‹: </string>
+ <string id="33054">シーズン</string>
++ <string id="33055">使用: </string>
++ <string id="33056">観る: </string>
++ <string id="33057">è´ã: </string>
++ <string id="33058">見る: </string>
++ <string id="33059">設定ã™ã‚‹: </string>
++ <string id="33060">Power</string>
+ <string id="33061">メニュー</string>
++ <string id="33062">å†ç”Ÿã™ã‚‹: </string>
++ <string id="33063">オプション</string>
++ <string id="33065">エディター</string>
++ <string id="33066">About your</string>
++ <string id="33067">星ã®è©•ä¾¡</string>
++ <string id="33068">ãƒãƒƒã‚¯ã‚°ãƒ©ã‚¦ãƒ³ãƒ‰</string>
++ <string id="33069">ãƒãƒƒã‚¯ã‚°ãƒ©ã‚¦ãƒ³ãƒ‰</string>
++ <string id="33070">カスタムãƒãƒƒã‚¯ã‚°ãƒ©ã‚¦ãƒ³ãƒ‰</string>
++ <string id="33071">カスタムãƒãƒƒã‚¯ã‚°ãƒ©ã‚¦ãƒ³ãƒ‰</string>
++ <string id="33072">README ã‚’ã¿ã‚‹</string>
++ <string id="33073">Changelog ã‚’ã¿ã‚‹</string>
++ <string id="33074">ã“ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã® %s ã¯</string>
++ <string id="33075">XBMC リビジョン %s ã¾ãŸã¯ã‚ˆã‚Šæ–°ã—ã„版ãŒå¿…è¦ã§ã™ã€‚</string>
++ <string id="33076">XBMC をアップデートã—ã¦ãã ã•ã„。</string>
++ <string id="33077">データãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“!</string>
++ <string id="33078">次ã®ãƒšãƒ¼ã‚¸</string>
++ <string id="33079">Love</string>
++ <string id="33080">Hate</string>
++ <string id="33081">This file is stacked, select the part you want to play from.</string>
++ <string id="33082">スクリプトã®ãƒ‘ス</string>
++ <string id="33083">カスタムスクリプト用ボタンを有効ã«ã™ã‚‹</string>
++
++ <string id="33100">èµ·å‹•ã«å¤±æ•—ã—ã¾ã—ãŸ: </string>
++ <string id="33101">Web サーãƒãƒ¼</string>
++ <string id="33102">イベントサーãƒãƒ¼</string>
++ <string id="33103">リモートコミュニケーションサーãƒãƒ¼</string>
++
++ <string id="33200">æ–°è¦ã‚³ãƒã‚¯ã‚·ãƒ§ãƒ³ã‚’検出</string>
++
++ <!-- translators: no need to add these to your language files -->
++ <!--
++ <string id="34000">Lame</string>
++ <string id="34001">Vorbis</string>
++ <string id="34002">Wav</string>
++ <string id="34003">DXVA2</string>
++ <string id="34004">VAAPI</string>
++ <string id="34005">Flac</string>
++ -->
++
++ <string id="34100">スピーカー設定</string>
++ <string id="34101">2.0</string>
++ <string id="34102">2.1</string>
++ <string id="34103">3.0</string>
++ <string id="34104">3.1</string>
++ <string id="34105">4.0</string>
++ <string id="34106">4.1</string>
++ <string id="34107">5.0</string>
++ <string id="34108">5.1</string>
++ <string id="34109">7.0</string>
++ <string id="34110">7.1</string>
++ <!-- 34112-34200 reserved for future use -->
++
++ <string id="34201">次ã®ã‚¢ã‚¤ãƒ†ãƒ ãŒã‚ã‚Šã¾ã›ã‚“</string>
++ <string id="34202">一ã¤å‰ã®ã‚¢ã‚¤ãƒ†ãƒ ãŒã‚ã‚Šã¾ã›ã‚“</string>
++
++ <string id="34300">Zeroconf èµ·å‹•ã«å¤±æ•—</string>
++ <string id="34301">Apple ã® Bonjour サービスã¯ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•ã‚Œã¦ã„ã¾ã™ã‹? 詳ã—ãã¯ãƒ­ã‚°ã‚’å‚ç…§ã—ã¦ä¸‹ã•ã„。</string>
++
++ <string id="34400">ビデオレンダリング</string>
++ <string id="34401">ビデオフィルタ/リサイズã®åˆæœŸåŒ–ã«å¤±æ•—ã—ã¾ã—ãŸã€‚代ã‚ã‚Šã«ãƒã‚¤ãƒªãƒ‹ã‚¢ã‚¹ã‚±ãƒ¼ãƒªãƒ³ã‚°ã‚’è¡Œã„ã¾ã™ã€‚</string>
++ <string id="34402">オーディオデãƒã‚¤ã‚¹ã®åˆæœŸåŒ–ã«å¤±æ•—</string>
++ <string id="34403">オーディオ設定を見直ã—ã¦ãã ã•ã„</string>
++
++ <string id="35000">周辺機器</string>
++
++ <string id="35001">汎用 HID デãƒã‚¤ã‚¹</string>
++ <string id="35002">汎用ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚¢ãƒ€ãƒ—ã‚¿</string>
++ <string id="35003">汎用ディスク</string>
++ <string id="35004">ã“ã®å‘¨è¾ºæ©Ÿå™¨ç”¨ã®è¨­å®šã¯ã‚ã‚Šã¾ã›ã‚“。</string>
++ <string id="35005">æ–°ã—ã„デãƒã‚¤ã‚¹ãŒè¨­å®šã•ã‚Œã¾ã—ãŸ</string>
++ <string id="35006">デãƒã‚¤ã‚¹ãŒå–り外ã•ã‚Œã¾ã—ãŸ</string>
++ <string id="35007">ã“ã®ãƒ‡ãƒã‚¤ã‚¹ã§ä½¿ã†ã‚­ãƒ¼ãƒžãƒƒãƒ—</string>
++ <string id="35008">キーマップ有効</string>
++ <string id="35009">ã“ã®ãƒ‡ãƒã‚¤ã‚¹ã§ã¯ã‚«ã‚¹ã‚¿ãƒ ã‚­ãƒ¼ãƒžãƒƒãƒ—を使ã‚ãªã„ã§ä¸‹ã•ã„</string>
++
++ <string id="35500">Location</string>
++ <string id="35501">Class</string>
++ <string id="35502">Name</string>
++ <string id="35503">Vendor</string>
++ <string id="35504">Product ID</string>
++
++ <string id="36000">Pulse-Eight CEC アダプタ</string>
++ <string id="36001">Pulse-Eight Nyxboard リモコン</string>
++ <string id="36002">Switch to keyboard side command</string>
++ <string id="36003">Switch to remote side command</string>
++ <string id="36004">Press "user" button command</string>
++ <string id="36005">Enable switch side commands</string>
++ <string id="36006">アダプタをオープンã§ãã¾ã›ã‚“ã§ã—ãŸ</string>
++ <string id="36007">XBMC 起動時ã«é›»æºã‚’入れるデãƒã‚¤ã‚¹</string>
++ <string id="36008">XBMC 終了時ã«é›»æºã‚’切るデãƒã‚¤ã‚¹</string>
++ <string id="36009">スクリーンセーãƒãƒ¼å‹•ä½œä¸­ã«ãƒ‡ãƒã‚¤ã‚¹ã‚’スタンãƒã‚¤ãƒ¢ãƒ¼ãƒ‰ã«ã™ã‚‹</string>
++ <string id="36010"></string>
++ <string id="36011">CEC ãƒãƒ¼ãƒˆãŒæ¤œå‡ºã§ãã¾ã›ã‚“ã§ã—ãŸã€‚手動ã§è¨­å®šã—ã¦ãã ã•ã„。</string>
++
++ <string id="36012">CEC アダプタã®åˆæœŸåŒ–ã«å¤±æ•—ã—ã¾ã—ãŸã€‚設定を見直ã—ã¦ãã ã•ã„。</string>
++ <string id="36013">ã“ã® libCEC インターフェースãƒãƒ¼ã‚¸ãƒ§ãƒ³ã¯æœªã‚µãƒãƒ¼ãƒˆã§ã™ã€‚%d ã¯ã€XBMC ãŒã‚µãƒãƒ¼ãƒˆã™ã‚‹ãƒãƒ¼ã‚¸ãƒ§ãƒ³ %d より大ãã„ãƒãƒ¼ã‚¸ãƒ§ãƒ³ç•ªå·ã§ã™ã€‚</string>
++ <string id="36014">TV ã®é›»æºãŒè½ã¨ã•ã‚ŒãŸã‚‰ PC をスタンãƒã‚¤ãƒ¢ãƒ¼ãƒ‰ã«ã™ã‚‹</string>
++ <string id="36015">HDMI ãƒãƒ¼ãƒˆç•ªå·</string>
++ <string id="36016">接続完了</string> <!-- max. 13 characters -->
++ <string id="36017">アダプタã¯ã‚ã‚Šã¾ã™ãŒ libCEC ãŒã‚ã‚Šã¾ã›ã‚“</string>
++ <string id="36018">TV ã®è¨€èªžè¨­å®šã‚’使ã£ã¦ãã ã•ã„</string>
++ <string id="36019">HDMI デãƒã‚¤ã‚¹ã«æŽ¥ç¶šã—ã¾ã—ãŸ</string>
++ <string id="36020">起動時㫠XBMC をアクティブソースã«ã™ã‚‹</string>
++ <string id="36021">物ç†ã‚¢ãƒ‰ãƒ¬ã‚¹ (HDMI ãƒãƒ¼ãƒˆã‚’上書ã)</string>
++ <string id="36022">COM ãƒãƒ¼ãƒˆ (ä¸è¦ã§ã‚ã‚Œã°ç©ºæ¬„ã«)</string>
++ <string id="36023">設定ãŒå¤‰æ›´ã•ã‚Œã¾ã—ãŸ</string>
++ <string id="36024">設定変更ã«å¤±æ•—ã—ã¾ã—ãŸã€‚設定内容を確èªã—ã¦ãã ã•ã„。</string>
++ <string id="36025">XBMC 終了時ã«'éžã‚¢ã‚¯ãƒ†ã‚£ãƒ–ソース'コマンドをé€ä¿¡</string>
+ </strings>
+diff --git a/language/Polish/strings.xml b/language/Polish/strings.xml
+index ac07e8e..21ecd37 100644
+--- a/language/Polish/strings.xml
++++ b/language/Polish/strings.xml
+@@ -1369,7 +1369,241 @@
+ <string id="16315">Lanczos3 (zoptymalizowany)</string>
+ <string id="16316">Auto</string>
+ <string id="17500">Wyświetlaj odliczanie czasu uśpienia</string>
+- <string id="19000">Wybierz kanał</string>
++
++ <!-- strings 19000 thru 19999 used for tv interface -->
++ <string id="19000">Przełącz na kanał</string>
++ <string id="19001">Osobne wyrazy wyszukiwania za pomocÄ… AND, OR lub NOT.</string>
++ <string id="19002">lub wyrażenia użyć w celu znalezienia dokładnego dopasowania, jak "Czarnoksiężnik z krainy Oz ".</string>
++ <string id="19003">Znajdź podobny program</string>
++ <string id="19004">Importowanie EPG od klientów</string>
++ <string id="19005">PVR informacje o strumieniu</string>
++ <string id="19006">UrzÄ…dzenie odbiorcze</string>
++ <string id="19007">Aktualizacja statusu urzÄ…dzenia</string>
++ <string id="19008">Jakość sygnału</string>
++ <string id="19009">SNR</string>
++ <string id="19010">BER</string>
++ <string id="19011">UNC</string>
++ <string id="19012">PVR Backend</string>
++ <string id="19013">Niekodowanych</string>
++ <string id="19014">Fixed</string>
++ <string id="19015">Kodowanie</string>
++ <string id="19016">PVR Backend %i - %s</string>
++ <string id="19017">Nagrania TV</string>
++ <string id="19018">Domyślny folder dla miniatur PVR</string>
++ <string id="19019">Kanały</string>
++ <string id="19020">TV</string>
++ <string id="19021">Radio</string>
++ <string id="19022">Ukryte</string>
++ <string id="19023">Kanały TV</string>
++ <string id="19024">Kanały Radiowe</string>
++ <string id="19025">NadchodzÄ…ce nagrania</string>
++ <string id="19026">Dodaj timer...</string>
++ <string id="19027">Brak wyników wyszukiwania</string>
++ <string id="19028">Brak wpisów EPG</string>
++ <string id="19029">Kanał</string>
++ <string id="19030">Teraz</string>
++ <string id="19031">Następny</string>
++ <string id="19032">Linia czasu</string>
++ <string id="19033">Informacje</string>
++ <string id="19034">Już rozpoczął nagrywanie na tym kanale</string>
++ <string id="19035">Ten kanał nie może być odtwarzany. Sprawdź dziennik(log)</string>
++ <string id="19036">To nagranie nie może być odtworzone. Sprawdź dziennik(log). .</string>
++ <string id="19037">Pokaż jakość sygnału</string>
++ <string id="19038">Nie jest obsługiwany przez PVR backend.</string>
++ <string id="19039">Czy na pewno chcesz ukryć ten kanał?</string>
++ <string id="19040">Timer</string>
++ <string id="19041">Czy na pewno chcesz zmienić nazwę tego nagrania</string>
++ <string id="19042">Czy na pewno chcesz zmienić nazwę tego timer-a?</string>
++ <string id="19043">Nagrywanie</string>
++ <string id="19044">Proszę sprawdzić konfigurację lub dziennik(log)</string>
++ <string id="19045">Żaden klient PVR nie urchomił sie. Poczekaj na start klientów PVR lub sprawdz dziennik(log).</string>
++ <string id="19046">Nowy kanał</string>
++ <string id="19047">Informacje o programie</string>
++ <string id="19048">ZarzÄ…dzanie Grupami</string>
++ <string id="19049">Pokaż kanał</string>
++ <string id="19050">Pokaż widoczne kanały</string>
++ <string id="19051">Pokaż ukryte kanały</string>
++ <string id="19052">Przenieść kanał do:</string>
++ <string id="19053">Informacje o nagrywaniu</string>
++ <string id="19054">Ukryj kanał</string>
++ <string id="19055">Brak dostępnych informacji</string>
++ <string id="19056">Nowy timer</string>
++ <string id="19057">Edytuj timer</string>
++ <string id="19058">Timer aktywny</string>
++ <string id="19059">Zatrzymaj nagrywanie</string>
++ <string id="19060">Usuń timer</string>
++ <string id="19061">Dodaj timer</string>
++ <string id="19062">Sortuj według: Kanał</string>
++ <string id="19063">Idź do początku</string>
++ <string id="19064">Idź do końca</string>
++ <string id="19065">Domyślnie okno EPG</string>
++ <string id="19066">Åadowanie nagraÅ„ od klientów</string>
++ <string id="19067">To wydarzenie jest już zarejestrowane.</string>
++ <string id="19068">Nie można skasować tego nagrania. Sprawdź dziennik(log)</string>
++ <string id="19069">EPG</string>
++ <string id="19070">EPG skanowanie timeout</string>
++ <string id="19071">EPG skanuj co:</string>
++ <string id="19072">Nie przechowywać w bazie danych EPG</string>
++ <string id="19073">Opóźnienie przełączania kanałów</string>
++ <string id="19074">Active:</string>
++ <string id="19075">Nazwa:</string>
++ <string id="19076">Folder:</string>
++ <string id="19077">Radio:</string>
++ <string id="19078">Kanał:</string>
++ <string id="19079">Day:</string>
++ <string id="19080">Rozpoczęcie:</string>
++ <string id="19081">Koniec:</string>
++ <string id="19082">Priorytet:</string>
++ <string id="19083">Lifetime (dni):</string>
++ <string id="19084">Dzień pierwszy:</string>
++ <string id="19085">Brak kanału %u</string>
++ <string id="19086">Pn-__-__-__-__-__-__</string>
++ <string id="19087">__-Wt-__-__-__-__-__</string>
++ <string id="19088">__-__-Åšr-__-__-__-__</string>
++ <string id="19089">__-__-__-Czw-__-__-__</string>
++ <string id="19090">__-__-__-__-Pt-__-__</string>
++ <string id="19091">__-__-__-__-__-Sob-__</string>
++ <string id="19092">__-__-__-__-__-__-Niedz</string>
++ <string id="19093">Pn-Wt-Åšr-Czw-Pt-__-__</string>
++ <string id="19094">Pn-Wt-Åšr-Czw-Pt-Sob-__</string>
++ <string id="19095">Pn-Wt-Åšr-Czw-Pt-Sob-Niedz</string>
++ <string id="19096">__-__-__-__-__-Sob-Niedz</string>
++ <string id="19097">Wprowadź nazwę nagrania</string>
++ <string id="19098">Uwaga</string>
++ <string id="19099">Timer obecny</string>
++ <string id="19100">Czy na pewno chcesz usunąć ten kanał, w tym wszystkie timer-y na tym kanale??</string>
++ <string id="19101">Ten kanał jest obecnie używany do oglądania.</string>
++ <string id="19102">Proszę przełączyć na inny kanał.</string>
++ <string id="19103">Przeszukaj brakujÄ…ce IKONY</string>
++ <string id="19104">Wprowadź nazwę folderu do zapisu</string>
++ <string id="19105">Rozmiar:</string>
++ <string id="19106">Następny timer</string>
++ <string id="19107">w</string>
++ <string id="19108">Nagrania nie zsynchronizowane. Sprawdź dziennik(log).</string>
++ <string id="19109">Nie można zapisać timer-a. Sprawdź dziennik(log).</string>
++ <string id="19110">Wystąpił nieoczekiwany błąd. Spróbuj ponownie później lub sprawdzić dziennik(log)</string>
++ <string id="19111">Błąd PVR backend. Sprawdź dziennik(log).</string>
++ <string id="19112">Timer-y nie zsynchronizowane. Sprawdź dziennik(log).</string>
++ <string id="19113">Dalej</string>
++ <string id="19114">Wersja</string>
++ <string id="19115">Adres</string>
++ <string id="19116">Rozmiar dysku</string>
++ <string id="19117">Wyszukiwanie kanałów</string>
++ <string id="19118">Nie można użyć funkcji PVR podczas wyszukiwania.</string>
++ <string id="19119">Na którym serwerze chcesz szukać?</string>
++ <string id="19120">Numer klienta</string>
++ <string id="19121">Unikać powtórzeń</string>
++ <string id="19122">Timer jest w czasie nagrywania. Czy na pewno chcesz usunąć ten timer?</string>
++ <string id="19123">Tylko kanały FTA</string>
++ <string id="19124">Ignoruj obecne timer-y</string>
++ <string id="19125">Ignoruj obecne nagrywania</string>
++ <string id="19126">Czas rozpoczęcia</string>
++ <string id="19127">Czas zakończenia</string>
++ <string id="19128">Data rozpoczęcia</string>
++ <string id="19129">Data zakończenia</string>
++ <string id="19130">Minimalny czas trwania</string>
++ <string id="19131">Maksymalny czas</string>
++ <string id="19132">Dołącz nieznane gatunki</string>
++ <string id="19133">Szukany ciÄ…g</string>
++ <string id="19134">Dołącz opis</string>
++ <string id="19135">Case sensitive</string>
++ <string id="19136">Kanał niedostępny</string>
++ <string id="19137">Brak zdefiniowanych grup</string>
++ <string id="19138">Prosze utowrzyć najpierw grupe</string>
++ <string id="19139">Nazwa nowej grupy</string>
++ <string id="19141">Group</string>
++ <string id="19142">Przewodnik szukania</string>
++ <string id="19143">ZarzÄ…dzanie GrupÄ…</string>
++ <string id="19144">Brak zdefiniowanych grup</string>
++ <string id="19145">Zgrupowane</string>
++ <string id="19146">Grupy</string>
++ <string id="19147">PVR backend nie obsługuje tego działania. Sprawdź dziennik(log).</string>
++ <string id="19148">Kanał</string>
++ <string id="19149">Pn</string>
++ <string id="19150">Wt</string>
++ <string id="19151">Åšr</string>
++ <string id="19152">Czw</string>
++ <string id="19153">Pt</string>
++ <string id="19154">Sob</string>
++ <string id="19155">Niedz</string>
++ <string id="19156">z</string>
++ <string id="19157">Następne nagranie</string>
++ <string id="19158">Aktualne nagranie</string>
++ <string id="19159">z</string>
++ <string id="19160">do</string>
++ <string id="19161">Na</string>
++ <string id="19162">Nagrywanie aktywne</string>
++ <string id="19163">Nagrania</string>
++ <string id="19164">Nie można rozpocząć nagrywanie. Sprawdź dziennik(log)</string>
++ <string id="19165">Przełącz</string>
++ <string id="19166">PVR informacje</string>
++ <string id="19167">Skanuj w poszukiwaniu brakujÄ…cych IKON</string>
++ <string id="19168">Przełączaj kanał bez naciskania OK</string>
++ <string id="19169">Ukryj okno informacyjne o video</string>
++ <string id="19170">Timeout przy uruchamianiu odtwarzania</string>
++ <string id="19171">Uruchom zminimalizowany</string>
++ <string id="19172">Czas nagrywania w locie</string>
++ <string id="19173">Domyślny priorytet nagrywania</string>
++ <string id="19174">Domyślny lifetime nagrywania</string>
++ <string id="19175">Margines na poczÄ…tku nagrywania</string>
++ <string id="19176">Margines na końcu nagrywania</string>
++ <string id="19177">Odtwarzanie</string>
++ <string id="19178">Pokaż informacje o kanale przy przełączaniu kanałów</string>
++ <string id="19179">Automatyczne ukrywanie informacji o kanale</string>
++ <string id="19180">TV</string>
++ <string id="19181">Menu/OSD</string>
++ <string id="19182">Dni do wyświetlenia w EPG </string>
++ <string id="19184">Czas trwania informacji o kanale</string>
++ <string id="19185">Zresetowania bazy danych PVR</string>
++ <string id="19186">Wszystkie dane w bazie danych PVR sÄ… usuwane</string>
++ <string id="19187">zresetowania bazy danych EPG</string>
++ <string id="19188">Dane EPG sÄ… usuwane</string>
++ <string id="19189">Zacznij od ostatniego oglądanego kanału</string>
++ <string id="19190">Zminimalizowane</string>
++ <string id="19191">Usługi PVR</string>
++ <string id="19192">Brak podłączonych backend-ów PVR obsługujących skanowanie kanałów</string>
++ <string id="19193">wyszukiwanie kanałów nie można uruchomić. Sprawdź dziennik(log)</string>
++ <string id="19194">Kontynuować?</string>
++ <string id="19195">Działania klienta</string>
++ <string id="19196">PVR client specific actions</string>
++ <string id="19197">Nagrywanie rozpoczęło się o: %s</string>
++ <string id="19198">Zakończeniu nagrywania o: %s</string>
++ <string id="19199">Manager kanałów</string>
++ <string id="19200">EPG źródło:</string>
++ <string id="19201">Nazwa kanału:</string>
++ <string id="19202">Ikona kanału:</string>
++ <string id="19203">Edytuj kanał</string>
++ <string id="19204">Nowy kanał</string>
++ <string id="19205">ZarzÄ…dzanie GrupÄ…</string>
++ <string id="19206">Aktywuj EPG:</string>
++ <string id="19207">Grupa:</string>
++ <string id="19208">Wprowadź nazwe nowego kanału</string>
++ <string id="19209">XBMC wirtualny backend</string>
++ <string id="19210">Klient</string>
++ <string id="19211">Usuń kanał</string>
++ <string id="19212">Ta lista zawiera zmiany</string>
++ <string id="19213">Wybierz backend</string>
++ <string id="19214">Wprowadź prawidłowy adres URL dla nowego kanału</string>
++ <string id="19215">PVR backend nie wspiera timer-ów.</string>
++ <string id="19216">Wszystkie kanały radiowe</string>
++ <string id="19217">Wszystkie kanały TV</string>
++ <string id="19218">Widoczne</string>
++ <string id="19219">Niezgrupowane kanały</string>
++ <string id="19220">Kanały w</string>
++ <string id="19221">Zsychronizuj grupy kanałów z backend-em</string>
++ <string id="19222">EPG</string>
++ <string id="19223">Wtyczka PVR nie może zostać uruchomiona. Sprawdź ustawienia lub uzyskać więcej informacji z dziennik(log)</string>
++ <string id="19224">Nagrywanie zostało przerwane</string>
++ <string id="19225">nagrywanie zaplanowane</string>
++ <string id="19226">Rozpoczęło się nagrywanie</string>
++ <string id="19227">Nagrywanie zakończone</string>
++ <string id="19228">Nagrywanie usunięte</string>
++ <string id="19229">Zamykaj OSD kanału po przełączniu</string>
++ <string id="19230">Zapobieganie aktualizacji EPG podczas odtwarzania strumienia TV</string>
++ <string id="19231">Zawsze używaj kolejność kanałów z backend-u/ów</string>
++ <string id="19232">Wyczyść wyniki wyszukiwania</string>
++ <string id="19233">Wyświetl powiadomienie aktualizacji timer-a</string>
++
+ <string id="20000">Katalog zapisywanej muzyki</string>
+ <string id="20001">Użyj zewnętrznego odtwarzacza DVD</string>
+ <string id="20002">Zewnętrzny odtwarzacz DVD</string>
+@@ -2092,6 +2326,17 @@
+ <string id="33103">Serwer zdalnej komunikacji</string>
+ <string id="33200">Wykryto nowe połączenie</string>
+ <string id="34100">System głośników</string>
++ <string id="34101">2.0</string>
++ <string id="34102">2.1</string>
++ <string id="34103">3.0</string>
++ <string id="34104">3.1</string>
++ <string id="34105">4.0</string>
++ <string id="34106">4.1</string>
++ <string id="34107">5.0</string>
++ <string id="34108">5.1</string>
++ <string id="34109">7.0</string>
++ <string id="34110">7.1</string>
++
+ <string id="34201">Nie mogę znaleźć następnej pozycji do odtworzenia</string>
+ <string id="34202">Nie mogę znaleźć poprzedniej pozycji do odtworzenia</string>
+ <string id="34300">BÅ‚Ä…d podczas startu zeroconf</string>
+@@ -2127,4 +2372,4 @@
+ <string id="36016">Połączono</string>
+ <string id="36017">Znaleziono adapter, ale brak biblioteki libcec</string>
+ <string id="36018">Użyj ustawienia języka z TV</string>
+-</strings>
+\ No newline at end of file
++</strings>
+diff --git a/language/Russian/langinfo.xml b/language/Russian/langinfo.xml
+index 326cb34..19bed3b 100644
+--- a/language/Russian/langinfo.xml
++++ b/language/Russian/langinfo.xml
+@@ -1,4 +1,4 @@
+-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+ <language locale="ru">
+ <charsets>
+ <gui unicodefont="true">utf-8</gui>
+@@ -18,4 +18,24 @@
+ <speedunit>mps</speedunit>
+ </region>
+ </regions>
++ <sorttokens>
++ <token>The</token>
++ <token>A</token>
++ <token>An</token>
++ <token>Der</token>
++ <token>Die</token>
++ <token>Das</token>
++ <token>Le</token>
++ <token>La</token>
++ <token>Les</token>
++ <token>Un</token>
++ <token>Une</token>
++ <token>Des</token>
++ <token>Il</token>
++ <token>Lo</token>
++ <token>La</token>
++ <token>Gli</token>
++ <token>Uno</token>
++ <token>Una</token>
++ </sorttokens>
+ </language>
+\ No newline at end of file
+diff --git a/language/Russian/strings.xml b/language/Russian/strings.xml
+index 85d2985..f5d6557 100644
+--- a/language/Russian/strings.xml
++++ b/language/Russian/strings.xml
+@@ -1,6 +1,5 @@
+ <?xml version="1.0" encoding="utf-8" standalone="yes"?>
+ <!-- Russian – current edition by Roman_V_M -->
+-<!--Based on English cd69c7030b0caec990b6143fac65ca8168dccf71 (23.01.2012)-->
+ <strings>
+ <string id="0">Программы</string>
+ <string id="1">Фотографии</string>
+@@ -54,7 +53,7 @@
+ <string id="60">окт.</string>
+ <string id="61">ноÑб.</string>
+ <string id="62">дек.</string>
+-
++
+ <string id="71">С</string>
+ <string id="72">ССВ</string>
+ <string id="73">СВ</string>
+@@ -876,9 +875,15 @@
+ <string id="10020">Скрипты</string>
+ <string id="10021">Веб-браузер</string>
+
++ <string id="10025">Видео</string>
++
+ <string id="10028">Видео/плейлиÑÑ‚</string>
++ <string id="10029">Экран входа</string>
++
+ <string id="10034">ÐаÑтройки - Профили</string>
+
++ <string id="10040">Браузер дополнений</string>
++
+ <string id="10100">Диалог "Да/Ðет"</string>
+ <string id="10101">Диалог выполнениÑ</string>
+
+@@ -935,6 +940,9 @@
+ <string id="12318">8</string>
+ <string id="12319">9</string>
+ <string id="12320">c</string>
++ <string id="12321">Ok</string>
++ <string id="12322">*</string>
++
+ <string id="12321">Ок</string>
+ <string id="12322">*</string>
+ <string id="12325">Заблокировано. Введите код…</string>
+@@ -1019,7 +1027,7 @@
+
+ <string id="13100">Фильтр мерцаниÑ</string>
+ <string id="13101">По выбору драйвера (требуетÑÑ Ð¿ÐµÑ€ÐµÐ·Ð°Ð¿ÑƒÑк)</string>
+-
++
+ <string id="13105">Ð’ÐµÑ€Ñ‚Ð¸ÐºÐ°Ð»ÑŒÐ½Ð°Ñ ÑинхронизациÑ</string>
+ <string id="13106">Отключена</string>
+ <string id="13107">Только при проÑмотре видео</string>
+@@ -1067,11 +1075,11 @@
+
+ <string id="13200">Профили</string>
+ <string id="13201">Удалить профиль "%s"?</string>
+-
++
+ <string id="13204">ПоÑледний загруженный профиль:</string>
+ <string id="13205">ÐеизвеÑтно</string>
+ <string id="13206">ПерезапиÑать</string>
+-
++
+ <string id="13208">Таймер напоминаний</string>
+ <string id="13209">Период таймера напоминаний (в мин.)</string>
+ <string id="13210">Запущен; Ñработает через %i мин.</string>
+@@ -1172,7 +1180,7 @@
+ <string id="13359">Выбрать ÑÑкиз иÑполнителÑ</string>
+ <string id="13360">Создавать ÑÑкизы</string>
+ <string id="13361">Включить микрофон</string>
+-
++
+ <string id="13375">Включить уÑтройÑтво</string>
+ <string id="13376">ГромкоÑÑ‚ÑŒ</string>
+ <string id="13377">ОÑновной режим проÑмотра</string>
+@@ -1496,7 +1504,7 @@
+ <string id="16039">Выкл.</string>
+ <string id="16040">Ðвто</string>
+ <string id="16041">Вкл.</string>
+-
++
+ <string id="16100">Ð’Ñе видео</string>
+ <string id="16101">Ðе проÑмотрено</string>
+ <string id="16102">ПроÑмотрено</string>
+@@ -1537,12 +1545,427 @@
+ <string id="16322">Spline36</string>
+ <string id="16323">Оптимизир. Spline36</string>
+ <string id="16324">Blend (программный)</string>
+-
++
+ <string id="16400">ПоÑтобработка</string>
+
+ <string id="17500">Переход диÑÐ¿Ð»ÐµÑ Ð² ÑпÑщий режим через</string>
+
++
++ <string id="17997">%i MiByte</string>
++ <string id="17998">%i чаÑов</string>
++ <string id="17999">%i дней</string>
++
++ <!-- strings 19000 thru 19999 used for tv interface -->
+ <string id="19000">ПереключитьÑÑ Ð½Ð° канал</string>
++ <string id="19001">РазделÑйте различные Ñлова Ð´Ð»Ñ Ð¿Ð¾Ð¸Ñка, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ AND, OR и/или NOT.</string>
++ <string id="19002">Или иÑпользуйте фразы Ð´Ð»Ñ Ð¿Ð¾Ð»Ð½Ð¾Ñ‚ÐµÐºÑтового поиÑка, например: "Ðццкий Сотона".</string>
++ <string id="19003">Ðайти похожую программу</string>
++ <string id="19004">Загрузить EPG из клиентов</string>
++ <string id="19005">Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ потоке PVR</string>
++ <string id="19006">Принимающее уÑтройÑтво</string>
++ <string id="19007">Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ð¿Ñ€Ð¸ÐµÐ¼Ð°</string>
++ <string id="19008">КачеÑтво Ñигнала</string>
++ <string id="19009">SNR</string>
++ <string id="19010">BER</string>
++ <string id="19011">UNC</string>
++ <string id="19012">Backend</string>
++ <string id="19013">Free To Air</string>
++ <string id="19014">ФикÑированно</string>
++ <string id="19015">РаÑкодирование</string>
++ <string id="19016">Сервер %i - %s</string>
++ <string id="19017">ЗапиÑи ТВ</string>
++ <string id="19018">ОÑÐ½Ð¾Ð²Ð½Ð°Ñ Ð¿Ð°Ð¿ÐºÐ° Ð´Ð»Ñ Ð¼Ð¸Ð½Ð¸Ð°Ñ‚ÑŽÑ€ ТВ каналов</string>
++ <string id="19019">Каналы</string>
++ <string id="19020">ТВ</string>
++ <string id="19021">Радио</string>
++ <string id="19022">Скрытые</string>
++ <string id="19023">ТВ каналы</string>
++ <string id="19024">Каналы радио</string>
++ <string id="19025">Будущие запиÑи</string>
++ <string id="19026">Добавить таймер...</string>
++ <string id="19027">Ðет результатов</string>
++ <string id="19028">Ðет информации ТВ Гида</string>
++ <string id="19029">ТВ Гид</string>
++ <string id="19030">Текущее</string>
++ <string id="19031">Следующее</string>
++ <string id="19032">Шкала времени</string>
++ <string id="19033">ИнформациÑ</string>
++ <string id="19034">ЗапиÑÑŒ учже начата</string>
++ <string id="19035">Канал не может быть проигран</string>
++ <string id="19036">ЗапиÑÑŒ не может быть проиграна</string>
++ <string id="19037">Показать качеÑтво Ñигнала</string>
++ <string id="19038">Ð¡ÐµÐ¹Ñ‡Ð°Ñ Ð½Ðµ поддерживаетÑÑ!</string>
++ <string id="19039">Подтвердить Ñкрытие канала</string>
++ <string id="19040">Таймер</string>
++ <string id="19041">Переименовать запиÑÑŒ?</string>
++ <string id="19042">Переименовать таймер?</string>
++ <string id="19043">ЗапиÑÑŒ</string>
++ <string id="19044">ПожалуйÑта проверьте наÑтройки Backend</string>
++ <string id="19045">Ðет доÑтупных PVR клиентов</string>
++ <string id="19046">Ðовый канал</string>
++ <string id="19047">Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ программе</string>
++ <string id="19048">Управление группами</string>
++ <string id="19049">Показать канал</string>
++ <string id="19050">Показать обычные каналы</string>
++ <string id="19051">Показать Ñкрытые каналы</string>
++ <string id="19052">ПеремеÑтить в:</string>
++ <string id="19053">Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ запиÑи</string>
++ <string id="19054">СпрÑтать канал</string>
++ <string id="19055">Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð½ÐµÐ´Ð¾Ñтупна!</string>
++ <string id="19056">Ðовый таймер</string>
++ <string id="19057">Правка таймера</string>
++ <string id="19058">Вкл/Выкл таймер</string>
++ <string id="19059">ОÑтановить запиÑÑŒ</string>
++ <string id="19060">Удалить таймер</string>
++ <string id="19061">Ðовый таймер</string>
++ <string id="19062">Сортировка: Каналы</string>
++ <string id="19063">В начало</string>
++ <string id="19064">В конец</string>
++ <string id="19065">ОÑÐ½Ð¾Ð²Ð½Ð°Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð¼Ð° гида</string>
++ <string id="19066">Загрузить информацию о запиÑи из клиента</string>
++ <string id="19067">Таймер уже уÑтановлен на Ñто времÑ</string>
++ <string id="19068">ÐÐµÐ»ÑŒÐ·Ñ ÑƒÐ´Ð°Ð»Ð¸Ñ‚ÑŒ запиÑÑŒ!</string>
++ <string id="19069">ТВ-Гид</string>
++ <string id="19070">Тайм-аут ÑÐºÐ°Ð½Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¢Ð’-Гида</string>
++ <string id="19071">Тайм-аут Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¢Ð’-Гида</string>
++ <string id="19072">Игнорировать базу ТВ Гида PVR-Ñервера</string>
++ <string id="19073">Тайм-аут запуÑка канала</string>
++ <string id="19074">Ðктивный:</string>
++ <string id="19075">ИмÑ:</string>
++ <string id="19076">ДиректориÑ:</string>
++ <string id="19077">Радио:</string>
++ <string id="19078">Канал:</string>
++ <string id="19079">День:</string>
++ <string id="19080">Ðачало:</string>
++ <string id="19081">Конец:</string>
++ <string id="19082">Приоритет:</string>
++ <string id="19083">Ð’Ñ€ÐµÐ¼Ñ Ð¶Ð¸Ð·Ð½Ð¸ (дн):</string>
++ <string id="19084">Первый день:</string>
++ <string id="19085">Ðезнакомый канал %u</string>
++ <string id="19086">Пн-__-__-__-__-__-__</string>
++ <string id="19087">__-Ð’Ñ‚-__-__-__-__-__</string>
++ <string id="19088">__-__-Ср-__-__-__-__</string>
++ <string id="19089">__-__-__-Чт-__-__-__</string>
++ <string id="19090">__-__-__-__-Пт-__-__</string>
++ <string id="19091">__-__-__-__-__-Сб-__</string>
++ <string id="19092">__-__-__-__-__-__-Ð’Ñ</string>
++ <string id="19093">Пн-Вт-Ср-Чт-Пт-__-__</string>
++ <string id="19094">Пн-Вт-Ср-Чт-Пт-Сб-__</string>
++ <string id="19095">Пн-Ð’Ñ‚-Ср-Чт-Пт-Сб-Ð’Ñ</string>
++ <string id="19096">__-__-__-__-__-Сб-Ð’Ñ</string>
++ <string id="19097">Введите Ð¸Ð¼Ñ Ð·Ð°Ð¿Ð¸Ñи</string>
++ <string id="19098">Внимание</string>
++ <string id="19099">Текущий таймер</string>
++ <string id="19100">Удалить канал и таймер?</string>
++ <string id="19101">Канал проÑматриваетÑÑ</string>
++ <string id="19102">ПожалуйÑта переключите канал!</string>
++ <string id="19103">ПоиÑк пропавших миниатюр</string>
++ <string id="19104">Введите директорию Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñей</string>
++ <string id="19105">Размер:</string>
++ <string id="19106">След. таймер в</string>
++ <string id="19107">в</string>
++ <string id="19108">ЗапиÑÑŒ не Ñинхронна!</string>
++ <string id="19109">ÐÐµÐ»ÑŒÐ·Ñ Ñохранить таймер!</string>
++ <string id="19110">Пробовать Ñнова...</string>
++ <string id="19111">Ошибка Ñервера!</string>
++ <string id="19112">Таймеры не Ñинхронны!</string>
++ <string id="19113">Далее</string>
++ <string id="19114">ВерÑиÑ</string>
++ <string id="19115">ÐдреÑ</string>
++ <string id="19116">Размер</string>
++ <string id="19117">ПоиÑк каналов</string>
++ <string id="19118">Ðевозможно выполнить во Ð²Ñ€ÐµÐ¼Ñ Ð¿Ð¾Ð¸Ñка каналов!</string>
++ <string id="19119">Ðа каком Ñервере хотите иÑкать?</string>
++ <string id="19120">Ðомер клиента</string>
++ <string id="19121">Избежать повторов</string>
++ <string id="19122">Таймер еще запиÑывает - удалить?</string>
++ <string id="19123">Free-To-Air only</string>
++ <string id="19124">Игнорировать текущие таймеры</string>
++ <string id="19125">Игнорировать текущие запиÑи</string>
++ <string id="19126">Ð’Ñ€ÐµÐ¼Ñ Ñтарта</string>
++ <string id="19127">Ð’Ñ€ÐµÐ¼Ñ Ð¾Ñтанова</string>
++ <string id="19128">Дата Ñтарта</string>
++ <string id="19129">Дата оÑтанова</string>
++ <string id="19130">Мин. длительноÑÑ‚ÑŒ</string>
++ <string id="19131">МакÑ. длительноÑÑ‚ÑŒ</string>
++ <string id="19132">Включать неизвеÑтные жанры</string>
++ <string id="19133">Строка поиÑка</string>
++ <string id="19134">Включать опиÑание</string>
++ <string id="19135">УчеÑÑ‚ÑŒ региÑÑ‚Ñ€</string>
++ <string id="19136">Канал недоÑтупен</string>
++ <string id="19137">Группы неопределены</string>
++ <string id="19138">ПожалуйÑта Ñоздайте первую</string>
++ <string id="19139">Ð˜Ð¼Ñ Ð½Ð¾Ð²Ð¾Ð¹ группы</string>
++ <string id="19140">Ð’Ñе каналы</string>
++ <string id="19141">Группы</string>
++ <string id="19142">Гид по поиÑку</string>
++ <string id="19143">Управление группами</string>
++ <string id="19144">Без групп</string>
++ <string id="19145">В группе</string>
++ <string id="19146">Группы</string>
++ <string id="19147">ÐеÑовмеÑтимый PVR-Server!</string>
++ <string id="19148">Канал</string>
++ <string id="19149">Пн</string>
++ <string id="19150">Ð’Ñ‚</string>
++ <string id="19151">Ср</string>
++ <string id="19152">Чт</string>
++ <string id="19153">Пт</string>
++ <string id="19154">Сб</string>
++ <string id="19155">Ð’Ñ</string>
++ <string id="19156">Ñ</string>
++ <string id="19157">Ð¡Ð»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ</string>
++ <string id="19158">Ð¢ÐµÐºÑƒÑ‰Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ</string>
++ <string id="19159">Ñ</string>
++ <string id="19160">до</string>
++ <string id="19161">Ð’</string>
++ <string id="19162">Идет запиÑÑŒ</string>
++ <string id="19163">ЗапиÑи</string>
++ <string id="19164">ÐÐµÐ»ÑŒÐ·Ñ Ð½Ð°Ñ‡Ð°Ñ‚ÑŒ запиÑÑŒ</string>
++ <string id="19165">Переключить</string>
++ <string id="19166">Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ PVR</string>
++ <string id="19167">ПоиÑк потерÑнных Ñрлыков</string>
++ <string id="19168">Переключение канала без Ð½Ð°Ð¶Ð°Ñ‚Ð¸Ñ OK</string>
++ <string id="19169">СпрÑтать информационный блок о длине видео</string>
++ <string id="19170">Тайм-аут ÑÐºÐ°Ð½Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ ÐºÐ°Ð°Ð½Ð°Ð»Ð¾Ð²</string>
++ <string id="19171">ЗапуÑтить минимизированным</string>
++ <string id="19172">Ð’Ñ€ÐµÐ¼Ñ Ð´Ð»Ñ Ð¼Ð³Ð½Ð¾Ð²ÐµÐ½Ð½Ð¾Ð¹ запиÑи</string>
++ <string id="19173">Приоритет по-умолчанию</string>
++ <string id="19174">Ð’Ñ€ÐµÐ¼Ñ Ð¶Ð¸Ð·Ð½Ð¸ по-умолчанию</string>
++ <string id="19175">Интервал запиÑи до времени Ñтарта</string>
++ <string id="19176">Интервал запиÑи поÑле окончаниÑ</string>
++ <string id="19177">Повтор</string>
++ <string id="19178">Показывать информацию при переключении канала</string>
++ <string id="19179">Timeout requested channel info</string>
++ <string id="19180">ТВ</string>
++ <string id="19181">Меню / OSD</string>
++ <string id="19182">КоличеÑтво отображаемыемых в ТВ Гиде дней</string>
++ <string id="19183">Ð’Ñ€ÐµÐ¼Ñ Ð·Ð°Ð´ÐµÑ€Ð¶ÐºÐ¸ Ð´Ð»Ñ Ð¢Ð’ Гида</string>
++ <string id="19184">Ð’Ñ€ÐµÐ¼Ñ Ð¿Ð¾ÐºÐ°Ð·Ð° информации о канале</string>
++ <string id="19185">ОчиÑтить базу данных ТВ</string>
++ <string id="19186">Ð’ÑÑ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¸Ð· базы удалена</string>
++ <string id="19187">Удалить данные ТВ-Гида и получить новые</string>
++ <string id="19188">EPG Ñтерто</string>
++ <string id="19189">Показать поÑледний проÑматриваемый канал при Ñтарте</string>
++ <string id="19190">Мини</string>
++ <string id="19191">PVR service</string>
++ <string id="19192">Сканирование каналов не поддерживаетÑÑ Ñервером</string>
++ <string id="19193">Сканирование каналов не может быть запущено</string>
++ <string id="19194">Продолжить?</string>
++ <string id="19195">ДейÑÑ‚Ð²Ð¸Ñ ÐºÐ»Ð¸ÐµÐ½Ñ‚Ð°</string>
++ <string id="19196">Специальные дейÑÑ‚Ð²Ð¸Ñ ÐºÐ»Ð¸ÐµÐ½Ñ‚Ð° PVR</string>
++ <string id="19197">ЗапиÑÑŒ начата в: %s</string>
++ <string id="19198">ЗапиÑÑŒ закончена в: %s</string>
++ <string id="19199">Управление каналами</string>
++ <string id="19200">ИÑточник EPG:</string>
++ <string id="19201">Ð˜Ð¼Ñ ÐºÐ°Ð½Ð°Ð»Ð°:</string>
++ <string id="19202">Ярлык канала:</string>
++ <string id="19203">Редактировать</string>
++ <string id="19204">Ðовый канал</string>
++ <string id="19205">Управление группами</string>
++ <string id="19206">Ðктивировать EPG:</string>
++ <string id="19207">Группа:</string>
++ <string id="19208">Введите Ð¸Ð¼Ñ ÐºÐ°Ð½Ð°Ð»Ð°</string>
++ <string id="19209">Виртуальный канал XBMC</string>
++ <string id="19210">Клиент</string>
++ <string id="19211">Удалить канал</string>
++ <string id="19212">СпиÑок изменений</string>
++ <string id="19213">Выберите клиент канала</string>
++ <string id="19214">Введите верный URL!</string>
++ <string id="19215">PVR-Server не поддерживает таймеры!</string>
++
++ <string id="19499">Другой/ÐеизвеÑтен</string>
++ <string id="19500">Фильм/Драма</string>
++ <string id="19501">Детектив/Триллер</string>
++ <string id="19502">ПриключениÑ/ВеÑтерн/Война</string>
++ <string id="19503">Ðаука/ФÑнтези/УжаÑÑ‹</string>
++ <string id="19504">КомедиÑ</string>
++ <string id="19505">Мелодрамма/Быт</string>
++ <string id="19506">Романтика</string>
++ <string id="19507">КлаÑÑика/РелигиÑ/ИÑториÑ/Драма</string>
++ <string id="19508">Ð”Ð»Ñ Ð²Ð·Ñ€Ð¾Ñлых/Драма</string>
++ <string id="19509"></string>
++ <string id="19510"></string>
++ <string id="19511"></string>
++ <string id="19512"></string>
++ <string id="19513"></string>
++ <string id="19514"></string>
++ <string id="19515"></string>
++ <string id="19516">ÐовоÑти/СобытиÑ</string>
++ <string id="19517">ÐовоÑти/Погода</string>
++ <string id="19518">ÐовоÑти</string>
++ <string id="19519">ДокументалиÑтика</string>
++ <string id="19520">ДиÑкуÑÑии/Интервью/Дебаты</string>
++ <string id="19521"></string>
++ <string id="19522"></string>
++ <string id="19523"></string>
++ <string id="19524"></string>
++ <string id="19525"></string>
++ <string id="19526"></string>
++ <string id="19527"></string>
++ <string id="19528"></string>
++ <string id="19529"></string>
++ <string id="19530"></string>
++ <string id="19531"></string>
++ <string id="19532">Шоу/Игровое шоу</string>
++ <string id="19533">Игровое шоу/СоÑÑ‚ÑзаниÑ</string>
++ <string id="19534">Различные шоу</string>
++ <string id="19535">ГоворильнÑ</string>
++ <string id="19536"></string>
++ <string id="19537"></string>
++ <string id="19538"></string>
++ <string id="19539"></string>
++ <string id="19540"></string>
++ <string id="19541"></string>
++ <string id="19542"></string>
++ <string id="19543"></string>
++ <string id="19544"></string>
++ <string id="19545"></string>
++ <string id="19546"></string>
++ <string id="19547"></string>
++ <string id="19548">Спорт</string>
++ <string id="19549">СобытиÑ</string>
++ <string id="19550">Спортивные ÑобытиÑ</string>
++ <string id="19551">Футболл</string>
++ <string id="19552">ТенниÑ/Сквош</string>
++ <string id="19553">Командный Ñпорт</string>
++ <string id="19554">Ðтлетика</string>
++ <string id="19555">МотоÑпорт</string>
++ <string id="19556">Водный Ñпорт</string>
++ <string id="19557">Зимний Ñпорт</string>
++ <string id="19558">Equestrian</string>
++ <string id="19559">Боевые иÑкуÑтва</string>
++ <string id="19560"></string>
++ <string id="19561"></string>
++ <string id="19562"></string>
++ <string id="19563"></string>
++ <string id="19564">ДетÑкие/ЮношеÑкие программы</string>
++ <string id="19565">Ð”Ð»Ñ Ð´Ð¾ÑˆÐºÐ¾Ð»ÑŒÐ½Ð¸ÐºÐ¾Ð²</string>
++ <string id="19566">Развлекательные программы Ñ 6 до 14 лет</string>
++ <string id="19567">Развлекательные программы Ñ 11 до 16 лет</string>
++ <string id="19568">Информационный/Обучающий</string>
++ <string id="19569">Мультфильмы/Куклы</string>
++ <string id="19570"></string>
++ <string id="19571"></string>
++ <string id="19572"></string>
++ <string id="19573"></string>
++ <string id="19574"></string>
++ <string id="19575"></string>
++ <string id="19576"></string>
++ <string id="19577"></string>
++ <string id="19578"></string>
++ <string id="19579"></string>
++ <string id="19580">Музыка/Балет/Танцы</string>
++ <string id="19581">Рок/ПопÑа</string>
++ <string id="19582">КлаÑÑичеÑÐºÐ°Ñ Ð¼ÑƒÐ·Ñ‹ÐºÐ°</string>
++ <string id="19583">ÐÐ°Ñ€Ð¾Ð´Ð½Ð°Ñ Ð¼ÑƒÐ·Ñ‹ÐºÐ°</string>
++ <string id="19584">Опера</string>
++ <string id="19585">Балет</string>
++ <string id="19586"></string>
++ <string id="19587"></string>
++ <string id="19588"></string>
++ <string id="19589"></string>
++ <string id="19590"></string>
++ <string id="19591"></string>
++ <string id="19592"></string>
++ <string id="19593"></string>
++ <string id="19594"></string>
++ <string id="19595"></string>
++ <string id="19596">ИÑкуÑтво/Культура</string>
++ <string id="19597">ПерформанÑ</string>
++ <string id="19598">Прикладное иÑкуÑтво</string>
++ <string id="19599">РелигиÑ</string>
++ <string id="19600">ПопулÑрное иÑкуÑтво</string>
++ <string id="19601">Литература</string>
++ <string id="19602">Фильм/Кино</string>
++ <string id="19603">ЭкÑпериментальное видео</string>
++ <string id="19604">Широковещательный</string>
++ <string id="19605">New Media</string>
++ <string id="19606">ИÑкуÑтво/Культура</string>
++ <string id="19607">Мода</string>
++ <string id="19608"></string>
++ <string id="19609"></string>
++ <string id="19610"></string>
++ <string id="19611"></string>
++ <string id="19612">Политика/Экономика</string>
++ <string id="19613">Журналы/СообщениÑ/ДокументалиÑтика</string>
++ <string id="19614">Экономика/Социальна ÑÑфера</string>
++ <string id="19615">Жизнь замечательных людей</string>
++ <string id="19616"></string>
++ <string id="19617"></string>
++ <string id="19618"></string>
++ <string id="19619"></string>
++ <string id="19620"></string>
++ <string id="19621"></string>
++ <string id="19622"></string>
++ <string id="19623"></string>
++ <string id="19624"></string>
++ <string id="19625"></string>
++ <string id="19626"></string>
++ <string id="19627"></string>
++ <string id="19628">Обучение/Ðаука</string>
++ <string id="19629">Природа/Животные/ÐžÐºÑ€ÑƒÐ¶Ð°ÑŽÑ‰Ð°Ñ Ñреда</string>
++ <string id="19630">Технологии/ЕÑтеÑтвенные науки</string>
++ <string id="19631">Медицина/ФизиологиÑ/ПÑихологиÑ</string>
++ <string id="19632">Заграница/ПутешеÑтвие</string>
++ <string id="19633">Социум/СпиритичеÑкие науки</string>
++ <string id="19634">Образование</string>
++ <string id="19635">Языки</string>
++ <string id="19636"></string>
++ <string id="19637"></string>
++ <string id="19638"></string>
++ <string id="19639"></string>
++ <string id="19640"></string>
++ <string id="19641"></string>
++ <string id="19642"></string>
++ <string id="19643"></string>
++ <string id="19644">Отдых/Хобби</string>
++ <string id="19645">Туризм/ПутешеÑтвиÑ</string>
++ <string id="19646">Сделай Ñам</string>
++ <string id="19647">ИнженериÑ</string>
++ <string id="19648">Ð¤Ð¸Ñ‚Ð½ÐµÑ Ð¸ здоровье</string>
++ <string id="19649">КулинариÑ</string>
++ <string id="19650">Advertisement/Shopping</string>
++ <string id="19651">Gardening</string>
++ <string id="19652"></string>
++ <string id="19653"></string>
++ <string id="19654"></string>
++ <string id="19655"></string>
++ <string id="19656"></string>
++ <string id="19657"></string>
++ <string id="19658"></string>
++ <string id="19659"></string>
++ <string id="19660">Спец. характериÑтики</string>
++ <string id="19661">В оригинале</string>
++ <string id="19662">Черно-белое</string>
++ <string id="19663">Ðеизданное</string>
++ <string id="19664">ПрÑмой Ñфир</string>
++ <string id="19665"></string>
++ <string id="19666"></string>
++ <string id="19667"></string>
++ <string id="19668"></string>
++ <string id="19669"></string>
++ <string id="19670"></string>
++ <string id="19671"></string>
++ <string id="19672"></string>
++ <string id="19673"></string>
++ <string id="19674"></string>
++ <string id="19675"></string>
++ <string id="19676">Драма</string>
++ <string id="19677">Детектив/Триллер</string>
++ <string id="19678">ПриключениÑ/ВеÑтерн/Война</string>
++ <string id="19679">Ðаука/ФÑнтези/УжаÑÑ‹</string>
++ <string id="19680">КомедиÑ</string>
++ <string id="19681">Мыло/Мелодрамма/Фольклор</string>
++ <string id="19682">Романтика</string>
++ <string id="19683">ИÑториÑ/РелигиÑ</string>
++ <string id="19684">Ð”Ð»Ñ Ð²Ð·Ñ€Ð¾Ñлых</string>
++ <string id="19685"></string>
++ <string id="19686"></string>
++ <string id="19687"></string>
++ <string id="19688"></string>
++ <string id="19689"></string>
++ <string id="19690"></string>
++ <string id="19691"></string>
+
+ <string id="20000">Папка Ñохраненной музыки</string>
+ <string id="20001">ИÑпользовать внешний проигрыватель DVD</string>
+@@ -1773,7 +2196,7 @@
+ <string id="20326">Это ÑброÑит калибровочные Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð´Ð»Ñ %s</string>
+ <string id="20327">до значений по умолчанию</string>
+ <string id="20328">Укажите назначение</string>
+-
++ <string id="20329">Файлы в отдельных папках Ñ Ð½Ð°Ð·Ð²Ð°Ð½Ð¸ÐµÐ¼ фильма</string>
+ <string id="20330">ИÑпользовать имена папок Ð´Ð»Ñ Ð¿Ð¾Ð¸Ñка</string>
+ <string id="20331">Файл</string>
+ <string id="20332">ИÑпользовать имена файлов или папок Ð´Ð»Ñ Ð¿Ð¾Ð¸Ñка?</string>
+@@ -1863,7 +2286,7 @@
+ <string id="20416">Премьера</string>
+ <string id="20417">Сценарий</string>
+ <string id="20418"></string>
+- <string id="20419">Показывать метаданные в предÑтавлении файлов</string>
++ <string id="20419">Показывать Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ñ Ð¸Ð· медиатеки вмеÑто имен файлов</string>
+
+ <string id="20420">Ðикогда</string>
+ <string id="20421">ЕÑли один Ñезон</string>
+@@ -1925,7 +2348,7 @@
+ <string id="21366">Папка Ñубтитров</string>
+ <string id="21367">Папка Ð´Ð»Ñ Ñ„Ð¸Ð»ÑŒÐ¼Ð¾Ð² и альтернативных Ñубтитров</string>
+ <string id="21368">Игнорировать шрифты Ñубтитров ASS/SSA</string>
+-
++
+ <string id="21369">Включить мышь и ÑенÑорный Ñкран</string>
+ <string id="21370">Звуки интерфейÑа во Ð²Ñ€ÐµÐ¼Ñ Ð¿Ñ€Ð¾Ð¸Ð³Ñ€Ñ‹Ð²Ð°Ð½Ð¸Ñ</string>
+ <string id="21371">ЭÑкизы</string>
+@@ -2013,7 +2436,7 @@
+ <string id="21453">ÐšÐ¾Ñ€Ð½ÐµÐ²Ð°Ñ Ð¤Ð¡</string>
+ <string id="21454">КÑш заполнен</string>
+ <string id="21455">Объем кÑша недоÑтаточен Ð´Ð»Ñ Ð½ÐµÐ¿Ñ€ÐµÑ€Ñ‹Ð²Ð½Ð¾Ð³Ð¾ проигрываниÑ</string>
+-
++
+ <string id="21460">РаÑположение Ñубтитров</string>
+ <string id="21461">ФикÑированное</string>
+ <string id="21462">Снизу видео</string>
+@@ -2329,7 +2752,7 @@
+ <string id="33101">Веб-Ñервер</string>
+ <string id="33102">Сервер Ñобытий</string>
+ <string id="33103">Сервер удаленного доÑтупа</string>
+-
++
+ <string id="33200">Обнаружено новое подключение</string>
+
+ <!-- translators: no need to add these to your language files -->
+@@ -2355,10 +2778,10 @@
+
+ <string id="34201">Ðе удаетÑÑ Ð½Ð°Ð¹Ñ‚Ð¸ Ñледующий файл</string>
+ <string id="34202">Ðе удаетÑÑ Ð½Ð°Ð¹Ñ‚Ð¸ предыдущий файл</string>
+-
++
+ <string id="34300">Ðе удалоÑÑŒ запуÑтить zeroconf</string>
+ <string id="34301">Проверьте, уÑтановлена ли Ñлужба Apple Bonjour. См. журнал.</string>
+-
++
+ <string id="34400">Обработчик видео</string>
+ <string id="34401">Ðе удалоÑÑŒ активировать фильтры видео. ИÑпользуетÑÑ Ð¼Ð°Ñштабирование bilinear.</string>
+ <string id="34402">Ðе удалоÑÑŒ активировать аудиоуÑтройÑтво</string>
+@@ -2380,7 +2803,7 @@
+ <string id="35502">Ðазвание</string>
+ <string id="35503">Производитель</string>
+ <string id="35504">ID продукта</string>
+-
++
+ <string id="36000">Ðдаптер Pulse-Eight CEC</string>
+ <string id="36001">Pulse-Eight Nyxboard</string>
+ <string id="36002">Включить дополнительные команды клавиатуры</string>
+diff --git a/language/Slovak/strings.xml b/language/Slovak/strings.xml
+index 64f3e4e..becb3a0 100644
+--- a/language/Slovak/strings.xml
++++ b/language/Slovak/strings.xml
+@@ -1,1600 +1,1376 @@
+-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+ <!--Language file translated with Team XBMC Translator-->
+ <!--Translator: Michal Iro-->
+ <!--Email: mixo@frco.sk-->
+-<!--Date of translation: 09/21/2011-->
++<!--Date of translation: 04/09/2012-->
+ <!--$Revision$-->
+ <strings>
+ <string id="0">Programy</string>
+ <string id="1">Obrázky</string>
+ <string id="2">Hudba</string>
+- <string id="3">Video</string>
+-
++ <string id="3">Videá</string>
+ <string id="4">TV program</string>
+ <string id="5">Nastavenia</string>
++ <string id="6">XBMC SVN</string>
+ <string id="7">Správca súborov</string>
+ <string id="8">PoÄasie</string>
+ <string id="9">xbmc media center</string>
+ <string id="11">Pondelok</string>
+-
+ <string id="12">Utorok</string>
+ <string id="13">Streda</string>
+ <string id="14">Å tvrtok</string>
+ <string id="15">Piatok</string>
+ <string id="16">Sobota</string>
+ <string id="17">Nedeľa</string>
+-
+ <string id="21">Január</string>
+ <string id="22">Február</string>
+ <string id="23">Marec</string>
+ <string id="24">Apríl</string>
+ <string id="25">Máj</string>
+ <string id="26">Jún</string>
+-
+ <string id="27">Júl</string>
+ <string id="28">August</string>
+ <string id="29">September</string>
+ <string id="30">Október</string>
+ <string id="31">November</string>
+ <string id="32">December</string>
+-
+ <string id="41">Po</string>
+ <string id="42">Ut</string>
+ <string id="43">St</string>
+ <string id="44">Å t</string>
+ <string id="45">Pi</string>
+ <string id="46">So</string>
+-
+ <string id="47">Ne</string>
+ <string id="51">Jan</string>
+ <string id="52">Feb</string>
+ <string id="53">Mar</string>
+ <string id="54">Apr</string>
+ <string id="55">Máj</string>
+-
+ <string id="56">Jún</string>
+ <string id="57">Júl</string>
+ <string id="58">Aug</string>
+ <string id="59">Sep</string>
+ <string id="60">Okt</string>
+ <string id="61">Nov</string>
+-
+ <string id="62">Dec</string>
+ <string id="71">S</string>
+ <string id="72">SSV</string>
+ <string id="73">SV</string>
+ <string id="74">VSV</string>
+ <string id="75">V</string>
+-
+ <string id="76">VJV</string>
+ <string id="77">JV</string>
+ <string id="78">JJV</string>
+ <string id="79">J</string>
+ <string id="80">JJZ</string>
+ <string id="81">JZ</string>
+-
+ <string id="82">ZJZ</string>
+ <string id="83">Z</string>
+ <string id="84">ZSZ</string>
+ <string id="85">SZ</string>
+ <string id="86">SSZ</string>
+ <string id="87">premenlivé</string>
+-
+ <string id="98">Zobrazenie: Auto</string>
+ <string id="99">Zobrazenie: Auto veľké</string>
+ <string id="100">Zobrazenie: Ikony</string>
+ <string id="101">Zobrazenie: Zoznam</string>
+ <string id="102">Prezrieť</string>
+ <string id="103">Usporiadať: Názov</string>
+-
+ <string id="104">Usporiadať: Dátum</string>
+ <string id="105">Usporiadať: Veľkosť</string>
+ <string id="106">Nie</string>
+ <string id="107">Ãno</string>
+ <string id="108">Prezentácia</string>
+ <string id="109">Vytvoriť náhľady</string>
+-
+ <string id="110">Vytvoriť náhľady</string>
+ <string id="111">Skratky</string>
+ <string id="112">PAUZA</string>
+ <string id="113">Akualizácia sa nepodarila</string>
+ <string id="114">Inštalácia sa nepodarila</string>
+ <string id="115">Kopírovať</string>
+-
+ <string id="116">Presunúť</string>
+ <string id="117">Vymazať</string>
+ <string id="118">Premenovať</string>
+ <string id="119">Nový prieÄinok</string>
+ <string id="120">Potvrdenie kopírovania</string>
+ <string id="121">Potvrdenie presunutia</string>
+-
+ <string id="122">Potvrdenie vymazania</string>
+ <string id="123">Kopírovať tieto súbory?</string>
+ <string id="124">Presunúť tieto súbory?</string>
+ <string id="125">Vymazať tieto súbory?</string>
+- <string id="126">Stav</string>
+- <string id="127">Objekty</string>
+-
++ <string id="126">Priebeh</string>
++ <string id="127">Objekt(ov)</string>
+ <string id="128">Všeobecné</string>
+ <string id="129">Prezentácia</string>
+ <string id="130">Systémové informácie</string>
+ <string id="131">Obrazovka</string>
+- <string id="132">Album</string>
+- <string id="133">Interprét</string>
+-
+- <string id="134">Skladba</string>
+- <string id="135">Štýl/Žáner</string>
+- <string id="136">Playlist-y</string>
++ <string id="132">Albumy</string>
++ <string id="133">Interpréti</string>
++ <string id="134">Skladby</string>
++ <string id="135">Žánre</string>
++ <string id="136">Playlisty</string>
+ <string id="137">Hľadať</string>
+ <string id="138">SYSTÉMOVÉ INFORMÃCIE</string>
+ <string id="139">Teploty:</string>
+-
+ <string id="140">CPU:</string>
+ <string id="141">GPU:</string>
+ <string id="142">ÄŒas:</string>
+- <string id="143">Aktuálny stav:</string>
++ <string id="143">Aktuálne:</string>
+ <string id="144">Build:</string>
+ <string id="145">Sieť:</string>
+-
+ <string id="146">Typ:</string>
+ <string id="147">Statická</string>
+ <string id="148">DHCP</string>
+ <string id="149">MAC</string>
+ <string id="150">IP</string>
+- <string id="151">Prepojenie: </string>
+-
++ <string id="151">Stav: </string>
+ <string id="152">PoloviÄný duplex</string>
+ <string id="153">Plný duplex</string>
+- <string id="154">Úložisko</string>
+- <string id="155">Jednotka</string>
+- <string id="156">Voľných</string>
++ <string id="154">Kapacita</string>
++ <string id="155">Disk</string>
++ <string id="156">Voľné miesto</string>
+ <string id="157">Video</string>
+-
+ <string id="158">Voľná pamäť</string>
+ <string id="159">Žiadne prepojenie</string>
+ <string id="160">Voľné</string>
+- <string id="161">Nedostupný</string>
++ <string id="161">Nedostupné</string>
+ <string id="162">Vysunúť disk</string>
+ <string id="163">Čítam</string>
+-
+- <string id="164">Žiaden disk</string>
+- <string id="165">Disk dostupný</string>
++ <string id="164">Žiadny disk</string>
++ <string id="165">Disk vložený</string>
+ <string id="166">Vzhľad</string>
+ <string id="169">Rozlíšenie</string>
+- <string id="170">Prispôsobiť obnovovaciu frekvenciu obrazovky k videu</string>
+- <string id="172">Dátum vydania:</string>
+-
+- <string id="173">Zobrazenie 4:3 videa</string>
++ <string id="170">Prispôsobovať obnovovaciu frekvenciu obrazovky podľa videa</string>
++ <string id="172">Dátum vydania</string>
++ <string id="173">Zobrazovať 4:3 videa</string>
+ <string id="175">Charakter:</string>
+ <string id="176">Štýly</string>
+ <string id="179">Skladba</string>
+ <string id="180">Dĺžka</string>
+- <string id="181">Výber albumu</string>
+-
+- <string id="182">Stopy</string>
++ <string id="181">Vyberte albumu</string>
++ <string id="182">Skladby</string>
+ <string id="183">Recenzia</string>
+ <string id="184">Obnoviť</string>
+ <string id="185">Vyhľadávanie albumu</string>
+ <string id="186">OK</string>
+ <string id="187">Albumy nenájdené!</string>
+-
+ <string id="188">Vybrať všetko</string>
+ <string id="189">Získavanie informácií o médiu</string>
+ <string id="190">Uložiť</string>
+ <string id="191">Náhodné</string>
+ <string id="192">VyÄistiÅ¥</string>
+ <string id="193">Prehľadať</string>
+-
+ <string id="194">Vyhľadávam...</string>
+- <string id="195">Bez informácií!</string>
+- <string id="196">Výber filmu:</string>
++ <string id="195">Informácie nenájdené!</string>
++ <string id="196">Vyberte film:</string>
+ <string id="197">Zisťujem informácie o %s</string>
+ <string id="198">NaÄítanie detailov o filme</string>
+ <string id="199">Webové rozhranie</string>
+-
+- <string id="202">Obsah</string>
+- <string id="203">Popis</string>
++ <string id="202">StruÄné info</string>
++ <string id="203">Obsah filmu</string>
+ <string id="205">Hlasov</string>
+- <string id="206">Herci</string>
++ <string id="206">Obsadenie</string>
+ <string id="207">Obsah</string>
+ <string id="208">Prehrať</string>
+-
+- <string id="209">Ďalší</string>
+- <string id="210">Predchádzajúci</string>
+- <string id="213">Kalibrácia rozhrania...</string>
+- <string id="214">Kalibrácia videa...</string>
++ <string id="209">Násl.</string>
++ <string id="210">Pred.</string>
++ <string id="213">Kalibrácia používateľského rozhrania...</string>
++ <string id="214">Kalibrácia obrazu...</string>
+ <string id="215">ZmäkÄenie</string>
+- <string id="216">Veľkosť priblíženia</string>
+-
++ <string id="216">Priblíženie</string>
+ <string id="217">Pomer strán</string>
+ <string id="218">DVD disk</string>
+ <string id="219">Prosím vložte disk</string>
+ <string id="220">Vzdialené zdieľanie</string>
+ <string id="221">Sieť nie je pripojená</string>
+ <string id="222">Zrušiť</string>
+-
+ <string id="224">Rýchlosť</string>
+- <string id="225">Vertikálny posun</string>
++ <string id="225">Vertikálne posunutie</string>
+ <string id="226">Testovací obraz...</string>
+- <string id="227">Vyhľadať audio CD na freedb</string>
+- <string id="228">Nahodný playlist pri otvorení</string>
+- <string id="229">Zastaviť disk</string>
+-
++ <string id="227">Vyhľadávať názvy skladieb z CD na freedb.org</string>
++ <string id="228">Náhodne prehrávaÅ¥ skladby po naÄítaní playlist-u</string>
++ <string id="229">Uspávať disk</string>
+ <string id="230">Video filtre</string>
+ <string id="231">Žiadne</string>
+ <string id="232">Bodový</string>
+ <string id="233">Lineárny</string>
+ <string id="234">Anizotropický</string>
+ <string id="235">Quincunx</string>
+-
+ <string id="236">Gaussovsko kubický</string>
+ <string id="237">Zmenšenie</string>
+ <string id="238">ZväÄÅ¡enie</string>
+- <string id="239">VymazaÅ¥ playlist pri ukonÄení</string>
+- <string id="240">Vybrať zobrazenie</string>
++ <string id="239">OdstrániÅ¥ playlist po skonÄení prehrávania</string>
++ <string id="240">Možnosti zobrazenia</string>
+ <string id="241">Celá obrazovka #%d</string>
+-
+- <string id="242">Okno</string>
++ <string id="242">V Okne</string>
+ <string id="243">Obnovovacia frekvencia</string>
+ <string id="244">Celá obrazovka</string>
+- <string id="245">Kalibrácia: (%i,%i)->(%i,%i) (Zoom x%2.2f) AR:%2.2f:1 (Pixels: %2.2f:1) (VShift: %2.2f)</string>
++ <string id="245">Zmena veľkosti: (%i,%i)->(%i,%i) (Priblíženie x%2.2f) AR:%2.2f:1 (Pixelov: %2.2f:1) (VShift: %2.2f)</string>
+ <string id="247">Skripty</string>
+ <string id="248">Jazyk</string>
+-
+ <string id="249">Hudba</string>
+- <string id="250">Vizualizácie</string>
+- <string id="251">Nastaviť cieľový adresár</string>
++ <string id="250">Vizualizácia</string>
++ <string id="251">Vyberte cieľový prieÄinok</string>
+ <string id="252">Stereo výstup do všetkých reproduktorov</string>
+ <string id="253">PoÄet kanálov</string>
+ <string id="254">- DTS kompatibilný receiver</string>
+-
+ <string id="255">CDDB</string>
+- <string id="256">Dotazujem freedb pre CDDB info</string>
++ <string id="256">Získavnie informácií o CD</string>
+ <string id="257">Chyba</string>
+ <string id="258">PovoliÅ¥ Äítanie tagov</string>
+ <string id="259">Otváranie</string>
+ <string id="260">Shoutcast</string>
+-
+- <string id="261">ÄŒakanie na Å¡tart...</string>
++ <string id="261">Prebieha príprava spustenia...</string>
+ <string id="262">Výstup skript</string>
+ <string id="263">Povoliť ovládanie XBMC cez HTTP</string>
+- <string id="264">Nahrávanie</string>
+- <string id="265">Stop Náhr.</string>
+- <string id="266">Usporiadať: Stopa</string>
+-
+- <string id="267">Usporiadať: Čas</string>
+- <string id="268">Usporiadať: Názov</string>
+- <string id="269">Usporiadať: Interprét</string>
+- <string id="270">Usporiadať: Album</string>
++ <string id="264">Nahrávať</string>
++ <string id="265">UkonÄiÅ¥ nahrávanie</string>
++ <string id="266">Zoradiť podľa: Stopy</string>
++ <string id="267">Zoradiť podľa: Času</string>
++ <string id="268">Zoradiť podľa: Názvu</string>
++ <string id="269">Zoradiť podľa: Interpréta</string>
++ <string id="270">Zoradiť podľa: Albumu</string>
+ <string id="271">Naj 100</string>
+- <string id="272">Vrchná ľavá kompenzácia presnímania</string>
+-
+- <string id="273">Spodná pravá kompenzácia presnímania</string>
++ <string id="272">Nastavenie ľavého horného okraja</string>
++ <string id="273">Nastavenie pravého spodného okraja</string>
+ <string id="274">Pozícia titulkov</string>
+- <string id="275">Úprava Pixel Ratia</string>
+- <string id="276">Posunom šipiek zmeníte veľkosť presnímania</string>
+- <string id="277">Posuňte Äiaru pre zmenu pozície titulkov</string>
+- <string id="278">Zmeňte pravouholník nech je štvorec</string>
+-
+- <string id="279">Nie je možné otvoriť nastavenia</string>
++ <string id="275">Nastavenie pomeru strán</string>
++ <string id="276">Šípkami nastavíte okraj</string>
++ <string id="277">Posunutím Äiari zmeníte pozíciu titulkov</string>
++ <string id="278">Upravte obdĺžnik tak, aby vznikol perfektný štvorec</string>
++ <string id="279">Nepodarilo sa otvoriť nastavenia</string>
+ <string id="280">Použitie predvolených nastavení</string>
+ <string id="281">Prosím skontrolujte .xml súbory</string>
+ <string id="282">nájdených %i položiek</string>
+ <string id="283">Výsledky hľadania</string>
+ <string id="284">Žiadny výsledok</string>
+-
+ <string id="287">Titulky</string>
+ <string id="288">Písmo</string>
+ <string id="289">- Veľkosť</string>
+ <string id="290">Kompresia dynamického rozsahu</string>
+- <string id="291">Obraz</string>
++ <string id="291">Video</string>
+ <string id="292">Zvuk</string>
+-
+- <string id="293">Vybrať titulky</string>
++ <string id="293">Vyberte titulky</string>
+ <string id="294">Vytvoriť záložku</string>
+ <string id="296">Vymazať záložku</string>
+- <string id="297">Oneskorenie zvuku</string>
++ <string id="297">Posunutie zvuku</string>
+ <string id="298">Záložky</string>
+ <string id="299">- AAC kompatibilný receiver</string>
+-
+ <string id="300">- MP1 kompatibilný receiver</string>
+ <string id="301">- MP2 kompatibilný receiver</string>
+ <string id="302">- MP3 kompatibilný receiver</string>
+ <string id="303">Oneskorenie</string>
+ <string id="304">Jazyk</string>
+ <string id="305">Aktivované</string>
+-
+- <string id="306">Neprekladaný</string>
++ <string id="306">Neprekladané video</string>
+ <string id="312">(0=auto)</string>
+ <string id="313">Čistenie databázy</string>
+ <string id="314">Pripravujem...</string>
+ <string id="315">Chyba databázy</string>
+ <string id="316">Vyhľadávam skladby...</string>
+-
+ <string id="317">VyÄistenie databázy úspeÅ¡né</string>
+ <string id="318">ÄŒistenie skladieb...</string>
+ <string id="319">Chyba pri Äistení skladieb</string>
+ <string id="320">Čistenie interprétov...</string>
+ <string id="321">Chyba pri Äistení interprétov</string>
+ <string id="322">Čistenie žánrov...</string>
+-
+ <string id="323">Chyba pri Äistení žánrov</string>
+ <string id="324">ÄŒistenie ciest...</string>
+ <string id="325">Chyba pri Äistení ciest</string>
+ <string id="326">ÄŒistenie albumov...</string>
+ <string id="327">Chyba pri Äistení albumov</string>
+ <string id="328">Zapisujem zmeny...</string>
+-
+ <string id="329">Chyba pri zapisovaní zmien</string>
+ <string id="330">Toto môže chvíľku trvať...</string>
+ <string id="331">Komprimujem databázu...</string>
+ <string id="332">Chyba pri komprimácii databázy</string>
+- <string id="333">Chcete vyÄistiÅ¥ knižnicu?</string>
+- <string id="334">Čistenie knižnice...</string>
+-
+- <string id="335">Å tart</string>
++ <string id="333">Naozaj chcete vyÄistiÅ¥ knižnicu?</string>
++ <string id="334">VyÄistiÅ¥ knižnicu...</string>
++ <string id="335">Spustiť</string>
+ <string id="336">Konverzia snímkovania</string>
+ <string id="337">Audio výstup</string>
+ <string id="338">Analógový</string>
+ <string id="339">Digitálny</string>
+ <string id="340">Rôzny interpréti</string>
+-
+ <string id="341">CD/DVD</string>
+ <string id="342">Filmy</string>
+ <string id="343">Prispôsobenie snímkovania</string>
+ <string id="344">Herec</string>
+ <string id="345">Rok</string>
+ <string id="346">Zvyšovať hlasitosť pri downmixe</string>
+-
+ <string id="350">Programy</string>
+- <string id="351">Vypnúť</string>
++ <string id="351">Vypnuté</string>
+ <string id="352">Zahmliť</string>
+ <string id="353">ÄŒierna</string>
+ <string id="354">Matrix Trails</string>
+- <string id="355">ÄŒas neÄinnosti pre Å¡etriÄ obrazovky</string>
+-
++ <string id="355">ÄŒas neÄinnosti potrebný pre aktiváciu Å¡etriÄa obrazovky</string>
+ <string id="356">Å etriÄ obrazovky</string>
+- <string id="357">ÄŒas neÄinnosti pre vypnutie</string>
++ <string id="357">ÄŒas neÄinnosti potrebný pre vypnutie systému</string>
+ <string id="358">VÅ¡etky albumy</string>
+- <string id="359">Nedávno pridané albumy</string>
++ <string id="359">Naposledy pridané albumy</string>
+ <string id="360">Å etriÄ obrazovky</string>
+- <string id="361">Ceľková prezentácia</string>
+-
++ <string id="361">Rekurzívna prezentácia</string>
+ <string id="362">Hladina blednutia Å¡etriÄa obrazovky</string>
+ <string id="363">Usporiadať: Súbor</string>
+ <string id="364">- Dolby Digital (AC3) kompatibilný reciever</string>
+- <string id="365">Usporiadať: Názov</string>
+- <string id="366">Usporiadať: Rok</string>
+- <string id="367">Usporiadať: Hodnotenie</string>
+-
++ <string id="365">Zoradiť podľa: Názvu</string>
++ <string id="366">Zoradiť podľa: Roku</string>
++ <string id="367">Zoradiť podľa: Hodnotenia</string>
+ <string id="368">IMDb</string>
+ <string id="369">Názov</string>
+- <string id="370">búrka</string>
++ <string id="370">búrky</string>
+ <string id="371">ÄiastoÄne</string>
+ <string id="372">prevažne</string>
+ <string id="373">slneÄno</string>
+-
+- <string id="374">zamraÄene</string>
++ <string id="374">zamraÄené</string>
+ <string id="375">sneženie</string>
+ <string id="376">dážÄ</string>
+- <string id="377">slabý</string>
+- <string id="378">dop.</string>
+- <string id="379">pop.</string>
+-
++ <string id="377">slabý(é)</string>
++ <string id="378">AM</string>
++ <string id="379">PM</string>
+ <string id="380">prehánky</string>
+ <string id="381">obÄas</string>
+- <string id="382">premenlivé</string>
++ <string id="382">miestami</string>
+ <string id="383">veterno</string>
+ <string id="384">silný</string>
+ <string id="385">pekne</string>
+-
+ <string id="386">jasno</string>
+ <string id="387">oblaÄno</string>
+ <string id="388">ranné</string>
+ <string id="389">prehánky</string>
+- <string id="390">veterno</string>
++ <string id="390">snehové prehánky</string>
+ <string id="391">min.</string>
+-
+ <string id="392">stred</string>
+ <string id="393">max.</string>
+ <string id="394">hmla</string>
+ <string id="395">mrholenie</string>
+- <string id="396">Vyberte miesto</string>
+- <string id="397">Obnoviť po</string>
+-
++ <string id="396">Vyberte mesto</string>
++ <string id="397">Obnovovať po</string>
+ <string id="398">Jednotky teploty</string>
+ <string id="399">Jednotky rýchlosti</string>
+ <string id="400">PoÄasie</string>
+ <string id="401">Teplota</string>
+- <string id="402">Ovzdušie</string>
++ <string id="402">Zobrazenie</string>
+ <string id="403">UV Index</string>
+-
+ <string id="404">Vietor</string>
+ <string id="405">Rosný bod</string>
+ <string id="406">Vlhkosť</string>
+ <string id="409">Predvolené</string>
+ <string id="410">Pristupujem k weather.com</string>
+- <string id="411">Čítam poÄasie pre:</string>
+-
+- <string id="412">Nie je možné naÄítaÅ¥ dáta pre poÄasie</string>
++ <string id="411">Získavam poÄasie pre:</string>
++ <string id="412">nepodarilo sa naÄítaÅ¥ dáta o poÄasí</string>
+ <string id="413">Manuálne</string>
+ <string id="414">Pre tento album nie je žiadna recenzia</string>
+ <string id="415">Sťahujem náhľad...</string>
+- <string id="416">Nie je prístupné</string>
++ <string id="416">Nedostupné</string>
+ <string id="417">Zobrazenie: Veľké ikony</string>
+-
+- <string id="418">Povoliť knižnicu</string>
+- <string id="419">Vysoké</string>
++ <string id="418">Noc</string>
++ <string id="419">Deň</string>
+ <string id="420">HDMI</string>
+ <string id="422">Odstrániť informácie o albume</string>
+- <string id="423">Odstrániť CDDB info</string>
++ <string id="423">Odstrániť CD info</string>
+ <string id="424">Vybrať</string>
+-
+- <string id="425">Neboli nájdené informácie pre album.</string>
+- <string id="426">Nebolo nájdené CDDB informácie.</string>
++ <string id="425">Informácie pre album neboli nájdené.</string>
++ <string id="426">CD informácie neboli nájdené</string>
+ <string id="427">Disk</string>
+- <string id="428">Vložte správne CD/DVD</string>
+- <string id="429">Prosím vložte ÄalÅ¡ie CD/DVD</string>
+- <string id="430">Usporiadať: DVD#</string>
+-
++ <string id="428">Vložte CD/DVD</string>
++ <string id="429">Prosím vložte disk s oznaÄením:</string>
++ <string id="430">Zoradiť podľa: DVD#</string>
+ <string id="431">Žiadna medzipamäť</string>
+ <string id="432">Odstrániť film z knižnice</string>
+- <string id="433">Naozaj odstrániť '%s'?</string>
+- <string id="434">Z %s rychlosti %i %s</string>
+- <string id="437">Externý disk</string>
++ <string id="433">Naozaj chcete odstrániť '%s'?</string>
++ <string id="434">%s %i %s</string>
++ <string id="437">Vymeniteľný disk</string>
+ <string id="438">Otváranie súboru</string>
+-
+ <string id="439">Medzipamäť</string>
+ <string id="440">Harddisk</string>
+ <string id="441">UDF</string>
+- <string id="442">LAN</string>
++ <string id="442">Lokálna sieť</string>
+ <string id="443">Internet</string>
+ <string id="444">Video</string>
+-
+ <string id="445">Audio</string>
+ <string id="446">DVD</string>
+- <string id="447">Automatické spustenie</string>
++ <string id="447">Automaticky prehrávať médiá</string>
+ <string id="448">LCD</string>
+- <string id="449">Povolené</string>
++ <string id="449">Aktívne</string>
+ <string id="450">Stĺpcov</string>
+-
+- <string id="451">Adresa riadku 1</string>
+- <string id="452">Adresa riadku 2</string>
+- <string id="453">Adresa riadku 3</string>
+- <string id="454">Adresa riadku 4</string>
++ <string id="451">Adresa 1 riadku</string>
++ <string id="452">Adresa 2 riadku</string>
++ <string id="453">Adresa 3 riadku</string>
++ <string id="454">Adresa 4 riadku</string>
+ <string id="455">Riadky</string>
+- <string id="456">Mód</string>
+-
++ <string id="456">režim</string>
+ <string id="457">Prepnúť zobrazenie</string>
+ <string id="459">Titulky</string>
+ <string id="460">Zvuková stopa</string>
+ <string id="461">[aktívny]</string>
+ <string id="462">Titulky</string>
+ <string id="463">Podsvietenie</string>
+-
+ <string id="464">Jas</string>
+ <string id="465">Kontrast</string>
+ <string id="466">Gamma</string>
+ <string id="467">Typ</string>
+- <string id="468">OSD pozíciu zmeníte posunom Äiary</string>
++ <string id="468">Pozíciu OSD zmeníte posunom Äiary</string>
+ <string id="469">Pozícia OSD</string>
+-
+ <string id="470">Autori</string>
+ <string id="471">ModÄip</string>
+ <string id="474">Vypnúť</string>
+ <string id="475">Iba hudba</string>
+ <string id="476">Hudba a Video</string>
+ <string id="477">Nedá sa otvoriť playlist</string>
+-
+ <string id="478">OSD</string>
+ <string id="479">Vzhľad a jazyk</string>
+ <string id="480">Prostredie</string>
+ <string id="481">Nastavenia zvuk</string>
+ <string id="482">O XBMC</string>
+ <string id="485">Odstrániť album</string>
+-
+ <string id="486">Opakovať</string>
+ <string id="487">Opakovať jeden</string>
+ <string id="488">OpakovaÅ¥ prieÄinok</string>
+- <string id="489">Automaticky prehraÅ¥ ÄalÅ¡iu skladbu</string>
+- <string id="491">- Použiť veľké ikony</string>
++ <string id="489">Automaticky prehrávaÅ¥ ÄalÅ¡iu skladbu</string>
++ <string id="491">- Používať veľké ikony</string>
+ <string id="492">Velkosť vobsubs</string>
+-
+ <string id="493">PokroÄilé nastavenia (iba expert!)</string>
+ <string id="494">Celková hlasitosť</string>
+- <string id="495">PrepoÄítavaÅ¥ videa do rozlíšenia GUI</string>
++ <string id="495">PrepoÄítavaÅ¥ videa do GUI rozlíšenia</string>
+ <string id="496">Kalibrácia</string>
+- <string id="497">Schovať prípony médií</string>
+- <string id="498">Usporiadať: Typ</string>
+-
++ <string id="497">Zobrazovať prípony médií</string>
++ <string id="498">Zoradiť podľa: Typu</string>
+ <string id="499">Nedá sa pripojiť k www.allmusic.com</string>
+ <string id="500">Sťahovanie informácií o albume zlyhalo</string>
+- <string id="501">Hľadanie názvov albumov...</string>
++ <string id="501">Hľadanie názvu albumov...</string>
+ <string id="502">Otvoriť</string>
+ <string id="503">Pracujem</string>
+ <string id="504">Prázdny</string>
+-
+- <string id="505">NaÄítanie informácií o médiu zo súborov...</string>
+- <string id="507">Usporiadať: Použitie</string>
+- <string id="510">Povoliť vizualizácie</string>
+- <string id="511">Povoliť zmenu video módu</string>
++ <string id="505">Prebieha naÄítanie informácií zo súborov...</string>
++ <string id="507">Zoradiť podľa: Použitia</string>
++ <string id="510">Zobrazovať vizualizácie</string>
++ <string id="511">Povoliť zmenu video režimu</string>
+ <string id="512">Úvodné okno</string>
+- <string id="513">Domáce okno</string>
+-
++ <string id="513">Domovská stránka</string>
+ <string id="514">Manuálne nastavenia</string>
+ <string id="515">Žáner</string>
+- <string id="517">Nedávno prehrávané albumy</string>
++ <string id="517">Naposledy prehrávané albumy</string>
+ <string id="518">Spustiť</string>
+ <string id="519">Spustiť...</string>
+ <string id="521">Kompilácie</string>
+-
+ <string id="522">Odstrániť zdroj</string>
+- <string id="523">Zmena média</string>
+- <string id="524">Vybrať playlist</string>
++ <string id="523">Prejsť do inej sekcie</string>
++ <string id="524">Vyberte playlist</string>
+ <string id="525">Nový playlist...</string>
+ <string id="526">Pridať do playlist-u</string>
+ <string id="527">Manuálne pridať do knižnice</string>
+-
+- <string id="528">Napíšte názov</string>
++ <string id="528">Zadajte názov</string>
+ <string id="529">Chyba: Duplicitný názov</string>
+- <string id="530">Vybrať žáner</string>
++ <string id="530">Vyberte žáner</string>
+ <string id="531">Nový žáner</string>
+ <string id="532">Manuálne pridanie</string>
+ <string id="533">Vložiť žáner</string>
+-
+ <string id="534">Zobrazenie: %s</string>
+ <string id="535">Zoznam</string>
+ <string id="536">Ikony</string>
+- <string id="537">Veľký zoznam</string>
+- <string id="538">Veľké ikony</string>
+- <string id="539">Široký</string>
+-
+- <string id="540">Veľmi široký</string>
+- <string id="541">Ikony albumov</string>
+- <string id="542">Ikony DVD</string>
++ <string id="537">Zoznam II</string>
++ <string id="538">Ikony II</string>
++ <string id="539">Stĺpce</string>
++ <string id="540">Stĺpce II</string>
++ <string id="541">Náhľady albumov</string>
++ <string id="542">Náhľady DVD</string>
+ <string id="543">DVD</string>
+- <string id="544">Info o médiu</string>
++ <string id="544">Informácie o médiu</string>
+ <string id="545">Zariadenie pre výstup zvuku</string>
+-
+ <string id="546">Zariadenie pre prestup zvuku</string>
+ <string id="547">Životopis pre tohoto interpréta nie je dostupný</string>
+ <string id="548">Prevádzať viackanálový zvuk na stereo</string>
+- <string id="550">Usporiadať: %s</string>
++ <string id="550">Zoradiť: %s</string>
+ <string id="551">Názov</string>
+ <string id="552">Dátum</string>
+-
+ <string id="553">Veľkosť</string>
+ <string id="554">Stopa</string>
+ <string id="555">ÄŒas</string>
+ <string id="556">Názov</string>
+ <string id="557">Interprét</string>
+ <string id="558">Album</string>
+-
+ <string id="559">Playlist</string>
+ <string id="560">ID</string>
+ <string id="561">Súbor</string>
+ <string id="562">Rok</string>
+ <string id="563">Hodnotenie</string>
+ <string id="564">Typ</string>
+-
+ <string id="565">Použitie</string>
+ <string id="566">Autor albumu</string>
+- <string id="567">PoÄet prehraní</string>
+- <string id="568">Poslednéhé prehranie</string>
++ <string id="567">PoÄet prehratí</string>
++ <string id="568">Naposledy prehrané</string>
+ <string id="569">Komentár</string>
+ <string id="570">Dátum pridania</string>
+-
+ <string id="571">Predvolené</string>
+ <string id="572">Štúdio</string>
+- <string id="573">Cesta</string>
++ <string id="573">Umiestnenie</string>
+ <string id="574">Krajina</string>
+ <string id="575">Práve prebieha</string>
+- <string id="576">Krát hrané</string>
+-
++ <string id="576">PoÄet prehratí</string>
+ <string id="580">Smer usporiadania</string>
+ <string id="581">Spôsob usporiadania</string>
+- <string id="582">Mód zobrazenia</string>
++ <string id="582">režim zobrazenia</string>
+ <string id="583">PamätaÅ¥ si zobrazenie pre každý prieÄinok</string>
+ <string id="584">Stúpajúci</string>
+ <string id="585">Klesajúci</string>
+-
+ <string id="586">Upraviť playlist</string>
+ <string id="587">Filter</string>
+- <string id="588">Zrušiť párty mód</string>
+- <string id="589">Párty mód</string>
+- <string id="590">Náhodne</string>
++ <string id="588">Zrušiť párty režim</string>
++ <string id="589">Párty režim</string>
++ <string id="590">Náhodné</string>
+ <string id="591">Vypnuté</string>
+-
+ <string id="592">Jeden</string>
+ <string id="593">VÅ¡etky</string>
+ <string id="594">Vypnuté</string>
+ <string id="595">Opakovať: Vypnuté</string>
+- <string id="596">Opakovať: Jedno</string>
++ <string id="596">Opakovať: 1</string>
+ <string id="597">Opakovať: Všetko</string>
+-
+- <string id="600">Stiahnuť audio CD</string>
++ <string id="600">Skopírovať CD</string>
+ <string id="601">Stredná</string>
+ <string id="602">Štandardná</string>
+ <string id="603">Extrémna</string>
+ <string id="604">Konštantný datový tok</string>
+ <string id="605">SÅ¥ahujem...</string>
+-
+ <string id="607">Do:</string>
+- <string id="608">Nie je možné stiahnuť CD alebo stopu</string>
++ <string id="608">Nepodarilo sa skopírovať CD alebo stopu</string>
+ <string id="609">CDDA cesta nie je nastavená.</string>
+- <string id="610">Stiahnuť audio stopu</string>
++ <string id="610">Skopírovať audio stopu</string>
+ <string id="611">Zadaj Äíslo</string>
+ <string id="612">Bitov/Vzorka</string>
+-
+ <string id="613">Vzorkovacia frekvencia</string>
+- <string id="620">CD sťahovanie</string>
++ <string id="620">Audio CD</string>
+ <string id="621">Enkodér</string>
+ <string id="622">Kvalita</string>
+ <string id="623">Datový tok</string>
+ <string id="624">Zahrnúť Äíslo stopy</string>
+-
+ <string id="625">VÅ¡etky skladby od</string>
+- <string id="629">Režim obrazu</string>
++ <string id="629">Režim zobrazenia</string>
+ <string id="630">Normálne</string>
+ <string id="631">ZväÄÅ¡iÅ¥</string>
+ <string id="632">Natiahnuť 4:3</string>
+- <string id="633">Natiahnuť 14:9</string>
+-
++ <string id="633">Široké priblíženie</string>
+ <string id="634">Natiahnuť 16:9</string>
+- <string id="635">Pôvodná veľkosť</string>
+- <string id="636">Vlastný</string>
+- <string id="637">Úprava zosilnenia</string>
+- <string id="638">Spôsob úpravy zosilnenia</string>
+- <string id="639">Používať úroveň stopy</string>
+-
+- <string id="640">Používať úroveň albumu</string>
+- <string id="641">Úroveň predzosilenia - Súbory s úpravou zosilnenia</string>
+- <string id="642">Úroveň predzosilenia - Súbory bez úpravy zosilnenia</string>
+- <string id="643">Zakázať odrezanie pri súboroch s úpravou zosilnenia</string>
+- <string id="644">OrezaÅ¥ Äierne okraje</string>
++ <string id="635">Originálna veľkosť</string>
++ <string id="636">Vlastné</string>
++ <string id="637">ReplayGain / stará sa o minimálny rozdiel hlasitosti v rôznych skladbách</string>
++ <string id="638">ReplayGain úprava hlasitosti</string>
++ <string id="639">Použiť úroveň stopy</string>
++ <string id="640">Použiť úroveň albumu</string>
++ <string id="641">Úroveň - ReplayGain súbory</string>
++ <string id="642">Úroveň - Súbory bez ReplayGain</string>
++ <string id="643">Zabrániť predimenzovaniu pri súboroch s ReplayGain</string>
++ <string id="644">OrezávaÅ¥ Äierne okraje</string>
+ <string id="645">Treba rozbaliÅ¥ veľký súbor. PokraÄovaÅ¥?</string>
+-
+ <string id="646">Odstrániť z knižnice</string>
+- <string id="647">Export video knižnice</string>
+- <string id="648">Import video knižnice</string>
++ <string id="647">Exportovať video knižnicu</string>
++ <string id="648">Importovať video knižnicu</string>
+ <string id="649">Prebieha import</string>
+ <string id="650">Prebieha export</string>
+- <string id="651">Vybrať knižnicu</string>
+-
+- <string id="652">Rok</string>
+- <string id="653">Aktualizácia knižnice</string>
+- <string id="654">Zobraziť ladiace informácie</string>
+- <string id="655">Vybrať spúšťací súbor</string>
+- <string id="656">Vybrať playlist</string>
+- <string id="657">VybraÅ¥ prieÄinok</string>
+-
++ <string id="651">Vyberte knižnicu</string>
++ <string id="652">Roky</string>
++ <string id="653">Aktualizovať knižnicu</string>
++ <string id="654">Zobrazovať ladiace informácie</string>
++ <string id="655">Vyberte spúšťací súbor</string>
++ <string id="656">Vyberte playlist</string>
++ <string id="657">Vyberte prieÄinok</string>
+ <string id="658">Informácie o skladbe</string>
+ <string id="659">Nelineárne roztiahnutie</string>
+ <string id="660">Zosilnenie hlasitosti</string>
+ <string id="661">Vyberte prieÄinok pre export</string>
+ <string id="662">Súbor už nieje dostupný.</string>
+- <string id="663">Želáte si ho odstrániť z knižnice?</string>
+-
++ <string id="663">Prajete si ho odstrániť z knižnice?</string>
+ <string id="664">Vyberte Skript</string>
+ <string id="665">Úroveň kompresie</string>
+ <string id="700">VyÄistiÅ¥ knižnicu</string>
+ <string id="701">Odstraňujem staré skladby z knižnice</string>
+- <string id="702">Táto cesta bola už skenovaná</string>
++ <string id="702">Táto cesta bola už prehľadaná</string>
+ <string id="705">Sieť</string>
+-
+ <string id="706">- Server</string>
+ <string id="708">Použiť HTTP proxy server pre prístup na internet</string>
+ <string id="711">Internet protokol (IP)</string>
+ <string id="712">Nesprávny port. Hodnota musí byť medzi 1 a 65535.</string>
+ <string id="713">HTTP Proxy</string>
+- <string id="715">Priradenie</string>
+-
++ <string id="715">- Priradenie</string>
+ <string id="716">Automatický (DHCP)</string>
+ <string id="717">Manuálne (Statický)</string>
+ <string id="719">- IP Adresa</string>
+ <string id="720">- Sieťová maska</string>
+ <string id="721">- Predvolená brána</string>
+ <string id="722">- DNS server</string>
+-
+ <string id="723">Uložiť a reštartovať</string>
+ <string id="724">Nesprávna adresa. Hodnota musí byť AAA.BBB.CCC.DDD</string>
+ <string id="725">s Äíslami v rozsahu 0 a 255.</string>
+ <string id="726">Zmeny neboli uložené. PokraÄovaÅ¥ bez uloženia?</string>
+ <string id="727">Web server</string>
+ <string id="728">FTP server</string>
+-
+ <string id="730">- Port</string>
+ <string id="732">Uložiť a použiť</string>
+ <string id="733">- Heslo</string>
+ <string id="734">Žiadne heslo</string>
+ <string id="735">- Písmo</string>
+ <string id="736">- Štýl</string>
+-
+ <string id="737">- Farba</string>
+ <string id="738">ObyÄajné</string>
+ <string id="739">TuÄné</string>
+ <string id="740">Kurzíva</string>
+ <string id="741">TuÄné kurzíva</string>
+ <string id="742">Biele</string>
+-
+ <string id="743">Žlté</string>
+ <string id="744">Súbory</string>
+- <string id="745">Žiadna zoskenovaná informácia pre toto zobrazenie</string>
+- <string id="746">Prepnúť do súborového zobrazenia</string>
++ <string id="745">Pre toto zobrazenie neodpovedajú žiadne položky</string>
++ <string id="746">Vypnite prosím režim knižnice</string>
+ <string id="747">Chyba pri naÄítaní obrázku</string>
+ <string id="748">Upraviť cestu</string>
+-
+ <string id="749">OtoÄiÅ¥</string>
+ <string id="750">Ste si istý?</string>
+- <string id="751">Odstraňovanie zdroju</string>
++ <string id="751">Odstránenie zdroju</string>
+ <string id="754">Pridať zástupcu programu</string>
+ <string id="755">Upraviť cestu programu</string>
+ <string id="756">Upraviť názov programu</string>
+-
+- <string id="757">Upraviť hĺbku pre cestu</string>
+- <string id="759">Zobrazenie: Veľké ikony</string>
++ <string id="757">Upraviť hĺbku cesty</string>
++ <string id="759">Zobrazenie: Veľký zoznam</string>
+ <string id="760">Žltá</string>
+ <string id="761">Biela</string>
+ <string id="762">Modrá</string>
+ <string id="763">Svetlozelená</string>
+-
+ <string id="764">Žltozelená</string>
+ <string id="765">Ružová</string>
+- <string id="766">Rezervované</string>
+- <string id="767">Rezervované</string>
++ <string id="766">Svetlošedá</string>
++ <string id="767">Šedá</string>
+ <string id="770">Chyba %i: zdieľanie nie je prístupné</string>
+- <string id="772">Audio zariadenie</string>
+-
+- <string id="773">Vyhľadávam</string>
+- <string id="774">PrieÄinok prezentácie</string>
++ <string id="772">Audio výstup</string>
++ <string id="773">Prebieha hľadanie</string>
++ <string id="774">PrieÄinok s prezentáciami</string>
+ <string id="775">Sieťové rozhranie</string>
+ <string id="776">- Názov bezdrôtovej siete (ESSID)</string>
+ <string id="777">- Heslo bezdrôtovej siete</string>
+ <string id="778">- BezpeÄnost bezdrôtovej siete</string>
+-
+ <string id="779">Uložiť a použiť nastavenie siete</string>
+ <string id="780">Bez Å¡ifrovania</string>
+ <string id="781">WEP</string>
+ <string id="782">WPA</string>
+ <string id="783">WPA2</string>
+ <string id="784">Prebieha použitie zmien nastavenia siete. PoÄkajte prosím.</string>
+-
+ <string id="785">Reštart sieťového rozhrania bol úspešný.</string>
+ <string id="786">Štart sieťového rozhrania bol neúspešný.</string>
+ <string id="787">Rozhranie zakázané</string>
+ <string id="788">Sieťové rozhranie bolo zakázané.</string>
+ <string id="789">Názov bezdrôtovej siete (ESSID)</string>
+ <string id="791">Povoliť ovládanie XBMC aplikáciami na tomto systéme</string>
+-
+ <string id="792">Port</string>
+ <string id="793">Rozsah portov</string>
+- <string id="794">Povoliť ovládanie XBMC aplikáciami z iného systému</string>
+- <string id="795">Oneskorenie odozvy pre prvý stisk (ms)</string>
+- <string id="796">Oneskorenie odozvy pre dlhý stisk (ms)</string>
++ <string id="794">Povoliť ovládanie XBMC aplikáciami z iného systému</string>
++ <string id="795">Úvodné oneskorenie opakovania (ms)</string>
++ <string id="796">Následujúce oneskorenie opakovania (ms)</string>
+ <string id="797">Maximálny poÄet klientov</string>
+-
+ <string id="798">Internetový prístup</string>
+ <string id="850">Bolo zadané nesprávne Äíslo portu</string>
+ <string id="851">Povolený rozsah portu je 1-65535</string>
+ <string id="852">Povolený rozsah portu je 1024-65535</string>
+- <string id="998">Pridať Hudbu...</string>
+- <string id="999">Pridať Videá...</string>
+-
+- <string id="1000">Ukážka Å¡etriÄa obrazovky</string>
++ <string id="998">Pridať hudbu...</string>
++ <string id="999">Pridať videá...</string>
++ <string id="1000">- Náhľad</string>
+ <string id="1001">Nedá sa pripojiť</string>
+- <string id="1002">XBMC sa nemohol pripojiť na sieťové miesto.</string>
++ <string id="1002">XBMC sa nemohol pripojiť na sieťové umiestnenie.</string>
+ <string id="1003">Toto môže byť kvôli nedostupnosti siete.</string>
+ <string id="1004">Radi by ste to aj tak pridali?</string>
+ <string id="1006">IP adresa</string>
+-
+- <string id="1007">Pridať sieťové miesto</string>
++ <string id="1007">Pridať sieťové umiestnenie</string>
+ <string id="1008">Protokol</string>
+ <string id="1009">Adresa servera</string>
+ <string id="1010">Názov servera</string>
+ <string id="1011">Vzdialená cesta</string>
+ <string id="1012">Zdieľaný prieÄinok</string>
+-
+ <string id="1013">Port</string>
+- <string id="1014">Meno</string>
+- <string id="1015">Vybrať sieťový server</string>
+- <string id="1016">Napíšte sieťovú adresu servera</string>
+- <string id="1017">Napíšte cestu na serveri</string>
+- <string id="1018">Napíšte Äíslo portu</string>
+-
+- <string id="1019">Napíšte meno</string>
+- <string id="1020">Pridať %s zdroj</string>
+- <string id="1021">Vložte cestu, alebo vyberte lokalitu média.</string>
+- <string id="1022">Napíšte názov pre tento média zdroj.</string>
+- <string id="1023">Vybrať nové zdieľanie</string>
+- <string id="1024">Vybrať</string>
+-
+- <string id="1025">Nie je možné naÄítaÅ¥ informácie prieÄinku.</string>
++ <string id="1014">Užívateľské meno</string>
++ <string id="1015">Vyberte sieťový server</string>
++ <string id="1016">Zadajte sieťovú adresu servera</string>
++ <string id="1017">Zadajte cestu na server</string>
++ <string id="1018">Zadajte Äíslo portu</string>
++ <string id="1019">Zadajte užívateľské meno</string>
++ <string id="1020">Pridajte cestu pre: %s</string>
++ <string id="1021">Zadajte cestu, alebo vyberte umiestnenie médií.</string>
++ <string id="1022">Zadajte názov pre tento zdroj médií.</string>
++ <string id="1023">Vyberte umiestnenie</string>
++ <string id="1024">Prechádzať</string>
++ <string id="1025">Nepodarilo sa naÄítaÅ¥ informácie prieÄinku.</string>
+ <string id="1026">Pridať zdroj</string>
+ <string id="1027">Upraviť zdroj</string>
+ <string id="1028">Upraviť %s zdroj</string>
+- <string id="1029">Napíšte nový názov</string>
+- <string id="1030">Vybrať obrázok</string>
+-
+- <string id="1031">VybraÅ¥ prieÄinok s obrázkami</string>
+- <string id="1032">Pridať sieťové miesto...</string>
+- <string id="1033">Vybrať súbor</string>
++ <string id="1029">Zadajte nový názov</string>
++ <string id="1030">Vyberte obrázok</string>
++ <string id="1031">Vyberte prieÄinok s obrázkami</string>
++ <string id="1032">Pridať sieťové umiestnenie...</string>
++ <string id="1033">Vyberte súbor</string>
+ <string id="1034">Podmenu</string>
+- <string id="1035">PovoliÅ¥ tlaÄítka podmenu</string>
++ <string id="1035">AktivovaÅ¥ tlaÄítka podmenu</string>
+ <string id="1036">Obľúbené</string>
+-
+- <string id="1037">Doplnky - Video</string>
+- <string id="1038">Doplnky - Hudba</string>
+- <string id="1039">Doplnky - Obrázky</string>
++ <string id="1037">-Video (doplnky)</string>
++ <string id="1038">-Hudba (doplnky)</string>
++ <string id="1039">-Obrázky (doplnky)</string>
+ <string id="1040">Prebieha naÄítanie prieÄinku</string>
+ <string id="1041">NaÄítaných %i položiek</string>
+ <string id="1042">NaÄítaných %i z %i položiek</string>
+-
+- <string id="1043">Doplnky - Programy</string>
++ <string id="1043">-Programy (doplnky)</string>
+ <string id="1044">Nastavenie náhľadu doplnku</string>
+ <string id="1045">Nastavenie doplnku</string>
+ <string id="1046">Prístupové body (AP)</string>
+ <string id="1047">Ostatné ...</string>
+ <string id="1048">- Užívateľské meno</string>
+-
+ <string id="1049">Nastavenia skriptu</string>
+ <string id="1050">Single</string>
+ <string id="1051">Zadajte webovú adresu</string>
+ <string id="1200">SMB klient</string>
+ <string id="1202">Pracovná skupina</string>
+ <string id="1203">Predvolené meno</string>
+-
+ <string id="1204">Predvolené heslo</string>
+ <string id="1207">WINS server</string>
+ <string id="1208">PripojiÅ¥ SMB zdieľané prieÄinky</string>
+ <string id="1210">Odstrániť</string>
+ <string id="1211">Hudba</string>
+ <string id="1212">Video</string>
+-
+ <string id="1213">Obrázky</string>
+ <string id="1214">Súbory</string>
+- <string id="1215">Hudba a Video </string>
+- <string id="1216">Hudba a Obrázky</string>
+- <string id="1217">Hudba a Súbory</string>
+- <string id="1218">Video a Obrázky</string>
+-
+- <string id="1219">Video a Súbory</string>
+- <string id="1220">Obrázky a Súbory</string>
+- <string id="1221">Hudba a Video a Obrázky</string>
+- <string id="1222">Hudba a Video a Obrázky a Súbory</string>
+- <string id="1223">Zakázané</string>
+- <string id="1226">Súbory a Hudba a Video</string>
+-
+- <string id="1227">Súbory a Obrázky a Hudba</string>
+- <string id="1228">Súbory a Obrázky a Video</string>
+- <string id="1229">Hudba a Programy</string>
+- <string id="1230">Video a Programy</string>
+- <string id="1231">Obrázky a Programy</string>
+- <string id="1232">Hudba a Video a Obrázky a Programy</string>
+-
+- <string id="1233">Programy a Video a Hudba</string>
+- <string id="1234">Programy a Obrázky a Hudba</string>
+- <string id="1235">Programy a Obrázky a Video</string>
++ <string id="1215">Hudba &amp; Video </string>
++ <string id="1216">Hudba &amp; Obrázky</string>
++ <string id="1217">Hudba &amp; Súbory</string>
++ <string id="1218">Video &amp; Obrázky</string>
++ <string id="1219">Video &amp; Súbory</string>
++ <string id="1220">Obrázky &amp; Súbory</string>
++ <string id="1221">Hudba &amp; Video &amp; Obrázky</string>
++ <string id="1222">Hudba &amp; Video &amp; Obrázky &amp; Súbory</string>
++ <string id="1223">Vypnuté</string>
++ <string id="1226">Súbory &amp; Hudba &amp; Video</string>
++ <string id="1227">Súbory &amp; Obrázky &amp; Hudba</string>
++ <string id="1228">Súbory &amp; Obrázky &amp; Video</string>
++ <string id="1229">Hudba &amp; Programy</string>
++ <string id="1230">Video &amp; Programy</string>
++ <string id="1231">Obrázky &amp; Programy</string>
++ <string id="1232">Hudba &amp; Video &amp; Obrázky &amp; Programy</string>
++ <string id="1233">Programy &amp; Video &amp; Hudba</string>
++ <string id="1234">Programy &amp; Obrázky &amp; Hudba</string>
++ <string id="1235">Programy &amp; Obrázky &amp; Video</string>
+ <string id="1250">Autodetekcia</string>
+ <string id="1251">Autodetekcia systému</string>
+ <string id="1252">Prezývka</string>
+-
+ <string id="1254">Opýtať sa na pripojenie</string>
+ <string id="1255">Poslať FTP meno a heslo</string>
+ <string id="1256">Ping interval</string>
+- <string id="1257">Chcete sa pripojiť na autodetekovaný system?</string>
++ <string id="1257">Chcete sa pripojiť na autodetekovaný systém?</string>
+ <string id="1260">OznamovaÅ¥ tieto služby Äalším poÄítaÄom cez Zeroconf</string>
+ <string id="1270">Povoliť XBMC príjem AirPlay obsahu</string>
+ <string id="1271">Názov zariadenia</string>
+ <string id="1272">- Použiť ochranu heslom</string>
+ <string id="1300">Vlastné zariadenie pre výstup audia</string>
+-
+ <string id="1301">Vlastné zariadenie pre passthrough zvuku</string>
+ <string id="1396">ustupujúci</string>
+ <string id="1397">a</string>
+ <string id="1398">mrznúci</string>
+ <string id="1399">neskôr</string>
+ <string id="1400">ojedinele</string>
+-
+ <string id="1401">prehánky</string>
+ <string id="1402">búrky</string>
+ <string id="1403">slneÄno</string>
+ <string id="1404">zatiahnuté</string>
+ <string id="1405">v</string>
+ <string id="1406">blízkom</string>
+-
+ <string id="1407">okolí</string>
+ <string id="1408">ľadové</string>
+ <string id="1409">krištáliky</string>
+ <string id="1410">bezvetrie</string>
+ <string id="1411">s</string>
+ <string id="1412">veterno</string>
+-
+ <string id="1413">mrholenie</string>
+ <string id="1414">silná búrka</string>
+ <string id="1415">mrholenie</string>
+ <string id="1416">zahmlené</string>
+- <string id="1417">Krupy</string>
+- <string id="1418">Búrky</string>
+-
+- <string id="1419">Búrky s prehánkami</string>
++ <string id="1417">krupy</string>
++ <string id="1418">búrky</string>
++ <string id="1419">búrky s prehánkami</string>
+ <string id="1420">Stredné</string>
+ <string id="1421">Velmi vysoké</string>
+ <string id="1422">Veterno</string>
+ <string id="1423">Hmla</string>
+- <string id="1450">AktivovaÅ¥ úsporný režim pri neÄinnosti</string>
+-
++ <string id="1450">AktivovaÅ¥ úsporný režim obrazovky pri neÄinnosti</string>
+ <string id="2050">Dĺžka</string>
+ <string id="2100">Chyba skriptu! : %s</string>
+ <string id="2101">Je vyžadovaná novšia verzia - Viac informácií v logu</string>
+- <string id="4501">Typ LCD</string>
++ <string id="4501">Aktivovať LCD/VFD</string>
+ <string id="10000">Domov</string>
+ <string id="10001">Programy</string>
+-
+ <string id="10002">Obrázky</string>
+ <string id="10003">Správca súborov</string>
+ <string id="10004">Nastavenie</string>
+ <string id="10005">Hudba</string>
+ <string id="10006">Video</string>
+ <string id="10007">Systémové informácie</string>
+-
+ <string id="10008">Nastavenia - Hlavné</string>
+ <string id="10009">Nastavenia - Obrazovky</string>
+ <string id="10010">Nastavenia - Zobrazenie - Kalibrácia GUI</string>
+ <string id="10011">Nastavenia - Video - Kalibrácia obrazovky</string>
+ <string id="10012">Nastavenia - Obrázky</string>
+ <string id="10013">Nastavenia - Programy</string>
+-
+ <string id="10014">Nastavenia - PoÄasie</string>
+ <string id="10015">Nastavenia - Hudba</string>
+ <string id="10016">Nastavenia - Systém</string>
+ <string id="10017">Nastavenia - Videá</string>
+ <string id="10018">Nastavenia - Sieť</string>
+ <string id="10019">Nastavenia - Zobrazenie</string>
+-
+ <string id="10020">Skripty</string>
+ <string id="10021">Webový prehliadaÄ</string>
++ <string id="10025">Videá</string>
+ <string id="10028">Videá/Playlist</string>
++ <string id="10029">Úvodná obrazovka</string>
+ <string id="10034">Nastavenia - Profily</string>
++ <string id="10040">Doplnok-prehliadaÄ</string>
+ <string id="10100">Ãno/Nie dialog</string>
+ <string id="10101">Dialóg spracovania</string>
+-
+ <string id="10210">Prebieha vyhľadávanie titulkov...</string>
+ <string id="10211">Prebieha naÄítavanie titulkov...</string>
+ <string id="10212">prerušenie</string>
+ <string id="10213">naÄítanie do medzipamäte</string>
+ <string id="10214">Otváranie streamu</string>
+ <string id="10500">Hudba/Playlist</string>
+-
+ <string id="10501">Hudba/Súbory</string>
+ <string id="10502">Hudba/Knižnica</string>
+ <string id="10503">Playlist editor</string>
+ <string id="10504">Naj 100 skladieb</string>
+- <string id="10505">Naj 100 Albumov</string>
++ <string id="10505">Naj 100 albumov</string>
+ <string id="10506">Programy</string>
+-
+ <string id="10507">Konfigurácia</string>
+ <string id="10508">PredpoveÄ poÄasia</string>
+ <string id="10509">Hranie po sieti</string>
+ <string id="10510">Rozšírenia</string>
+ <string id="10511">Systémové informácie</string>
+ <string id="10516">Hudba - Knižnica</string>
+-
+- <string id="10517">Teraz hrá - Hudba</string>
+- <string id="10522">Teraz hrá - Video</string>
++ <string id="10517">Práve hrá - Hudba</string>
++ <string id="10522">Práve hrá - Video</string>
+ <string id="10523">Informácie o albume</string>
+ <string id="10524">Informácie o filme</string>
+ <string id="12000">Výberový dialóg</string>
+ <string id="12001">Hudba/Info</string>
+-
+ <string id="12002">Dialóg OK</string>
+ <string id="12003">Video/Info</string>
+ <string id="12004">Skripty/Info</string>
+ <string id="12005">Video na celú obrazovku</string>
+- <string id="12006">Audio vizualizácie</string>
+- <string id="12008">Dialóg pre súbory dokopy</string>
+-
++ <string id="12006">Vizualizácie hudby</string>
++ <string id="12008">Dialóg pri spojovaní súborov</string>
+ <string id="12009">Prerábam index...</string>
+- <string id="12010">Vrátiť sa do hudby</string>
+- <string id="12011">Vrátiť sa do videa</string>
+- <string id="12021">Å tart od zaÄiatku</string>
+- <string id="12022">PokraÄovaÅ¥ od poslednej pozície</string>
++ <string id="12010">Vrátiť sa do sekcie 'Hudba'</string>
++ <string id="12011">Vrátiť sa do sekcie 'Videá'</string>
++ <string id="12021">ZaÄaÅ¥ od zaÄiatku</string>
++ <string id="12022">PokraÄovaÅ¥ od %s</string>
+ <string id="12310">0</string>
+-
+ <string id="12311">1</string>
+ <string id="12312">2</string>
+ <string id="12313">3</string>
+ <string id="12314">4</string>
+ <string id="12315">5</string>
+ <string id="12316">6</string>
+-
+ <string id="12317">7</string>
+ <string id="12318">8</string>
+ <string id="12319">9</string>
+ <string id="12320">c</string>
+- <string id="12321">ok</string>
++ <string id="12321">Ok</string>
+ <string id="12322">*</string>
+-
+- <string id="12325">Zamknuté! Vložte kód...</string>
++ <string id="12325">Zamknuté! Vložte heslo...</string>
+ <string id="12326">Zadajte heslo</string>
+- <string id="12327">Zadajte hlavný kód</string>
+- <string id="12328">Zadajte odomykací kód</string>
++ <string id="12327">Zadajte hlavné heslo</string>
++ <string id="12328">Zadajte heslo pre odomknutie</string>
+ <string id="12329">alebo stlaÄte C pre zruÅ¡enie</string>
+ <string id="12330">Vložte kombináciu tlaÄidiel a</string>
+-
+- <string id="12331">stlaÄte Start alebo Back pre zruÅ¡enie</string>
++ <string id="12331">stlaÄte OK, alebo Späť pre zruÅ¡enie</string>
+ <string id="12332">Nastaviť zámok</string>
+ <string id="12333">Odomknúť</string>
+ <string id="12334">Resetnúť zámok</string>
+ <string id="12335">Odstrániť zámok</string>
+ <string id="12337">Číselné Heslo</string>
+-
+- <string id="12338">Kombinácie tlaÄidiel na Gamepad-e</string>
++ <string id="12338">Kombinácia tlaÄidiel na Gamepad-e</string>
+ <string id="12339">Textové Heslo</string>
+- <string id="12340">Napíšte nové heslo</string>
+- <string id="12341">Znovu napíšte nové heslo</string>
++ <string id="12340">Zadajte nové heslo</string>
++ <string id="12341">Znovu zadajte nové heslo</string>
+ <string id="12342">Nesprávne heslo,</string>
+- <string id="12343">možností ostáva </string>
+-
+- <string id="12344">Napísané heslá nie sú totožné.</string>
++ <string id="12343">možnosí ostáva </string>
++ <string id="12344">Zadané heslá nie sú totožné.</string>
+ <string id="12345">Vstup Nepovolený</string>
+ <string id="12346">Limit pre opakovanie bol prekroÄený.</string>
+ <string id="12347">Systém sa teraz vypne.</string>
+ <string id="12348">Položka zamknutá</string>
+ <string id="12353">Reaktivovať zámok</string>
+-
+ <string id="12356">Zmeniť zámok</string>
+- <string id="12357">Zdieľať zámok</string>
+- <string id="12358">Heslo bolo prázdne. Skúste znova.</string>
++ <string id="12357">Uzamknúť zdroj</string>
++ <string id="12358">Žiadne heslo, skúste znova.</string>
+ <string id="12360">Hlavný zámok</string>
+ <string id="12362">Vypnúť systém ak boli prekroÄené pokusy na hlavný zámok</string>
+ <string id="12367">Hlavný kÄ¾ÃºÄ nie je správny!</string>
+-
+ <string id="12368">Prosím vložte správny hlavný kľúÄ!</string>
+- <string id="12373">Nastavenia a Správca súborov</string>
++ <string id="12373">Nastavenia &amp; Správca súborov</string>
+ <string id="12376">Nastaviť ako predvolené pre všetky filmy</string>
+ <string id="12377">Toto vymaže všetky uložené hodnoty</string>
+- <string id="12378">Čas zobrazenia obrázku</string>
+- <string id="12379">Použiť efekty priblíženia a posunutia obrázku</string>
+-
++ <string id="12378">Doba zobrazenia každého obrázku</string>
++ <string id="12379">Používať efekty priblíženia a posunutia obrázku</string>
+ <string id="12383">12 hodinový formát</string>
+ <string id="12384">24 hodinový formát</string>
+ <string id="12385">Deň/Mesiac</string>
+ <string id="12386">Mesiac/Deň</string>
+- <string id="12390">Systém žije</string>
+- <string id="12391">Minúty</string>
+-
++ <string id="12390">Doba používania</string>
++ <string id="12391">Minút</string>
+ <string id="12392">Hodiny</string>
+ <string id="12393">Dni</string>
+- <string id="12394">Celkovo žije</string>
++ <string id="12394">Celková doba používania</string>
+ <string id="12395">Stav batérie</string>
+ <string id="12600">PoÄasie</string>
+ <string id="12900">Å etriÄ obrazovky</string>
+-
+ <string id="12901">OSD na celú obrazovku</string>
+ <string id="13000">Systém</string>
+ <string id="13001">Okamžitý HD Spindown</string>
+- <string id="13002">Len Video</string>
++ <string id="13002">Iba Video</string>
+ <string id="13003">- Oneskorenie</string>
+ <string id="13004">- Minimálna dĺžka súboru</string>
+-
+ <string id="13005">Vypnúť</string>
+ <string id="13008">Predvolený spôsob vypnutia</string>
+ <string id="13009">UkonÄiÅ¥</string>
+ <string id="13010">Dlhodobý spánok</string>
+ <string id="13011">Úsporný režim</string>
+ <string id="13012">UkonÄiÅ¥</string>
+-
+ <string id="13013">Reštartovať</string>
+ <string id="13014">Minimalizovať</string>
+ <string id="13015">Funkcia tlaÄítka pre vypnutie</string>
+ <string id="13016">Vypnúť systém</string>
+ <string id="13020">Existuje nejaké aktívne pripojenie, (napr. cez SSH) ?</string>
+- <string id="13021">Pripojený vymeniteľný pevný disk</string>
+-
+- <string id="13022">NebezpeÄne odobraÅ¥ zariadenie</string>
++ <string id="13021">Vymeniteľný pevný disk pripojený</string>
++ <string id="13022">NebezpeÄné vybratie zariadenia</string>
+ <string id="13023">Zariadenie bolo úspšne odobrané</string>
+ <string id="13024">Joystick bol pripojený</string>
+ <string id="13025">Joystick bol odpojený</string>
+ <string id="13050">Nízka úroveň batérie</string>
+ <string id="13100">Blikací filter</string>
+-
+- <string id="13101">Nastavené ovladaÄom (vyžaduje reÅ¡tart)</string>
++ <string id="13101">Nastavené ovládaÄom (vyžaduje reÅ¡tart)</string>
+ <string id="13105">Vertikálna synchronizácia obrazovky (V-Sync)</string>
+ <string id="13106">Zakázané</string>
+ <string id="13107">Povolené poÄas prehrávania videa</string>
+ <string id="13108">Vždy povolené</string>
+ <string id="13109">Test rozlíšenia</string>
+-
+ <string id="13110">Uložiť rozlíšenie?</string>
+ <string id="13111">Chcete ponechať toto rozlíšenie?</string>
+- <string id="13112">Vysoko kvalitný softwarový prepoÄet</string>
++ <string id="13112">Vysoko kvalitný prepoÄet (upscaling)</string>
+ <string id="13113">Zakázané</string>
+- <string id="13114">Povolené pre SD videa</string>
++ <string id="13114">Povolené pre SD obsah</string>
+ <string id="13115">Vždy povolené</string>
+-
+- <string id="13116">Metóda prepoÄtu</string>
++ <string id="13116">Spôsob prepoÄtu</string>
+ <string id="13117">Bicubic</string>
+ <string id="13118">Lanczos</string>
+ <string id="13119">Sinc</string>
+ <string id="13120">VDPAU</string>
+ <string id="13121">VDPAU HQ prepoÄet (upscaling)</string>
+-
+ <string id="13122">VDPAU farebná konverzia - Studio level</string>
+ <string id="13130">Stmavovať ostatné obrazovky</string>
+ <string id="13131">Zakázané</string>
+- <string id="13132">Vypni obrazovku</string>
++ <string id="13132">Povolené</string>
+ <string id="13140">Zistené aktívne spojenie!</string>
+ <string id="13141">Ak budete pokraÄovaÅ¥, môžeÅ¥e stratiÅ¥ ovládanie XBMC</string>
+-
+ <string id="13142">natrvalo. UrÄiÅ¥e chcete zastaviÅ¥ event server?</string>
+- <string id="13144">Zmeniť mód diaľkového ovládania Apple?</string>
++ <string id="13144">Zmeniť režim diaľkového ovládania Apple?</string>
+ <string id="13145">Ak práve používate diaľkové ovládanie Apple na ovládanie</string>
+- <string id="13146">XBMC, zmenou nastavenia možete ovplyvniť vašu</string>
++ <string id="13146">XBMC, zmenou nastavenia možete ovplyvniť Vašu</string>
+ <string id="13147">schopnosÅ¥ ovládaÅ¥ ho. Chcete pokraÄovaÅ¥?</string>
+- <string id="13159">Maska siete:</string>
+-
++ <string id="13159">Maska podsiete</string>
+ <string id="13160">Brána</string>
+ <string id="13161">Primárny DNS</string>
+ <string id="13162">Inicializácia zlyhala</string>
+ <string id="13170">Nikdy</string>
+ <string id="13171">IhneÄ</string>
+ <string id="13172">Po %i sekundách</string>
+-
+ <string id="13173">Dátum inštalácie disku:</string>
+- <string id="13174">PoÄet zapnutí disku</string>
++ <string id="13174">PoÄet zapnutí disku:</string>
+ <string id="13200">Profily</string>
+ <string id="13201">Vymazať profil '%s'?</string>
+ <string id="13204">Posledný otvorený profil:</string>
+ <string id="13205">Neznámy</string>
+-
+ <string id="13206">Prepísať</string>
+ <string id="13208">Budík</string>
+- <string id="13209">Budíkový interval (v minútach)</string>
+- <string id="13210">NaÄasovaný, budík o %im</string>
++ <string id="13209">Interval budíku (v minútach)</string>
++ <string id="13210">Budík nastavený, budenie o %im</string>
+ <string id="13211">Budík!</string>
+ <string id="13212">Zrušené (zostávalo %im%is)</string>
+-
+ <string id="13213">%2.0fm</string>
+ <string id="13214">%2.0fs</string>
+ <string id="13249">Hľadať titulky v RARoch</string>
+ <string id="13250">Hľadať titulky...</string>
+ <string id="13251">Presunúť položku</string>
+ <string id="13252">Presunúť položku sem</string>
+-
+ <string id="13253">Zrušiť presunutie</string>
+ <string id="13270">Hardware:</string>
+- <string id="13271">Záťaž CPU:</string>
++ <string id="13271">Zaťaženie CPU:</string>
+ <string id="13274">Pripojený, ale DNS je nedostupný</string>
+ <string id="13275">Hard Disk</string>
+ <string id="13276">DVD-ROM</string>
+-
+- <string id="13277">Úložisko</string>
++ <string id="13277">Voľné miesto</string>
+ <string id="13278">Predvolené</string>
+ <string id="13279">Sieť</string>
+ <string id="13280">Video</string>
+- <string id="13281">Hardware</string>
+- <string id="13283">OperaÄný systém</string>
+-
++ <string id="13281">CPU a RAM</string>
++ <string id="13283">OperaÄný systém:</string>
+ <string id="13284">Rýchlosť CPU:</string>
+ <string id="13286">Video enkodér:</string>
+ <string id="13287">Rozlíšenie obrazovky:</string>
+ <string id="13292">A/V Kábel:</string>
+ <string id="13294">DVD Región:</string>
+ <string id="13295">Internet:</string>
+-
+ <string id="13296">Pripojený</string>
+ <string id="13297">Nie je Pripojený. Skontrolujte sieťové nastavenia.</string>
+ <string id="13299">Cieľová teplota</string>
+ <string id="13300">OtáÄky ventilátora</string>
+ <string id="13301">Automatické regulovanie teploty</string>
+ <string id="13302">Nastavenie otáÄok ventilátora</string>
+-
+- <string id="13303">Písmo vzhľadu</string>
+- <string id="13304">Povoliť preklápanie obojsmerných reťazcov</string>
+- <string id="13305">Povoliť RSS spravodajstvo</string>
+- <string id="13306">SchovaÅ¥ rodiÄovský prieÄinok</string>
+- <string id="13307">Šablóna názvu stopy</string>
++ <string id="13303">- Písmo</string>
++ <string id="13304">AktivovaÅ¥ opaÄné titulky (arabÄina, hebrejÄina, ...)</string>
++ <string id="13305">Aktivovať RSS spravodajstvo</string>
++ <string id="13306">ZobrazovaÅ¥ položku pre návrat do nadradeného prieÄinku</string>
++ <string id="13307">Šablona názvu stopy</string>
+ <string id="13308">Naozaj chcete reštartovať systém</string>
+-
+- <string id="13309">alebo len XBMC?</string>
++ <string id="13309">alebo iba XBMC?</string>
+ <string id="13310">Efekt zväÄÅ¡enia</string>
+ <string id="13311">Efekt splynutia</string>
+ <string id="13312">Redukcia Äiernych okrajov</string>
+ <string id="13313">Reštart</string>
+- <string id="13314">Prelínanie</string>
+-
++ <string id="13314">Prelínanie zvuku medzi skladbami</string>
+ <string id="13315">Aktualizovať náhľady</string>
+- <string id="13316">Náhľady rekurzívne</string>
++ <string id="13316">Rekurzívne náhľady</string>
+ <string id="13317">Prezentácia</string>
+- <string id="13318">Prezentácia rekurzívna</string>
+- <string id="13319">Náhodne</string>
++ <string id="13318">Rekurzívna prezentácia</string>
++ <string id="13319">Náhodné poradie</string>
+ <string id="13320">Stereo</string>
+-
+- <string id="13321">Len ľavý</string>
+- <string id="13322">Len pravý</string>
+- <string id="13323">Povoliť podporu karaoke</string>
++ <string id="13321">Iba ľavý</string>
++ <string id="13322">Iba pravý</string>
++ <string id="13323">Aktivovať podporu karaoke</string>
+ <string id="13324">Priehľadnosť pozadia</string>
+ <string id="13325">Priehľadnosť popredia</string>
+ <string id="13326">A/V oneskorenie</string>
+-
+ <string id="13327">Karaoke</string>
+ <string id="13328">%s nebolo nájdené</string>
+ <string id="13329">Chyba pri otváraní %s</string>
+- <string id="13330">Nie je možné otvoriť %s</string>
++ <string id="13330">Nepodarilo sa otvoriť %s</string>
+ <string id="13331">Chyba: nedostatok pamäte</string>
+ <string id="13332">Premiestniť hore</string>
+-
+ <string id="13333">Premiestniť dole</string>
+ <string id="13334">Upraviť názov</string>
+- <string id="13335">Spraviť predvolené</string>
++ <string id="13335">Nastaviť ako predvolené</string>
+ <string id="13336">OdstrániÅ¥ tlaÄidlo</string>
+ <string id="13340">Nechať tak ako je</string>
+ <string id="13341">Zelená</string>
+-
+ <string id="13342">Oranžová</string>
+ <string id="13343">Červená</string>
+ <string id="13344">Cyklovať</string>
+ <string id="13345">Vypnúť LED pri prehrávaní</string>
+- <string id="13346">Informácie o filme</string>
+- <string id="13347">Položka do zásoby</string>
+-
++ <string id="13346">Detailné informácie</string>
++ <string id="13347">Pridať do fronty</string>
+ <string id="13348">Hľadať v IMDb...</string>
+ <string id="13349">Hľadať nový obsah</string>
+- <string id="13350">Teraz hrá...</string>
++ <string id="13350">Práve hrá...</string>
+ <string id="13351">Informácie o albume</string>
+- <string id="13352">Skenovať položku do knižnice</string>
+- <string id="13353">Zastaviť skenovanie</string>
+-
++ <string id="13352">Pridať do knižnice</string>
++ <string id="13353">Zastaviť vyhľadávanie</string>
+ <string id="13354">Vykresľovacia metóda</string>
+ <string id="13355">Nízkokvalitný Pixel Shader</string>
+ <string id="13356">Hardvérové prekrývanie</string>
+ <string id="13357">Vysokokvalitný Pixel Shader</string>
+ <string id="13358">Prehrať položku</string>
+ <string id="13359">Nastaviť náhľad pre interpréta</string>
+-
+- <string id="13360">Vytvoriť náhľady</string>
++ <string id="13360">Automaticky vytvárať náhľady</string>
+ <string id="13361">Povoliť hlas</string>
+ <string id="13375">Povoliť zariadenie</string>
+ <string id="13376">Hlasitosť</string>
+- <string id="13377">Predvolený zobrazovací mód</string>
++ <string id="13377">Predvolený zobrazovací režim</string>
+ <string id="13378">Predvolený jas</string>
+-
+ <string id="13379">Predvolený kontrast</string>
+ <string id="13380">Predvolená gamma</string>
+- <string id="13381">PokraÄovaÅ¥</string>
++ <string id="13381">PokraÄovaÅ¥ v prehrávaní videa</string>
+ <string id="13382">Maska hlasu - port 1</string>
+ <string id="13383">Maska hlasu - port 2</string>
+ <string id="13384">Maska hlasu - port 3</string>
+-
+ <string id="13385">Maska hlasu - port 4</string>
+- <string id="13386">PoužiÅ¥ vyhľadávanie na základe Äasu</string>
+- <string id="13387">Šablóna názvu stopy - vpravo</string>
++ <string id="13386">PretáÄaÅ¥ video na základe Äasového intervalu</string>
++ <string id="13387">Šablona názvu stopy - vpravo</string>
+ <string id="13388">Predvoľby</string>
+ <string id="13389">Pre túto vizualizáciu&#10;nie sú k dispozícii žiadne predvoľby</string>
+ <string id="13390">Pre túto vizualizáciu&#10;nie sú k dispozícii žiadne nastavenia</string>
+-
+ <string id="13391">Vysunúť/Zasunúť</string>
+- <string id="13392">Použiť vizualizácie pri prehrávaní hudby</string>
++ <string id="13392">ZobrazovaÅ¥ vizualizáciu poÄas prehrávania hudby</string>
+ <string id="13393">VypoÄítaÅ¥ veľkosÅ¥</string>
+ <string id="13394">PoÄítanie veľkosti prieÄinka</string>
+ <string id="13395">Nastavenia videa</string>
+- <string id="13396">Nastavenie hudby a titulkov</string>
+-
+- <string id="13397">Povoliť titulky</string>
++ <string id="13396">Nastavenie zvuku a titulkov</string>
++ <string id="13397">Zobrazovať titulky</string>
+ <string id="13398">Záložky</string>
+- <string id="13399">Ignorovať "The" pri usporiadaní</string>
+- <string id="13400">Stišovať stopy</string>
+- <string id="13401">Vybrať pre %s</string>
+- <string id="13402">Zobraziť pozíciu stopy</string>
+-
+- <string id="13403">Vymazať predvolené</string>
++ <string id="13399">Ignorovať 'The' pri usporiadaní</string>
++ <string id="13400">Plynulé prelínanie skladieb na albume</string>
++ <string id="13401">Hľadať %s</string>
++ <string id="13402">Zobrazovať pozíciu stopy</string>
++ <string id="13403">Zrušiť ako predvolené</string>
+ <string id="13404">PokraÄovaÅ¥</string>
+ <string id="13405">Náhľady</string>
+ <string id="13406">Informácie o obrázku</string>
+ <string id="13407">%s predvolieb</string>
+ <string id="13408">(Hodnotenie užívateľov IMDb)</string>
+-
+ <string id="13409">Naj 250</string>
+ <string id="13410">Naladiť na Last.FM</string>
+ <string id="13411">Minimálna rýchlosť ventilátora</string>
+- <string id="13412">Pustiť odtiaľto</string>
++ <string id="13412">Prehrať odtiaľto</string>
+ <string id="13413">SÅ¥ahujem</string>
+- <string id="13414">Schovať interprétov bez kompilácii</string>
+-
++ <string id="13414">Zobrazovať aj tých interprétov, ktorí sú iba na kompiláciach</string>
+ <string id="13415">Vykresľovacia metóda</string>
+ <string id="13416">Zistiť automaticky</string>
+ <string id="13417">Jednoduchý shader (ARB)</string>
+ <string id="13418">PokroÄilý shader (GLSL)</string>
+ <string id="13419">Softvér</string>
+ <string id="13420">BezpeÄne odobraÅ¥</string>
+-
+ <string id="13421">VDPAU</string>
+- <string id="13422">ZaÄaÅ¥ slideshow tu</string>
++ <string id="13422">ZaÄaÅ¥ prezentáciu tu</string>
+ <string id="13423">Zapamätať pre túto cestu</string>
+ <string id="13424">Použiť objekty vyrovnávacej pamäte pixelu</string>
+ <string id="13425">Aktivovať hardwarovú akceleráciu (VAAPI)</string>
+- <string id="13426">Povoliť hardwarovú akceleráciu (VAAPI) </string>
+-
+- <string id="13427">Povoliť hardwarovú akceleráciu (DXVA2)</string>
+- <string id="13428">Povoliť hardwarovú akceleráciu (CrystalHD)</string>
+- <string id="13429">Povoliť hardwarovú akceleráciu (VDADecoder)</string>
+- <string id="13430">Povoliť hardwarovú akceleráciu (OpenMax)</string>
++ <string id="13426">Aktivovať hardwarovú akceleráciu (VAAPI) </string>
++ <string id="13427">Aktivovať hardwarovú akceleráciu (DXVA2)</string>
++ <string id="13428">Aktivovať hardwarovú akceleráciu (CrystalHD)</string>
++ <string id="13429">Aktivovať hardwarovú akceleráciu (VDADecoder)</string>
++ <string id="13430">Aktivovať hardwarovú akceleráciu (OpenMax)</string>
+ <string id="13431">Pixel Shadery</string>
+- <string id="13432">Povoliť hardwarovú akceleráciu (Video Toolbox)</string>
+-
++ <string id="13432">Aktivovať hardwarovú akceleráciu (Video Toolbox)</string>
+ <string id="13500">Spôsob synchronizácie A/V</string>
+- <string id="13501">podľa audia</string>
+- <string id="13502">podľa videa (vynechať/duplikovať audio)</string>
+- <string id="13503">podľa videa (prispôsobiť rýchlosť audia)</string>
++ <string id="13501">Podľa audia</string>
++ <string id="13502">Podľa videa (vynechať/duplikovať audio)</string>
++ <string id="13503">Podľa videa (prispôsobiť rýchlosť audia)</string>
+ <string id="13504">Maximálna zmena rýchlosti (%)</string>
+ <string id="13505">Kvalita zmeny rýchlosti</string>
+-
+ <string id="13506">Nízka (rýchla)</string>
+ <string id="13507">Stredná</string>
+ <string id="13508">Vysoká</string>
+ <string id="13509">Veľmi vysoká (pomalé!)</string>
+ <string id="13510">Synchronizovať prehrávanie videa k obrazovke</string>
+ <string id="13550">Pozastaviť prehrávanie pri zmene obnovovacej frekvencie</string>
+-
+ <string id="13551">Vypnuté</string>
+ <string id="13552">%.1f Sekunda</string>
+ <string id="13553">%.1f Sekúnd</string>
+ <string id="13600">Diaľkové ovládanie Apple</string>
+- <string id="13602">Vždy spustené</string>
++ <string id="13602">Povoliť spustenie XBMC diaľkovým ovládaním</string>
+ <string id="13603">ÄŒas oneskorenia sekvencie</string>
+-
+ <string id="13610">Zakázaný</string>
+ <string id="13611">Štandardný</string>
+ <string id="13612">Univerzálne diaľkové ovládanie</string>
+ <string id="13613">MultifunkÄné diaľkové ovládanie (Harmony)</string>
+- <string id="13620">OvládaÄ pre ovládanie Apple je nainÅ¡talovaný.</string>
+- <string id="13621">Podporu pre ovládanie Apple nie je možné povoliť.</string>
+-
+- <string id="14000">Spolu</string>
+- <string id="14001">Samostatne</string>
++ <string id="13620">Diaľkové ovládanie Apple Chyba</string>
++ <string id="13621">Podpora diaľkového ovládania Apple by mohla byť zapnutá.</string>
++ <string id="14000">ZluÄovaÅ¥</string>
++ <string id="14001">NezluÄovaÅ¥</string>
+ <string id="14003">Sťahujem playlist súbor...</string>
+ <string id="14004">SÅ¥ahujem zoznam streamov...</string>
+ <string id="14005">Parsujem zoznam streamov...</string>
+ <string id="14006">SÅ¥ahovanie zoznamu streamov zlyhalo</string>
+-
+ <string id="14007">Sťahovanie súboru playlist-u zlyhalo</string>
+ <string id="14009">PrieÄinok s hrami</string>
+- <string id="14010">Autoprepínanie pre náhľady založené na</string>
+- <string id="14011">Povoliť autoprepínanie na náhľady</string>
+- <string id="14012">- Používať veľké ikony</string>
+- <string id="14013">- Prepínanie založené na</string>
+-
+- <string id="14014">- Percentá</string>
++ <string id="14010">Prepínať na režim náhľadov</string>
++ <string id="14011">Automaticky prepínať náhľady</string>
++ <string id="14012">- ak sú veľké ikony</string>
++ <string id="14013">- podľa pravidla</string>
++ <string id="14014">- podľa percent</string>
+ <string id="14015">Žiadne súbory a najmenej jeden náhľad</string>
+ <string id="14016">Aspoň jeden súbor a jeden náhľad</string>
+ <string id="14017">Podiel náhľadov</string>
+ <string id="14018">Zobrazenie</string>
+ <string id="14019">Zmeniť kód oblasti 1</string>
+-
+ <string id="14020">Zmeniť kód oblasti 2</string>
+ <string id="14021">Zmeniť kód oblasti 3</string>
+ <string id="14022">Knižnica</string>
+ <string id="14023">Žiadna TV</string>
+- <string id="14024">Napíšte najbližšie veľké mesto</string>
++ <string id="14024">Zadajte najbližšie veľké mesto</string>
+ <string id="14025">Medzipamäť - Video/Audio/DVD - HDD</string>
+-
+ <string id="14026">Medzipamäť - Video - DVDRom</string>
+ <string id="14027">- Lokálna sieť</string>
+ <string id="14028">- Internet</string>
+ <string id="14030">Medzipamäť - Audio - DVDRom</string>
+ <string id="14031">- Lokálna sieť</string>
+ <string id="14032">- Internet</string>
+-
+ <string id="14034">Medzipamäť - DVD - DVDRom</string>
+ <string id="14035">- Lokálna sieť</string>
+ <string id="14036">Servery</string>
+ <string id="14038">Sieťové nastavenia boli zmenené</string>
+ <string id="14039">XBMC vyžaduje reštart pre zmenu</string>
+ <string id="14040">sieťových nastavení. Reštartovať?</string>
+-
+ <string id="14041">Limitovať rýchlosť internetového pripojenia</string>
+- <string id="14043">- Vypínanie poÄas prehrávania</string>
++ <string id="14043">- Vypnúť pri hraní</string>
+ <string id="14044">%i min</string>
+ <string id="14045">%i sek</string>
+ <string id="14046">%i ms</string>
+ <string id="14047">%i %%</string>
+-
+ <string id="14048">%i kbps</string>
+ <string id="14049">%i kb</string>
+ <string id="14050">%i.0 dB</string>
+ <string id="14051">Formát Času</string>
+ <string id="14052">Formát Dátumu</string>
+ <string id="14053">GUI Filtre</string>
+-
+- <string id="14055">Používať skenovanie na pozadí</string>
+- <string id="14056">Zastaviť skenovanie</string>
+- <string id="14057">Nie je možne zoskenovať informácie pre médiá</string>
++ <string id="14055">Použiť vyhľadávanie na pozadí</string>
++ <string id="14056">Zastaviť vyhľadávanie</string>
++ <string id="14057">Nie je možné vykonaÅ¥, poÄas hľadania informácií o médiu</string>
+ <string id="14058">Krupicový efekt na film</string>
+- <string id="14059">Hľadať náhľady na vzdialených zložkách</string>
++ <string id="14059">Hľadať náhľady na sieťových jednotkách</string>
+ <string id="14060">Medzipamäť - Ostatné - Internet</string>
+-
+ <string id="14061">Automatický</string>
+- <string id="14062">Napíšte meno pre</string>
++ <string id="14062">Zadajte užívateľské meno pre</string>
+ <string id="14063">Dátum a Äas</string>
+ <string id="14064">Nastaviť Dátum</string>
+ <string id="14065">Nastaviť Čas</string>
+- <string id="14066">Napíšte Äas v 24 hodinovom formáte HH:MM</string>
+-
+- <string id="14067">Napíšte dátum v DD/MM/YYYY formáte</string>
+- <string id="14068">Napíšte IP adresu</string>
++ <string id="14066">Zadajte Äas v 24 hodinovom formáte HH:MM</string>
++ <string id="14067">Zadajte dátum v DD/MM/YYYY formáte</string>
++ <string id="14068">Zadajte IP adresu</string>
+ <string id="14069">Použiť tieto nastavenia teraz?</string>
+- <string id="14070">Teraz použiť zmeny</string>
++ <string id="14070">Použiť zmeny teraz</string>
+ <string id="14071">Povoliť premenovanie a mazanie súborov</string>
+ <string id="14074">NastaviÅ¥ Äasové pásmo</string>
+-
+ <string id="14075">Automaticky upraviÅ¥ hodiny na letný Äas</string>
+ <string id="14076">Pridať medzi obľúbené</string>
+ <string id="14077">Odstrániť z obľúbených</string>
+- <string id="14078">Farby vzhľadu</string>
+- <string id="14079">Časové pásmo - krajiny</string>
++ <string id="14078">- Farby</string>
++ <string id="14079">Časové pásmo krajiny</string>
+ <string id="14080">Časové pásmo</string>
+-
+- <string id="14081">Zoznam súborov</string>
+- <string id="14082">Zobraziť EXIF informácie o obrázku</string>
++ <string id="14081">Možnosti súborov</string>
++ <string id="14082">Zobrazovať informácie o obrázku (EXIF)</string>
+ <string id="14083">UprednostniÅ¥ režim okna celej obrazovky pred skutoÄnou celou obrazovkou</string>
+ <string id="14084">Pridávať skladby do fronty pri ich výbere</string>
+- <string id="14085">Automaticky prehrať Audio CD</string>
++ <string id="14085">Automaticky prehrávať Audio CD</string>
+ <string id="14086">Prehrávanie</string>
+-
+ <string id="14087">DVD</string>
+- <string id="14088">Automaticky prehrať DVD</string>
++ <string id="14088">Automaticky prehrávať DVD</string>
+ <string id="14089">Font pre textové titulky</string>
+ <string id="14090">Oblasť a jazyk</string>
+ <string id="14091">Znaková sada</string>
+ <string id="14092">Ladenie / Debug</string>
+-
+ <string id="14093">BezpeÄnosÅ¥</string>
+ <string id="14094">Vstupné zariadenia</string>
+ <string id="14095">Napájanie</string>
+ <string id="15015">Odstrániť</string>
+ <string id="15016">Hry</string>
+ <string id="15019">Pridať</string>
+-
+ <string id="15052">Heslo</string>
+ <string id="15100">Knižnica</string>
+ <string id="15101">Databáza</string>
+ <string id="15102">* VÅ¡etky albumy</string>
+ <string id="15103">* Všetci interpréti</string>
+ <string id="15104">* VÅ¡etky skladby</string>
+-
+ <string id="15105">* Všetky žánre</string>
+ <string id="15107">Buferujem...</string>
+ <string id="15108">Zvuky navigácie</string>
+- <string id="15109">Predvolený vzhľad</string>
+- <string id="15111">Téma vzhľadu</string>
++ <string id="15109">Predvolené</string>
++ <string id="15111">- Téma</string>
+ <string id="15112">Predvolená téma</string>
+-
+ <string id="15200">Last.FM</string>
+ <string id="15201">Last.FM - Odosielať informácie o skladbách</string>
+- <string id="15202">Last.FM meno</string>
+- <string id="15203">Last.FM heslo</string>
++ <string id="15202">Last.FM - Užívateľské meno</string>
++ <string id="15203">Last.FM - Heslo</string>
+ <string id="15204">Nedá sa nadviazať spojenie: spím...</string>
+ <string id="15205">Prosím aktualizujte XBMC</string>
+-
+ <string id="15206">Zlá autorizácia: Skontrolujte meno a heslo</string>
+ <string id="15207">Pripojený</string>
+ <string id="15208">Odpojený</string>
+ <string id="15209">Interval pre posielanie %i</string>
+ <string id="15210">NaÄítaných %i skladieb</string>
+ <string id="15211">Posielam...</string>
+-
+ <string id="15212">Pošlem za %i sekund</string>
+ <string id="15213">Prehrať použitím...</string>
+ <string id="15214">Používať hladkú A/V synchornizáciu</string>
+- <string id="15215">Schovať názvy súborov v náhľadoch</string>
+- <string id="15216">Prehrať v párty móde</string>
++ <string id="15215">Skryť názvy súborov v náhľadoch</string>
++ <string id="15216">Prehrať v párty režime</string>
+ <string id="15217">Libre.fm - Odosielať informácie o skladbách</string>
+-
+ <string id="15218">Libre.fm - Užívateľské meno</string>
+ <string id="15219">Libre.fm - Heslo</string>
+ <string id="15220">Libre.fm</string>
+- <string id="15221">Online služby</string>
+- <string id="15250">Last.FM - Odosielať informácie o rádiách</string>
++ <string id="15221">Služby</string>
++ <string id="15250">Last.fm - Odosielať informácie o rádiách</string>
+ <string id="15251">Spájam sa s Last.FM...</string>
+-
+ <string id="15252">Nastavujem stanicu...</string>
+ <string id="15253">Hľadať podobných interprétov...</string>
+- <string id="15254">HľadaÅ¥ podobné znaÄky...</string>
++ <string id="15254">Hľadať podobné slová...</string>
+ <string id="15255">Váš profil (%name%)</string>
+- <string id="15256">NajpoužívanejÅ¡ie znaÄky</string>
+- <string id="15257">Najlepší interpréti pre znaÄku %name%</string>
+-
+- <string id="15258">NajlepÅ¡ie albumy pre znaÄku %name%</string>
+- <string id="15259">NajlepÅ¡ie piesne pre znaÄku %name%</string>
+- <string id="15260">PoÄúvaÅ¥ znaÄku %name% na Last.FM radiu</string>
++ <string id="15256">Najpoužívanejšie slová</string>
++ <string id="15257">Najlepší interpréti pre slovo %name%</string>
++ <string id="15258">Najlepšie albumy pre slovo %name%</string>
++ <string id="15259">Najlepšie skladby pre slovo %name%</string>
++ <string id="15260">PoÄúvaÅ¥ slovo %name% na Last.FM radiu</string>
+ <string id="15261">Podobní interpréti ako %name%</string>
+- <string id="15262">Najlepšie %name% albumy</string>
+- <string id="15263">Najlepšie %name% piesne</string>
+-
+- <string id="15264">Najpoužívanejšie %name% tagy</string>
++ <string id="15262">Najlepšie albumy %name%</string>
++ <string id="15263">Najlepšie skladby %name%</string>
++ <string id="15264">Najpoužívanejšie slová %name%</string>
+ <string id="15265">NajväÄší fanúškovia %name%</string>
+ <string id="15266">PoÄúvaÅ¥ %name% fanúškov Last.FM radiu</string>
+ <string id="15267">PoÄúvaÅ¥ %name% podobných interprétov na Last.FM radiu</string>
+ <string id="15268">Najlepší interpréti pre užívateľa %name%</string>
+ <string id="15269">Najlepšie albumy pre užívateľa %name%</string>
+-
+- <string id="15270">Najlepšie piesne pre užívateľa %name%</string>
++ <string id="15270">Najlepšie skladby pre užívateľa %name%</string>
+ <string id="15271">Kamaráti užívateľa %name%</string>
+ <string id="15272">Susedia užívateľa %name%</string>
+ <string id="15273">Týždenné rebríÄky interprétov pre %name%</string>
+ <string id="15274">Týždenné rebríÄky albumov pre %name%</string>
+- <string id="15275">Týždenné rebríÄky piesní pre %name%</string>
+-
++ <string id="15275">Týždenné rebríÄky skladieb pre %name%</string>
+ <string id="15276">PoÄúvaÅ¥ %name% susedov Last.FM radiu</string>
+ <string id="15277">PoÄúvaÅ¥ %name% osobný Last.FM radiu</string>
+- <string id="15278">PoÄúvaÅ¥ %name% obľúbene piesne Last.FM radiu</string>
++ <string id="15278">PoÄúvaÅ¥ %name% obľúbene skladby Last.FM radiu</string>
+ <string id="15279">SÅ¥ahujem zoznam z Last.FM...</string>
+- <string id="15280">Nie je možné stiahnuť zoznam z Last.FM...</string>
+- <string id="15281">Napíšte meno interpréta pre hľadanie podobných</string>
+-
+- <string id="15282">Napíšte znaÄku pre hľadanie podobných</string>
+- <string id="15283">Piesne nedávno poÄúvané užívateľom %name%</string>
+- <string id="15284">PoÄúvaÅ¥ %name% odporúÄania Last.FM radio</string>
++ <string id="15280">Nepodarilo sa stiahnuť zoznam z Last.FM...</string>
++ <string id="15281">Zadajte meno interpréta pre hľadanie podobných</string>
++ <string id="15282">Zadajte slovo pre hľadanie podobných</string>
++ <string id="15283">Naposledy poÄúvané skladby užívateľom %name%</string>
++ <string id="15284">PoÄúvaÅ¥ %name% odporúÄania Last.FM</string>
+ <string id="15285">Obľúbené tagy užívateľa %name%</string>
+- <string id="15287">Chcete pridať túto skladbu k vaším obľúbeným?</string>
++ <string id="15287">Chcete pridať túto skladbu k Vaším obľúbeným?</string>
+ <string id="15288">Chcete zakázať túto skladbu?</string>
+-
+- <string id="15289">Skladba pridaná k vaším obľúbeným: '%s'.</string>
+- <string id="15290">Skladbu '%s' sa nepodarilo pridať k vaším obľúbeným.</string>
++ <string id="15289">Skladba pridaná k Vaším obľúbeným: '%s'.</string>
++ <string id="15290">Skladbu '%s' sa nepodarilo pridať k Vaším obľúbeným.</string>
+ <string id="15291">Zakázané: '%s'.</string>
+ <string id="15292">Nepodarilo sa zakázať '%s'.</string>
+ <string id="15293">Ostatné obľúbené skladby užívateľa %name%</string>
+ <string id="15294">Ostatné zakázané skladby užívateľa %name%</string>
+-
+- <string id="15295">Odstrániť z obľúbených skladieb?</string>
++ <string id="15295">Odstrániť z obľúbených skladieb</string>
+ <string id="15296">Povoliť zakázanú</string>
+- <string id="15297">Chcete odstrániť túto skladbu z vašich obľúbených?</string>
++ <string id="15297">Chcete odstrániť túto skladbu z Vašich obľúbených?</string>
+ <string id="15298">Chcete opäť povoliť túto zakázanú skladbu?</string>
+- <string id="15300">Cesta nenájdená alebo nesprávna</string>
+- <string id="15301">Nie je možné spojenie zo sieťovým serverom</string>
+-
++ <string id="15300">Cesta nenájdená</string>
++ <string id="15301">Nepodarilo sa spojenie zo sieťovým serverom</string>
+ <string id="15302">Servery neboli nájdené</string>
+ <string id="15303">Skupina nebola nájdená</string>
+- <string id="15310">Otváranie multi-cestového zdroja</string>
+- <string id="15311">Cesta:</string>
+- <string id="16000">Všeobecné</string>
+- <string id="16002">Hľadanie CDDB</string>
+-
++ <string id="15310">Otváranie prieÄinku s viacerými cestami</string>
++ <string id="15311">Umiestnenie:</string>
++ <string id="16000">Hlavné</string>
++ <string id="16002">Vyhľadávanie na internete</string>
+ <string id="16003">PrehrávaÄ</string>
+- <string id="16004">Prehrať médiá z disku</string>
++ <string id="16004">Prehrávať médiá z disku (CD/DVD/BluRay)</string>
+ <string id="16008">Zadajte nový názov</string>
+- <string id="16009">Napíšte názov filmu</string>
+- <string id="16010">Napíšte názov profilu</string>
+- <string id="16011">Napíšte názov albumu</string>
+-
+- <string id="16012">Napíšte názov playlist-u</string>
+- <string id="16013">Napíšte názov súboru</string>
+- <string id="16014">Napíšte názov prieÄinka</string>
+- <string id="16015">Vložte adresár</string>
++ <string id="16009">Zadajte názov filmu</string>
++ <string id="16010">Zadajte názov profilu</string>
++ <string id="16011">Zadajte názov albumu</string>
++ <string id="16012">Zadajte názov playlist-u</string>
++ <string id="16013">Zadajte názov súboru</string>
++ <string id="16014">Zadajte názov prieÄinka</string>
++ <string id="16015">Prejdite do prieÄinka</string>
+ <string id="16016">Dostupné možnosti: %A, %T, %N, %B, %D, %G, %Y, %F, %S</string>
+- <string id="16017">Napíšte hľadaný výraz</string>
+-
++ <string id="16017">Zadajte hľadaný výraz</string>
+ <string id="16018">Žiadne</string>
+- <string id="16019">Auto Výber</string>
++ <string id="16019">Automaticky</string>
+ <string id="16020">Deinterlácia</string>
+ <string id="16021">Bob</string>
+ <string id="16022">Bob (OpaÄný)</string>
+- <string id="16023">Prekladaný materiál /Interlaced Handling</string>
+-
+ <string id="16024">Prerušujem...</string>
+- <string id="16025">Napíšte meno interpréta</string>
+- <string id="16026">Prehrávanie playlist-u zrušené</string>
+- <string id="16027">Príliš veľa nasledujúcich položiek zlyhalo</string>
++ <string id="16025">Zadajte meno interpréta</string>
++ <string id="16026">Prehrávanie zlyhalo</string>
++ <string id="16027">Prehrávanie jednej, alebo viacerých položiek zlyhalo</string>
+ <string id="16028">Zadajte hodnotu</string>
+ <string id="16029">Skontrolujte Log súbor pre viac informácií.</string>
+-
+- <string id="16030">Párty mód prerušený.</string>
+- <string id="16031">Žiadne skladby neboli nájdené v knižnici.</string>
++ <string id="16030">Párty režim prerušený.</string>
++ <string id="16031">V knižnici nie sú žiadne odpovedajúce skladby.</string>
+ <string id="16032">Nedá sa inicializovať databáza.</string>
+- <string id="16033">Nedá sa otvoriť databáza.</string>
+- <string id="16034">Nedajú sa naÄítaÅ¥ skladby z databázy.</string>
+- <string id="16035">Párty mód playlist</string>
+-
++ <string id="16033">Databáza sa nedá otvoriť.</string>
++ <string id="16034">Skladby sa nedajú naÄítaÅ¥ z databázy.</string>
++ <string id="16035">Párty režim playlist</string>
+ <string id="16036">De-interlácia (PoloviÄná)</string>
+ <string id="16037">Deinterlácia videa</string>
+ <string id="16038">Spôsob deinterlácie</string>
+@@ -1606,35 +1382,30 @@
+ <string id="16102">Videné</string>
+ <string id="16103">OznaÄiÅ¥ ako videné</string>
+ <string id="16104">OznaÄiÅ¥ ako nevidené</string>
+-
+ <string id="16105">Upraviť titul</string>
+ <string id="16200">Operácia bola prerušená</string>
+ <string id="16201">Kopírovanie zlyhalo</string>
+ <string id="16202">Zlyhalo kopírovanie najmenej jedného súboru</string>
+ <string id="16203">Presunutie zlyhalo</string>
+ <string id="16204">Zlyhal presun najmenej jedného súboru</string>
+-
+ <string id="16205">Mazanie zlyhalo</string>
+ <string id="16206">Zlyhalo zmazanie najmenej jedného súboru</string>
+ <string id="16300">Spôsob úpravy videa</string>
+- <string id="16301">Nearest Neighbour</string>
+- <string id="16302">Bilinear</string>
+- <string id="16303">Bicubic</string>
+-
++ <string id="16301">Najbližší sused</string>
++ <string id="16302">Bilineárny</string>
++ <string id="16303">Bikubický</string>
+ <string id="16304">Lanczos2</string>
+ <string id="16305">Lanczos3</string>
+ <string id="16306">Sinc8</string>
+- <string id="16307">Bicubic (software)</string>
++ <string id="16307">Bikubický (software)</string>
+ <string id="16308">Lanczos (software)</string>
+ <string id="16309">Sinc (software)</string>
+-
+ <string id="16310">DoÄasný</string>
+ <string id="16311">DoÄasný/Spatial</string>
+ <string id="16312">(VDPAU) Redukcia Å¡umu</string>
+ <string id="16313">(VDPAU) Ostrosť</string>
+ <string id="16314">Inverzný Telecine</string>
+ <string id="16315">Lanczos3 optimalizovaný</string>
+-
+ <string id="16316">Automaticky</string>
+ <string id="16317">DoÄasný (PoloviÄný)</string>
+ <string id="16318">DoÄasný/Spatial (poloviÄný)</string>
+@@ -1643,747 +1414,647 @@
+ <string id="16321">DXVA Best</string>
+ <string id="16322">Spline36</string>
+ <string id="16323">Spline36 optimalizovaná</string>
++ <string id="16324">Software Blend</string>
+ <string id="16400">Post-processing</string>
+- <string id="17500">ZobraziÅ¥ odpoÄet do uspania</string>
+-
++ <string id="17500">ZobraziÅ¥ Äas do vypnutia</string>
+ <string id="19000">Prepnúť na kanál</string>
+- <string id="20000">PrieÄinok pre sÅ¥ahovanie CDDA</string>
+- <string id="20001">PoužiÅ¥ externý prehrávaÄ</string>
++ <string id="20000">PrieÄinok pre ukladanie hudby</string>
++ <string id="20001">PoužiÅ¥ externý DVD prehrávaÄ</string>
+ <string id="20002">Externý DVD prehrávaÄ</string>
+ <string id="20003">PrieÄinok s trainermi</string>
+- <string id="20004">PrieÄinok screenshotov</string>
+-
++ <string id="20004">PrieÄinok pre ukladanie obrázkov (screenshotov)</string>
+ <string id="20006">PrieÄinok playlist-ov</string>
+ <string id="20007">Nahrávky</string>
+ <string id="20008">Screenshoty</string>
+ <string id="20009">Použiť XBMC</string>
+ <string id="20011">Hudobné playlist-y</string>
+ <string id="20012">Video playlist-y</string>
+-
+ <string id="20013">Chcete spustiť túto hru?</string>
+- <string id="20014">Usporiadať: Playlist</string>
+- <string id="20015">IMDb náhľad</string>
+- <string id="20016">SúÄasný náhľad</string>
++ <string id="20014">Zoradiť podľa: Playlistu</string>
++ <string id="20015">Internetový náhľad</string>
++ <string id="20016">Aktuálny náhľad</string>
+ <string id="20017">Lokálny náhľad</string>
+- <string id="20018">Žiaden náhľad</string>
+-
++ <string id="20018">Bez náhľadu</string>
+ <string id="20019">Zvoliť náhľad</string>
+ <string id="20022"></string>
+ <string id="20023">Konflikt</string>
+- <string id="20024">Skenovať nové</string>
+- <string id="20025">Skenovať všetko</string>
++ <string id="20024">Prehľadať nové</string>
++ <string id="20025">Prehľadať všetko</string>
+ <string id="20026">Región</string>
+-
+ <string id="20037">Sumár</string>
+- <string id="20038">Zamknúť hudbu</string>
+- <string id="20039">Zamknúť video</string>
+- <string id="20040">Zamknúť obrázky</string>
+- <string id="20041">Zamknúť programy, uloženia a skripty</string>
++ <string id="20038">Zamknúť sekciu Hudba</string>
++ <string id="20039">Zamknúť sekciu Video</string>
++ <string id="20040">Zamknúť sekciu Obrázky</string>
++ <string id="20041">Zamknúť Programy &amp; skripty</string>
+ <string id="20042">Zamknúť správcu súborov</string>
+-
+ <string id="20043">Zamknúť nastavenia</string>
+ <string id="20044">NaÄisto naÅ¡tartovaÅ¥</string>
+- <string id="20045">Vstúpiť do admin módu</string>
+- <string id="20046">Opustiť admin mód</string>
++ <string id="20045">Vstúpiť do hlavného režimu</string>
++ <string id="20046">Opustiť hlavný režim</string>
+ <string id="20047">Vytvoriť profil '%s' ?</string>
+- <string id="20048">Štartovať s prázdnymi nastaveniami</string>
+-
+- <string id="20049">Najlepšie možné</string>
+- <string id="20050">Autoprepínanie medzi 16x9 a 4x3</string>
++ <string id="20048">SpustiÅ¥ s Äistými nastaveniami</string>
++ <string id="20049">Najlepšie dostupné</string>
++ <string id="20050">Automaticky prepínať 16:9 a 4:3 režimy</string>
+ <string id="20051">PovažovaÅ¥ zlúÄené súbory za jeden súbor</string>
+ <string id="20052">Výstraha</string>
+- <string id="20053">Admin mód deaktivovaný</string>
+- <string id="20054">Admin mód aktivovaný</string>
+-
++ <string id="20053">Hlavný režim deaktivovaný</string>
++ <string id="20054">Hlavný režim aktivovaný</string>
+ <string id="20055">Allmusic.com náhľad</string>
+ <string id="20057">Odstrániť náhľad</string>
+ <string id="20058">Pridať profil...</string>
+- <string id="20059">Stiahnuť informácie pre všetky albumy</string>
+- <string id="20060">Informácie o médiu</string>
++ <string id="20059">Získať informácie pre všetky albumy</string>
++ <string id="20060">Informácie o médiách</string>
+ <string id="20061">Oddelený</string>
+-
+ <string id="20062">Zdieľať s predvoleným</string>
+ <string id="20063">ZdieľaÅ¥ s predvoleným (len Äítanie)</string>
+ <string id="20064">Kopírovať predvolené</string>
+ <string id="20065">Obrázok profilu</string>
+ <string id="20066">Nastavenia zámku</string>
+ <string id="20067">Upraviť profil</string>
+-
+ <string id="20068">Zamknutie profilu</string>
+- <string id="20069">PrieÄinok nie je možné vytvoriÅ¥</string>
+- <string id="20070">Adresár profilov</string>
++ <string id="20069">PrieÄinok sa nepodarilo vytvoriÅ¥</string>
++ <string id="20070">PrieÄinok profilu</string>
+ <string id="20071">Å tartovaÅ¥ s Äistými zdrojmi médií</string>
+ <string id="20072">UbezpeÄte sa, že zvolený prieÄinok je zapisovateľný</string>
+- <string id="20073">a nový názov prieÄinku je korektný</string>
+-
+- <string id="20074">MPAA Hodnotenie:</string>
+- <string id="20075">Napíšte hlavný kód</string>
+- <string id="20076">Pýtať sa na hlavný kód pri štarte</string>
++ <string id="20073">a nový názov prieÄinku je správny</string>
++ <string id="20074">Prístupnosť</string>
++ <string id="20075">Zadajte hlavné heslo</string>
++ <string id="20076">Pýtať sa na hlavné heslo pri štarte</string>
+ <string id="20077">Nastavenie vzhľadu</string>
+ <string id="20078">- žiadny link -</string>
+- <string id="20079">Povoliť animácie</string>
+-
+- <string id="20080">ZakázaÅ¥ RSS poÄas hudby</string>
+- <string id="20081">PovoliÅ¥ záložkové tlaÄítka</string>
+- <string id="20082">Zobraziť XLink Kai informácie</string>
+- <string id="20083">Zobraziť informácie o hudbe</string>
+- <string id="20084">ZobraziÅ¥ informácie o poÄasí</string>
+- <string id="20085">Zobraziť systémové informácie</string>
+-
+- <string id="20086">Zobraziť voľné miesto na C: E: F:</string>
+- <string id="20087">Zobraziť voľné miesto na E: F: G:</string>
+- <string id="20088">PredpoveÄ poÄasia</string>
++ <string id="20079">Zobrazovať animácie</string>
++ <string id="20080">ZakázaÅ¥ RSS poÄas prehrávania hudby</string>
++ <string id="20081">Aktivovať záložky</string>
++ <string id="20082">Zobrazovať programy v hlavnom menu</string>
++ <string id="20083">Zobrazovať informácie o hudbe</string>
++ <string id="20084">ZobrazovaÅ¥ informácie o poÄasí</string>
++ <string id="20085">Zobrazovať systémové informácie</string>
++ <string id="20086">Zobrazovať voľné miesto na C: E: F:</string>
++ <string id="20087">Zobrazovať voľné miesto na E: F: G:</string>
++ <string id="20088">Informácie o poÄasí</string>
+ <string id="20089">Voľné miesto</string>
+- <string id="20090">Napíšte názov existujúceho zdieľania</string>
+- <string id="20091">Kód</string>
+-
+- <string id="20092">Otvoriť profil</string>
++ <string id="20090">Zadajte názov existujúceho zdieľania</string>
++ <string id="20091">Uzamykacie heslo</string>
++ <string id="20092">NaÄítaÅ¥ profil</string>
+ <string id="20093">Názov profilu</string>
+ <string id="20094">Zdroje médií</string>
+- <string id="20095">Napíšte kód pre uzamknutie profilu</string>
+- <string id="20096">Prihlásenie</string>
+- <string id="20097">Získavam informácie o albume</string>
+-
+- <string id="20098">Získavam informácie pre albume</string>
+- <string id="20099">Nie je možné stiahnuÅ¥ CD alebo stopu poÄas prehrávania CD</string>
+- <string id="20100">Hlavný kód a nastavenia</string>
+- <string id="20101">Vloženie hlavného kódu vždy povolí admin mód</string>
++ <string id="20095">Zadajte heslo pre uzamknutie profilu</string>
++ <string id="20096">Úvodná obrazovka</string>
++ <string id="20097">Získavanie informácií o albume</string>
++ <string id="20098">Ukladanie informácií o albume</string>
++ <string id="20099">CD, alebo stopu nie je možné uložiÅ¥ poÄas prehrávania CD</string>
++ <string id="20100">Hlavné heslo a nastavenia</string>
++ <string id="20101">Vložením hlavného hesla vždy aktivovať hlavný režim</string>
+ <string id="20102">alebo kopírovať z predvoleného?</string>
+ <string id="20103">Uložiť zmeny do profilu?</string>
+-
+ <string id="20104">Staré nastavenia nájdené.</string>
+ <string id="20105">Chcete ich použiť?</string>
+ <string id="20106">Staré zdroje médií nájdené.</string>
+ <string id="20107">Oddelený (zamknuté)</string>
+- <string id="20108">Hlavný</string>
+- <string id="20109">ZväÄÅ¡enie vzhľadu</string>
+-
+- <string id="20110">UPnP Klient</string>
+- <string id="20111">Autoštart</string>
++ <string id="20108">Hlavný prieÄinok</string>
++ <string id="20109">- ZväÄÅ¡enie</string>
++ <string id="20110">UPnP nastavenia</string>
++ <string id="20111">Autoštart UPnP klienta</string>
+ <string id="20112">Posledné prihlásenie: %s</string>
+ <string id="20113">Nikdy neprihlásený</string>
+ <string id="20114">Profil %i / %i</string>
+ <string id="20115">Prihlásenie / Výber profilu</string>
+-
+- <string id="20116">Použiť zámok na prihlasovacej obrazovke</string>
+- <string id="20117">Nesprávny kód.</string>
+- <string id="20118">Toto vyžaduje aby bol nastavený admin zámok.</string>
+- <string id="20119">Chcete to nastaviť teraz?</string>
++ <string id="20116">Aktivovať zámok na úvodnej obrazovke</string>
++ <string id="20117">Nesprávne uzamykacie heslo.</string>
++ <string id="20118">Vyžaduje sa nastavenie hlavného zámku.</string>
++ <string id="20119">Chcete ho nastaviť teraz?</string>
+ <string id="20120">NaÄítanie informácie o programe</string>
+- <string id="20121">Párty sa zaÄína!</string>
+-
+- <string id="20122">Pravda</string>
++ <string id="20121">Párty sa zaÄala!</string>
++ <string id="20122">Pravdivé</string>
+ <string id="20123">Miešam nápoje</string>
+ <string id="20124">Plním poháre</string>
+ <string id="20125">Prihlásený ako</string>
+ <string id="20126">Odhlásiť</string>
+- <string id="20128">Prejsť do hlavného</string>
+-
+- <string id="20129">Vlna</string>
+- <string id="20130">Vlna (naopak)</string>
++ <string id="20128">PrejsÅ¥ do hl. prieÄinku</string>
++ <string id="20129">Weave</string>
++ <string id="20130">Weave (naopak)</string>
+ <string id="20131">Miešať</string>
+ <string id="20132">Reštartovať video</string>
+- <string id="20133">Upraviť sieťové miesto</string>
+- <string id="20134">Odstrániť sieťové miesto</string>
+-
+- <string id="20135">Chcete skenovaÅ¥ prieÄinok?</string>
+- <string id="20136">Pamäťová karta</string>
+- <string id="20137">Pamäťová karta nainštalovaná</string>
+- <string id="20138">Nie je možné nainštalovať pamäťovú kartu</string>
++ <string id="20133">Upraviť sieťové umiestnenie</string>
++ <string id="20134">Odstrániť sieťové umiestnenie</string>
++ <string id="20135">Chcete prehľadaÅ¥ prieÄinok?</string>
++ <string id="20136">Pamäťová jednotka</string>
++ <string id="20137">Pamäťová jednotka nainštalovaná</string>
++ <string id="20138">Nepodarilo sa nainštalovať pamäťovú jednotku</string>
+ <string id="20139">V porte %i, slote %i</string>
+ <string id="20140">Zamknúť Å¡etriÄ obrazovky</string>
+-
+ <string id="20141">Nastaviť</string>
+- <string id="20142">Meno</string>
+- <string id="20143">Napíšte heslo pre</string>
+- <string id="20144">ÄŒasovaÄ vypnutia</string>
++ <string id="20142">Užívateľské meno</string>
++ <string id="20143">Zadajte heslo pre</string>
++ <string id="20144">Automatické vypnutie</string>
+ <string id="20145">Vypnúť za (v minútach)</string>
+- <string id="20146">Naštartované, vypnutie za %im</string>
+-
++ <string id="20146">Vypnutie je nastavené. Vypnutie za %im</string>
+ <string id="20147">Vypnutie za 30 minút</string>
+ <string id="20148">Vypnutie za 60 minút</string>
+ <string id="20149">Vypnutie za 120 minút</string>
+- <string id="20150">Vlastný ÄasovaÄ vypnutia</string>
++ <string id="20150">ÄŒasovaÄ vypnutia</string>
+ <string id="20151">ZruÅ¡iÅ¥ ÄasovaÄ vypnutia</string>
+ <string id="20152">Zamknúť nastavenia pre %s</string>
+-
+- <string id="20153">Vybrať...</string>
++ <string id="20153">Prechádzať...</string>
+ <string id="20154">Informácie základné</string>
+- <string id="20155">Informácie o úložisku</string>
++ <string id="20155">Informácie o hdd</string>
+ <string id="20156">Informácie o harddisku</string>
+ <string id="20157">Informácie o DVD-ROM</string>
+ <string id="20158">Informácie o sieti</string>
+-
+ <string id="20159">Informácie o videu</string>
+- <string id="20160">Informácie o hardware</string>
+- <string id="20161">Celkom: %s MB, použitých: %s MB, voľných: %s MB</string>
+- <string id="20162">Celkom HDD používaných: %u%% voľných: %u%%</string>
+- <string id="20163">%s: %s MB of %s MB voľných</string>
++ <string id="20160">Informácie o CPU a RAM</string>
++ <string id="20161">Celkom</string>
++ <string id="20162">Použité</string>
++ <string id="20163">z</string>
+ <string id="20164">Zamknutie nie je podporované</string>
+-
+- <string id="20165">Odomknutý</string>
++ <string id="20165">Nezamknutý</string>
+ <string id="20166">Zamknutý</string>
+ <string id="20167">Zamrznutý</string>
+- <string id="20168">Vyžaduje reset</string>
++ <string id="20168">Vyžaduje reštart</string>
+ <string id="20169">Týždeň</string>
+ <string id="20170">Linka</string>
+-
+- <string id="20171">Windows sieť (SMB)</string>
++ <string id="20171">Sieť Windows (SMB)</string>
+ <string id="20172">XBMSP server</string>
+ <string id="20173">FTP server</string>
+- <string id="20174">iTunes zdieľanie hudby (DAAP)</string>
++ <string id="20174">iTunes zdieľanie (DAAP)</string>
+ <string id="20175">UPnP Server</string>
+- <string id="20176">Zobraziť informácie o videu</string>
+-
+- <string id="20177">V poriadku</string>
++ <string id="20176">Zobrazovať informácie o videu</string>
++ <string id="20177">Hotovo</string>
+ <string id="20178">Shift</string>
+ <string id="20179">Caps Lock</string>
+ <string id="20180">Symboly</string>
+- <string id="20181">Backspace</string>
+- <string id="20182">Space</string>
+-
++ <string id="20181">Vymazať</string>
++ <string id="20182">Medzera</string>
+ <string id="20183">Obnoviť vzhľad</string>
+- <string id="20184">OtoÄiÅ¥ podľa EXIF informácií</string>
+- <string id="20185">Detekovanie hardwarových hodnôt</string>
++ <string id="20184">OtáÄaÅ¥ obrázky podľa EXIF informácií</string>
++ <string id="20185">Použiť zobrazenie 'Plagáty' pre TV seriály</string>
+ <string id="20186">Prosím Äakajte</string>
+- <string id="20189">Povoliť automatické rolovanie obsahu a recenzie</string>
++ <string id="20189">Aktivovať automatické skrolovanie obsahu &amp; recenzie</string>
+ <string id="20190">Vlastné</string>
+-
+- <string id="20191">Povoliť ladenie / debug log</string>
+- <string id="20192">ZískaÅ¥ informácie o albume, keÄ sa pridáva do knižnice</string>
+- <string id="20193">ZískaÅ¥ informácie o interprétovy, keÄ sa pridáva do knižnice</string>
+- <string id="20194">Predvolený sÅ¥ahovaÄ pre hudbu</string>
+- <string id="20195">ZmeniÅ¥ SÅ¥ahovaÄ</string>
+- <string id="20196">Export knižnice hudby</string>
+-
+- <string id="20197">Import knižnice hudby</string>
++ <string id="20191">ZaÄaÅ¥ ukladaÅ¥ informácie / debug log</string>
++ <string id="20192">SÅ¥ahovaÅ¥ dodatoÄné informácie poÄas aktualizácie</string>
++ <string id="20193">Predvolená služba pre získavanie informácií o albume</string>
++ <string id="20194">Predvolená služba pre získavanie informácií o interprétovy</string>
++ <string id="20195">ZmeniÅ¥ sÅ¥ahovaÄ</string>
++ <string id="20196">Exportovať hudbonú knižnicu</string>
++ <string id="20197">Importovať hudbonú knižnicu</string>
+ <string id="20198">Interprét nenájdený!</string>
+ <string id="20199">Sťahovanie informácií o interprétovy zlyhalo</string>
+- <string id="20250">Párty zahájená! (videa)</string>
++ <string id="20250">Párty sa zaÄala! (videa)</string>
+ <string id="20251">Miešam nápoje (videa)</string>
+- <string id="20252">Nalievam poháre (videa)</string>
+-
++ <string id="20252">Plním poháre (videa)</string>
+ <string id="20253">WebDAV server (HTTP)</string>
+ <string id="20254">WebDAV server (HTTPS)</string>
+ <string id="20255">Prvé prihlásenie, editujte svoj profil</string>
+ <string id="20256">HTS Tvheadend klient</string>
+ <string id="20257">VDR Streamdev klient</string>
+ <string id="20258">MythTV klient</string>
+-
+ <string id="20259">Sieťový Systém Súborov (NFS)</string>
+ <string id="20260">Secure Shell (SSH/SFTP)</string>
+ <string id="20261">Protokol súboru Apple (AFP)</string>
+- <string id="20300">Web server zložka (HTTP)</string>
+- <string id="20301">Web server zložka (HTTPS)</string>
+- <string id="20302">Nie je možné zapísaÅ¥ do prieÄinka:</string>
+- <string id="20303">Želáte si preskoÄiÅ¥ a prejsÅ¥ Äalej?</string>
+-
++ <string id="20300">Web server prieÄinok (HTTP)</string>
++ <string id="20301">Web server prieÄinok (HTTPS)</string>
++ <string id="20302">Nepodarilo sa zapísaÅ¥ do prieÄinka:</string>
++ <string id="20303">Prajete si preskoÄiÅ¥ a prejsÅ¥ Äalej?</string>
+ <string id="20304">RSS Spravodajstvo</string>
+- <string id="20307">Sekundárne DNS</string>
++ <string id="20307">Sekundárny DNS</string>
+ <string id="20308">DHCP Server:</string>
+ <string id="20309">VytvoriÅ¥ nový prieÄinok</string>
+ <string id="20310">Stmaviť LCD pri prehrávaní</string>
+- <string id="20311">Neznámy alebo na Doske (chránený)</string>
+-
+- <string id="20312">Stmaviť LCD pri pauze</string>
++ <string id="20311">Neznáme alebo obsiahnuté (chránené)</string>
++ <string id="20312">Stmaviť LCD pri pozastavení</string>
+ <string id="20314">Video - Knižnica</string>
+- <string id="20316">Usporiadať: ID</string>
++ <string id="20316">Zoradiť podľa: ID</string>
+ <string id="20324">HraÅ¥ ÄasÅ¥...</string>
+- <string id="20325">Časť %i / %i</string>
++ <string id="20325">VyÄistenie kalibrácie</string>
+ <string id="20326">Tímto zmažete uložené hodnoty kalibrácie pre %s</string>
+-
+ <string id="20327">na predvolené hodnoty.</string>
+- <string id="20328">Vybrať cieľ</string>
++ <string id="20328">Vybrať umiestnenie</string>
++ <string id="20329">Filmy sú v jednotlivých prieÄinkoch, ktoré zodpovedajú názvu filmu</string>
+ <string id="20330">VyhľadávaÅ¥ podľa názvu prieÄinku</string>
+ <string id="20331">Súbor</string>
+ <string id="20332">Chcete vyhľadávaÅ¥ podľa názvu súboru alebo prieÄinku?</string>
+- <string id="20333">Nastaviť obsah</string>
+-
++ <string id="20333">Pridať obsah</string>
+ <string id="20334">PrieÄinok</string>
+ <string id="20335">VyhľadávaÅ¥ v podprieÄinkoch?</string>
+ <string id="20336">Odomknúť zdroje</string>
+ <string id="20337">Herec</string>
+ <string id="20338">Film</string>
+- <string id="20339">Režisér</string>
+-
++ <string id="20339">Réžia</string>
+ <string id="20340">Chcete odstrániť všetky položky v</string>
+- <string id="20341">tejto ceste z knižnice?</string>
++ <string id="20341">tejto ceste z knižnice XBMC?</string>
+ <string id="20342">Filmy</string>
+ <string id="20343">Seriály</string>
+ <string id="20344">Tento prieÄinok obsahuje</string>
+- <string id="20345">Spustiť automatické prehľadanie</string>
+-
++ <string id="20345">Spustiť automatické prehľadávanie</string>
+ <string id="20346">VyhľadávaÅ¥ v podprieÄinkoch</string>
+ <string id="20347">ako</string>
+- <string id="20348">Režisér</string>
++ <string id="20348">Réžia</string>
+ <string id="20349">Na tejto ceste nebol nájdený žiadny video súbor!</string>
+ <string id="20350">hlasov</string>
+ <string id="20351">Informácie o seriáli</string>
+-
+ <string id="20352">Informácie o epizóde</string>
+ <string id="20353">NaÄítanie detailov o seriáli</string>
+ <string id="20354">Získavanie informácií o epizódach</string>
+ <string id="20355">NaÄítanie informácií o epizódach v prieÄinku</string>
+ <string id="20356">Vyberte seriál:</string>
+ <string id="20357">Zadajte názov seriálu</string>
+-
+ <string id="20358">Sezóna %i</string>
+ <string id="20359">Epizóda</string>
+ <string id="20360">Epizódy</string>
+ <string id="20361">NaÄítanie detailov o epizóde</string>
+ <string id="20362">Odstrániť epizódu z knižnice</string>
+ <string id="20363">Odstrániť seriál z knižnice</string>
+-
+ <string id="20364">Seriál</string>
+ <string id="20365">Obsah epizódy</string>
+ <string id="20366">* Všetky sezóny</string>
+- <string id="20367">Schovať sledované</string>
++ <string id="20367">Skryť videné</string>
+ <string id="20368">Kód produkcie</string>
+- <string id="20369">Zobraziť obsah pre nevidené videá</string>
+-
++ <string id="20369">Zobrazovať obsah pre nevidené videá</string>
+ <string id="20370">* nezobrazené pre spoilery *</string>
+ <string id="20371">Priradiť náhľad k sezóne</string>
+ <string id="20372">Obrázok sezóny</string>
+ <string id="20373">Sezóna</string>
+ <string id="20374">Sťahovanie informácií o filme</string>
+ <string id="20375">Odobrať obsah</string>
+-
+ <string id="20376">Pôvodný názov</string>
+- <string id="20377">Obnoviť informácie o seriáli</string>
+- <string id="20378">Obnoviť informácie pre všetky epizódy?</string>
++ <string id="20377">Aktualizovať informácie o seriáli</string>
++ <string id="20378">Aktualizovať informácie pre všetky epizódy?</string>
+ <string id="20379">PrieÄinok obsahuje jeden seriál</string>
+ <string id="20380">VylúÄiÅ¥ prieÄinok z prehľadávania</string>
+- <string id="20381">Špeciál</string>
+-
++ <string id="20381">Špeciálne</string>
+ <string id="20382">Automaticky sťiahnúť náhľad k sezónam</string>
+ <string id="20383">PrieÄinok obsahuje jedno video</string>
+- <string id="20384">Spojiť so seriálom</string>
+- <string id="20385">Odstrániť spojenie so seriálom</string>
++ <string id="20384">Priradiť k TV seriálu</string>
++ <string id="20385">Odstrániť z TV seriálu</string>
+ <string id="20386">Naposledy pridané filmy</string>
+ <string id="20387">Naposledy pridané epizódy</string>
+-
+- <string id="20388">Štúdio</string>
++ <string id="20388">Štúdiá</string>
+ <string id="20389">Videoklipy</string>
+ <string id="20390">Naposledy pridané videoklipy</string>
+ <string id="20391">Videoklip</string>
+ <string id="20392">Odstrániť videoklip z knižnice</string>
+ <string id="20393">Informácie o videoklipe</string>
+-
+ <string id="20394">NaÄítanie informácií o videoklipe</string>
+ <string id="20395">Zmiešané</string>
+ <string id="20396">Prejsť na albumy podľa interpréta</string>
+ <string id="20397">Prejsť na album</string>
+ <string id="20398">Prehrať skladbu</string>
+ <string id="20399">Prejsť na videoklipy z albumu</string>
+-
+ <string id="20400">Prejsť na videoklipy od interpréta</string>
+ <string id="20401">Prehrať videoklip</string>
+- <string id="20402">Automaticky stiahnuť náhľady hercov</string>
++ <string id="20402">SÅ¥ahovaÅ¥ náhľady hercov poÄas pridávania do knižnice</string>
+ <string id="20403">Nastaviť náhľad hercov</string>
+ <string id="20405">Odstrániť záložku epizódy</string>
+ <string id="20406">Nastaviť záložku epizódy</string>
+-
+ <string id="20407">Nastavenie SÅ¥ahovaÄa</string>
+ <string id="20408">Sťahujem informácie o videoklipe</string>
+ <string id="20409">Sťahujem informácie o seriáli</string>
+ <string id="20410">Ukážka</string>
+- <string id="20411">Jednoduchý mód</string>
+- <string id="20412">Zobraziť TV seriály zjednodušene</string>
+-
++ <string id="20411">Zúžený režim</string>
++ <string id="20412">Zobrazovať TV seriály v zúženom režime</string>
+ <string id="20413">FanArt-y</string>
+- <string id="20414">Zobraziť FanArt vo video knižnici</string>
++ <string id="20414">Zobrazovať FanArt v knižnici videí a hudby</string>
+ <string id="20415">Hľadá sa nový obsah</string>
+ <string id="20416">Premiéra</string>
+- <string id="20417">Autor</string>
+- <string id="20418"></string>
+-
+- <string id="20419">Zobraziť metadata v súboroch</string>
++ <string id="20417">Napísal</string>
++ <string id="20418">
++ </string>
++ <string id="20419">Nahradiť názvy súborov s názvami knižníc</string>
+ <string id="20420">Nikdy</string>
+ <string id="20421">Iba ak jedna sezóna</string>
+ <string id="20422">Vždy</string>
+ <string id="20423">Má ukážku</string>
+- <string id="20424">Nie</string>
+-
++ <string id="20424">Nepravdivé</string>
+ <string id="20425">Fanart prezentácia</string>
+ <string id="20426">Exportovať všetko do jedného súboru alebo</string>
+- <string id="20427">každú položku do samostatného súboru?</string>
++ <string id="20427">každú položku oddelene do samostatného súboru?</string>
+ <string id="20428">1 súbor</string>
+ <string id="20429">Oddelene</string>
+ <string id="20430">Exportovať náhľady a FanArt?</string>
+-
+ <string id="20431">Prepísať staré súbory?</string>
+- <string id="20432">Nezahrňovať položky v tomto umiestnení pri aktualizácii knižnice</string>
++ <string id="20432">Vynechať položky v tomto umiestnení pri aktualizácii knižnice</string>
+ <string id="20433">Získavať náhľady a informácie o videu zo súboru</string>
+ <string id="20434">Sady</string>
+ <string id="20435">Priradiť náhľad sade filmov</string>
+ <string id="20436">Exportovať náhľady hercov?</string>
+-
+ <string id="20437">Vyberte FanArt</string>
+ <string id="20438">Lokálny FanArt</string>
+- <string id="20439">Žiadny FanArt</string>
++ <string id="20439">Bez FanArt-u</string>
+ <string id="20440">Aktuálny FanArt</string>
+ <string id="20441">Internetový FanArt</string>
+ <string id="20442">Zmeniť obsah</string>
+-
+- <string id="20443">Prajete si aktualizovať informácie pre</string>
++ <string id="20443">Aktualizovať informácie pre</string>
+ <string id="20444">všetky položky v tomto umiestnení?</string>
+ <string id="20445">FanArt</string>
+ <string id="20446">Lokálne uložené informácie nájdené.</string>
+ <string id="20447">Ignorovať a aktualizovať z internetu?</string>
+- <string id="20448">Nie je možné stiahnuť informácie</string>
+-
++ <string id="20448">Nepodarilo sa stiahnuť informácie</string>
+ <string id="20449">Server je pravdepodobne nedostupný</string>
+ <string id="20450">Chcete pokraÄovaÅ¥ v aktualizácii?</string>
+ <string id="20451">Krajina</string>
+ <string id="20452">epizóda</string>
+ <string id="20453">epizódy</string>
+ <string id="20454">PoslucháÄ</string>
+-
+ <string id="20455">PoslucháÄi</string>
+- <string id="20456">Nastaviť fanart setu filmov</string>
+- <string id="20457">Set filmov</string>
+- <string id="21330">ZobraziÅ¥ skryté súbory a prieÄinky</string>
++ <string id="20456">Nastaviť fanart kolekcii filmov</string>
++ <string id="20457">Kolekcia filmov</string>
++ <string id="20458">Zoskupovať filmy do kolekcií</string>
++ <string id="21330">ZobrazovaÅ¥ skryté súbory a prieÄinky</string>
+ <string id="21331">TuxBox klient</string>
+- <string id="21332">POZOR: CieľovéTuxBox zariadenie je v nahrávacom móde!</string>
++ <string id="21332">POZOR: Cieľové TuxBox zariadenie je v nahrávacom režime!</string>
+ <string id="21333">Stream bude zastavený!</string>
+-
+ <string id="21334">Prepnutie na kanál: %s zlyhalo!</string>
+ <string id="21335">Chcete spustiť stream?</string>
+- <string id="21336">Pripájam k: %s</string>
++ <string id="21336">Prebieha pripojenie k: %s</string>
+ <string id="21337">TuxBox zariadenie</string>
+ <string id="21359">Pridať zdieľané médiá...</string>
+ <string id="21360">Zdielať video a hudobné knižnice pomocou UPnP</string>
+-
+ <string id="21364">Upraviť zdieľanie média</string>
+ <string id="21365">Odstrániť zdieľanie média</string>
+ <string id="21366">PrieÄinok s titulkami</string>
+ <string id="21367">Film a alternatívny prieÄinok s titulkami</string>
+- <string id="21369">Povoliť myš</string>
++ <string id="21368">Prepisovať ASS/SSA písmo titulkov</string>
++ <string id="21369">Aktivovať ovládanie myšou a podporu dotykových displejov</string>
+ <string id="21370">HraÅ¥ zvuky navigácie poÄas prehrávania média</string>
+-
+- <string id="21371">Náhľad</string>
++ <string id="21371">Náhľady</string>
+ <string id="21372">VnútiÅ¥ prehrávaÄu DVD región</string>
+- <string id="21373">Video zariadenie</string>
+- <string id="21374">Video vzhľad</string>
++ <string id="21373">Video výstup</string>
++ <string id="21374">Video aspekt</string>
+ <string id="21375">Normálny</string>
+ <string id="21376">Letterbox</string>
+-
+ <string id="21377">Širokouhlý</string>
+- <string id="21378">Povoliť 480p</string>
+- <string id="21379">Povoliť 720p</string>
+- <string id="21380">Povoliť 1080i</string>
++ <string id="21378">Aktivovať 480p</string>
++ <string id="21379">Aktivovať 720p</string>
++ <string id="21380">Aktivovať 1080i</string>
+ <string id="21381">Zadaj názov nového playlist-u</string>
+- <string id="21382">ZobraziÅ¥ tlaÄítko "PridaÅ¥ zdroj" v zozname súborov</string>
+-
+- <string id="21383">Povoliť rolovacie pruhy</string>
+- <string id="21384">Vytvoriť filtrovanie podľa sledovania vo video knižnici</string>
++ <string id="21382">Zobrazovať položku 'Pridať zdroj' v sekciách</string>
++ <string id="21383">Aktivovať Scrollbar</string>
++ <string id="21384">Aktivovať filter videných videí v knižnici</string>
+ <string id="21385">Otvoriť</string>
+- <string id="21386">Úroveň akustickej správy</string>
++ <string id="21386">Správa akustickej úrovne</string>
+ <string id="21387">Rýchly</string>
+ <string id="21388">Tichý</string>
+-
+- <string id="21389">Povoliť vlastné pozadie</string>
+- <string id="21390">Úroveň správy napájania</string>
+- <string id="21391">Veľa energie</string>
+- <string id="21392">Málo energie</string>
+- <string id="21393">Veľká pohotovosť</string>
+- <string id="21394">Malá pohotovosť</string>
+-
++ <string id="21389">Aktivovať vlastné pozadie</string>
++ <string id="21390">Správa napájania</string>
++ <string id="21391">Vysoký výkon / Vyššia spotreba</string>
++ <string id="21392">Nízky výkon / Nízka spotreba</string>
++ <string id="21393">Spánok / Vyššia spotreba</string>
++ <string id="21394">Spánok / Nízka spotreba</string>
+ <string id="21395">Súbory väÄÅ¡ie ako 4GB sa nedajú naÄítaÅ¥ do vyrovnávacej pamäte</string>
+ <string id="21396">Kapitola</string>
+ <string id="21397">Vysoko kvalitý Pixel Shader V2</string>
+- <string id="21398">Povoliť playlist pri štarte</string>
+- <string id="21399">Použi trasúce animácie</string>
+- <string id="21400">obsahje</string>
+-
++ <string id="21398">NaÄítaÅ¥ playlist pri Å¡tarte</string>
++ <string id="21399">Použiť 'trasúce' animácie</string>
++ <string id="21400">obsahuje</string>
+ <string id="21401">neobsahuje</string>
+ <string id="21402">je</string>
+ <string id="21403">nie je</string>
+- <string id="21404">zaÄína s</string>
+- <string id="21405">konÄí s</string>
++ <string id="21404">zaÄína na</string>
++ <string id="21405">konÄí na</string>
+ <string id="21406">viac ako</string>
+-
+ <string id="21407">menej ako</string>
+- <string id="21408">potom</string>
+- <string id="21409">predtým</string>
+- <string id="21410">nakoniec</string>
+- <string id="21411">vôbec nie</string>
++ <string id="21408">násl.</string>
++ <string id="21409">pred.</string>
++ <string id="21410">je posledný</string>
++ <string id="21411">nie je posledný</string>
+ <string id="21412">SÅ¥ahovaÄe</string>
+-
+ <string id="21413">Predvolený sÅ¥ahovaÄ pre filmy</string>
+ <string id="21414">Predvolený sÅ¥ahovaÄ pre TV seriály</string>
+ <string id="21415">Predvolený sÅ¥ahovaÄ pre klipy</string>
+- <string id="21416">PovoliÅ¥ odpoveÄ na základe jazyka sÅ¥ahovaÄa</string>
+- <string id="21417">- Nastavenia</string>
++ <string id="21416">AktivovaÅ¥ odpoveÄ na základe jazyka sÅ¥ahovaÄa</string>
++ <string id="21417">- Nastavenie</string>
+ <string id="21418">ViacjazyÄný</string>
+-
+ <string id="21419">Žiadne sÅ¥ahovaÄe nie sú k dispozícii</string>
+- <string id="21420">Nájst hodnotu</string>
++ <string id="21420">Nájsť hodnotu</string>
+ <string id="21421">Pravidlá inteligentného playlist-u</string>
+- <string id="21422">Zhodné položky kde</string>
++ <string id="21422">Položky, kde</string>
+ <string id="21423">Nové pravidlo...</string>
+ <string id="21424">Položky sa musia zhodovať</string>
+-
+- <string id="21425">všetky pravidlá</string>
+- <string id="21426">jedno alebo viac pravidiel</string>
+- <string id="21427">S limitom</string>
++ <string id="21425">vo všetkých pravidlách</string>
++ <string id="21426">v minimálne jednom pravidle</string>
++ <string id="21427">Limit</string>
+ <string id="21428">Bez limitu</string>
+ <string id="21429">Usporiadať podľa</string>
+- <string id="21430">stúpajúci</string>
+-
+- <string id="21431">klesajúci</string>
++ <string id="21430">vzostupne</string>
++ <string id="21431">zostupne</string>
+ <string id="21432">Upraviť inteligentný playlist</string>
+ <string id="21433">Názov playlist-u</string>
+- <string id="21434">Hľadaj položky kde</string>
++ <string id="21434">Nájsť položky, kde</string>
+ <string id="21435">Upraviť</string>
+ <string id="21436">%i položiek</string>
+-
+ <string id="21437">Nový inteligentný playlist...</string>
+ <string id="21438">%c disk</string>
+- <string id="21439">Upraviť párty mód pravidlá</string>
+- <string id="21440">Domáci prieÄinok</string>
+- <string id="21441">PoÄet sledovaní</string>
++ <string id="21439">Upraviť pravidlá párty režimu</string>
++ <string id="21440">Domovský prieÄinok</string>
++ <string id="21441">PoÄet zhliadnutí</string>
+ <string id="21442">Názov epizódy</string>
+-
+ <string id="21443">Rozlíšenie videa</string>
+ <string id="21444">PoÄet audio kanálov</string>
+ <string id="21445">Kodek videa</string>
+ <string id="21446">Koded audia</string>
+- <string id="21447">Jayzk audia</string>
++ <string id="21447">Jazyk audia</string>
+ <string id="21448">Jazyk titulkov</string>
+-
+- <string id="21449">Diaľkový ovládaÄ emuluje stlaÄenia klávesnice</string>
++ <string id="21449">Emulovať klávesnicu diaľkovým ovládaním</string>
+ <string id="21450">- Upraviť</string>
+- <string id="21451">Je vyžadované internetové pripojenie.</string>
+- <string id="21452">Získať Viac...</string>
+- <string id="21453">Koreňový adresár súborového systému</string>
+-
++ <string id="21451">Vyžadované internetové pripojenie.</string>
++ <string id="21452">Získať viac...</string>
++ <string id="21453">Hlavný prieÄinok súborového systému</string>
++ <string id="21454">Medzipamäť plná</string>
++ <string id="21455">Medzipamäť plná pred dosiahnutím potrebného množstva pre nepretržité prehrávanie</string>
+ <string id="21460">Umiestnenie titulkov</string>
+- <string id="21461">Fixné</string>
++ <string id="21461">Pevné</string>
+ <string id="21462">V spodnej Äasti videa</string>
+ <string id="21463">Pod videom</string>
+ <string id="21464">V hornej Äasti videa</string>
+ <string id="21465">Nad videom</string>
+-
+ <string id="21800">Názov súboru</string>
+-
+- <string id="21801">Cesta k súboru</string>
++ <string id="21801">Umiestnenie súboru</string>
+ <string id="21802">Veľkosť súboru</string>
+ <string id="21803">Dátum/Äas súboru</string>
+ <string id="21804">Číslo stránky</string>
+ <string id="21805">Rozlíšenie</string>
+- <string id="21806">Poznámka</string>
+-
+- <string id="21807">Farba/Äb</string>
+- <string id="21808">Jpeg spracovanie</string>
++ <string id="21806">Komentár</string>
++ <string id="21807">Farba/Č&amp;B</string>
++ <string id="21808">JPEG spracovanie</string>
+ <string id="21820">Dátum/Äas</string>
+ <string id="21821">Popis</string>
+ <string id="21822">ZnaÄka fotoaparátu</string>
+ <string id="21823">Typ fotoaparátu</string>
+-
+- <string id="21824">Poznámka exif </string>
++ <string id="21824">EXIF poznámka</string>
+ <string id="21825">Firmware</string>
+ <string id="21826">Clona</string>
+ <string id="21827">Ohnisková vzdialenosť</string>
+- <string id="21828">Vzdialenosť ohniska</string>
++ <string id="21828">Zaostrenie</string>
+ <string id="21829">Expozícia</string>
+-
+ <string id="21830">Čas expozície</string>
+- <string id="21831">Úprava expozície</string>
++ <string id="21831">Expozícia / bias</string>
+ <string id="21832">Spôsob expozície</string>
+ <string id="21833">Použitý blesk</string>
+ <string id="21834">Vyváženie bielej</string>
+ <string id="21835">Zdroj svetla</string>
+-
+- <string id="21836">Režim merania</string>
++ <string id="21836">Metering režim</string>
+ <string id="21837">ISO</string>
+ <string id="21838">Digitálne priblíženie</string>
+ <string id="21839">CCD šírka</string>
+ <string id="21840">GPS šírka</string>
+ <string id="21841">GPS dĺžka</string>
+-
+ <string id="21842">GPS výška</string>
+ <string id="21843">Orientácia</string>
+ <string id="21860">Doplnkové kategórie</string>
+ <string id="21861">KľuÄové slová</string>
+ <string id="21862">Titul</string>
+ <string id="21863">Autor</string>
+-
+- <string id="21864">Titulok</string>
++ <string id="21864">Nadpis</string>
+ <string id="21865">Špeciálne inštrukcie</string>
+ <string id="21866">Kategória</string>
+ <string id="21867">Podtitul</string>
+- <string id="21868">Podtitul titulu</string>
++ <string id="21868">Názov podtitulu</string>
+ <string id="21869">Hodnotenie</string>
+-
+ <string id="21870">Zdroj</string>
+ <string id="21871">Upozornenie o autorských právach</string>
+ <string id="21872">Názov objektu</string>
+ <string id="21873">Mesto</string>
+ <string id="21874">Štát</string>
+ <string id="21875">Krajina</string>
+-
+- <string id="21876">Oficiálny text distribútora</string>
++ <string id="21876">Pôvodná Tx Referencia</string>
+ <string id="21877">Dátum vytvorenia</string>
+ <string id="21878">OznaÄenie autorských práv</string>
+ <string id="21879">Kód krajiny</string>
+ <string id="21880">ReferenÄná služba</string>
+ <string id="21881">Povoliť ovládanie XBMC cez UPnP</string>
+-
+ <string id="21882">Pokus o preskoÄenie úvodu pred DVD menu</string>
+- <string id="21883">Skopírované audio CD</string>
+- <string id="21884">Vyhľadať informácie o všetkých interprétoch</string>
++ <string id="21883">Uložená hudba</string>
++ <string id="21884">Akualizovať informácie všetkých interprétov</string>
+ <string id="21885">Sťahujem informácie o albume</string>
+ <string id="21886">Sťahujem informácie o interprétovy</string>
+ <string id="21887">Životopis</string>
+-
+ <string id="21888">Diskografia</string>
+ <string id="21889">Hľadanie interpréta</string>
+- <string id="21890">Výber interpréta</string>
+- <string id="21891">Informácie interpréta</string>
++ <string id="21890">Vyberte interpréta</string>
++ <string id="21891">Informácie o interprétovy</string>
+ <string id="21892">Nástroje</string>
+- <string id="21893">Narodený</string>
+-
+- <string id="21894">Založený</string>
+- <string id="21895">Žánre</string>
+- <string id="21896">Rozpadnutý</string>
+- <string id="21897">Zomrel</string>
++ <string id="21893">Dátum narodenia</string>
++ <string id="21894">Dátum založenia</string>
++ <string id="21895">Zameranie</string>
++ <string id="21896">Dátum rozpadu</string>
++ <string id="21897">Dátum smrti</string>
+ <string id="21898">Aktívne roky</string>
+- <string id="21899">Názov</string>
+-
+- <string id="21900">Narodený/Založený</string>
++ <string id="21899">Label</string>
++ <string id="21900">Dátum Narodenia/Založenia</string>
+ <string id="22000">Aktualizovať knižnicu pri štarte</string>
+- <string id="22001">Vždy aktualizovať knižnicu na pozadí</string>
+- <string id="22002">- DNS prípona</string>
++ <string id="22001">Aktualizovať knižnicu na pozadí / Skryť proces aktualizácie</string>
++ <string id="22002">- DNS suffix</string>
+ <string id="22003">%2.3fs</string>
+ <string id="22004">Oneskorené o %2.3fs</string>
+-
+ <string id="22005">Vpredu o %2.3fs</string>
+- <string id="22006">Oneskorenie titulkov</string>
+- <string id="22007">OpenGL výrobca:</string>
+- <string id="22008">OpenGL karta:</string>
++ <string id="22006">Posunutie titulkov</string>
++ <string id="22007">OpenGL vendor:</string>
++ <string id="22008">OpenGL renderer:</string>
+ <string id="22009">OpenGL verzia:</string>
+- <string id="22010">Teplota GPU</string>
+-
+- <string id="22011">Teplota CPU</string>
++ <string id="22010">Teplota GPU:</string>
++ <string id="22011">Teplota CPU:</string>
+ <string id="22012">Celková Pamäť</string>
+ <string id="22013">Údaje profilu</string>
+- <string id="22014">PovoliÅ¥ stlmenie obrazovky poÄas pauzy videa</string>
++ <string id="22014">AktivovaÅ¥ stlmenie obrazovky poÄas pozastaveného videa</string>
+ <string id="22015">Všetky nahrávky</string>
+ <string id="22016">Podľa názvu</string>
+-
+ <string id="22017">Podľa skupiny</string>
+ <string id="22018">Živé kanály</string>
+ <string id="22019">Nahrávky podľa názvu</string>
+ <string id="22020">Návod</string>
+ <string id="22021">Odchylka pomeru strán k minimalizácii Äiernych okrajov</string>
+- <string id="22022">Zobraziť videá v zozname súborov</string>
+-
+- <string id="22023">DirectX výrobca:</string>
++ <string id="22022">Zobrazovať videá v zozname súborov</string>
++ <string id="22023">DirectX:</string>
+ <string id="22024">Direct3D verzia:</string>
+ <string id="22030">Font</string>
+ <string id="22031">- Veľkosť</string>
+ <string id="22032">- Farba</string>
+ <string id="22033">- Znaková sada</string>
+-
+- <string id="22034">Exportovať do HTML</string>
+- <string id="22035">Exportovať do CSV</string>
++ <string id="22034">Exportovať karaoke tituly do HTML</string>
++ <string id="22035">Exportovať karaoke tituly do CSV</string>
+ <string id="22036">Importovať karaoke tituly...</string>
+- <string id="22037">Automaticky zobraziť dialóg pre výber skladby</string>
++ <string id="22037">Automaticky zobrazovať okno výberu skladby</string>
+ <string id="22038">Exportovať karaoke tituly...</string>
+- <string id="22039">Vyber Äíslo skladby</string>
+-
++ <string id="22039">Zadajte Äíslo skladby</string>
+ <string id="22040">biela/zelená</string>
+ <string id="22041">biela/Äervená</string>
+ <string id="22042">biela/modrá</string>
+ <string id="22043">Äierna/biela</string>
+- <string id="22079">Predvolené akcie pri výbere položky</string>
+- <string id="22080">Zobraziť ponuku</string>
+-
++ <string id="22079">Predvolená akcia pri výbere položky</string>
++ <string id="22080">Vybrať</string>
+ <string id="22081">Zobraziť informácie</string>
+ <string id="22082">Viac...</string>
+ <string id="22083">Prehrať všetko</string>
+ <string id="23049">Teletext nie je k dispozícii</string>
+ <string id="23050">Povoliť teletext</string>
+ <string id="23051">Časť %i</string>
+-
+ <string id="23052">Prebieha naÄítanie %i bajtov</string>
+ <string id="23053">Prebieheha ukonÄovanie</string>
+ <string id="23054">Prebieha spustenie</string>
+- <string id="23100">Externý PrehrávaÄ Je Aktívny</string>
++ <string id="23100">Externý PrehrávaÄ Aktívny</string>
+ <string id="23101">Kliknite OK pre zatvorenie prehrávaÄa</string>
+ <string id="23104">Kliknite OK po ukonÄení prehrávania</string>
+-
+ <string id="24000">Doplnok</string>
+ <string id="24001">Doplnky</string>
+ <string id="24002">Nastavenia doplnku</string>
+- <string id="24003">Informácie doplnku</string>
++ <string id="24003">Informácie o doplnku</string>
+ <string id="24005">Zdroje médií</string>
+ <string id="24007">Informácie o filme</string>
+-
+ <string id="24008">Å etriÄ obrazovky</string>
+ <string id="24009">Skript</string>
+ <string id="24010">Vizualizácia</string>
+ <string id="24011">Repozitár doplnkov</string>
+ <string id="24012">Titulky</string>
+ <string id="24013">Texty skladieb</string>
+-
+- <string id="24014">Informácie o TV seriáli</string>
++ <string id="24014">Informácie o TV</string>
+ <string id="24015">Informácie o klipe</string>
+ <string id="24016">Informácie o albume</string>
+ <string id="24017">Informácie o interprétovy</string>
+ <string id="24018">Služby</string>
+- <string id="24020">Nastavenie</string>
+-
++ <string id="24020">Nastaviť</string>
+ <string id="24021">Deaktivovať</string>
+ <string id="24022">Aktivovať</string>
+ <string id="24023">Doplnok je deaktivovaný</string>
+ <string id="24027">PoÄasie</string>
+ <string id="24028">Weather.com (štandardný)</string>
+- <string id="24030">Tento doplnok nejde konfigurovať</string>
+-
++ <string id="24030">Tento doplnok sa nedá nastaviť</string>
+ <string id="24031">Chyba pri naÄítaní konfigurácie nastavenia</string>
+- <string id="24032">VÅ¡etky Doplnky</string>
+- <string id="24033">Získať Doplnky</string>
++ <string id="24032">VÅ¡etky doplnky</string>
++ <string id="24033">Získať doplnky</string>
+ <string id="24034">Skontrolovať aktualizácie</string>
+ <string id="24035">Vynútiť obnovu</string>
+ <string id="24036">Zmeny</string>
+-
+ <string id="24037">Odinštalovať</string>
+- <string id="24038">Nainštalovať</string>
++ <string id="24038">Inštalovať</string>
+ <string id="24039">Deaktivované Doplnky</string>
+ <string id="24040">(VyÄistiÅ¥ aktuálne nastavenia)</string>
+ <string id="24041">Inštalovať doplnky zo zip archívu</string>
+ <string id="24042">Stiahnuté %i%%</string>
+-
+ <string id="24043">Dostupné aktualizácie</string>
+ <string id="24044">Závislosti sa nestretli</string>
+ <string id="24045">Doplnok nemá správnu štruktúru</string>
+ <string id="24046">%s je použitá následovným nainštalovaným doplnkom</string>
+ <string id="24047">Tento doplnok sa nedá odinštalovať</string>
+- <string id="24048">Návrat</string>
+-
++ <string id="24048">Obnoviť zmeny</string>
+ <string id="24050">Dostupné doplnky</string>
+ <string id="24051">Verzia:</string>
+ <string id="24052">Upozornenie</string>
+ <string id="24053">Licencia:</string>
+ <string id="24054">Zmeny</string>
+- <string id="24059">Želáte si aktivovať tento doplnok?</string>
+-
+- <string id="24060">Želáte si deaktivovať tento doplnok?</string>
+- <string id="24061">Je k dispozícii aktualizácia doplnku!</string>
++ <string id="24059">Prajete si aktivovať tento doplnok?</string>
++ <string id="24060">Prajete si deaktivovať tento doplnok?</string>
++ <string id="24061">Aktualizácia doplnku je k dispozícii!</string>
+ <string id="24062">Aktivované Doplnky</string>
+ <string id="24063">Auto. aktualizácie</string>
+ <string id="24064">Doplnok bol aktivovaný</string>
+ <string id="24065">Doplnok bol aktualizovaný</string>
+-
+ <string id="24066">Naozaj chcete zrušiť sťahovanie doplnku?</string>
+ <string id="24067">Momentálne sťahované doplnky</string>
+ <string id="24068">Aktualizácia k dispozícii</string>
+ <string id="24069">Aktualizovať</string>
+ <string id="24070">NaÄítanie doplnku sa nepodarilo.</string>
+ <string id="24071">Objavila sa neznáma chyba.</string>
+-
+ <string id="24072">Vyžadovaná zmena nastavení</string>
+ <string id="24073">Nedá sa pripojiť</string>
+ <string id="24074">Je vyžadovaný reštart</string>
+@@ -2391,130 +2062,151 @@
+ <string id="24076">Potrebný doplnok</string>
+ <string id="24080">Pokúsiť sa o opätovné pripojenie?</string>
+ <string id="24089">Doplnok sa reštartuje</string>
+-
+- <string id="24090">Uzamknutie správcu doplnkov</string>
++ <string id="24090">Zamknúť správcu doplnkov</string>
+ <string id="24094">(aktuálny)</string>
+ <string id="24095">(na Äiernej listine)</string>
+ <string id="24096">Doplnok bol v repozitáry oznaÄený ako nefunkÄný.</string>
+ <string id="24097">Chcete ho deaktivovať?</string>
+ <string id="24098">NefunkÄný</string>
+-
+- <string id="24099">Želáte si použiť tento skin?</string>
+- <string id="24100">Pre použite tejto funkcie potrebujete stiahnuť Doplnok:</string>
+- <string id="24101">Želáte si stiahnuť tento doplnok?</string>
++ <string id="24099">Prajete si použiť tento vzhľad?</string>
++ <string id="24100">Pre používanie tejto funkcie je potrebné stiahnuť doplnok:</string>
++ <string id="24101">Prajete si stiahnuť tento doplnok?</string>
+ <string id="25000">Notifikácie</string>
+- <string id="29800">KnižniÄný mód</string>
++ <string id="29800">Režim knižnice</string>
+ <string id="29801">QWERTY klávesnica</string>
+- <string id="29802">Audio je preposielané receiveru</string>
+- <string id="33001">Kvalita traileru</string>
+-
++ <string id="29802">Preposielané audio</string>
++ <string id="33001">Kvalita ukážky</string>
+ <string id="33002">Streamovať</string>
+ <string id="33003">Stiahnuť</string>
+ <string id="33004">Stiahnuť a prehrať</string>
+ <string id="33005">Stiahnuť a uložiť</string>
+ <string id="33006">Dnes</string>
+ <string id="33007">Zajtra</string>
+-
+ <string id="33008">Prebieha ukladanie</string>
+ <string id="33009">Prebieha kopírovanie</string>
+- <string id="33010">Nastaviť adresár pre sťahovanie</string>
+- <string id="33011">Doba vyhladávania</string>
++ <string id="33010">NastaviÅ¥ prieÄinok pre sÅ¥ahovanie</string>
++ <string id="33011">Doba vyhľadávania</string>
+ <string id="33012">Krátky</string>
+ <string id="33013">Dlhý</string>
+-
+- <string id="33014">PoužiÅ¥ DVD prehrávaÄ namiesto bežného prehrávaÄa</string>
++ <string id="33014">PoužívaÅ¥ DVD prehrávaÄ namiesto bežného prehrávaÄa</string>
+ <string id="33015">Pred prehrávaním videa sa spýtať na jeho stiahnutie</string>
+ <string id="33016">Klipy</string>
+ <string id="33017">Pre aktiváciu reštartujte doplnok</string>
+ <string id="33018">Dnes veÄer</string>
+ <string id="33019">Zajtra veÄer</string>
+-
+ <string id="33020">Stav</string>
+ <string id="33021">Zrážky</string>
+ <string id="33022">Zrážky</string>
+ <string id="33023">Vlhkosť</string>
+- <string id="33024">zdá sa ako</string>
++ <string id="33024">Zdá sa ako</string>
+ <string id="33025">Pozorované</string>
+-
+ <string id="33026">Odchylka od normálu</string>
+ <string id="33027">Východ slnka</string>
+ <string id="33028">Západ slnka</string>
+ <string id="33029">Detaily</string>
+- <string id="33030">Outlook</string>
++ <string id="33030">Výhľad</string>
+ <string id="33031">Coverflow</string>
+-
+ <string id="33032">Preložiť text</string>
+ <string id="33033">Namapovať zoznam %s kategórie</string>
+ <string id="33034">36 Hodín</string>
+ <string id="33035">Mapy</string>
+ <string id="33036">Hodinu</string>
+ <string id="33037">Víkend</string>
+-
+ <string id="33038">%s deň</string>
+ <string id="33049">Upozornenie</string>
+ <string id="33050">Upozornenia</string>
+ <string id="33051">Vyberte Vašu</string>
+ <string id="33052">Zkontrolovať</string>
+ <string id="33053">Nastaviť</string>
+-
+ <string id="33054">Sezóny</string>
+ <string id="33055">Použiť</string>
+- <string id="33056">Sledovať</string>
++ <string id="33056">Pozerať</string>
+ <string id="33057">PoÄúvaÅ¥</string>
+ <string id="33058">Prezerať</string>
+- <string id="33059">Konfigurovať</string>
+-
++ <string id="33059">Nastaviť</string>
+ <string id="33060">Vypnutie</string>
+ <string id="33061">Menu</string>
+ <string id="33062">Prehrať</string>
+ <string id="33063">Nastavenia</string>
+ <string id="33065">Editor</string>
+ <string id="33066">O. . .</string>
+-
+ <string id="33067">Hodnotenie</string>
+ <string id="33068">Pozadie</string>
+ <string id="33069">Pozadia</string>
+ <string id="33070">Vlastné pozadie</string>
+ <string id="33071">Vlastné pozadia</string>
+ <string id="33072">Zobraziť Readme</string>
+-
+ <string id="33073">Zobraziť Históriu zmien</string>
+- <string id="33074">Pre spustenie tejto verzie %s je vyžadovaná</string>
++ <string id="33074">Pre spustenie tejto verzie %s je potrebná</string>
+ <string id="33075">XBMC revízia %s alebo vyššia.</string>
+ <string id="33076">Prosím aktualizujte XBMC.</string>
+ <string id="33077">Žiadne dáta neboly nájdené!</string>
+ <string id="33078">Ďalšia strana</string>
+-
+ <string id="33079">Milovať</string>
+ <string id="33080">Nenávidieť</string>
+- <string id="33081">Tento súbor sa skladá z viac Äastí. Vyberte tu, od ktorej chcete spustit prehrávanie.</string>
+- <string id="33082">Cesta ku skriptu</string>
++ <string id="33081">Tento súbor sa skladá z viacero Äastí. Vyberte tú, od ktorej chcete spustiÅ¥ prehrávanie.</string>
++ <string id="33082">Umiestnenie skriptu</string>
+ <string id="33083">PovoliÅ¥ vlastné tlaÄítko skriptu</string>
+ <string id="33100">Nepodarilo sa zaÄaÅ¥</string>
+-
+ <string id="33101">Webserver</string>
+- <string id="33102">Server Udalostí</string>
++ <string id="33102">Server udalostí</string>
+ <string id="33103">Vzdialený komunikaÄný server</string>
++ <string id="33200">Zistené nové pripojenie</string>
+ <string id="34000">Lame</string>
+ <string id="34001">Vorbis</string>
+ <string id="34002">Wav</string>
+-
+ <string id="34003">DXVA2</string>
+ <string id="34004">VAAPI</string>
+ <string id="34005">Flac</string>
+- <string id="34100">Nastavenie Reproduktorov</string>
++ <string id="34100">Nastavenie reproduktorov</string>
+ <string id="34101">2.0</string>
+ <string id="34102">2.1</string>
+-
+ <string id="34103">3.0</string>
+ <string id="34104">3.1</string>
+ <string id="34105">4.0</string>
+ <string id="34106">4.1</string>
+ <string id="34107">5.0</string>
+ <string id="34108">5.1</string>
+-
+ <string id="34109">7.0</string>
+ <string id="34110">7.1</string>
+- <string id="34201">Nie je možné nájsÅ¥ ÄalÅ¡iu položku pre prehratie</string>
+- <string id="34202">Nie je možné nájsť predošlú položku pre prehratie</string>
+-</strings>
++ <string id="34201">Nepodarilo sa nájsÅ¥ ÄalÅ¡iu položku</string>
++ <string id="34202">Nepodarilo sa nájsť predošlú položku</string>
++ <string id="34300">Spustenie zeroconf zlyhalo</string>
++ <string id="34301">Je Apple Bonjour služba nainštalovaná? Pozri log pre viac informácií.</string>
++ <string id="34400">Vykreslovanie videa</string>
++ <string id="34401">Video filtrovanie/škálovanie zlyhalo, použitie bilineárneho škálovania.</string>
++ <string id="34402">Nepodarilo sa inicializovať audio zariadenia</string>
++ <string id="34403">Skontrolujte Vaše audio nastavenia</string>
++ <string id="35000">Periférne zariadenia</string>
++ <string id="35001">HID zariadenie</string>
++ <string id="35002">Sieťová karta</string>
++ <string id="35003">Pevný disk</string>
++ <string id="35004">Pre toto periférne zariadenie nie sú žiadne nastavenia dostupné.</string>
++ <string id="35005">Nové zariadenie nakonfigurované</string>
++ <string id="35006">Zariadenie odstránené</string>
++ <string id="35007">Rozloženie kláves pre toto zariadenie</string>
++ <string id="35008">Rozloženie kláves povolené</string>
++ <string id="35500">Umiestnenie</string>
++ <string id="35501">Trieda</string>
++ <string id="35502">Meno</string>
++ <string id="35503">Výrobca</string>
++ <string id="35504">ID produktu</string>
++ <string id="36000">Pulse-Eight CEC adaptér</string>
++ <string id="36001">Pulse-Eight Nyxboard</string>
++ <string id="36002">Prepnúť na príkazy klávesnice</string>
++ <string id="36003">Prepnúť na príkazy diaľkového ovládania</string>
++ <string id="36004">StlaÄte tlaÄidlo príkazu 'užívateľ' (USER)</string>
++ <string id="36005">Aktivovať prepnutie strán príkazov</string>
++ <string id="36006">Adaptér sa nedá otvoriť</string>
++ <string id="36007">Zapnúť TV pri spúšťaní XBMC</string>
++ <string id="36008">Vypnúť zariadenia pri ukonÄení XBMC</string>
++ <string id="36009">AktivovaÅ¥ pohotovostný režim zariadení pri spustení Å¡etriÄa obrazovky</string>
++ <string id="36011">Nepodarilo sa zistiť CEC port. Nastavte ho manuálne.</string>
++ <string id="36012">Nepodarilo sa zistiť CEC adaptér.</string>
++ <string id="36013">Nepodporovaná verzia libcec rozhrania. %d je vyššia ako XBMC podporuje (%d)</string>
++ <string id="36014">Aktivovať pohotovstný režim PC pri vypnutí TV</string>
++ <string id="36015">HDMI portu</string>
++ <string id="36016">Pripojené</string>
++ <string id="36017">Adaptér nájdený, libcec nie je dostupný</string>
++ <string id="36018">Použiť nastavenia jazyku TV</string>
++</strings>
+\ No newline at end of file
+diff --git a/language/Slovenian/strings.xml b/language/Slovenian/strings.xml
+index 8f8d425..b2a1de9 100644
+--- a/language/Slovenian/strings.xml
++++ b/language/Slovenian/strings.xml
+@@ -1013,6 +1013,8 @@
+ <string id="13014">Pomanjšaj</string>
+ <string id="13015">Ukaz gumba za izklop</string>
+ <string id="13016">IzkljuÄi sistem</string>
++ <string id="13017">Spodbudi izklop ob nedejavnosti</string>
++ <string id="13018">OmogoÄi izklop ob nedejavnosti</string>
+
+ <string id="13020">Je druga seja aktivna, ssh?</string>
+ <string id="13021">PrikljuÄen zunanji trdi disk</string>
+@@ -1543,12 +1545,459 @@
+ <string id="16322">Spline36</string>
+ <string id="16323">Spline36 optimized</string>
+ <string id="16324">Programski Blend</string>
++ <string id="16325">Samodejno - Optimizirano za ION</string>
+
+ <string id="16400">Post-procesiranje</string>
+
+ <string id="17500">Prikaži Äas to zamaknjenega izklopa</string>
+
+- <string id="19000">Preklopi na kanal</string>
++ <string id="17997">%i MBajtov</string>
++ <string id="17998">%i ur</string>
++ <string id="17999">%i dni</string>
++
++ <!-- strings 19000 thru 19999 used for tv interface -->
++ <string id="19000">Preklopi na program</string>
++ <string id="19001">LoÄite iskalne besede z AND (in), OR (ali) in/ali NOT (brez).</string>
++ <string id="19002">ali uporabite celotne fraze, kot je "ÄŒarovnik iz Oza".</string>
++ <string id="19003">PoiÅ¡Äi podobne oddaje</string>
++ <string id="19004">Uvažam EPG iz odjemalcev</string>
++ <string id="19005">Informacija o pretoku PVR</string>
++ <string id="19006">Naprava zajema</string>
++ <string id="19007">Status naprave</string>
++ <string id="19008">Kakovost signala</string>
++ <string id="19009">SNR</string>
++ <string id="19010">BER</string>
++ <string id="19011">UNC</string>
++ <string id="19012">Hrbtenica PVR</string>
++ <string id="19013">BrezplaÄno</string>
++ <string id="19014">Fiksno</string>
++ <string id="19015">Enkripcija</string>
++ <string id="19016">Hrbtenica PVR %i - %s</string>
++ <string id="19017">Posnetki TV</string>
++ <string id="19018">Privzeta mapa za sliÄice PVR</string>
++ <string id="19019">Programi</string>
++ <string id="19020">TV</string>
++ <string id="19021">Radio</string>
++ <string id="19022">Skrito</string>
++ <string id="19023">TV programi</string>
++ <string id="19024">Radio programi</string>
++ <string id="19025">PrihajajoÄa snemanja</string>
++ <string id="19026">Dodaj Äasovnik...</string>
++ <string id="19027">Ni rezultatov iskanja</string>
++ <string id="19028">Ni vnosov EPG</string>
++ <string id="19029">Program</string>
++ <string id="19030">Trenutno</string>
++ <string id="19031">Naslednje</string>
++ <string id="19032">ÄŒasovni trak</string>
++ <string id="19033">Informacije</string>
++ <string id="19034">Snemanje na tem programu se je že priÄelo</string>
++ <string id="19035">Tega programa ni mogoÄe predvajati. Preverite zapisnik za podrobnosti.</string>
++ <string id="19036">Tega posnetka ni mogoÄe predvajati. Preverite zapisnik za podrobnosti.</string>
++ <string id="19037">Prikaži kakovost signala</string>
++ <string id="19038">Ni podprto s strani hrbtenice PVR.</string>
++ <string id="19039">Ste prepriÄani, da želite skriti ta program?</string>
++ <string id="19040">ÄŒasovnik</string>
++ <string id="19041">Ste prepriÄani, da želite preimenovati ta posnetek?</string>
++ <string id="19042">Ste prepriÄani, da želite preimenovati ta posnetek?</string>
++ <string id="19043">Snemanje</string>
++ <string id="19044">Preverite svoje nastavitve ali pa poiÅ¡Äite veÄ informacij v zapisniku.</string>
++ <string id="19045">Noben odjemalec PVR Å¡e ni bil zagnan. PoÄakajte na odjemalce PVR ali pa poiÅ¡Äite veÄ informacij v zapisniku.</string>
++ <string id="19046">Nov program</string>
++ <string id="19047">Informacija o oddaji</string>
++ <string id="19048">Upravljanje skupin</string>
++ <string id="19049">Prikaži program</string>
++ <string id="19050">Prikaži vidne programe</string>
++ <string id="19051">Prikaži skrite programe</string>
++ <string id="19052">Premakni program v:</string>
++ <string id="19053">Informacija o posnetku</string>
++ <string id="19054">Skrij program</string>
++ <string id="19055">Informacija ni na voljo</string>
++ <string id="19056">Nov Äasovnik</string>
++ <string id="19057">Uredi Äasovnik</string>
++ <string id="19058">ÄŒasovnik vkljuÄen</string>
++ <string id="19059">Ustavi snemanje</string>
++ <string id="19060">IzbriÅ¡i Äasovnik</string>
++ <string id="19061">Dodaj Äasovnik</string>
++ <string id="19062">Razvrsti: Program</string>
++ <string id="19063">Pojdi na zaÄetek</string>
++ <string id="19064">Pojdi na konec</string>
++ <string id="19065">Privzeto okno EPG</string>
++ <string id="19066">Prenašam posnetke z odjemalcev</string>
++ <string id="19067">Ta oddaja je že bila posneta.</string>
++ <string id="19068">Posnetka ni mogoÄe izbrisati. Preverite zapisnik za podrobnosti.</string>
++ <string id="19069">EPG</string>
++ <string id="19070">ÄŒas iskanja EPG</string>
++ <string id="19071">Interval posodabljanja EPG</string>
++ <string id="19072">Ne skranjuj EPG v bazi</string>
++ <string id="19073">Zamakni preklop programa</string>
++ <string id="19074">Aktivno:</string>
++ <string id="19075">Ime:</string>
++ <string id="19076">Mapa:</string>
++ <string id="19077">Radio:</string>
++ <string id="19078">Program:</string>
++ <string id="19079">Dan:</string>
++ <string id="19080">ZaÄetek:</string>
++ <string id="19081">Konec:</string>
++ <string id="19082">Prioriteta:</string>
++ <string id="19083">Življenjska doma (dnevi):</string>
++ <string id="19084">Prvi dan:</string>
++ <string id="19085">Neznan program %u</string>
++ <string id="19086">Po-__-__-__-__-__-__</string>
++ <string id="19087">__-To-__-__-__-__-__</string>
++ <string id="19088">__-__-Sr-__-__-__-__</string>
++ <string id="19089">__-__-__-ÄŒe-__-__-__</string>
++ <string id="19090">__-__-__-__-Pe-__-__</string>
++ <string id="19091">__-__-__-__-__-So-__</string>
++ <string id="19092">__-__-__-__-__-__-Ne</string>
++ <string id="19093">Po-To-Sr-ÄŒe-Pe-__-__</string>
++ <string id="19094">Po-To-Sr-ÄŒe-Pe-So-__</string>
++ <string id="19095">Po-To-Sr-ÄŒe-Pe-So-Ne</string>
++ <string id="19096">__-__-__-__-__-So-Ne</string>
++ <string id="19097">Vnesite ime za to snemanje</string>
++ <string id="19098">Opozorilo</string>
++ <string id="19099">Prisoten Äasovnik</string>
++ <string id="19100">Ste prepriÄani, da želite izbrisati ta program, vkljuÄno z vsemi njegovimi Äasovniki?</string>
++ <string id="19101">Ta program se trenutno predvaja.</string>
++ <string id="19102">Preklopite na drug program.</string>
++ <string id="19103">PoiÅ¡Äi manjkajoÄe ikone</string>
++ <string id="19104">Vnesite ime mape za snemanje</string>
++ <string id="19105">Velikost:</string>
++ <string id="19106">Naslednji Äasovnik</string>
++ <string id="19107">ob</string>
++ <string id="19108">Snemanja niso sinhronizirana. Preverite zapisnik za podrobnosti.</string>
++ <string id="19109">ÄŒasovnika ni mogoÄe shraniti. Preverite zapisnik za podrobnosti.</string>
++ <string id="19110">Zgodila se je nepriÄakovana napaka. Poskusite znova ali pa poiÅ¡Äite veÄ informacij v zapisniku.</string>
++ <string id="19111">Napaka v hrbtenici PVR. Preverite zapisnik za podrobnosti.</string>
++ <string id="19112">ÄŒasovniki niso sinhronizirani. Preverite zapisnik za podrobnosti.</string>
++ <string id="19113">Naslednje</string>
++ <string id="19114">RazliÄica</string>
++ <string id="19115">Naslov</string>
++ <string id="19116">Velikost</string>
++ <string id="19117">IÅ¡Äi programe</string>
++ <string id="19118">PVR funkcij ni mogoÄe uporabljati med iskanjem.</string>
++ <string id="19119">Na katerem strežniku želite iskati?</string>
++ <string id="19120">Å tevilka odjemalca</string>
++ <string id="19121">Izogni se ponavljanjem</string>
++ <string id="19122">ÄŒasovnik se Å¡e vedno snema. Ste prepriÄani, da ga želite izbrisati?</string>
++ <string id="19123">Samo brezplaÄni programi</string>
++ <string id="19124">Zanemari prisotne Äasovnike</string>
++ <string id="19125">Zanemari prisotne posnetke</string>
++ <string id="19126">ZaÄetni Äas</string>
++ <string id="19127">KonÄni Äas</string>
++ <string id="19128">ZaÄetni datum</string>
++ <string id="19129">KonÄni datum</string>
++ <string id="19130">Najmanjše trajanje</string>
++ <string id="19131">NajveÄje trajanje</string>
++ <string id="19132">VkljuÄi neznane žanre</string>
++ <string id="19133">KljuÄne besede</string>
++ <string id="19134">VkljuÄi opis</string>
++ <string id="19135">ObÄutljivo na velikosti Ärk</string>
++ <string id="19136">Program ni na voljo</string>
++ <string id="19137">Ni definiranih skupin</string>
++ <string id="19138">Najprej ustvarite skupino</string>
++ <string id="19139">Ime nove skupine</string>
++ <string id="19141">Skupina</string>
++ <string id="19142">PreiÅ¡Äi vodiÄ</string>
++ <string id="19143">Upravljanje skupin</string>
++ <string id="19144">Ni definiranih skupin</string>
++ <string id="19145">V skupini</string>
++ <string id="19146">Skupine</string>
++ <string id="19147">Hrbtenica PVR ne podpira tega ukaza. Preverite zapisnik za podrobnosti.</string>
++ <string id="19148">Program</string>
++ <string id="19149">Po</string>
++ <string id="19150">To</string>
++ <string id="19151">Sr</string>
++ <string id="19152">ÄŒe</string>
++ <string id="19153">Pe</string>
++ <string id="19154">So</string>
++ <string id="19155">Ne</string>
++ <string id="19156">od</string>
++ <string id="19157">Naslednje snemanje</string>
++ <string id="19158">Trenutno snemam</string>
++ <string id="19159">od</string>
++ <string id="19160">do</string>
++ <string id="19161">Na</string>
++ <string id="19162">Aktivno snemanje</string>
++ <string id="19163">Posnetki</string>
++ <string id="19164">Snemanja ni mogoÄe priÄeti. Preverite zapisnik za podrobnosti.</string>
++ <string id="19165">Preklopi</string>
++ <string id="19166">Informacije o PVR</string>
++ <string id="19167">PoiÅ¡Äi manjkajoÄe ikone</string>
++ <string id="19168">Preklopi programe brez pritiska na OK</string>
++ <string id="19169">Skrij informacijo o videu</string>
++ <string id="19170">Zamik pri zaÄetku predvajanja</string>
++ <string id="19171">Predvajanje priÄni pomanjÅ¡ano</string>
++ <string id="19172">Takošnje trajanje snemanja</string>
++ <string id="19173">Privzeta prioriteta snemanja</string>
++ <string id="19174">Privzeta življenjska doba snemanja</string>
++ <string id="19175">ObmoÄje na zaÄetku snemanja</string>
++ <string id="19176">ObmoÄje na koncu snemanja</string>
++ <string id="19177">Predvajanje</string>
++ <string id="19178">Prikažite informacijo o programu, ko prekljapljate med njimi</string>
++ <string id="19179">Samodejno skrij informacijo o programu</string>
++ <string id="19180">TV</string>
++ <string id="19181">Meni/OSD</string>
++ <string id="19182">KoliÄina prikazanih dni v EPG</string>
++ <string id="19184">Trajanje informacij o programu</string>
++ <string id="19185">Ponastavi bazo PVR</string>
++ <string id="19186">Vsi podatki v bazi PVR bodo izbrisani</string>
++ <string id="19187">Ponastavi bazo EPG</string>
++ <string id="19188">EPG se ponastavlja</string>
++ <string id="19189">Nadaljuj zadnji program ob zagonu</string>
++ <string id="19190">Minimirano</string>
++ <string id="19191">Storitev PVR</string>
++ <string id="19192">Nobena povezana hrbtenica PVR ne podpira iskanja programov.</string>
++ <string id="19193">Iskanja programov ni mogoÄe priÄeti. Preverite zapisnik za podrobnosti.</string>
++ <string id="19194">Nadaljujem?</string>
++ <string id="19195">Ukazi odjemalca</string>
++ <string id="19196">SpecifiÄni ukazi odjemalca PVR</string>
++ <string id="19197">Snemanje se je priÄelo: %s</string>
++ <string id="19198">Snemanje se je konÄalo: %s</string>
++ <string id="19199">Upravljalec programov</string>
++ <string id="19200">Vir EPG:</string>
++ <string id="19201">Ime programa:</string>
++ <string id="19202">Ikona programa:</string>
++ <string id="19203">Uredi program</string>
++ <string id="19204">Nov program</string>
++ <string id="19205">Upravljanje skupin</string>
++ <string id="19206">Aktiviran EPG:</string>
++ <string id="19207">Skupina:</string>
++ <string id="19208">Vnesite ime novega programa</string>
++ <string id="19209">Navidezna hrbtenica XBMC</string>
++ <string id="19210">Odjemalec</string>
++ <string id="19211">Izbriši program</string>
++ <string id="19212">Ta seznam vsebuje spremembe</string>
++ <string id="19213">Izberi hrbtenico</string>
++ <string id="19214">Vnesite veljaven URL za nov program</string>
++ <string id="19215">Hrbtenica PVR ne podpira Äasovnikov.</string>
++ <string id="19216">Vsi radijski progami</string>
++ <string id="19217">Vsi TV programi</string>
++ <string id="19218">Vidno</string>
++ <string id="19219">Programi brez skupine</string>
++ <string id="19220">Programi v</string>
++ <string id="19221">Sinhroniziraj skupine programov s hrbtenico</string>
++ <string id="19222">EPG</string>
++ <string id="19223">Nobenega dodatka PVR ni mogoÄe vkljuÄiti. Preverite svoje nastavitve ali pa poiÅ¡Äite veÄ informacij v zapisniku.</string>
++ <string id="19224">Preklicano snemanje</string>
++ <string id="19225">Nastavljeno snemanje</string>
++ <string id="19226">PriÄeto snemanje</string>
++ <string id="19227">ZakljuÄeno snemanje</string>
++ <string id="19228">Izbrisano snemanje</string>
++ <string id="19229">Zapri OSD po preklapljanju programov</string>
++ <string id="19230">OnemogoÄi posodobitve EPG med predvajanjem</string>
++ <string id="19231">Vedno uporabi vrstni red programov s hrbtenic(e)</string>
++ <string id="19232">PoÄisti rezultate iskanja</string>
++ <string id="19233">Prikaži obvestila o spremembah Äasovnikov</string>
++ <string id="19234">Uporabi Å¡tevilke programov s hrbtenice (samo pri enem vkljuÄenem dodatku PVR)</string>
++ <string id="19235">Upravljalec PVR se zaganja</string>
++ <string id="19236">uvažanje programov</string>
++ <string id="19237">uvažanje Äasovnikov</string>
++ <string id="19238">uvažanje posnetkov</string>
++ <string id="19239">zaganjanje niti v ozadju</string>
++ <string id="19240">Noben dodatek PVR ni omogoÄen</string>
++ <string id="19241">Upravljalec PVR je bil omogoÄen brez</string>
++ <string id="19242">omogoÄenih dodatkov. OmogoÄite vsaj enega</string>
++ <string id="19243">za uporabo funkcionalnosti PVR.</string>
++
++ <string id="19244">ÄŒas nedejavnosti hrbtenice</string>
++ <string id="19245">Nastavi ukaz za zbujanje (cmd [timestamp])</string>
++ <string id="19246">Zbudi se pred snemanjem</string>
++ <string id="19247">Dnevno zbujanje</string>
++ <string id="19248">ÄŒas dnevnega zbujanja (HH:MM:SS)</string>
++
++ <string id="19499">Drugo/Neznano</string>
++ <string id="19500">Film/Drama</string>
++ <string id="19501">Detektivka/Triler</string>
++ <string id="19502">PustolovÅ¡Äina/Vestern/Vojni</string>
++ <string id="19503">Znanstvena fantastika/Fantazija/Grozljivka</string>
++ <string id="19504">Komedija</string>
++ <string id="19505">Nadaljevanka/Melodrama/Folklora</string>
++ <string id="19506">Romanca</string>
++ <string id="19507">Resno/KlasiÄno/Versko/Zgodovinski film/Drama</string>
++ <string id="19508">Film za odrasle/Drama</string>
++ <string id="19509"></string>
++ <string id="19510"></string>
++ <string id="19511"></string>
++ <string id="19512"></string>
++ <string id="19513"></string>
++ <string id="19514"></string>
++ <string id="19515"></string>
++ <string id="19516">Novice/Trenutni dogodki</string>
++ <string id="19517">Novice/Vremenska napoved</string>
++ <string id="19518">Novice</string>
++ <string id="19519">Dokumentarec</string>
++ <string id="19520">Pogovor/Intervju/Debata</string>
++ <string id="19521"></string>
++ <string id="19522"></string>
++ <string id="19523"></string>
++ <string id="19524"></string>
++ <string id="19525"></string>
++ <string id="19526"></string>
++ <string id="19527"></string>
++ <string id="19528"></string>
++ <string id="19529"></string>
++ <string id="19530"></string>
++ <string id="19531"></string>
++ <string id="19532">Å ov/Igra</string>
++ <string id="19533">Igra/Kviz/Tekmovanje</string>
++ <string id="19534">Å ov</string>
++ <string id="19535">Pogovorna oddaja</string>
++ <string id="19536"></string>
++ <string id="19537"></string>
++ <string id="19538"></string>
++ <string id="19539"></string>
++ <string id="19540"></string>
++ <string id="19541"></string>
++ <string id="19542"></string>
++ <string id="19543"></string>
++ <string id="19544"></string>
++ <string id="19545"></string>
++ <string id="19546"></string>
++ <string id="19547"></string>
++ <string id="19548">Å port</string>
++ <string id="19549">Posebni dogodek</string>
++ <string id="19550">Å portna oddaja</string>
++ <string id="19551">Nogomet</string>
++ <string id="19552">Tenis/Squash</string>
++ <string id="19553">Ekipni Å¡porti</string>
++ <string id="19554">Atletika</string>
++ <string id="19555">Avtomobilizem</string>
++ <string id="19556">Vodni Å¡porti</string>
++ <string id="19557">Zimski Å¡porti</string>
++ <string id="19558">Jahanje</string>
++ <string id="19559">Borilne veÅ¡Äine</string>
++ <string id="19560"></string>
++ <string id="19561"></string>
++ <string id="19562"></string>
++ <string id="19563"></string>
++ <string id="19564">Otroška/Mladinska oddaja</string>
++ <string id="19565">Oddaja za predšolsko mladino</string>
++ <string id="19566">Zabavna oddaja od 6 do 14 let</string>
++ <string id="19567">Zabavna oddaja od 10 do 16 let</string>
++ <string id="19568">Informativna/Izobraževalna/Šolska oddaja</string>
++ <string id="19569">Risanka/Lutke</string>
++ <string id="19570"></string>
++ <string id="19571"></string>
++ <string id="19572"></string>
++ <string id="19573"></string>
++ <string id="19574"></string>
++ <string id="19575"></string>
++ <string id="19576"></string>
++ <string id="19577"></string>
++ <string id="19578"></string>
++ <string id="19579"></string>
++ <string id="19580">Glasba/Balet/Ples</string>
++ <string id="19581">Rock/Pop</string>
++ <string id="19582">Resna/KlasiÄna glasba</string>
++ <string id="19583">Narodnozabavna/Tradicionalna glasba</string>
++ <string id="19584">Musical/Opera</string>
++ <string id="19585">Balet</string>
++ <string id="19586"></string>
++ <string id="19587"></string>
++ <string id="19588"></string>
++ <string id="19589"></string>
++ <string id="19590"></string>
++ <string id="19591"></string>
++ <string id="19592"></string>
++ <string id="19593"></string>
++ <string id="19594"></string>
++ <string id="19595"></string>
++ <string id="19596">Umetnost/Kultura</string>
++ <string id="19597">Predstava</string>
++ <string id="19598">Slikarstvo</string>
++ <string id="19599">Verstva</string>
++ <string id="19600">Popularna/Tradicionalna umetnost</string>
++ <string id="19601">Literatura</string>
++ <string id="19602">Film/Kino</string>
++ <string id="19603">Eksperimentalni Film/Video</string>
++ <string id="19604">Oddajanje/Mediji</string>
++ <string id="19605">Novi mediji</string>
++ <string id="19606">Umetnost/Kulturna oddaja</string>
++ <string id="19607">Moda</string>
++ <string id="19608"></string>
++ <string id="19609"></string>
++ <string id="19610"></string>
++ <string id="19611"></string>
++ <string id="19612">Družba/Politika/Ekonomija</string>
++ <string id="19613">PoroÄilo/Dokumentarec</string>
++ <string id="19614">Ekonomija/Svetovanje</string>
++ <string id="19615">Znane osebe</string>
++ <string id="19616"></string>
++ <string id="19617"></string>
++ <string id="19618"></string>
++ <string id="19619"></string>
++ <string id="19620"></string>
++ <string id="19621"></string>
++ <string id="19622"></string>
++ <string id="19623"></string>
++ <string id="19624"></string>
++ <string id="19625"></string>
++ <string id="19626"></string>
++ <string id="19627"></string>
++ <string id="19628">Izobraževanje/Znanost/Dejstva</string>
++ <string id="19629">Narava/Živali/Okolje</string>
++ <string id="19630">Tehnologija/Naravoslovje</string>
++ <string id="19631">Medicina/Fiziologija/Psihologija</string>
++ <string id="19632">Tuje dežele/Odprave</string>
++ <string id="19633">Družba/Duhovnost</string>
++ <string id="19634">Nadaljnje izobraževanje</string>
++ <string id="19635">Jeziki</string>
++ <string id="19636"></string>
++ <string id="19637"></string>
++ <string id="19638"></string>
++ <string id="19639"></string>
++ <string id="19640"></string>
++ <string id="19641"></string>
++ <string id="19642"></string>
++ <string id="19643"></string>
++ <string id="19644">Sprostitev/Hobi</string>
++ <string id="19645">Turizem/Potovanja</string>
++ <string id="19646">Rokodelstvo</string>
++ <string id="19647">Avto-moto</string>
++ <string id="19648">Kondicija &amp; Zdravje</string>
++ <string id="19649">Kuhanje</string>
++ <string id="19650">Oglaševanje/Nakupovanje</string>
++ <string id="19651">Vrtnarstvo</string>
++ <string id="19652"></string>
++ <string id="19653"></string>
++ <string id="19654"></string>
++ <string id="19655"></string>
++ <string id="19656"></string>
++ <string id="19657"></string>
++ <string id="19658"></string>
++ <string id="19659"></string>
++ <string id="19660">Posebne znaÄilnosti</string>
++ <string id="19661">Prvotni jezik</string>
++ <string id="19662">ÄŒrno &amp; Belo</string>
++ <string id="19663">Neobjavljeno</string>
++ <string id="19664">Prenos v živo</string>
++ <string id="19665"></string>
++ <string id="19666"></string>
++ <string id="19667"></string>
++ <string id="19668"></string>
++ <string id="19669"></string>
++ <string id="19670"></string>
++ <string id="19671"></string>
++ <string id="19672"></string>
++ <string id="19673"></string>
++ <string id="19674"></string>
++ <string id="19675"></string>
++ <string id="19676">Drama</string>
++ <string id="19677">Detektivka/Triler</string>
++ <string id="19678">PostolovÅ¡Äina/Vestern/Vojni</string>
++ <string id="19679">Znanstvena fantastika/Fantazika/Grozljivka</string>
++ <string id="19680">Komedija</string>
++ <string id="19681">Nadaljevanka/Melodrama/Folklora</string>
++ <string id="19682">Romanca</string>
++ <string id="19683">Resno/KlasiÄno/Versko/Zgodovinsko</string>
++ <string id="19684">Za odrasle</string>
++ <string id="19685"></string>
++ <string id="19686"></string>
++ <string id="19687"></string>
++ <string id="19688"></string>
++ <string id="19689"></string>
++ <string id="19690"></string>
++ <string id="19691"></string>
+
+ <string id="20000">Mapa za shranjeno glasbo</string>
+ <string id="20001">Uporabi zunanji DVD predvajalnik</string>
+@@ -2188,6 +2637,7 @@
+ <string id="24016">Informacije o albumih</string>
+ <string id="24017">Informacije o izvajalcih</string>
+ <string id="24018">Storitve</string>
++ <string id="24019">Odjemalci PVR</string>
+
+ <string id="24020">Prilagodi</string>
+ <string id="24021">OnemogoÄi</string>
+@@ -2223,7 +2673,7 @@
+ <string id="24059">Želite omogoÄiti ta dodatek?</string>
+ <string id="24060">Želite onemogoÄiti ta dodatek?</string>
+ <string id="24061">Na voljo je posodobitev dodatka!</string>
+- <string id="24062">OmogoÄeni dodatki</string>
++ <string id="24062">NameÅ¡Äeni dodatki</string>
+ <string id="24063">Samodejna posodobitev</string>
+ <string id="24064">Dodatek je omogoÄen</string>
+ <string id="24065">Dodatek je posodobljen</string>
+diff --git a/language/Swedish/strings.xml b/language/Swedish/strings.xml
+index 1c5e26f..e2bf93d 100644
+--- a/language/Swedish/strings.xml
++++ b/language/Swedish/strings.xml
+@@ -1415,6 +1415,440 @@ den här visualiseringen</string>
+ <string id="16400">Efterbearbetning</string>
+ <string id="17500">Visa insommningstimeout</string>
+ <string id="19000">Byt till kanal</string>
++ <string id="19001">Separera sökorden genom att använda AND, OR och/eller NOT.</string>
++ <string id="19002">eller använd en fraser för att hitta en exakt matchning, exempelvis "The wizard of Oz".</string>
++ <string id="19003">Sök liknande program</string>
++ <string id="19004">Importera EPG från klienter</string>
++ <string id="19005">PVR ströminformation</string>
++ <string id="19006">Mottagarenhet</string>
++ <string id="19007">Status för mottagarenhet</string>
++ <string id="19008">Signalkvalitet</string>
++ <string id="19009">SNR</string>
++ <string id="19010">BER</string>
++ <string id="19011">UNC</string>
++ <string id="19012">PVR Backend</string>
++ <string id="19013">Frivisning</string>
++ <string id="19014">LÃ¥st</string>
++ <string id="19015">Kryptering</string>
++ <string id="19016">PVR Backend %i - %s</string>
++ <string id="19017">TV inspelningar</string>
++ <string id="19018">Standardmapp för PVR miniatyrer</string>
++ <string id="19019">Kanaler</string>
++ <string id="19020">TV</string>
++ <string id="19021">Radio</string>
++ <string id="19022">Gömda</string>
++ <string id="19023">TV kanaler</string>
++ <string id="19024">Radiokanaler</string>
++ <string id="19025">Kommande inslepningar</string>
++ <string id="19026">Lägg till timer...</string>
++ <string id="19027">Inga sökresultat</string>
++ <string id="19028">Inga EPG poster</string>
++ <string id="19029">Kanal</string>
++ <string id="19030">Nu</string>
++ <string id="19031">Nästa</string>
++ <string id="19032">Tidslinje</string>
++ <string id="19033">Information</string>
++ <string id="19034">Inspelning redan startad på denna kanal</string>
++ <string id="19035">Denna kanal kan inte spelas upp. Se loggen för mer information.</string>
++ <string id="19036">Denna inspelning kan inte spelas upp. Se loggen för mer information.</string>
++ <string id="19037">Visa signalkvalitet</string>
++ <string id="19038">Stödjs inte av PVR backendet</string>
++ <string id="19039">Är du säker att du vill gömma denna kanal?</string>
++ <string id="19040">Timers</string>
++ <string id="19041">Är du säker att du vill döpa om denna inspelning?</string>
++ <string id="19042">Är du säker att du vill döpa om denna timer?</string>
++ <string id="19043">Inspelning</string>
++ <string id="19044">Var god se över din konfiguration eller se loggen för mer information.</string>
++ <string id="19045">Inga PVR klienter har startats. Vänta tills att PVR klienterna har slutfört uppstarten, eller se loggen för mer information.</string>
++ <string id="19046">Ny kanal</string>
++ <string id="19047">Programinformation</string>
++ <string id="19048">Grupphantering</string>
++ <string id="19049">Visa kanal</string>
++ <string id="19050">Visa synliga kanaler</string>
++ <string id="19051">Visa gömda kanaler</string>
++ <string id="19052">Flytta kanal till:</string>
++ <string id="19053">Inspelningsinformation</string>
++ <string id="19054">Dölj kanal</string>
++ <string id="19055">Ingen information tillgänglig</string>
++ <string id="19056">Ny timer</string>
++ <string id="19057">Ändra timer</string>
++ <string id="19058">Timer aktiverad</string>
++ <string id="19059">Stoppa inspelning</string>
++ <string id="19060">Radera timer</string>
++ <string id="19061">Lägg till timer</string>
++ <string id="19062">Sortera efter: Kanal</string>
++ <string id="19063">Gå till början</string>
++ <string id="19064">GÃ¥ till slutet</string>
++ <string id="19065">Förvalt EPG fönster</string>
++ <string id="19066">Laddar inspelningar från klienter</string>
++ <string id="19067">Detta event spelas redan in.</string>
++ <string id="19068">Denna inspelning kunde inte raderas. Se loggen för mer information.</string>
++ <string id="19069">EPG</string>
++ <string id="19070">EPG-skanning timeout</string>
++ <string id="19071">Uppdateringsintervall för EPG</string>
++ <string id="19072">Lagra inte EPG i databasen</string>
++ <string id="19073">Fördröjning vid kanalbyte</string>
++ <string id="19074">Aktiv:</string>
++ <string id="19075">Namn:</string>
++ <string id="19076">Mapp:</string>
++ <string id="19077">Radio:</string>
++ <string id="19078">Kanal:</string>
++ <string id="19079">Dag:</string>
++ <string id="19080">Startar:</string>
++ <string id="19081">Slutar:</string>
++ <string id="19082">Prioritet:</string>
++ <string id="19083">Livstid (dagar):</string>
++ <string id="19084">Första dagen:</string>
++ <string id="19085">Okänd kanal %u</string>
++ <string id="19086">MÃ¥-__-__-__-__-__-__</string>
++ <string id="19087">__-Ti-__-__-__-__-__</string>
++ <string id="19088">__-__-On-__-__-__-__</string>
++ <string id="19089">__-__-__-To-__-__-__</string>
++ <string id="19090">__-__-__-__-Fr-__-__</string>
++ <string id="19091">__-__-__-__-__-Lö-__</string>
++ <string id="19092">__-__-__-__-__-__-Sö</string>
++ <string id="19093">MÃ¥-Ti-On-To-Fr-__-__</string>
++ <string id="19094">Må-Ti-On-To-Fr-Lö-__</string>
++ <string id="19095">Må-Ti-On-To-Fr-Lö-Sö</string>
++ <string id="19096">__-__-__-__-__-Lö-Sö</string>
++ <string id="19097">Ange namn för inspelningen</string>
++ <string id="19098">Varning</string>
++ <string id="19099">Timer närvarande</string>
++ <string id="19100">Är du säker att du vill ta bort denna kanal, inklusive kanalens alla timers?</string>
++ <string id="19101">Denna kanal för närvarande för uppspelning.</string>
++ <string id="19102">Var god byt till en annan kanal.</string>
++ <string id="19103">Skanna efter saknade kanalikoner</string>
++ <string id="19104">Ange mappnamn för inspelningen</string>
++ <string id="19105">Storlek:</string>
++ <string id="19106">Nästa timer den</string>
++ <string id="19107">vid</string>
++ <string id="19108">Inspelningarna är inte synkroniserade. Se loggen för mer information.</string>
++ <string id="19109">Kunde inte spara timern. Se loggen för mer information.</string>
++ <string id="19110">Ett oväntat fel inträrrade. Försök igen senare eller se loggen för mer information.</string>
++ <string id="19111">PVR backend fel. Se loggen för mer information.</string>
++ <string id="19112">Timers är inte synkroniserade. Se loggen för mer information.</string>
++ <string id="19113">Nästa</string>
++ <string id="19114">Version</string>
++ <string id="19115">Adress</string>
++ <string id="19116">Diskstorlek</string>
++ <string id="19117">Sök efter kanaler</string>
++ <string id="19118">PVR-funktionaliteten kan inte användas under pågående sökning.</string>
++ <string id="19119">På vilken server vill du söka?</string>
++ <string id="19120">Klientnummer</string>
++ <string id="19121">Unvik upprepningar</string>
++ <string id="19122">Denna inspelning är fortfarande aktiv. Är du säker att du vill ta bort denna timer?</string>
++ <string id="19123">Endast frivisningskanaler</string>
++ <string id="19124">Ignorera nuvarande timers</string>
++ <string id="19125">Ignorera nuvarande inspelningar</string>
++ <string id="19126">Starttid</string>
++ <string id="19127">Sluttid</string>
++ <string id="19128">Startdatum</string>
++ <string id="19129">Slutdatum</string>
++ <string id="19130">Varaktighetsminimum</string>
++ <string id="19131">Varaktighetsmaximum</string>
++ <string id="19132">Inkludera okända genrer</string>
++ <string id="19133">Söksträng</string>
++ <string id="19134">Inkludera beskrivning</string>
++ <string id="19135">Skiftlägeskänslig</string>
++ <string id="19136">Kanal ej tillgänglig</string>
++ <string id="19137">Inga grupper definerade</string>
++ <string id="19138">Var god skapa en grupp först</string>
++ <string id="19139">Den nya gruppens namn</string>
++ <string id="19141">Grupp</string>
++ <string id="19142">Sökguiden</string>
++ <string id="19143">Grupphantering</string>
++ <string id="19144">Inga grupper definerade</string>
++ <string id="19145">Grupperad</string>
++ <string id="19146">Grupper</string>
++ <string id="19147">Detta PVR backend stödjer inte denna handling. Se loggen för mer information.</string>
++ <string id="19148">Kanal</string>
++ <string id="19149">MÃ¥</string>
++ <string id="19150">Ti</string>
++ <string id="19151">On</string>
++ <string id="19152">To</string>
++ <string id="19153">Fr</string>
++ <string id="19154">Lö</string>
++ <string id="19155">Sö</string>
++ <string id="19156">från</string>
++ <string id="19157">Nästa inspelning</string>
++ <string id="19158">Nuvarande inspelning</string>
++ <string id="19159">från</string>
++ <string id="19160">till</string>
++ <string id="19161">Den</string>
++ <string id="19162">Aktiv inspelning</string>
++ <string id="19163">Inspelningar</string>
++ <string id="19164">Kunde inte starta inspelningen. Se loggen för mer information.</string>
++ <string id="19165">Byt till</string>
++ <string id="19166">PVR information</string>
++ <string id="19167">Skanna efter saknade ikoner</string>
++ <string id="19168">Byt kanal utan att trycka OK</string>
++ <string id="19169">Göm videoinformationsfönster</string>
++ <string id="19170">Tidsgräns för uppspelningsförsök</string>
++ <string id="19171">Starta uppspelningar minimerad</string>
++ <string id="19172">Varaktighet vid direktinspelning</string>
++ <string id="19173">Standardprioritet för inspelning</string>
++ <string id="19174">Standardlivstid för inspelning</string>
++ <string id="19175">Marginal vid början av en inspelning</string>
++ <string id="19176">Marginal vid slutet av en inspelning</string>
++ <string id="19177">Uppspelning</string>
++ <string id="19178">Visa kanalinformation vid kanalbyte</string>
++ <string id="19179">Göm kanalinformation automatiskt</string>
++ <string id="19180">TV</string>
++ <string id="19181">Meny/OSD</string>
++ <string id="19182">Antal dagar att visa i EPG</string>
++ <string id="19184">Kanalinformationens varaktighet</string>
++ <string id="19185">Töm PVR-databasen</string>
++ <string id="19186">All data i PVR databasen kommer att raderas</string>
++ <string id="19187">Töm EPG databasen</string>
++ <string id="19188">EPG raderas</string>
++ <string id="19189">Starta senaste kanalen vid uppstart</string>
++ <string id="19190">Minimerad</string>
++ <string id="19191">PVR tjänst</string>
++ <string id="19192">Ingen av dom anslutna PVR backenden stödjer kanalsökning.</string>
++ <string id="19193">Kanalen kunde inte startas. Se loggen för mer information.</string>
++ <string id="19194">Fortsätt?</string>
++ <string id="19195">Klienthandlingar</string>
++ <string id="19196">PVR klients specifika handlingar</string>
++ <string id="19197">Inspelningen startades: %s</string>
++ <string id="19198">Inspelningen slutfördes: %s</string>
++ <string id="19199">Kanalhanterare</string>
++ <string id="19200">EPG källa:</string>
++ <string id="19201">Kanalnamn:</string>
++ <string id="19202">Kanalikon:</string>
++ <string id="19203">Editera kanal</string>
++ <string id="19204">Ny kanal</string>
++ <string id="19205">Grupphantering</string>
++ <string id="19206">Aktivera EPG:</string>
++ <string id="19207">Grupp:</string>
++ <string id="19208">Ange den nya kanalens namn</string>
++ <string id="19209">XBMC virtuell backend</string>
++ <string id="19210">Klient</string>
++ <string id="19211">Ta bort kanal</string>
++ <string id="19212">Denna lista innehåller ändringar</string>
++ <string id="19213">Välj backend</string>
++ <string id="19214">Ange en korrekt URL för den nya kanalen</string>
++ <string id="19215">PVR backendet stödjer inte timers.</string>
++ <string id="19216">Alla radiokanaler</string>
++ <string id="19217">Alla TV kanaler</string>
++ <string id="19218">Synliga</string>
++ <string id="19219">Ogrupperade kanaler</string>
++ <string id="19220">Kanaler i</string>
++ <string id="19221">Synkronisera kanalgrupper med backend(s)</string>
++ <string id="19222">EPG</string>
++ <string id="19223">Inget PVR tillägg kunde aktiveras. Kontrollera dina inställningar eller se loggen för mer information.</string>
++ <string id="19224">Inspelning avbryten</string>
++ <string id="19225">Inspelning schemalagd</string>
++ <string id="19226">Inspelning startad</string>
++ <string id="19227">Inspelning slutförd</string>
++ <string id="19228">Inspelning raderad</string>
++ <string id="19229">Stäng kanalens OSD efter kanalbyte</string>
++ <string id="19230">Förhindra EPG uppdateringar vid aktiv TV strömmning</string>
++ <string id="19231">Använd alltid kanalnumrering från backend(s)</string>
++ <string id="19232">Rensa sökresultaten</string>
++ <string id="19233">Visa informationsfönster vid timeruppdateringar</string>
++ <string id="19234">Använd backends kanalnumrering (fungerar enbart med ett aktiverat PVR tillägg)</string>
++ <string id="19235">PVR hanteraren startar</string>
++ <string id="19236">importerar kanaler</string>
++ <string id="19237">importerar timers</string>
++ <string id="19238">importerar inspelningar</string>
++ <string id="19239">startar bakgrundstrådar</string>
++ <string id="19240">Inget PVR tillägg aktiverad</string>
++ <string id="19241">PVR hanteraren har startats utan något aktiverat PVR tillägg.</string>
++ <string id="19242">Aktivera åtminstånde ett PVR tillägg för att </string>
++ <string id="19243">aktivera PVR funktionaliteten.</string>
++ <string id="19499">Övrigt/Okänt</string>
++ <string id="19500">Film/Drama</string>
++ <string id="19501">Detektiv/Thriller</string>
++ <string id="19502">Äventyr/Västern/Krig</string>
++ <string id="19503">Science Fiction/Fantasi/Skräck</string>
++ <string id="19504">Komedi</string>
++ <string id="19505">SÃ¥pa/Melodrama/Folkloristisk</string>
++ <string id="19506">Romans</string>
++ <string id="19507">Allvarsam/Klassisker/Religös/Historisk Film/Drama</string>
++ <string id="19508">Vuxenfilm/Drama</string>
++ <string id="19509"></string>
++ <string id="19510"></string>
++ <string id="19511"></string>
++ <string id="19512"></string>
++ <string id="19513"></string>
++ <string id="19514"></string>
++ <string id="19515"></string>
++ <string id="19516">Nyheter/Aktuella händelser</string>
++ <string id="19517">Nyheter/Väder</string>
++ <string id="19518">Nyhetsmagasin</string>
++ <string id="19519">Dokumentär</string>
++ <string id="19520">Diskussion/Intervju/Debatt</string>
++ <string id="19521"></string>
++ <string id="19522"></string>
++ <string id="19523"></string>
++ <string id="19524"></string>
++ <string id="19525"></string>
++ <string id="19526"></string>
++ <string id="19527"></string>
++ <string id="19528"></string>
++ <string id="19529"></string>
++ <string id="19530"></string>
++ <string id="19531"></string>
++ <string id="19532">Föreställning/Spelföreställning</string>
++ <string id="19533">Game show/Frågesport/Tävling</string>
++ <string id="19534">Varitetsföreställning</string>
++ <string id="19535">Talkshow</string>
++ <string id="19536"></string>
++ <string id="19537"></string>
++ <string id="19538"></string>
++ <string id="19539"></string>
++ <string id="19540"></string>
++ <string id="19541"></string>
++ <string id="19542"></string>
++ <string id="19543"></string>
++ <string id="19544"></string>
++ <string id="19545"></string>
++ <string id="19546"></string>
++ <string id="19547"></string>
++ <string id="19548">Sport</string>
++ <string id="19549">Speciellt evenemang</string>
++ <string id="19550">Sportmagasin</string>
++ <string id="19551">Amerikans fotboll</string>
++ <string id="19552">Tennis/Squash</string>
++ <string id="19553">Lagsport</string>
++ <string id="19554">Gymnastik</string>
++ <string id="19555">Motorsport</string>
++ <string id="19556">Vattensport</string>
++ <string id="19557">Vinterports</string>
++ <string id="19558">Ridsport</string>
++ <string id="19559">Kampsport</string>
++ <string id="19560"></string>
++ <string id="19561"></string>
++ <string id="19562"></string>
++ <string id="19563"></string>
++ <string id="19564">Barnprogram/Ungdomsprogram</string>
++ <string id="19565">Program för förskolebarn</string>
++ <string id="19566">Underhållningsprogram för 6 till 14 år</string>
++ <string id="19567">Underhållningsprogram för 10 till 16 år</string>
++ <string id="19568">Information/Utbildning/Skolprogram</string>
++ <string id="19569">Tecknat/Marionett</string>
++ <string id="19570"></string>
++ <string id="19571"></string>
++ <string id="19572"></string>
++ <string id="19573"></string>
++ <string id="19574"></string>
++ <string id="19575"></string>
++ <string id="19576"></string>
++ <string id="19577"></string>
++ <string id="19578"></string>
++ <string id="19579"></string>
++ <string id="19580">Musik/Ballett/Dans</string>
++ <string id="19581">Rock/Pop</string>
++ <string id="19582">Allvarlig/Klassisk musik</string>
++ <string id="19583">Folk/Traditionell musik</string>
++ <string id="19584">Musikal/Opera</string>
++ <string id="19585">Ballett</string>
++ <string id="19586"></string>
++ <string id="19587"></string>
++ <string id="19588"></string>
++ <string id="19589"></string>
++ <string id="19590"></string>
++ <string id="19591"></string>
++ <string id="19592"></string>
++ <string id="19593"></string>
++ <string id="19594"></string>
++ <string id="19595"></string>
++ <string id="19596">Konst/Kultur</string>
++ <string id="19597">Scenkonst</string>
++ <string id="19598">Finkultur</string>
++ <string id="19599">Religion</string>
++ <string id="19600">Populärkultur/Traditionell konst</string>
++ <string id="19601">Litteratur</string>
++ <string id="19602">Film/Biograf</string>
++ <string id="19603">Experimentell film/Video</string>
++ <string id="19604">Utsändning/Press</string>
++ <string id="19605">New Media</string>
++ <string id="19606">Konst/Kulturmagasin</string>
++ <string id="19607">Mode</string>
++ <string id="19608"></string>
++ <string id="19609"></string>
++ <string id="19610"></string>
++ <string id="19611"></string>
++ <string id="19612">Social/Politisk/Ekonomi</string>
++ <string id="19613">Magasin/Rapporter/Dokumentär</string>
++ <string id="19614">Ekonomi/Social rådgivning</string>
++ <string id="19615">Anmärkningsvära människor</string>
++ <string id="19616"></string>
++ <string id="19617"></string>
++ <string id="19618"></string>
++ <string id="19619"></string>
++ <string id="19620"></string>
++ <string id="19621"></string>
++ <string id="19622"></string>
++ <string id="19623"></string>
++ <string id="19624"></string>
++ <string id="19625"></string>
++ <string id="19626"></string>
++ <string id="19627"></string>
++ <string id="19628">Utbildning/Vetenskap/Fakta</string>
++ <string id="19629">Natur/Djur/Miljö</string>
++ <string id="19630">Teknik/Naturvetenskap</string>
++ <string id="19631">Medicin/Fysiologi/Psykologi</string>
++ <string id="19632">Främmande länder/Expeditioner</string>
++ <string id="19633">Social/Andlig vetenskap</string>
++ <string id="19634">Ytterligare utbildning</string>
++ <string id="19635">Språk</string>
++ <string id="19636"></string>
++ <string id="19637"></string>
++ <string id="19638"></string>
++ <string id="19639"></string>
++ <string id="19640"></string>
++ <string id="19641"></string>
++ <string id="19642"></string>
++ <string id="19643"></string>
++ <string id="19644">Fritid/Hobby</string>
++ <string id="19645">Turism/Resa</string>
++ <string id="19646">Hantverk</string>
++ <string id="19647">Motor</string>
++ <string id="19648">Motion &amp; hälsa</string>
++ <string id="19649">Matlagning</string>
++ <string id="19650">Annonsering/Shopping</string>
++ <string id="19651">Trädgårdsskötsel</string>
++ <string id="19652"></string>
++ <string id="19653"></string>
++ <string id="19654"></string>
++ <string id="19655"></string>
++ <string id="19656"></string>
++ <string id="19657"></string>
++ <string id="19658"></string>
++ <string id="19659"></string>
++ <string id="19660">Speciella egenskaper</string>
++ <string id="19661">Hemspråk</string>
++ <string id="19662">Svartvitt</string>
++ <string id="19663">Opublicerat</string>
++ <string id="19664">Direktsändning</string>
++ <string id="19665"></string>
++ <string id="19666"></string>
++ <string id="19667"></string>
++ <string id="19668"></string>
++ <string id="19669"></string>
++ <string id="19670"></string>
++ <string id="19671"></string>
++ <string id="19672"></string>
++ <string id="19673"></string>
++ <string id="19674"></string>
++ <string id="19675"></string>
++ <string id="19676">Drama</string>
++ <string id="19677">Detektiv/Thriller</string>
++ <string id="19678">Äventyr/Västern/Krig</string>
++ <string id="19679">Science Fiction/Fantasi/Skräck</string>
++ <string id="19680">Komedi</string>
++ <string id="19681">SÃ¥pa/Melodrama/Folkloristisk</string>
++ <string id="19682">Romantik</string>
++ <string id="19683">Allvarlig/Klassisk religion/Historia</string>
++ <string id="19684">Vuxen</string>
++ <string id="19685"></string>
++ <string id="19686"></string>
++ <string id="19687"></string>
++ <string id="19688"></string>
++ <string id="19689"></string>
++ <string id="19690"></string>
++ <string id="19691"></string>
+ <string id="20000">Mapp för sparad musik</string>
+ <string id="20001">Använd extern DVD-spelare</string>
+ <string id="20002">Extern DVD-spelare</string>
+diff --git a/lib/addons/library.xbmc.addon/Makefile.in b/lib/addons/library.xbmc.addon/Makefile.in
+new file mode 100644
+index 0000000..cd8b009
+--- /dev/null
++++ b/lib/addons/library.xbmc.addon/Makefile.in
+@@ -0,0 +1,27 @@
++ARCH=@ARCH@
++INCLUDES=-I. -I../../../xbmc/addons/include -I../../../xbmc
++DEFINES+=
++CXXFLAGS=-fPIC
++LIBNAME=libXBMC_addon
++OBJS=$(LIBNAME).o
++
++LIB_SHARED=../../../addons/library.xbmc.addon/$(LIBNAME)-$(ARCH).so
++
++all: $(LIB_SHARED)
++
++$(LIB_SHARED): $(OBJS)
++ifeq ($(findstring osx,$(ARCH)), osx)
++ $(CXX) $(LDFLAGS) -Wl,-alias_list,@abs_top_srcdir@/xbmc/cores/DllLoader/exports/wrapper_mach_alias \
++ -bundle -undefined dynamic_lookup -read_only_relocs suppress -o $@ \
++ @abs_top_srcdir@/xbmc/cores/DllLoader/exports/wrapper.o $(OBJS)
++else
++ $(CXX) $(CFLAGS) $(LDFLAGS) -shared -g -o $(LIB_SHARED) $(OBJS)
++endif
++
++CLEAN_FILES = \
++ $(LIBNAME).so \
++
++DISTCLEAN_FILES= \
++ Makefile \
++
++include ../../../Makefile.include
+diff --git a/lib/addons/library.xbmc.addon/libXBMC_addon.cpp b/lib/addons/library.xbmc.addon/libXBMC_addon.cpp
+new file mode 100644
+index 0000000..009618f
+--- /dev/null
++++ b/lib/addons/library.xbmc.addon/libXBMC_addon.cpp
+@@ -0,0 +1,152 @@
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <stdarg.h>
++#include <string>
++#include "../../../addons/library.xbmc.addon/libXBMC_addon.h"
++#include "../../../xbmc/addons/AddonCallbacks.h"
++
++#ifdef _WIN32
++#include <windows.h>
++#define DLLEXPORT __declspec(dllexport)
++#else
++#define DLLEXPORT
++#endif
++
++
++using namespace std;
++using namespace ADDON;
++
++AddonCB *m_Handle = NULL;
++CB_AddOnLib *m_cb = NULL;
++
++extern "C"
++{
++
++DLLEXPORT int XBMC_register_me(void *hdl)
++{
++ if (!hdl)
++ fprintf(stderr, "libXBMC_addon-ERROR: XBMC_register_me is called with NULL handle !!!\n");
++ else
++ {
++ m_Handle = (AddonCB*) hdl;
++ m_cb = m_Handle->AddOnLib_RegisterMe(m_Handle->addonData);
++ if (!m_cb)
++ fprintf(stderr, "libXBMC_addon-ERROR: XBMC_register_me can't get callback table from XBMC !!!\n");
++ else
++ return 1;
++ }
++ return 0;
++}
++
++DLLEXPORT void XBMC_unregister_me()
++{
++ if (m_Handle && m_cb)
++ m_Handle->AddOnLib_UnRegisterMe(m_Handle->addonData, m_cb);
++}
++
++DLLEXPORT void XBMC_log(const addon_log_t loglevel, const char *format, ... )
++{
++ if (m_cb == NULL)
++ return;
++
++ char buffer[16384];
++ va_list args;
++ va_start (args, format);
++ vsprintf (buffer, format, args);
++ va_end (args);
++ m_cb->Log(m_Handle->addonData, loglevel, buffer);
++}
++
++DLLEXPORT bool XBMC_get_setting(const char* settingName, void *settingValue)
++{
++ if (m_cb == NULL)
++ return false;
++
++ return m_cb->GetSetting(m_Handle->addonData, settingName, settingValue);
++}
++
++/*DLLEXPORT bool XBMC_update_setting(const char* settingName, void *settingValue)
++{
++ if (m_cb == NULL)
++ return false;
++
++ return m_cb->UpdateSetting(m_Handle->addonData, settingName, settingValue);
++}*/
++
++
++DLLEXPORT void XBMC_queue_notification(const queue_msg_t type, const char *format, ... )
++{
++ if (m_cb == NULL)
++ return;
++
++ char buffer[16384];
++ va_list args;
++ va_start (args, format);
++ vsprintf (buffer, format, args);
++ va_end (args);
++ m_cb->QueueNotification(m_Handle->addonData, type, buffer);
++}
++
++DLLEXPORT void XBMC_unknown_to_utf8(string &str)
++{
++ if (m_cb == NULL)
++ return;
++
++ string buffer = m_cb->UnknownToUTF8(str.c_str());
++ str = buffer;
++}
++
++DLLEXPORT const char* XBMC_get_localized_string(int dwCode)
++{
++ if (m_cb == NULL)
++ return "";
++
++ return m_cb->GetLocalizedString(m_Handle->addonData, dwCode);
++}
++
++DLLEXPORT const char* XBMC_get_dvd_menu_language()
++{
++ if (m_cb == NULL)
++ return "";
++
++ return m_cb->GetDVDMenuLanguage(m_Handle->addonData);
++}
++
++DLLEXPORT const char* XBMC_get_localized_date(time_t time, bool bLongDate, bool bWithShortNames)
++{
++ if (m_cb == NULL)
++ return "";
++
++ return m_cb->GetLocalizedDate(m_Handle->addonData, time, bLongDate, bWithShortNames);
++}
++
++DLLEXPORT const char* XBMC_get_localized_time(time_t time, bool bWithSeconds)
++{
++ if (m_cb == NULL)
++ return "";
++
++ return m_cb->GetLocalizedTime(m_Handle->addonData, time, bWithSeconds);
++}
++
++};
+diff --git a/lib/addons/library.xbmc.addon/project/VS2010Express/libXBMC_addon.vcxproj b/lib/addons/library.xbmc.addon/project/VS2010Express/libXBMC_addon.vcxproj
+new file mode 100644
+index 0000000..e4339a5
+--- /dev/null
++++ b/lib/addons/library.xbmc.addon/project/VS2010Express/libXBMC_addon.vcxproj
+@@ -0,0 +1,89 @@
++<?xml version="1.0" encoding="utf-8"?>
++<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
++ <ItemGroup Label="ProjectConfigurations">
++ <ProjectConfiguration Include="Debug|Win32">
++ <Configuration>Debug</Configuration>
++ <Platform>Win32</Platform>
++ </ProjectConfiguration>
++ <ProjectConfiguration Include="Release|Win32">
++ <Configuration>Release</Configuration>
++ <Platform>Win32</Platform>
++ </ProjectConfiguration>
++ </ItemGroup>
++ <PropertyGroup Label="Globals">
++ <ProjectGuid>{2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}</ProjectGuid>
++ <RootNamespace>XBMC_VDR</RootNamespace>
++ <Keyword>Win32Proj</Keyword>
++ </PropertyGroup>
++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
++ <ConfigurationType>DynamicLibrary</ConfigurationType>
++ <CharacterSet>MultiByte</CharacterSet>
++ </PropertyGroup>
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
++ <ConfigurationType>DynamicLibrary</ConfigurationType>
++ <CharacterSet>MultiByte</CharacterSet>
++ </PropertyGroup>
++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
++ <ImportGroup Label="ExtensionSettings">
++ </ImportGroup>
++ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
++ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
++ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
++ </ImportGroup>
++ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
++ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
++ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
++ </ImportGroup>
++ <PropertyGroup Label="UserMacros" />
++ <PropertyGroup>
++ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
++ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\..\..\..\addons\library.xbmc.addon\</OutDir>
++ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Debug\</IntDir>
++ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\..\..\..\addons\library.xbmc.addon\</OutDir>
++ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Release\</IntDir>
++ </PropertyGroup>
++ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
++ <ClCompile>
++ <Optimization>Disabled</Optimization>
++ <AdditionalIncludeDirectories>..\..\..\..\..\xbmc\addons\include;..\..\..\..\..\xbmc\addons;..\..\..\..\..\xbmc\cores\dvdplayer\DVDDemuxers;..\..\..\..\..\xbmc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
++ <PreprocessorDefinitions>_WIN32;_DEBUG;_WINDOWS;VDR_EXPORTS;_WIN32PC;_WINSOCKAPI_;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++ <MinimalRebuild>true</MinimalRebuild>
++ <ExceptionHandling>Sync</ExceptionHandling>
++ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
++ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
++ <PrecompiledHeader>
++ </PrecompiledHeader>
++ <WarningLevel>Level3</WarningLevel>
++ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
++ </ClCompile>
++ <Link>
++ <OutputFile>..\..\..\..\..\addons\library.xbmc.addon\$(ProjectName).dll</OutputFile>
++ <GenerateDebugInformation>true</GenerateDebugInformation>
++ </Link>
++ </ItemDefinitionGroup>
++ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
++ <ClCompile>
++ <AdditionalIncludeDirectories>..\..\..\..\..\xbmc\addons\include;..\..\..\..\..\xbmc\addons;..\..\..\..\..\xbmc\cores\dvdplayer\DVDDemuxers;..\..\..\..\..\xbmc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
++ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;HAS_SDL_OPENGL;HAS_SDL;_USRDLL;XBMC_VDR_EXPORTS;_WIN32PC;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++ <ExceptionHandling>Sync</ExceptionHandling>
++ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
++ <PrecompiledHeader>
++ </PrecompiledHeader>
++ <WarningLevel>Level3</WarningLevel>
++ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
++ </ClCompile>
++ <Link>
++ <OutputFile>..\..\..\..\..\addons\library.xbmc.addon\$(ProjectName).dll</OutputFile>
++ </Link>
++ </ItemDefinitionGroup>
++ <ItemGroup>
++ <ClCompile Include="..\..\libXBMC_addon.cpp" />
++ </ItemGroup>
++ <ItemGroup>
++ <ClInclude Include="..\..\..\..\..\addons\library.xbmc.addon\libXBMC_addon.h" />
++ </ItemGroup>
++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
++ <ImportGroup Label="ExtensionTargets">
++ </ImportGroup>
++</Project>
+\ No newline at end of file
+diff --git a/lib/addons/library.xbmc.addon/project/VS2010Express/libXBMC_addon.vcxproj.filters b/lib/addons/library.xbmc.addon/project/VS2010Express/libXBMC_addon.vcxproj.filters
+new file mode 100644
+index 0000000..916673c
+--- /dev/null
++++ b/lib/addons/library.xbmc.addon/project/VS2010Express/libXBMC_addon.vcxproj.filters
+@@ -0,0 +1,19 @@
++<?xml version="1.0" encoding="utf-8"?>
++<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
++ <ItemGroup>
++ <Filter Include="Source Files">
++ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
++ <Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
++ </Filter>
++ </ItemGroup>
++ <ItemGroup>
++ <ClCompile Include="..\..\libXBMC_addon.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ </ItemGroup>
++ <ItemGroup>
++ <ClInclude Include="..\..\..\..\..\addons\library.xbmc.addon\libXBMC_addon.h">
++ <Filter>Source Files</Filter>
++ </ClInclude>
++ </ItemGroup>
++</Project>
+\ No newline at end of file
+diff --git a/lib/addons/library.xbmc.gui/Makefile.in b/lib/addons/library.xbmc.gui/Makefile.in
+new file mode 100644
+index 0000000..b5df484
+--- /dev/null
++++ b/lib/addons/library.xbmc.gui/Makefile.in
+@@ -0,0 +1,27 @@
++ARCH=@ARCH@
++INCLUDES=-I. -I../../../xbmc/addons/include -I../../../xbmc -I../../../xbmc/cores/dvdplayer/DVDDemuxers
++DEFINES+=
++CXXFLAGS=-fPIC
++LIBNAME=libXBMC_gui
++OBJS=$(LIBNAME).o
++
++LIB_SHARED=../../../addons/library.xbmc.gui/$(LIBNAME)-$(ARCH).so
++
++all: $(LIB_SHARED)
++
++$(LIB_SHARED): $(OBJS)
++ifeq ($(findstring osx,$(ARCH)), osx)
++ $(CXX) $(LDFLAGS) -Wl,-alias_list,@abs_top_srcdir@/xbmc/cores/DllLoader/exports/wrapper_mach_alias \
++ -bundle -undefined dynamic_lookup -read_only_relocs suppress -o $@ \
++ @abs_top_srcdir@/xbmc/cores/DllLoader/exports/wrapper.o $(OBJS)
++else
++ $(CXX) $(CFLAGS) $(LDFLAGS) -shared -g -o $(LIB_SHARED) $(OBJS)
++endif
++
++CLEAN_FILES = \
++ $(LIB_SHARED) \
++
++DISTCLEAN_FILES= \
++ Makefile \
++
++include ../../../Makefile.include
+diff --git a/lib/addons/library.xbmc.gui/libXBMC_gui.cpp b/lib/addons/library.xbmc.gui/libXBMC_gui.cpp
+new file mode 100644
+index 0000000..1e74e75
+--- /dev/null
++++ b/lib/addons/library.xbmc.gui/libXBMC_gui.cpp
+@@ -0,0 +1,673 @@
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <stdarg.h>
++#include <string>
++#include "../../../addons/library.xbmc.gui/libXBMC_gui.h"
++#include "addons/AddonCallbacks.h"
++
++#ifdef _WIN32
++#include <windows.h>
++#define DLLEXPORT __declspec(dllexport)
++#else
++#define DLLEXPORT
++#endif
++
++using namespace std;
++
++AddonCB *m_Handle = NULL;
++CB_GUILib *m_cb = NULL;
++
++extern "C"
++{
++
++DLLEXPORT int GUI_register_me(void *hdl)
++{
++ if (!hdl)
++ fprintf(stderr, "libXBMC_gui-ERROR: GUILib_register_me is called with NULL handle !!!\n");
++ else
++ {
++ m_Handle = (AddonCB*) hdl;
++ m_cb = m_Handle->GUILib_RegisterMe(m_Handle->addonData);
++ if (!m_cb)
++ fprintf(stderr, "libXBMC_gui-ERROR: GUILib_register_me can't get callback table from XBMC !!!\n");
++ else
++ return 1;
++ }
++ return 0;
++}
++
++DLLEXPORT void GUI_unregister_me()
++{
++ if (m_Handle && m_cb)
++ m_Handle->GUILib_UnRegisterMe(m_Handle->addonData, m_cb);
++}
++
++DLLEXPORT void GUI_lock()
++{
++ m_cb->Lock();
++}
++
++DLLEXPORT void GUI_unlock()
++{
++ m_cb->Unlock();
++}
++
++DLLEXPORT int GUI_get_screen_height()
++{
++ return m_cb->GetScreenHeight();
++}
++
++DLLEXPORT int GUI_get_screen_width()
++{
++ return m_cb->GetScreenWidth();
++}
++
++DLLEXPORT int GUI_get_video_resolution()
++{
++ return m_cb->GetVideoResolution();
++}
++
++
++// Dialogs
++DLLEXPORT int GUI_Dialog_ShowYesNo(const char* heading, const char* line0, const char* line1, const char* line2, int* bCanceled, const char* noLabel, const char* yesLabel)
++{
++ return m_cb->Dialog_ShowYesNo(heading, line0, line1, line2, bCanceled, noLabel, yesLabel);
++}
++
++DLLEXPORT CAddonGUIWindow* GUI_Window_create(const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog)
++{
++ return new CAddonGUIWindow(xmlFilename, defaultSkin, forceFallback, asDialog);
++}
++
++DLLEXPORT void GUI_Window_destroy(CAddonGUIWindow* p)
++{
++ delete p;
++}
++
++DLLEXPORT bool GUI_Window_OnClick(GUIHANDLE handle, int controlId)
++{
++ CAddonGUIWindow *window = (CAddonGUIWindow*) handle;
++ return window->OnClick(controlId);
++}
++
++DLLEXPORT bool GUI_Window_OnFocus(GUIHANDLE handle, int controlId)
++{
++ CAddonGUIWindow *window = (CAddonGUIWindow*) handle;
++ return window->OnFocus(controlId);
++}
++
++DLLEXPORT bool GUI_Window_OnInit(GUIHANDLE handle)
++{
++ CAddonGUIWindow *window = (CAddonGUIWindow*) handle;
++ return window->OnInit();
++}
++
++DLLEXPORT bool GUI_Window_OnAction(GUIHANDLE handle, int actionId)
++{
++ CAddonGUIWindow *window = (CAddonGUIWindow*) handle;
++ return window->OnAction(actionId);
++}
++
++DLLEXPORT bool GUI_Window_OnContextMenu(GUIHANDLE handle,int controlId,int itemNumber, unsigned int contextButtonId)
++{
++ CAddonGUIWindow *window = (CAddonGUIWindow*) handle;
++ return window->OnContextMenu(controlId,itemNumber, contextButtonId);
++}
++
++CAddonGUIWindow::CAddonGUIWindow(const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog)
++{
++ CBOnInit = NULL;
++ CBOnClick = NULL;
++ CBOnFocus = NULL;
++ CBOnContextMenu = NULL;
++ if (m_Handle && m_cb)
++ {
++ m_WindowHandle = m_cb->Window_New(m_Handle->addonData, xmlFilename, defaultSkin, forceFallback, asDialog);
++ if (!m_WindowHandle)
++ fprintf(stderr, "libXBMC_gui-ERROR: cGUIWindow can't create window class from XBMC !!!\n");
++
++ m_cb->Window_SetCallbacks(m_Handle->addonData, m_WindowHandle, this, GUI_Window_OnInit, GUI_Window_OnClick, GUI_Window_OnFocus, GUI_Window_OnAction,GUI_Window_OnContextMenu);
++ }
++}
++
++CAddonGUIWindow::~CAddonGUIWindow()
++{
++ if (m_Handle && m_cb && m_WindowHandle)
++ {
++ m_cb->Window_Delete(m_Handle->addonData, m_WindowHandle);
++ m_WindowHandle = NULL;
++ }
++}
++
++bool CAddonGUIWindow::Show()
++{
++ return m_cb->Window_Show(m_Handle->addonData, m_WindowHandle);
++}
++
++void CAddonGUIWindow::Close()
++{
++ m_cb->Window_Close(m_Handle->addonData, m_WindowHandle);
++}
++
++void CAddonGUIWindow::DoModal()
++{
++ m_cb->Window_DoModal(m_Handle->addonData, m_WindowHandle);
++}
++
++bool CAddonGUIWindow::OnInit()
++{
++ if (!CBOnInit)
++ return false;
++
++ return CBOnInit(m_cbhdl);
++}
++
++bool CAddonGUIWindow::OnClick(int controlId)
++{
++ if (!CBOnClick)
++ return false;
++
++ return CBOnClick(m_cbhdl, controlId);
++}
++
++bool CAddonGUIWindow::OnFocus(int controlId)
++{
++ if (!CBOnFocus)
++ return false;
++
++ return CBOnFocus(m_cbhdl, controlId);
++}
++
++bool CAddonGUIWindow::OnAction(int actionId)
++{
++ if (!CBOnAction)
++ return false;
++
++ return CBOnAction(m_cbhdl, actionId);
++}
++
++bool CAddonGUIWindow::OnContextMenu(int controlId,int itemNumber, unsigned int contextButtonId)
++{
++ if (!CBOnContextMenu)
++ return false;
++
++ return CBOnContextMenu(m_cbhdl, controlId,itemNumber, contextButtonId);
++}
++
++bool CAddonGUIWindow::SetFocusId(int iControlId)
++{
++ return m_cb->Window_SetFocusId(m_Handle->addonData, m_WindowHandle, iControlId);
++}
++
++int CAddonGUIWindow::GetFocusId()
++{
++ return m_cb->Window_GetFocusId(m_Handle->addonData, m_WindowHandle);
++}
++
++bool CAddonGUIWindow::SetCoordinateResolution(int res)
++{
++ return m_cb->Window_SetCoordinateResolution(m_Handle->addonData, m_WindowHandle, res);
++}
++
++void CAddonGUIWindow::SetProperty(const char *key, const char *value)
++{
++ m_cb->Window_SetProperty(m_Handle->addonData, m_WindowHandle, key, value);
++}
++
++void CAddonGUIWindow::SetPropertyInt(const char *key, int value)
++{
++ m_cb->Window_SetPropertyInt(m_Handle->addonData, m_WindowHandle, key, value);
++}
++
++void CAddonGUIWindow::SetPropertyBool(const char *key, bool value)
++{
++ m_cb->Window_SetPropertyBool(m_Handle->addonData, m_WindowHandle, key, value);
++}
++
++void CAddonGUIWindow::SetPropertyDouble(const char *key, double value)
++{
++ m_cb->Window_SetPropertyDouble(m_Handle->addonData, m_WindowHandle, key, value);
++}
++
++const char *CAddonGUIWindow::GetProperty(const char *key) const
++{
++ return m_cb->Window_GetProperty(m_Handle->addonData, m_WindowHandle, key);
++}
++
++int CAddonGUIWindow::GetPropertyInt(const char *key) const
++{
++ return m_cb->Window_GetPropertyInt(m_Handle->addonData, m_WindowHandle, key);
++}
++
++bool CAddonGUIWindow::GetPropertyBool(const char *key) const
++{
++ return m_cb->Window_GetPropertyBool(m_Handle->addonData, m_WindowHandle, key);
++}
++
++double CAddonGUIWindow::GetPropertyDouble(const char *key) const
++{
++ return m_cb->Window_GetPropertyDouble(m_Handle->addonData, m_WindowHandle, key);
++}
++
++void CAddonGUIWindow::ClearProperties()
++{
++ m_cb->Window_ClearProperties(m_Handle->addonData, m_WindowHandle);
++}
++
++int CAddonGUIWindow::GetListSize()
++{
++ return m_cb->Window_GetListSize(m_Handle->addonData, m_WindowHandle);
++}
++
++void CAddonGUIWindow::ClearList()
++{
++ m_cb->Window_ClearList(m_Handle->addonData, m_WindowHandle);
++}
++
++GUIHANDLE CAddonGUIWindow::AddStringItem(const char *name, int itemPosition)
++{
++ return m_cb->Window_AddStringItem(m_Handle->addonData, m_WindowHandle, name, itemPosition);
++}
++
++void CAddonGUIWindow::AddItem(GUIHANDLE item, int itemPosition)
++{
++ m_cb->Window_AddItem(m_Handle->addonData, m_WindowHandle, item, itemPosition);
++}
++
++void CAddonGUIWindow::AddItem(CAddonListItem *item, int itemPosition)
++{
++ m_cb->Window_AddItem(m_Handle->addonData, m_WindowHandle, item->m_ListItemHandle, itemPosition);
++}
++
++void CAddonGUIWindow::RemoveItem(int itemPosition)
++{
++ m_cb->Window_RemoveItem(m_Handle->addonData, m_WindowHandle, itemPosition);
++}
++
++GUIHANDLE CAddonGUIWindow::GetListItem(int listPos)
++{
++ return m_cb->Window_GetListItem(m_Handle->addonData, m_WindowHandle, listPos);
++}
++
++void CAddonGUIWindow::SetCurrentListPosition(int listPos)
++{
++ m_cb->Window_SetCurrentListPosition(m_Handle->addonData, m_WindowHandle, listPos);
++}
++
++int CAddonGUIWindow::GetCurrentListPosition()
++{
++ return m_cb->Window_GetCurrentListPosition(m_Handle->addonData, m_WindowHandle);
++}
++
++void CAddonGUIWindow::SetControlLabel(int controlId, const char *label)
++{
++ m_cb->Window_SetControlLabel(m_Handle->addonData, m_WindowHandle, controlId, label);
++}
++
++void CAddonGUIWindow::AddContextMenuButton(int controlId,unsigned int contextButtonId,const char* label)
++{
++ m_cb->Window_AddContextMenuButton(m_Handle->addonData,m_WindowHandle,controlId,contextButtonId,label);
++}
++
++///-------------------------------------
++/// cGUISpinControl
++
++DLLEXPORT CAddonGUISpinControl* GUI_control_get_spin(CAddonGUIWindow *window, int controlId)
++{
++ return new CAddonGUISpinControl(window, controlId);
++}
++
++DLLEXPORT void GUI_control_release_spin(CAddonGUISpinControl* p)
++{
++ delete p;
++}
++
++CAddonGUISpinControl::CAddonGUISpinControl(CAddonGUIWindow *window, int controlId)
++ : m_Window(window)
++ , m_ControlId(controlId)
++{
++ m_SpinHandle = m_cb->Window_GetControl_Spin(m_Handle->addonData, m_Window->m_WindowHandle, controlId);
++}
++
++void CAddonGUISpinControl::SetVisible(bool yesNo)
++{
++ if (m_SpinHandle)
++ m_cb->Control_Spin_SetVisible(m_Handle->addonData, m_SpinHandle, yesNo);
++}
++
++void CAddonGUISpinControl::SetText(const char *label)
++{
++ if (m_SpinHandle)
++ m_cb->Control_Spin_SetText(m_Handle->addonData, m_SpinHandle, label);
++}
++
++void CAddonGUISpinControl::Clear()
++{
++ if (m_SpinHandle)
++ m_cb->Control_Spin_Clear(m_Handle->addonData, m_SpinHandle);
++}
++
++void CAddonGUISpinControl::AddLabel(const char *label, int iValue)
++{
++ if (m_SpinHandle)
++ m_cb->Control_Spin_AddLabel(m_Handle->addonData, m_SpinHandle, label, iValue);
++}
++
++int CAddonGUISpinControl::GetValue()
++{
++ if (!m_SpinHandle)
++ return -1;
++
++ return m_cb->Control_Spin_GetValue(m_Handle->addonData, m_SpinHandle);
++}
++
++void CAddonGUISpinControl::SetValue(int iValue)
++{
++ if (m_SpinHandle)
++ m_cb->Control_Spin_SetValue(m_Handle->addonData, m_SpinHandle, iValue);
++}
++///-------------------------------------
++/// cGUIListContainer
++
++DLLEXPORT CAddonGUIListContainer* GUI_control_get_listcontainer(CAddonGUIWindow *window, int controlId)
++{
++ return new CAddonGUIListContainer(window, controlId);
++}
++
++DLLEXPORT void GUI_control_release_listcontainer(CAddonGUIListContainer* p)
++{
++ delete p;
++}
++
++CAddonGUIListContainer::CAddonGUIListContainer(CAddonGUIWindow *window, int controlId)
++ : m_Window(window)
++ , m_ControlId(controlId)
++ ,m_Items(NULL)
++ ,m_ListHandle(NULL)
++{
++ m_ListHandle = m_cb->Window_GetControl_ListContainer(m_Handle->addonData, m_Window->m_WindowHandle, controlId,&m_Items);
++}
++
++void CAddonGUIListContainer::SetVisible(bool yesNo)
++{
++ if (m_ListHandle)
++ m_cb->Control_ListContainer_SetVisible(m_Handle->addonData, m_ListHandle, yesNo);
++}
++
++void CAddonGUIListContainer::AddItem(CAddonListItem *item)
++{
++ if (!m_ListHandle || !item)
++ return;
++ m_cb->Control_ListContainer_AddItems(m_Handle->addonData,m_ListHandle,m_Items,&(item->m_ListItemHandle),1);
++}
++
++void CAddonGUIListContainer::AddItems(CAddonListItem* items[],int size)
++{
++ if (!m_ListHandle || !items)
++ return;
++ vector<GUIHANDLE> vecItems;
++ for(int i = 0; i < size; i++)
++ vecItems.push_back(items[i]->m_ListItemHandle);
++ m_cb->Control_ListContainer_AddItems(m_Handle->addonData,m_ListHandle,m_Items,&vecItems[0],vecItems.size());
++}
++
++
++CAddonListItem* CAddonGUIListContainer::GetItem(int index)
++{
++ if (!m_ListHandle)
++ return NULL;
++ GUIHANDLE item=(GUIHANDLE)m_cb->Control_ListContainer_GetItem(m_Handle->addonData,m_ListHandle,m_Items,index);
++ return new CAddonListItem(item);
++}
++
++int CAddonGUIListContainer::GetSelected()
++{
++ if (!m_ListHandle)
++ return -1;
++ return m_cb->Control_ListContainer_GetSelected(m_Handle->addonData,m_ListHandle);
++}
++
++void CAddonGUIListContainer::ResetList()
++{
++ if (!m_ListHandle)
++ return;
++ m_cb->Control_ListContainer_Reset(m_Handle->addonData,m_ListHandle,m_Items);
++}
++/*
++class CAddonGUIListContainer
++{
++public:
++ CAddonGUIListContainer(CAddonGUIWindow *window, int controlId);
++ ~CAddonGUIListContainer() {}
++
++ virtual void SetVisible(bool yesNo);
++ virtual void AddItems(CAddonListItem *items);
++ virtual void GetItem(bool yesNo);
++
++private:
++ CAddonGUIWindow *m_Window;
++ int m_ControlId;
++ GUIHANDLE m_ButtonHandle;
++};*/
++
++///-------------------------------------
++/// cGUIRadioButton
++
++DLLEXPORT CAddonGUIRadioButton* GUI_control_get_radiobutton(CAddonGUIWindow *window, int controlId)
++{
++ return new CAddonGUIRadioButton(window, controlId);
++}
++
++DLLEXPORT void GUI_control_release_radiobutton(CAddonGUIRadioButton* p)
++{
++ delete p;
++}
++
++CAddonGUIRadioButton::CAddonGUIRadioButton(CAddonGUIWindow *window, int controlId)
++ : m_Window(window)
++ , m_ControlId(controlId)
++{
++ m_ButtonHandle = m_cb->Window_GetControl_RadioButton(m_Handle->addonData, m_Window->m_WindowHandle, controlId);
++}
++
++void CAddonGUIRadioButton::SetVisible(bool yesNo)
++{
++ if (m_ButtonHandle)
++ m_cb->Control_RadioButton_SetVisible(m_Handle->addonData, m_ButtonHandle, yesNo);
++}
++
++void CAddonGUIRadioButton::SetText(const char *label)
++{
++ if (m_ButtonHandle)
++ m_cb->Control_RadioButton_SetText(m_Handle->addonData, m_ButtonHandle, label);
++}
++
++void CAddonGUIRadioButton::SetSelected(bool yesNo)
++{
++ if (m_ButtonHandle)
++ m_cb->Control_RadioButton_SetSelected(m_Handle->addonData, m_ButtonHandle, yesNo);
++}
++
++bool CAddonGUIRadioButton::IsSelected()
++{
++ if (!m_ButtonHandle)
++ return false;
++
++ return m_cb->Control_RadioButton_IsSelected(m_Handle->addonData, m_ButtonHandle);
++}
++
++
++///-------------------------------------
++/// cGUIProgressControl
++
++DLLEXPORT CAddonGUIProgressControl* GUI_control_get_progress(CAddonGUIWindow *window, int controlId)
++{
++ return new CAddonGUIProgressControl(window, controlId);
++}
++
++DLLEXPORT void GUI_control_release_progress(CAddonGUIProgressControl* p)
++{
++ delete p;
++}
++
++CAddonGUIProgressControl::CAddonGUIProgressControl(CAddonGUIWindow *window, int controlId)
++ : m_Window(window)
++ , m_ControlId(controlId)
++{
++ m_ProgressHandle = m_cb->Window_GetControl_Progress(m_Handle->addonData, m_Window->m_WindowHandle, controlId);
++}
++
++void CAddonGUIProgressControl::SetPercentage(float fPercent)
++{
++ if (m_ProgressHandle)
++ m_cb->Control_Progress_SetPercentage(m_Handle->addonData, m_ProgressHandle, fPercent);
++}
++
++float CAddonGUIProgressControl::GetPercentage() const
++{
++ if (!m_ProgressHandle)
++ return 0.0;
++
++ return m_cb->Control_Progress_GetPercentage(m_Handle->addonData, m_ProgressHandle);
++}
++
++void CAddonGUIProgressControl::SetInfo(int iInfo)
++{
++ if (m_ProgressHandle)
++ m_cb->Control_Progress_SetInfo(m_Handle->addonData, m_ProgressHandle, iInfo);
++}
++
++int CAddonGUIProgressControl::GetInfo() const
++{
++ if (!m_ProgressHandle)
++ return -1;
++
++ return m_cb->Control_Progress_GetInfo(m_Handle->addonData, m_ProgressHandle);
++}
++
++string CAddonGUIProgressControl::GetDescription() const
++{
++ if (!m_ProgressHandle)
++ return "";
++
++ return m_cb->Control_Progress_GetDescription(m_Handle->addonData, m_ProgressHandle);
++}
++
++
++///-------------------------------------
++/// cListItem
++
++DLLEXPORT CAddonListItem* GUI_ListItem_create(const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path)
++{
++ return new CAddonListItem(label, label2, iconImage, thumbnailImage, path);
++}
++
++DLLEXPORT void GUI_ListItem_destroy(CAddonListItem* p)
++{
++ delete p;
++}
++
++CAddonListItem::CAddonListItem(const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path)
++{
++ m_ListItemHandle = m_cb->ListItem_Create(m_Handle->addonData, label, label2, iconImage, thumbnailImage, path);
++}
++
++CAddonListItem::CAddonListItem(GUIHANDLE ListItemHandle)
++ : m_ListItemHandle(ListItemHandle)
++{
++}
++
++/*CAddonListItem::~CAddonListItem(void)
++{
++ //m_ListItemHandle will leak if it has not been assigned to a list (i.e. a smartpointer)
++}*/
++
++const char *CAddonListItem::GetLabel()
++{
++ if (!m_ListItemHandle)
++ return "";
++
++ return m_cb->ListItem_GetLabel(m_Handle->addonData, m_ListItemHandle);
++}
++
++void CAddonListItem::SetLabel(const char *label)
++{
++ if (m_ListItemHandle)
++ m_cb->ListItem_SetLabel(m_Handle->addonData, m_ListItemHandle, label);
++}
++
++const char *CAddonListItem::GetLabel2()
++{
++ if (!m_ListItemHandle)
++ return "";
++
++ return m_cb->ListItem_GetLabel2(m_Handle->addonData, m_ListItemHandle);
++}
++
++void CAddonListItem::SetLabel2(const char *label)
++{
++ if (m_ListItemHandle)
++ m_cb->ListItem_SetLabel2(m_Handle->addonData, m_ListItemHandle, label);
++}
++
++void CAddonListItem::SetIconImage(const char *image)
++{
++ if (m_ListItemHandle)
++ m_cb->ListItem_SetIconImage(m_Handle->addonData, m_ListItemHandle, image);
++}
++
++void CAddonListItem::SetThumbnailImage(const char *image)
++{
++ if (m_ListItemHandle)
++ m_cb->ListItem_SetThumbnailImage(m_Handle->addonData, m_ListItemHandle, image);
++}
++
++void CAddonListItem::SetInfo(const char *Info)
++{
++ if (m_ListItemHandle)
++ m_cb->ListItem_SetInfo(m_Handle->addonData, m_ListItemHandle, Info);
++}
++
++void CAddonListItem::SetProperty(const char *key, const char *value)
++{
++ if (m_ListItemHandle)
++ m_cb->ListItem_SetProperty(m_Handle->addonData, m_ListItemHandle, key, value);
++}
++
++const char *CAddonListItem::GetProperty(const char *key) const
++{
++ if (!m_ListItemHandle)
++ return "";
++
++ return m_cb->ListItem_GetProperty(m_Handle->addonData, m_ListItemHandle, key);
++}
++
++void CAddonListItem::SetPath(const char *Path)
++{
++ if (m_ListItemHandle)
++ m_cb->ListItem_SetPath(m_Handle->addonData, m_ListItemHandle, Path);
++}
++
++
++};
+diff --git a/lib/addons/library.xbmc.gui/project/VS2010Express/libXBMC_gui.vcxproj b/lib/addons/library.xbmc.gui/project/VS2010Express/libXBMC_gui.vcxproj
+new file mode 100644
+index 0000000..7b886bf
+--- /dev/null
++++ b/lib/addons/library.xbmc.gui/project/VS2010Express/libXBMC_gui.vcxproj
+@@ -0,0 +1,86 @@
++<?xml version="1.0" encoding="utf-8"?>
++<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
++ <ItemGroup Label="ProjectConfigurations">
++ <ProjectConfiguration Include="Debug|Win32">
++ <Configuration>Debug</Configuration>
++ <Platform>Win32</Platform>
++ </ProjectConfiguration>
++ <ProjectConfiguration Include="Release|Win32">
++ <Configuration>Release</Configuration>
++ <Platform>Win32</Platform>
++ </ProjectConfiguration>
++ </ItemGroup>
++ <PropertyGroup Label="Globals">
++ <ProjectGuid>{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}</ProjectGuid>
++ <RootNamespace>XBMC_VDR</RootNamespace>
++ <Keyword>Win32Proj</Keyword>
++ </PropertyGroup>
++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
++ <ConfigurationType>DynamicLibrary</ConfigurationType>
++ <CharacterSet>MultiByte</CharacterSet>
++ </PropertyGroup>
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
++ <ConfigurationType>DynamicLibrary</ConfigurationType>
++ <CharacterSet>MultiByte</CharacterSet>
++ </PropertyGroup>
++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
++ <ImportGroup Label="ExtensionSettings">
++ </ImportGroup>
++ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
++ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
++ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
++ </ImportGroup>
++ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
++ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
++ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
++ </ImportGroup>
++ <PropertyGroup Label="UserMacros" />
++ <PropertyGroup>
++ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
++ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\..\..\..\addons\library.xbmc.gui\</OutDir>
++ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Debug\</IntDir>
++ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\..\..\..\addons\library.xbmc.gui\</OutDir>
++ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Release\</IntDir>
++ </PropertyGroup>
++ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
++ <ClCompile>
++ <Optimization>Disabled</Optimization>
++ <AdditionalIncludeDirectories>..\..\..\..\..\xbmc;..\..\..\..\..\xbmc\addons\include;..\..\..\..\..\xbmc\cores\dvdplayer\DVDDemuxers;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
++ <PreprocessorDefinitions>_WIN32;_DEBUG;_WINDOWS;VDR_EXPORTS;_WIN32PC;_WINSOCKAPI_;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++ <MinimalRebuild>true</MinimalRebuild>
++ <ExceptionHandling>Sync</ExceptionHandling>
++ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
++ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
++ <PrecompiledHeader>
++ </PrecompiledHeader>
++ <WarningLevel>Level3</WarningLevel>
++ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
++ </ClCompile>
++ <Link>
++ <OutputFile>..\..\..\..\..\addons\library.xbmc.gui\$(ProjectName).dll</OutputFile>
++ <GenerateDebugInformation>true</GenerateDebugInformation>
++ </Link>
++ </ItemDefinitionGroup>
++ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
++ <ClCompile>
++ <AdditionalIncludeDirectories>..\..\..\..\..\xbmc;..\..\..\..\..\xbmc\addons\include;..\..\..\..\..\xbmc\cores\dvdplayer\DVDDemuxers;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
++ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;HAS_SDL_OPENGL;HAS_SDL;_USRDLL;XBMC_VDR_EXPORTS;_WIN32PC;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++ <ExceptionHandling>Sync</ExceptionHandling>
++ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
++ <PrecompiledHeader>
++ </PrecompiledHeader>
++ <WarningLevel>Level3</WarningLevel>
++ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
++ </ClCompile>
++ <Link>
++ <OutputFile>../../../../../addons/library.xbmc.gui/$(ProjectName).dll</OutputFile>
++ </Link>
++ </ItemDefinitionGroup>
++ <ItemGroup>
++ <ClCompile Include="..\..\libXBMC_gui.cpp" />
++ </ItemGroup>
++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
++ <ImportGroup Label="ExtensionTargets">
++ </ImportGroup>
++</Project>
+\ No newline at end of file
+diff --git a/lib/addons/library.xbmc.gui/project/VS2010Express/libXBMC_gui.vcxproj.filters b/lib/addons/library.xbmc.gui/project/VS2010Express/libXBMC_gui.vcxproj.filters
+new file mode 100644
+index 0000000..7ccd231
+--- /dev/null
++++ b/lib/addons/library.xbmc.gui/project/VS2010Express/libXBMC_gui.vcxproj.filters
+@@ -0,0 +1,14 @@
++<?xml version="1.0" encoding="utf-8"?>
++<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
++ <ItemGroup>
++ <Filter Include="Source Files">
++ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
++ <Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
++ </Filter>
++ </ItemGroup>
++ <ItemGroup>
++ <ClCompile Include="..\..\libXBMC_gui.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ </ItemGroup>
++</Project>
+\ No newline at end of file
+diff --git a/lib/addons/library.xbmc.pvr/Makefile.in b/lib/addons/library.xbmc.pvr/Makefile.in
+new file mode 100644
+index 0000000..1edf411
+--- /dev/null
++++ b/lib/addons/library.xbmc.pvr/Makefile.in
+@@ -0,0 +1,27 @@
++ARCH=@ARCH@
++INCLUDES=-I. -I../../../xbmc/addons/include -I../../../xbmc -I../../../xbmc/cores/dvdplayer/DVDDemuxers
++DEFINES+=
++CXXFLAGS=-fPIC
++LIBNAME=libXBMC_pvr
++OBJS=$(LIBNAME).o
++
++LIB_SHARED=../../../addons/library.xbmc.pvr/$(LIBNAME)-$(ARCH).so
++
++all: $(LIB_SHARED)
++
++$(LIB_SHARED): $(OBJS)
++ifeq ($(findstring osx,$(ARCH)), osx)
++ $(CXX) $(LDFLAGS) -Wl,-alias_list,@abs_top_srcdir@/xbmc/cores/DllLoader/exports/wrapper_mach_alias \
++ -bundle -undefined dynamic_lookup -read_only_relocs suppress -o $@ \
++ @abs_top_srcdir@/xbmc/cores/DllLoader/exports/wrapper.o $(OBJS)
++else
++ $(CXX) $(CFLAGS) $(LDFLAGS) -shared -g -o $(LIB_SHARED) $(OBJS)
++endif
++
++CLEAN_FILES = \
++ $(LIB_SHARED) \
++
++DISTCLEAN_FILES= \
++ Makefile \
++
++include ../../../Makefile.include
+diff --git a/lib/addons/library.xbmc.pvr/libXBMC_pvr.cpp b/lib/addons/library.xbmc.pvr/libXBMC_pvr.cpp
+new file mode 100644
+index 0000000..585abde
+--- /dev/null
++++ b/lib/addons/library.xbmc.pvr/libXBMC_pvr.cpp
+@@ -0,0 +1,181 @@
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#define USE_DEMUX // enable including of the demuxer packet structure
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <stdarg.h>
++#include <string>
++#include "../../../addons/library.xbmc.pvr/libXBMC_pvr.h"
++#include "addons/AddonCallbacks.h"
++#include "cores/dvdplayer/DVDDemuxers/DVDDemuxPacket.h"
++
++#ifdef _WIN32
++#include <windows.h>
++#define DLLEXPORT __declspec(dllexport)
++#else
++#define DLLEXPORT
++#endif
++
++using namespace std;
++
++AddonCB *m_Handle = NULL;
++CB_PVRLib *m_cb = NULL;
++
++extern "C"
++{
++
++DLLEXPORT int PVR_register_me(void *hdl)
++{
++ if (!hdl)
++ fprintf(stderr, "libXBMC_pvr-ERROR: PVRLib_register_me is called with NULL handle !!!\n");
++ else
++ {
++ m_Handle = (AddonCB*) hdl;
++ m_cb = m_Handle->PVRLib_RegisterMe(m_Handle->addonData);
++ if (!m_cb)
++ fprintf(stderr, "libXBMC_pvr-ERROR: PVRLib_register_me can't get callback table from XBMC !!!\n");
++ else
++ return 1;
++ }
++ return 0;
++}
++
++DLLEXPORT void PVR_unregister_me()
++{
++ if (m_Handle && m_cb)
++ m_Handle->PVRLib_UnRegisterMe(m_Handle->addonData, m_cb);
++}
++
++DLLEXPORT void PVR_transfer_epg_entry(const PVR_HANDLE handle, const EPG_TAG *epgentry)
++{
++ if (m_cb == NULL)
++ return;
++
++ m_cb->TransferEpgEntry(m_Handle->addonData, handle, epgentry);
++}
++
++DLLEXPORT void PVR_transfer_channel_entry(const PVR_HANDLE handle, const PVR_CHANNEL *chan)
++{
++ if (m_cb == NULL)
++ return;
++
++ m_cb->TransferChannelEntry(m_Handle->addonData, handle, chan);
++}
++
++DLLEXPORT void PVR_transfer_timer_entry(const PVR_HANDLE handle, const PVR_TIMER *timer)
++{
++ if (m_cb == NULL)
++ return;
++
++ m_cb->TransferTimerEntry(m_Handle->addonData, handle, timer);
++}
++
++DLLEXPORT void PVR_transfer_recording_entry(const PVR_HANDLE handle, const PVR_RECORDING *recording)
++{
++ if (m_cb == NULL)
++ return;
++
++ m_cb->TransferRecordingEntry(m_Handle->addonData, handle, recording);
++}
++
++DLLEXPORT void PVR_add_menu_hook(PVR_MENUHOOK *hook)
++{
++ if (m_cb == NULL)
++ return;
++
++ m_cb->AddMenuHook(m_Handle->addonData, hook);
++}
++
++DLLEXPORT void PVR_recording(const char *Name, const char *FileName, bool On)
++{
++ if (m_cb == NULL)
++ return;
++
++ m_cb->Recording(m_Handle->addonData, Name, FileName, On);
++}
++
++DLLEXPORT void PVR_trigger_channel_update()
++{
++ if (m_cb == NULL)
++ return;
++
++ m_cb->TriggerChannelUpdate(m_Handle->addonData);
++}
++
++DLLEXPORT void PVR_trigger_channel_groups_update()
++{
++ if (m_cb == NULL)
++ return;
++
++ m_cb->TriggerChannelGroupsUpdate(m_Handle->addonData);
++}
++
++DLLEXPORT void PVR_trigger_timer_update()
++{
++ if (m_cb == NULL)
++ return;
++
++ m_cb->TriggerTimerUpdate(m_Handle->addonData);
++}
++
++DLLEXPORT void PVR_trigger_recording_update()
++{
++ if (m_cb == NULL)
++ return;
++
++ m_cb->TriggerRecordingUpdate(m_Handle->addonData);
++}
++
++DLLEXPORT void PVR_free_demux_packet(DemuxPacket* pPacket)
++{
++ if (m_cb == NULL)
++ return;
++
++ m_cb->FreeDemuxPacket(m_Handle->addonData, pPacket);
++}
++
++DLLEXPORT DemuxPacket* PVR_allocate_demux_packet(int iDataSize)
++{
++ if (m_cb == NULL)
++ return NULL;
++
++ return m_cb->AllocateDemuxPacket(m_Handle->addonData, iDataSize);
++}
++
++DLLEXPORT void PVR_transfer_channel_group(const PVR_HANDLE handle, const PVR_CHANNEL_GROUP *group)
++{
++ if (m_cb == NULL)
++ return;
++
++ m_cb->TransferChannelGroup(m_Handle->addonData, handle, group);
++}
++
++DLLEXPORT void PVR_transfer_channel_group_member(const PVR_HANDLE handle, const PVR_CHANNEL_GROUP_MEMBER *member)
++{
++ if (m_cb == NULL)
++ return;
++
++ m_cb->TransferChannelGroupMember(m_Handle->addonData, handle, member);
++}
++
++};
+diff --git a/lib/addons/library.xbmc.pvr/project/VS2010Express/libXBMC_pvr.vcxproj b/lib/addons/library.xbmc.pvr/project/VS2010Express/libXBMC_pvr.vcxproj
+new file mode 100644
+index 0000000..0d9986a
+--- /dev/null
++++ b/lib/addons/library.xbmc.pvr/project/VS2010Express/libXBMC_pvr.vcxproj
+@@ -0,0 +1,86 @@
++<?xml version="1.0" encoding="utf-8"?>
++<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
++ <ItemGroup Label="ProjectConfigurations">
++ <ProjectConfiguration Include="Debug|Win32">
++ <Configuration>Debug</Configuration>
++ <Platform>Win32</Platform>
++ </ProjectConfiguration>
++ <ProjectConfiguration Include="Release|Win32">
++ <Configuration>Release</Configuration>
++ <Platform>Win32</Platform>
++ </ProjectConfiguration>
++ </ItemGroup>
++ <PropertyGroup Label="Globals">
++ <ProjectGuid>{6D8C91F8-992F-4C83-9DE3-485D64EF8420}</ProjectGuid>
++ <RootNamespace>XBMC_VDR</RootNamespace>
++ <Keyword>Win32Proj</Keyword>
++ </PropertyGroup>
++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
++ <ConfigurationType>DynamicLibrary</ConfigurationType>
++ <CharacterSet>MultiByte</CharacterSet>
++ </PropertyGroup>
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
++ <ConfigurationType>DynamicLibrary</ConfigurationType>
++ <CharacterSet>MultiByte</CharacterSet>
++ </PropertyGroup>
++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
++ <ImportGroup Label="ExtensionSettings">
++ </ImportGroup>
++ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
++ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
++ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
++ </ImportGroup>
++ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
++ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
++ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
++ </ImportGroup>
++ <PropertyGroup Label="UserMacros" />
++ <PropertyGroup>
++ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
++ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\..\..\..\addons\library.xbmc.pvr\</OutDir>
++ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Debug\</IntDir>
++ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\..\..\..\addons\library.xbmc.pvr\</OutDir>
++ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Release\</IntDir>
++ </PropertyGroup>
++ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
++ <ClCompile>
++ <Optimization>Disabled</Optimization>
++ <AdditionalIncludeDirectories>..\..\..\..\..\xbmc;..\..\..\..\..\xbmc\addons\include;..\..\..\..\..\xbmc\cores\dvdplayer\DVDDemuxers;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
++ <PreprocessorDefinitions>_WIN32;_DEBUG;_WINDOWS;VDR_EXPORTS;_WIN32PC;_WINSOCKAPI_;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++ <MinimalRebuild>true</MinimalRebuild>
++ <ExceptionHandling>Sync</ExceptionHandling>
++ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
++ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
++ <PrecompiledHeader>
++ </PrecompiledHeader>
++ <WarningLevel>Level3</WarningLevel>
++ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
++ </ClCompile>
++ <Link>
++ <OutputFile>..\..\..\..\..\addons\library.xbmc.pvr\$(ProjectName).dll</OutputFile>
++ <GenerateDebugInformation>true</GenerateDebugInformation>
++ </Link>
++ </ItemDefinitionGroup>
++ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
++ <ClCompile>
++ <AdditionalIncludeDirectories>..\..\..\..\..\xbmc;..\..\..\..\..\xbmc\addons\include;..\..\..\..\..\xbmc\cores\dvdplayer\DVDDemuxers;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
++ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;HAS_SDL_OPENGL;HAS_SDL;_USRDLL;XBMC_VDR_EXPORTS;_WIN32PC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++ <ExceptionHandling>Sync</ExceptionHandling>
++ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
++ <PrecompiledHeader>
++ </PrecompiledHeader>
++ <WarningLevel>Level3</WarningLevel>
++ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
++ </ClCompile>
++ <Link>
++ <OutputFile>..\..\..\..\..\addons\library.xbmc.pvr\$(ProjectName).dll</OutputFile>
++ </Link>
++ </ItemDefinitionGroup>
++ <ItemGroup>
++ <ClCompile Include="..\..\libXBMC_pvr.cpp" />
++ </ItemGroup>
++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
++ <ImportGroup Label="ExtensionTargets">
++ </ImportGroup>
++</Project>
+\ No newline at end of file
+diff --git a/lib/addons/library.xbmc.pvr/project/VS2010Express/libXBMC_pvr.vcxproj.filters b/lib/addons/library.xbmc.pvr/project/VS2010Express/libXBMC_pvr.vcxproj.filters
+new file mode 100644
+index 0000000..db93c59
+--- /dev/null
++++ b/lib/addons/library.xbmc.pvr/project/VS2010Express/libXBMC_pvr.vcxproj.filters
+@@ -0,0 +1,18 @@
++<?xml version="1.0" encoding="utf-8"?>
++<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
++ <ItemGroup>
++ <Filter Include="Source Files">
++ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
++ <Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
++ </Filter>
++ <Filter Include="Header Files">
++ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
++ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
++ </Filter>
++ </ItemGroup>
++ <ItemGroup>
++ <ClCompile Include="..\..\libXBMC_pvr.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ </ItemGroup>
++</Project>
+\ No newline at end of file
+diff --git a/lib/cmyth/Win32/libcmyth.def b/lib/cmyth/Win32/libcmyth.def
+index 7ea4356..a4ed819 100644
+--- a/lib/cmyth/Win32/libcmyth.def
++++ b/lib/cmyth/Win32/libcmyth.def
+@@ -98,6 +98,7 @@ EXPORTS
+ cmyth_chanlist_get_count
+ cmyth_channel_chanid
+ cmyth_channel_channum
++ cmyth_channel_channumstr
+ cmyth_channel_name
+ cmyth_channel_icon
+ cmyth_channel_visible
+@@ -117,6 +118,8 @@ EXPORTS
+ cmyth_proginfo_category
+ cmyth_proginfo_seriesid
+ cmyth_proginfo_programid
++ cmyth_proginfo_recordid
++ cmyth_proginfo_priority
+ cmyth_proginfo_stars
+ cmyth_proginfo_playgroup
+ cmyth_proginfo_originalairdate
+@@ -215,4 +218,66 @@ EXPORTS
+ cmyth_ringbuf_read
+ cmyth_file_read
+ cmyth_livetv_read
+- cmyth_get_cutlist
+\ No newline at end of file
++ cmyth_get_cutlist
++ cmyth_mysql_update_timer
++ cmyth_mysql_delete_timer
++ cmyth_mysql_add_timer
++ cmyth_mysql_get_timers
++ cmyth_mysql_is_radio
++ cmyth_livetv_chain_duration
++ cmyth_timer_recordid
++ cmyth_timer_chanid
++ cmyth_timer_starttime
++ cmyth_timer_endtime
++ cmyth_timer_title
++ cmyth_timer_description
++ cmyth_timer_type
++ cmyth_timer_category
++ cmyth_timer_subtitle
++ cmyth_timer_priority
++ cmyth_timer_startoffset
++ cmyth_timer_endoffset
++ cmyth_timer_searchtype
++ cmyth_timer_inactive
++ cmyth_timer_callsign
++ cmyth_timer_dup_method
++ cmyth_timer_dup_in
++ cmyth_timer_rec_group
++ cmyth_timer_store_group
++ cmyth_timer_play_group
++ cmyth_timer_autotranscode
++ cmyth_timer_userjobs
++ cmyth_timer_autocommflag
++ cmyth_timer_autoexpire
++ cmyth_timer_maxepisodes
++ cmyth_timer_maxnewest
++ cmyth_timerlist_get_item
++ cmyth_timerlist_get_count
++ cmyth_mysql_get_channelgroups
++ cmyth_mysql_get_channelids_in_group
++ cmyth_channel_sourceid
++ cmyth_channel_multiplex
++ cmyth_mysql_get_recorder_list
++ cmyth_mysql_get_prog_finder_time_title_chan
++ cmyth_conn_set_setting
++ cmyth_conn_get_backend_hostname
++ cmyth_conn_get_client_hostname
++ cmyth_conn_get_setting
++ cmyth_timer_transcoder
++ cmyth_mysql_get_storagegroups
++ cmyth_mysql_get_playgroups
++ cmyth_mysql_get_recprofiles
++ cmyth_mysql_get_cardtype
++ cmyth_channel_callsign
++ cmyth_get_watched_status_mysql
++ cmyth_set_watched_status_mysql
++ cmyth_file_position
++ cmyth_update_file_length
++ cmyth_storagegroup_filelist
++ cmyth_storagegroup_filelist_get_item
++ cmyth_storagegroup_filelist_count
++ cmyth_storagegroup_get_filelist
++ cmyth_storagegroup_file_get_filename
++ cmyth_storagegroup_file_get_lastmodified
++ cmyth_storagegroup_file_get_size
++
+diff --git a/lib/cmyth/Win32/libcmyth.vcxproj b/lib/cmyth/Win32/libcmyth.vcxproj
+index a698d8a..9619138 100644
+--- a/lib/cmyth/Win32/libcmyth.vcxproj
++++ b/lib/cmyth/Win32/libcmyth.vcxproj
+@@ -54,7 +54,7 @@
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..;../include;include;../librefmem;../libcmyth;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+- <PreprocessorDefinitions>WIN32;DEBUG;_LIB;inline=__inline; _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++ <PreprocessorDefinitions>WIN32;DEBUG;_LIB;inline=__inline;_CRT_SECURE_NO_WARNINGS;_USE_32BIT_TIME_T;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>true</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+@@ -75,6 +75,7 @@
+ <DataExecutionPrevention>
+ </DataExecutionPrevention>
+ <ImportLibrary>$(Configuration)\vs2010\$(TargetName).lib</ImportLibrary>
++ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ <PostBuildEvent>
+ <Command>
+@@ -94,7 +95,7 @@
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>..;../include;include;../librefmem;../libcmyth;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+- <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;inline=__inline; _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++ <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;inline=__inline;_CRT_SECURE_NO_WARNINGS;_USE_32BIT_TIME_T;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+@@ -113,6 +114,7 @@
+ <DataExecutionPrevention>
+ </DataExecutionPrevention>
+ <ImportLibrary>$(Configuration)\vs2010\$(TargetName).lib</ImportLibrary>
++ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ <PostBuildEvent>
+ <Command>
+diff --git a/lib/cmyth/include/cmyth/cmyth.h b/lib/cmyth/include/cmyth/cmyth.h
+index 52f2f68..090381d 100644
+--- a/lib/cmyth/include/cmyth/cmyth.h
++++ b/lib/cmyth/include/cmyth/cmyth.h
+@@ -255,7 +255,7 @@ extern cmyth_file_t cmyth_conn_connect_file(cmyth_proginfo_t prog,
+ * \return file handle
+ */
+ extern cmyth_file_t cmyth_conn_connect_path(char* path, cmyth_conn_t control,
+- unsigned buflen, int tcp_rcvbuf);
++ unsigned buflen, int tcp_rcvbuf,char* sgToGetFrom);
+
+ /**
+ * Create a ring buffer connection to a recorder.
+@@ -349,6 +349,30 @@ extern int cmyth_conn_get_protocol_version(cmyth_conn_t conn);
+ extern char * cmyth_conn_get_setting(cmyth_conn_t conn,
+ const char* hostname, const char* setting);
+
++/**
++ * Set a MythTV setting for a hostname
++ * \param conn connection handle
++ * \param hostname hostname to apply the setting to
++ * \param setting the setting name to set
++ * \param value the value of the setting
++ * \retval <0 for failure
++ */
++extern int cmyth_conn_set_setting(cmyth_conn_t conn,
++ const char* hostname, const char* setting, const char* value);
++
++/**
++ * Get the backend hostname used in the settings database
++ * \param conn connection handle
++ * \retval ref counted string with backend hostname
++ */
++extern char* cmyth_conn_get_backend_hostname(cmyth_conn_t conn);
++
++/**
++ * Get the client hostname used when creating the backend connection
++ * \param conn connection handle
++ * \retval ref counted string with client hostname
++ */
++extern char* cmyth_conn_get_client_hostname(cmyth_conn_t conn);
+ /*
+ * -----------------------------------------------------------------
+ * Event Operations
+@@ -524,6 +548,8 @@ extern cmyth_livetv_chain_t cmyth_livetv_chain_create(char * chainid);
+
+ extern cmyth_file_t cmyth_livetv_get_cur_file(cmyth_recorder_t rec);
+
++extern long long cmyth_livetv_chain_duration(cmyth_recorder_t rec);
++
+ extern int cmyth_livetv_chain_switch(cmyth_recorder_t rec, int dir);
+
+ extern int cmyth_livetv_chain_switch_last(cmyth_recorder_t rec);
+@@ -573,6 +599,9 @@ extern int cmyth_database_set_user(cmyth_database_t db, char *user);
+ extern int cmyth_database_set_pass(cmyth_database_t db, char *pass);
+ extern int cmyth_database_set_name(cmyth_database_t db, char *name);
+
++extern int cmyth_get_watched_status_mysql(cmyth_database_t db, int recordid);
++extern int cmyth_set_watched_status_mysql(cmyth_database_t db, int recordid, int watchedStat);
++
+ /*
+ * -----------------------------------------------------------------
+ * Ring Buffer Operations
+@@ -805,6 +834,20 @@ extern char *cmyth_proginfo_seriesid(cmyth_proginfo_t prog);
+ extern char *cmyth_proginfo_programid(cmyth_proginfo_t prog);
+
+ /**
++ * Retrieve the record ID of the matching record rule
++ * \param prog proginfo handle
++ * \return unsigned long
++ */
++extern unsigned long cmyth_proginfo_recordid(cmyth_proginfo_t prog);
++
++/**
++ * Retrieve the priority of a program
++ * \param prog proginfo handle
++ * \return long
++ */
++extern long cmyth_proginfo_priority(cmyth_proginfo_t prog);
++
++/**
+ * Retrieve the critics rating (number of stars) of a program.
+ * \param prog proginfo handle
+ * \return null-terminated string
+@@ -962,6 +1005,10 @@ extern unsigned long long cmyth_file_start(cmyth_file_t file);
+
+ extern unsigned long long cmyth_file_length(cmyth_file_t file);
+
++extern unsigned long long cmyth_file_position(cmyth_file_t file);
++
++extern int cmyth_update_file_length(cmyth_file_t file, unsigned long long newLength);
++
+ extern int cmyth_file_get_block(cmyth_file_t file, char *buf,
+ unsigned long len);
+
+@@ -988,6 +1035,10 @@ extern long cmyth_channel_chanid(cmyth_channel_t channel);
+
+ extern long cmyth_channel_channum(cmyth_channel_t channel);
+
++extern char * cmyth_channel_channumstr(cmyth_channel_t channel);
++
++extern char * cmyth_channel_callsign(cmyth_channel_t channel);
++
+ extern char * cmyth_channel_name(cmyth_channel_t channel);
+
+ extern char * cmyth_channel_icon(cmyth_channel_t channel);
+@@ -1076,4 +1127,107 @@ extern int cmyth_get_delete_list(cmyth_conn_t, char *, cmyth_proglist_t);
+ extern int cmyth_mythtv_remove_previos_recorded(cmyth_database_t db,char *query);
+
+ extern cmyth_chanlist_t cmyth_mysql_get_chanlist(cmyth_database_t db);
++
++/*tsp*/
++extern int cmyth_mysql_is_radio(cmyth_database_t db, int chanid);
++
++/* timers */
++struct cmyth_timer;
++typedef struct cmyth_timer* cmyth_timer_t;
++
++struct cmyth_timerlist;
++typedef struct cmyth_timerlist* cmyth_timerlist_t;
++
++extern int cmyth_timer_recordid(cmyth_timer_t timer);
++extern int cmyth_timer_chanid(cmyth_timer_t timer);
++extern time_t cmyth_timer_starttime(cmyth_timer_t timer);
++extern time_t cmyth_timer_endtime(cmyth_timer_t timer);
++extern char* cmyth_timer_title(cmyth_timer_t timer);
++extern char* cmyth_timer_description(cmyth_timer_t timer);
++extern int cmyth_timer_type(cmyth_timer_t timer);
++extern char* cmyth_timer_category(cmyth_timer_t timer);
++extern char* cmyth_timer_subtitle(cmyth_timer_t timer);
++extern int cmyth_timer_priority(cmyth_timer_t timer);
++extern int cmyth_timer_startoffset(cmyth_timer_t timer);
++extern int cmyth_timer_endoffset(cmyth_timer_t timer);
++extern int cmyth_timer_searchtype(cmyth_timer_t timer);
++extern int cmyth_timer_inactive(cmyth_timer_t timer);
++extern char* cmyth_timer_callsign(cmyth_timer_t timer);
++extern int cmyth_timer_dup_method(cmyth_timer_t timer);
++extern int cmyth_timer_dup_in(cmyth_timer_t timer);
++extern char* cmyth_timer_rec_group(cmyth_timer_t timer);
++extern char* cmyth_timer_store_group(cmyth_timer_t timer);
++extern char* cmyth_timer_play_group(cmyth_timer_t timer);
++extern int cmyth_timer_autotranscode(cmyth_timer_t timer);
++extern int cmyth_timer_userjobs(cmyth_timer_t timer);
++extern int cmyth_timer_autocommflag(cmyth_timer_t timer);
++extern int cmyth_timer_autoexpire(cmyth_timer_t timer);
++extern int cmyth_timer_maxepisodes(cmyth_timer_t timer);
++extern int cmyth_timer_maxnewest(cmyth_timer_t timer);
++extern int cmyth_timer_transcoder(cmyth_timer_t timer);
++
++
++extern cmyth_timer_t cmyth_timerlist_get_item(cmyth_timerlist_t pl, int index);
++extern int cmyth_timerlist_get_count(cmyth_timerlist_t pl);
++
++extern cmyth_timerlist_t cmyth_mysql_get_timers(cmyth_database_t db);
++
++
++extern int cmyth_mysql_add_timer(cmyth_database_t db, int chanid,char* callsign,char* description, time_t starttime, time_t endtime,char* title,char* category,int type,char* subtitle,int priority,int startoffset,int endoffset,int searchtype,int inactive,
++ int dup_method, int dup_in, char* rec_group, char* store_group, char* play_group, int autotranscode, int userjobs, int autocommflag, int autoexpire, int maxepisodes, int maxnewest, int transcoder);
++extern int cmyth_mysql_delete_timer(cmyth_database_t db, int recordid);
++extern int cmyth_mysql_update_timer(cmyth_database_t db, int recordid, int chanid,char* callsign,char* description, time_t starttime, time_t endtime,char* title,char* category, int type,char* subtitle,int priority,int startoffset,int endoffset,int searchtype,int inactive,
++ int dup_method, int dup_in, char* rec_group, char* store_group, char* play_group, int autotranscode, int userjobs, int autocommflag, int autoexpire, int maxepisodes, int maxnewest, int transcoder);
++
++typedef struct cmyth_channelgroups {
++ char channelgroup[65];
++ unsigned int ID;
++} cmyth_channelgroups_t;
++
++extern int cmyth_mysql_get_channelgroups(cmyth_database_t db,cmyth_channelgroups_t** changroups);
++extern int cmyth_mysql_get_channelids_in_group(cmyth_database_t db,unsigned int groupid,int** chanids);
++
++extern int cmyth_channel_sourceid(cmyth_channel_t channel);
++extern int cmyth_channel_multiplex(cmyth_channel_t channel);
++
++typedef struct cmyth_rec {
++ int recid;
++ int sourceid;
++} cmyth_rec_t;
++
++extern int cmyth_mysql_get_recorder_list(cmyth_database_t db,cmyth_rec_t** reclist);
++
++extern int cmyth_mysql_get_prog_finder_time_title_chan(cmyth_database_t db,cmyth_program_t *prog, char* title,time_t starttime,int chanid);
++
++extern int cmyth_mysql_get_storagegroups(cmyth_database_t db, char** *profiles);
++extern int cmyth_mysql_get_playgroups(cmyth_database_t db, char** *profiles);
++
++typedef struct cmyth_recprofile{
++int id;
++char name[128];
++char cardtype[32];
++} cmyth_recprofile_t;
++
++extern int cmyth_mysql_get_recprofiles(cmyth_database_t db, cmyth_recprofile_t** profiles);
++
++extern char* cmyth_mysql_get_cardtype(cmyth_database_t db, int chanid);
++
++/* Get a storage group file list */
++extern int cmyth_storagegroup_filelist(cmyth_conn_t control, char** *sgFilelist, char* sg2List, char* mythostname);
++
++struct cmyth_storagegroup_filelist;
++typedef struct cmyth_storagegroup_filelist* cmyth_storagegroup_filelist_t;
++
++struct cmyth_storagegroup_file;
++typedef struct cmyth_storagegroup_file* cmyth_storagegroup_file_t;
++
++extern cmyth_storagegroup_file_t cmyth_storagegroup_filelist_get_item(cmyth_storagegroup_filelist_t fl, int index);
++extern int cmyth_storagegroup_filelist_count(cmyth_storagegroup_filelist_t fl);
++
++extern cmyth_storagegroup_filelist_t cmyth_storagegroup_get_filelist(cmyth_conn_t control,char* storagegroup, char* hostname);
++
++extern char* cmyth_storagegroup_file_get_filename(cmyth_storagegroup_file_t file);
++extern unsigned long cmyth_storagegroup_file_get_lastmodified(cmyth_storagegroup_file_t file);
++extern unsigned long long cmyth_storagegroup_file_get_size(cmyth_storagegroup_file_t file);
++
+ #endif /* __CMYTH_H */
+diff --git a/lib/cmyth/include/refmem/atomic.h b/lib/cmyth/include/refmem/atomic.h
+index 71d9a6e..9aba22e 100644
+--- a/lib/cmyth/include/refmem/atomic.h
++++ b/lib/cmyth/include/refmem/atomic.h
+@@ -74,7 +74,7 @@ __mvp_atomic_decrement(mvp_atomic_t *valp)
+ "lock xaddl %0, (%1);"
+ " dec %0;"
+ : "=r" (__val)
+- : "r" (valp), "0" (0x1)
++ : "r" (valp), "0" (-0x1)
+ : "cc", "memory"
+ );
+ #elif defined __i386__
+diff --git a/lib/cmyth/libcmyth/cmyth_local.h b/lib/cmyth/libcmyth/cmyth_local.h
+index 40bed11..5dc56e8 100644
+--- a/lib/cmyth/libcmyth/cmyth_local.h
++++ b/lib/cmyth/libcmyth/cmyth_local.h
+@@ -39,12 +39,16 @@
+
+ #ifdef _MSC_VER
+ #pragma warning(disable:4267)
+-#define pthread_mutex_lock(a)
+-#define pthread_mutex_unlock(a)
+-#define PTHREAD_MUTEX_INITIALIZER NULL;
+-typedef void* pthread_mutex_t;
++//#define pthread_mutex_lock(a)
++//#define pthread_mutex_unlock(a)
++#define pthread_mutex_lock(a) EnterCriticalSection(a)
++#define pthread_mutex_unlock(a) LeaveCriticalSection(a)
++//#define PTHREAD_MUTEX_INITIALIZER NULL;
++#define PTHREAD_MUTEX_INITIALIZER InitializeCriticalSection(&mutex);
++//typedef void* pthread_mutex_t;
++typedef CRITICAL_SECTION pthread_mutex_t;
+ extern pthread_mutex_t mutex;
+-#define mutex __cmyth_mutex
++//#define mutex __cmyth_mutex
+ #define SHUT_RDWR SD_BOTH
+ typedef SOCKET cmyth_socket_t;
+ typedef int socklen_t;
+@@ -135,6 +139,9 @@ struct cmyth_channel {
+ char *name;
+ char *icon;
+ int visible;
++ /* tsp - added sourceID and multiplex */
++ int sourceid;
++ int multiplex;
+ };
+
+ struct cmyth_chanlist {
+@@ -142,6 +149,49 @@ struct cmyth_chanlist {
+ int chanlist_count;
+ };
+
++
++/* tsp: Added timer */
++
++struct cmyth_timer {
++ int recordid;
++ int chanid;
++ time_t starttime;
++ time_t endtime;
++ char* title;
++ char* description;
++ int type;
++ char* category;
++ char* subtitle;
++ int priority;
++ int startoffset;
++ int endoffset;
++ int searchtype;
++ int inactive;
++ char* callsign;
++
++ int dup_method;
++ int dup_in;
++ char* rec_group;
++ char* store_group;
++ char* play_group;
++ int autotranscode;
++ int userjobs;
++ int autocommflag;
++ int autoexpire;
++ int maxepisodes;
++ int maxnewest;
++ int transcoder;
++ /*
++ char* profile;
++ int prefinput;
++ */
++ };
++
++struct cmyth_timerlist {
++ cmyth_timer_t *timerlist_list;
++ int timerlist_count;
++};
++
+ /* Sergio: Added to support the tvguide functionality */
+ struct cmyth_tvguide_progs {
+ cmyth_program_t * progs;
+@@ -163,6 +213,25 @@ struct cmyth_recorder {
+ };
+
+ /**
++ * MythTV proglist
++ */
++
++struct cmyth_storagegroup_filelist {
++ cmyth_storagegroup_file_t *storagegroup_filelist_list;
++ int storagegroup_filelist_count;
++};
++
++
++struct cmyth_storagegroup_file {
++ char* filename;
++ char* storagegroup;
++ char* hostname;
++ unsigned long modified;
++ unsigned long size;
++};
++
++
++/**
+ * MythTV file connection
+ */
+ struct cmyth_file {
+@@ -177,6 +246,8 @@ struct cmyth_file {
+ cmyth_conn_t file_control; /**< master backend connection */
+ };
+
++long long cmyth_file_seek_unlocked(cmyth_file_t file, long long offset, int whence);
++
+ struct cmyth_ringbuf {
+ cmyth_conn_t conn_data;
+ long file_id;
+diff --git a/lib/cmyth/libcmyth/connection.c b/lib/cmyth/libcmyth/connection.c
+index 9d5fb0d..10ff548 100644
+--- a/lib/cmyth/libcmyth/connection.c
++++ b/lib/cmyth/libcmyth/connection.c
+@@ -44,9 +44,32 @@
+ #include <cmyth_local.h>
+
+ static char * cmyth_conn_get_setting_unlocked(cmyth_conn_t conn, const char* hostname, const char* setting);
++static int cmyth_conn_set_setting_unlocked(cmyth_conn_t conn, const char* hostname, const char* setting, const char* value);
++#ifdef _MSC_VER
++CRITICAL_SECTION mutex;
+
+-pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
++BOOL APIENTRY DllMain(HANDLE hModule,
++ DWORD ul_reason_for_call,
++ LPVOID lpReserved)
++{
++ switch( ul_reason_for_call ) {
++ case DLL_PROCESS_ATTACH:
++ InitializeCriticalSection(&mutex);
++ break;
++ /*case DLL_THREAD_ATTACH:
++ ...
++ case DLL_THREAD_DETACH:
++ ...*/
++ case DLL_PROCESS_DETACH:
++ DeleteCriticalSection(&mutex);
++ break;
++ }
++ return TRUE;
++}
+
++#else
++pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
++#endif
+ typedef struct {
+ int version;
+ char token[9]; // 8 characters + the terminating NULL character
+@@ -553,7 +576,7 @@ cmyth_conn_connect_file(cmyth_proginfo_t prog, cmyth_conn_t control,
+ cmyth_dbg(CMYTH_DBG_PROTO, "%s: connecting data connection\n",
+ __FUNCTION__);
+ if (control->conn_version >= 17) {
+- myth_host = cmyth_conn_get_setting_unlocked(control, prog->proginfo_host,
++ myth_host = cmyth_conn_get_setting_unlocked(control, prog->proginfo_hostname,
+ "BackendServerIP");
+ }
+ if (!myth_host) {
+@@ -674,7 +697,7 @@ cmyth_conn_connect_file(cmyth_proginfo_t prog, cmyth_conn_t control,
+ */
+ cmyth_file_t
+ cmyth_conn_connect_path(char* path, cmyth_conn_t control,
+- unsigned buflen, int tcp_rcvbuf)
++ unsigned buflen, int tcp_rcvbuf, char* sgToGetFrom)
+ {
+ cmyth_conn_t conn = NULL;
+ char *announcement = NULL;
+@@ -716,7 +739,8 @@ cmyth_conn_connect_path(char* path, cmyth_conn_t control,
+ __FUNCTION__, host, port, buflen);
+ goto shut;
+ }
+- ann_size += strlen(path) + strlen(my_hostname);
++
++ ann_size += strlen(path) + strlen(my_hostname) + strlen(sgToGetFrom) + 6;
+ announcement = malloc(ann_size);
+ if (!announcement) {
+ cmyth_dbg(CMYTH_DBG_ERROR,
+@@ -724,10 +748,16 @@ cmyth_conn_connect_path(char* path, cmyth_conn_t control,
+ __FUNCTION__, ann_size);
+ goto shut;
+ }
+- if (control->conn_version >= 44) {
+- sprintf(announcement, "ANN FileTransfer %s[]:[]%s[]:[]",
+- my_hostname, path);
+- }
++ if (control->conn_version >= 44) { /*TSP: from version 44 according to the source code*/
++ if (strlen(sgToGetFrom) > 1) {
++ sprintf(announcement, "ANN FileTransfer %s 0 0 0[]:[]%s[]:[]%s",
++ my_hostname, path, sgToGetFrom);
++ }
++ else {
++ sprintf(announcement, "ANN FileTransfer %s[]:[]%s[]:[]",
++ my_hostname, path);
++ }
++ }
+ else {
+ sprintf(announcement, "ANN FileTransfer %s[]:[]%s",
+ my_hostname, path);
+@@ -1287,6 +1317,8 @@ cmyth_conn_get_protocol_version(cmyth_conn_t conn)
+ }
+
+
++
++
+ int
+ cmyth_conn_get_free_recorder_count(cmyth_conn_t conn)
+ {
+@@ -1347,6 +1379,93 @@ cmyth_conn_get_setting(cmyth_conn_t conn, const char* hostname, const char* sett
+ return result;
+ }
+
++int cmyth_conn_set_setting(cmyth_conn_t conn,
++ const char* hostname, const char* setting, const char* value)
++{
++ int result = -1;
++
++ pthread_mutex_lock(&mutex);
++ result = cmyth_conn_set_setting_unlocked(conn, hostname, setting, value);
++ pthread_mutex_unlock(&mutex);
++
++ return result;
++}
++
++char *
++cmyth_conn_get_backend_hostname(cmyth_conn_t conn)
++{
++ int count, err;
++ char* result = NULL;
++
++ pthread_mutex_lock(&mutex);
++ if(conn->conn_version < 17) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: protocol version doesn't support QUERY_HOSTNAME\n",
++ __FUNCTION__);
++ return NULL;
++ }
++
++ if (!conn) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: no connection\n",
++ __FUNCTION__);
++ return NULL;
++ }
++
++ if ((err = cmyth_send_message(conn, "QUERY_HOSTNAME")) < 0) {
++ cmyth_dbg(CMYTH_DBG_ERROR,
++ "%s: cmyth_send_message() failed (%d)\n",
++ __FUNCTION__, err);
++ goto err;
++ }
++
++ if ((count=cmyth_rcv_length(conn)) < 0) {
++ cmyth_dbg(CMYTH_DBG_ERROR,
++ "%s: cmyth_rcv_length() failed (%d)\n",
++ __FUNCTION__, count);
++ goto err;
++ }
++
++ result = ref_alloc(count+1);
++ count -= cmyth_rcv_string(conn, &err,
++ result, count, count);
++ if (err < 0) {
++ cmyth_dbg(CMYTH_DBG_ERROR,
++ "%s: cmyth_rcv_string() failed (%d)\n",
++ __FUNCTION__, err);
++ goto err;
++ }
++
++ while(count > 0 && !err) {
++ char buffer[100];
++ count -= cmyth_rcv_string(conn, &err, buffer, sizeof(buffer)-1, count);
++ buffer[sizeof(buffer)-1] = 0;
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: odd left over data %s\n", __FUNCTION__, buffer);
++ }
++ pthread_mutex_unlock(&mutex);
++
++ if(!strcmp("-1",result)) {
++ cmyth_dbg(CMYTH_DBG_PROTO,
++ "%s: Failed to retrieve backend hostname.\n",
++ __FUNCTION__);
++ return NULL;
++ }
++ return result;
++err:
++ pthread_mutex_unlock(&mutex);
++ if(result)
++ ref_release(result);
++
++ return NULL;
++}
++
++char *
++cmyth_conn_get_client_hostname(cmyth_conn_t conn)
++{
++ char* result=NULL;
++ result = ref_strdup(my_hostname);
++ return result;
++}
++
++
+ static char *
+ cmyth_conn_get_setting_unlocked(cmyth_conn_t conn, const char* hostname, const char* setting)
+ {
+@@ -1398,6 +1517,12 @@ cmyth_conn_get_setting_unlocked(cmyth_conn_t conn, const char* hostname, const c
+ cmyth_dbg(CMYTH_DBG_ERROR, "%s: odd left over data %s\n", __FUNCTION__, buffer);
+ }
+
++ if(!strcmp("-1",result)) {
++ cmyth_dbg(CMYTH_DBG_PROTO,
++ "%s: Setting: %s or hostname: %s not found.\n",
++ __FUNCTION__, setting,hostname);
++ return NULL;
++ }
+ return result;
+ err:
+ if(result)
+@@ -1406,3 +1531,37 @@ err:
+ return NULL;
+ }
+
++static int cmyth_conn_set_setting_unlocked(cmyth_conn_t conn,
++ const char* hostname, const char* setting, const char* value)
++{
++ char msg[1024];
++ int err = 0;
++
++ if(conn->conn_version < 17) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: protocol version doesn't support SET_SETTING\n",
++ __FUNCTION__);
++ return -1;
++ }
++
++ if (!conn) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: no connection\n",
++ __FUNCTION__);
++ return -2;
++ }
++
++ snprintf(msg, sizeof(msg), "SET_SETTING %s %s %s", hostname, setting, value);
++ if ((err = cmyth_send_message(conn, msg)) < 0) {
++ cmyth_dbg(CMYTH_DBG_ERROR,
++ "%s: cmyth_send_message() failed (%d)\n",
++ __FUNCTION__, err);
++ return -3;
++ }
++
++ if (cmyth_rcv_okay(conn, "OK") < 0) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_okay() failed\n",
++ __FUNCTION__);
++ return -4;
++ }
++
++ return 1;
++}
+diff --git a/lib/cmyth/libcmyth/event.c b/lib/cmyth/libcmyth/event.c
+index f340ac6..9404d45 100644
+--- a/lib/cmyth/libcmyth/event.c
++++ b/lib/cmyth/libcmyth/event.c
+@@ -88,16 +88,24 @@ cmyth_event_get(cmyth_conn_t conn, char * data, int len)
+ event = CMYTH_EVENT_LIVETV_CHAIN_UPDATE;
+ strncpy(data, tmp + 20, len);
+ } else if (strncmp(tmp, "SIGNAL", 6) == 0) {
++ int dstlen=len;
+ event = CMYTH_EVENT_SIGNAL;
++
+ /* get slock, signal, seen_pat, matching_pat */
+- while (count > 0) {
+- /* get signalmonitorvalue name */
++ while (count > 0) {
++
++ /* get signalmonitorvalue name */
+ consumed = cmyth_rcv_string(conn, &err, tmp, sizeof(tmp) - 1, count);
+ count -= consumed;
+-
++ /*strncat(data,tmp,dstlen-2);
++ strncat(data,"=",2);
++ dstlen -= consumed;*/
+ /* get signalmonitorvalue status */
+ consumed = cmyth_rcv_string(conn, &err, tmp, sizeof(tmp) - 1, count);
+ count -= consumed;
++ strncat(data,tmp,dstlen-2);
++ strncat(data,";",2);
++ dstlen -= consumed;
+ }
+ } else if (strncmp(tmp, "ASK_RECORDING", 13) == 0) {
+ event = CMYTH_EVENT_ASK_RECORDING;
+@@ -164,8 +172,8 @@ cmyth_event_select(cmyth_conn_t conn, struct timeval *timeout)
+ int ret;
+ cmyth_socket_t fd;
+
+- cmyth_dbg(CMYTH_DBG_DEBUG, "%s [%s:%d]: (trace) {\n", __FUNCTION__,
+- __FILE__, __LINE__);
++ /*cmyth_dbg(CMYTH_DBG_DEBUG, "%s [%s:%d]: (trace) {\n", __FUNCTION__,
++ __FILE__, __LINE__);*/
+
+ if (conn == NULL)
+ return -EINVAL;
+@@ -177,8 +185,8 @@ cmyth_event_select(cmyth_conn_t conn, struct timeval *timeout)
+
+ ret = select((int)fd+1, &fds, NULL, NULL, timeout);
+
+- cmyth_dbg(CMYTH_DBG_DEBUG, "%s [%s:%d]: (trace) }\n",
+- __FUNCTION__, __FILE__, __LINE__);
++ /*cmyth_dbg(CMYTH_DBG_DEBUG, "%s [%s:%d]: (trace) }\n",
++ __FUNCTION__, __FILE__, __LINE__);*/
+
+ return ret;
+ }
+diff --git a/lib/cmyth/libcmyth/file.c b/lib/cmyth/libcmyth/file.c
+index 74408ed..5379f99 100644
+--- a/lib/cmyth/libcmyth/file.c
++++ b/lib/cmyth/libcmyth/file.c
+@@ -254,6 +254,56 @@ cmyth_file_length(cmyth_file_t file)
+ }
+
+ /*
++ * cmyth_update_file_length(cmyth_file_t file, unsigned long long newLength)
++ *
++ * Scope: PUBLIC
++ *
++ * Description
++ *
++ * Updates a files length, with a value returned from a UPDATE_FILE_SIZE event
++ *
++ * Return Value:
++ *
++ * Sucess: a int value >= 0
++ *
++ * Failure: a int containing -errno
++ */
++int
++cmyth_update_file_length(cmyth_file_t file, unsigned long long newLength)
++{
++ if (!file) {
++ return -EINVAL;
++ }
++ file->file_length = newLength;
++ return 0;
++}
++
++/*
++ * cmyth_file_position(cmyth_file_t p)
++ *
++ * Scope: PUBLIC
++ *
++ * Description
++ *
++ * Obtain the position in the data of a file.
++ *
++ * Return Value:
++ *
++ * Sucess: a long long value >= 0
++ *
++ * Failure: a long long containing -errno
++ */
++unsigned long long
++cmyth_file_position(cmyth_file_t file)
++{
++ if (!file) {
++ return -EINVAL;
++ }
++ return file->file_pos;
++}
++
++
++/*
+ * cmyth_file_get_block(cmyth_file_t file, char *buf, unsigned long len)
+ *
+ * Scope: PUBLIC
+@@ -505,6 +555,89 @@ cmyth_file_seek(cmyth_file_t file, long long offset, int whence)
+ return ret;
+ }
+
++long long
++cmyth_file_seek_unlocked(cmyth_file_t file, long long offset, int whence)
++{
++ char msg[128];
++ int err;
++ int count;
++ long long c;
++ long r;
++ long long ret;
++
++ if (file == NULL)
++ return -EINVAL;
++
++ if ((offset == 0) && (whence == SEEK_CUR))
++ return file->file_pos;
++
++ if ((offset == file->file_pos) && (whence == SEEK_SET))
++ return file->file_pos;
++
++ while(file->file_pos < file->file_req) {
++ c = file->file_req - file->file_pos;
++ if(c > sizeof(msg))
++ c = sizeof(msg);
++
++ if (cmyth_file_get_block(file, msg, (unsigned long)c) < 0)
++ return -1;
++ }
++
++
++ snprintf(msg, sizeof(msg),
++ "QUERY_FILETRANSFER %ld[]:[]SEEK[]:[]%d[]:[]%d[]:[]%d[]:[]%d[]:[]%d",
++ file->file_id,
++ (int32_t)(offset >> 32),
++ (int32_t)(offset & 0xffffffff),
++ whence,
++ (int32_t)(file->file_pos >> 32),
++ (int32_t)(file->file_pos & 0xffffffff));
++
++ if ((err = cmyth_send_message(file->file_control, msg)) < 0) {
++ cmyth_dbg(CMYTH_DBG_ERROR,
++ "%s: cmyth_send_message() failed (%d)\n",
++ __FUNCTION__, err);
++ ret = err;
++ goto out;
++ }
++
++ if ((count=cmyth_rcv_length(file->file_control)) < 0) {
++ cmyth_dbg(CMYTH_DBG_ERROR,
++ "%s: cmyth_rcv_length() failed (%d)\n",
++ __FUNCTION__, count);
++ ret = count;
++ goto out;
++ }
++ if ((r=cmyth_rcv_long_long(file->file_control, &err, &c, count)) < 0) {
++ cmyth_dbg(CMYTH_DBG_ERROR,
++ "%s: cmyth_rcv_long_long() failed (%d)\n",
++ __FUNCTION__, r);
++ ret = err;
++ goto out;
++ }
++
++ switch (whence) {
++ case SEEK_SET:
++ file->file_pos = offset;
++ break;
++ case SEEK_CUR:
++ file->file_pos += offset;
++ break;
++ case SEEK_END:
++ file->file_pos = file->file_length - offset;
++ break;
++ }
++
++ file->file_req = file->file_pos;
++ if(file->file_pos > file->file_length)
++ file->file_length = file->file_pos;
++
++ ret = file->file_pos;
++
++ out:
++
++ return ret;
++}
+ /*
+ * cmyth_file_read(cmyth_recorder_t rec, char *buf, unsigned long len)
+ *
+diff --git a/lib/cmyth/libcmyth/livetv.c b/lib/cmyth/libcmyth/livetv.c
+index 96e50a2..2cb67a2 100644
+--- a/lib/cmyth/libcmyth/livetv.c
++++ b/lib/cmyth/libcmyth/livetv.c
+@@ -805,6 +805,34 @@ int cmyth_livetv_chain_read(cmyth_recorder_t rec, char *buf, unsigned long len)
+ }
+
+ /*
++ * cmyth_livetv_chain_duration(cmyth_recorder_t file)
++ *
++ * Scope: PUBLIC
++ *
++ * Description
++ *
++ * Get current chain duration
++ *
++ * Return Value:
++ *
++ * Sucess: chain duration
++ *
++ * Failure: an int containing -errno
++ */
++
++long long
++cmyth_livetv_chain_duration(cmyth_recorder_t rec)
++{
++ int cur, ct;
++ long long ret=0;
++ ct = rec->rec_livetv_chain->chain_ct;
++ for (cur = 0; cur < ct; cur++) {
++ ret += rec->rec_livetv_chain->chain_files[cur]->file_length;
++ }
++ return ret;
++}
++
++/*
+ * cmyth_livetv_chain_seek(cmyth_recorder_t file, long long offset, int whence)
+ *
+ * Scope: PUBLIC
+@@ -869,6 +897,11 @@ cmyth_livetv_chain_seek(cmyth_recorder_t rec, long long offset, int whence)
+ }
+ return offset;
+ }
++ else
++ {
++ cur = rec->rec_livetv_chain->chain_current;
++ fp = rec->rec_livetv_chain->chain_files[cur];
++ }
+
+ offset += fp->file_req;
+
+@@ -893,7 +926,7 @@ cmyth_livetv_chain_seek(cmyth_recorder_t rec, long long offset, int whence)
+
+ pthread_mutex_lock(&mutex);
+
+- ret = cmyth_file_seek(fp, offset, whence);
++ ret = cmyth_file_seek_unlocked(fp, offset, whence);
+
+ PRINTF("** SSDEBUG: new pos %lld after seek command\n", ret);
+
+@@ -1047,6 +1080,13 @@ cmyth_spawn_live_tv(cmyth_recorder_t rec, unsigned buflen, int tcp_rcvbuf,
+ goto err;
+ }
+
++ for(i=0; i<20; i++) {
++ if(cmyth_recorder_is_recording(rec) != 1)
++ sleep(1);
++ else
++ break;
++ }
++
+ if ((rtrn = cmyth_livetv_chain_setup(rec, tcp_rcvbuf,
+ prog_update_callback)) == NULL) {
+ *err = "Failed to setup livetv.";
+diff --git a/lib/cmyth/libcmyth/mysql_query.c b/lib/cmyth/libcmyth/mysql_query.c
+index 6370e13..7cb7d4e 100644
+--- a/lib/cmyth/libcmyth/mysql_query.c
++++ b/lib/cmyth/libcmyth/mysql_query.c
+@@ -368,3 +368,4 @@ cmyth_mysql_query(cmyth_mysql_query_t * query)
+ }
+ return 0;
+ }
++
+diff --git a/lib/cmyth/libcmyth/mythtv_mysql.c b/lib/cmyth/libcmyth/mythtv_mysql.c
+index 3ff23fa..75bf485 100644
+--- a/lib/cmyth/libcmyth/mythtv_mysql.c
++++ b/lib/cmyth/libcmyth/mythtv_mysql.c
+@@ -57,12 +57,21 @@ cmyth_database_close(cmyth_database_t db)
+ }
+ }
+
++static void
++cmyth_database_destroy(cmyth_database_t db)
++{
++ cmyth_dbg(CMYTH_DBG_DEBUG, "%s\n", __FUNCTION__);
++ cmyth_database_close(db);
++}
++
+ cmyth_database_t
+ cmyth_database_init(char *host, char *db_name, char *user, char *pass)
+ {
+ cmyth_database_t rtrn = ref_alloc(sizeof(*rtrn));
+ cmyth_dbg(CMYTH_DBG_DEBUG, "%s\n", __FUNCTION__);
+
++ ref_set_destroy(rtrn, (ref_destroy_t)cmyth_database_destroy);
++
+ if (rtrn != NULL) {
+ rtrn->db_host = ref_strdup(host);
+ rtrn->db_user = ref_strdup(user);
+@@ -277,6 +286,72 @@ cmyth_get_offset_mysql(cmyth_database_t db, int type, char *recordid, int chanid
+ }
+ }
+
++int
++cmyth_get_watched_status_mysql(cmyth_database_t db, int recordid)
++{
++
++ MYSQL_RES *res= NULL;
++ MYSQL_ROW row;
++ const char *query_str = "SELECT watched FROM recorded WHERE recordid=?";
++ int retval = 0;
++ cmyth_mysql_query_t * query;
++
++ query = cmyth_mysql_query_create(db,query_str);
++
++ if (cmyth_mysql_query_param_long(query,recordid) < 0)
++ {
++ cmyth_dbg(CMYTH_DBG_ERROR,"%s, binding of query parameters failed! Maybe we're out of memory?\n", __FUNCTION__);
++ ref_release(query);
++ return -1;
++ }
++ res = cmyth_mysql_query_result(query);
++ ref_release(query);
++ if(res == NULL)
++ {
++ cmyth_dbg(CMYTH_DBG_ERROR,"%s, finalisation/execution of query failed!\n", __FUNCTION__);
++ return -1;
++ }
++
++
++ if (row = mysql_fetch_row(res)) {
++ retval = safe_atoi(row[0]);
++ mysql_free_result(res);
++ return retval;
++ }
++ else
++ return 0;
++}
++
++int
++cmyth_set_watched_status_mysql(cmyth_database_t db, int recordid, int watchedStat)
++{
++ cmyth_mysql_query_t * query;
++
++ if (watchedStat > 1) watchedStat = 1;
++ if (watchedStat < 0) watchedStat = 0;
++
++ query = cmyth_mysql_query_create(db,"UPDATE recorded SET watched = ? WHERE recordid = ?");
++
++ if(cmyth_mysql_query_param_long(query,watchedStat) < 0
++ || cmyth_mysql_query_param_long(query,recordid) < 0
++ )
++ {
++ cmyth_dbg(CMYTH_DBG_ERROR,"%s, binding of query parameters failed! Maybe we're out of memory?\n", __FUNCTION__);
++ ref_release(query);
++ return -1;
++ }
++
++ if(cmyth_mysql_query(query) < 0)
++ {
++ cmyth_dbg(CMYTH_DBG_ERROR,"%s, finalisation/execution of query failed!\n", __FUNCTION__);
++ ref_release(query);
++ return -1;
++ }
++ ref_release(query);
++ return 0;
++
++}
++
+ char *
+ cmyth_get_recordid_mysql(cmyth_database_t db, int chanid, char *title, char *subtitle, char *description, char *seriesid, char *programid)
+ {
+@@ -442,7 +517,7 @@ cmyth_mysql_get_guide(cmyth_database_t db, cmyth_program_t **prog, time_t startt
+ {
+ MYSQL_RES *res= NULL;
+ MYSQL_ROW row;
+- const char *query_str = "SELECT program.chanid,UNIX_TIMESTAMP(program.starttime),UNIX_TIMESTAMP(program.endtime),program.title,program.description,program.subtitle,program.programid,program.seriesid,program.category,channel.channum,channel.callsign,channel.name,channel.sourceid FROM program INNER JOIN channel ON program.chanid=channel.chanid WHERE ( ( starttime>=? and starttime<? ) OR ( starttime <? and endtime > ?) ) ORDER BY (channel.channum + 0), program.starttime ASC ";
++ const char *query_str = "SELECT program.chanid,UNIX_TIMESTAMP(program.starttime),UNIX_TIMESTAMP(program.endtime),program.title,program.description,program.subtitle,program.programid,program.seriesid,program.category,channel.channum,channel.callsign,channel.name,channel.sourceid FROM program INNER JOIN channel ON program.chanid=channel.chanid WHERE ((program.endtime > ? and program.endtime < ?) or (program.starttime >= ? and program.starttime <= ?) or (program.starttime <= ? and program.endtime >= ?)) ORDER BY (channel.channum + 0), program.starttime ASC ";
+ int rows=0;
+ int n=0;
+ cmyth_mysql_query_t * query;
+@@ -451,7 +526,9 @@ cmyth_mysql_get_guide(cmyth_database_t db, cmyth_program_t **prog, time_t startt
+ if(cmyth_mysql_query_param_unixtime(query,starttime) < 0
+ || cmyth_mysql_query_param_unixtime(query,endtime) < 0
+ || cmyth_mysql_query_param_unixtime(query,starttime) < 0
+- || cmyth_mysql_query_param_unixtime(query,starttime) < 0)
++ || cmyth_mysql_query_param_unixtime(query,endtime) < 0
++ || cmyth_mysql_query_param_unixtime(query,starttime) < 0
++ || cmyth_mysql_query_param_unixtime(query,endtime) < 0)
+ {
+ cmyth_dbg(CMYTH_DBG_ERROR,"%s, binding of query parameters failed! Maybe we're out of memory?\n", __FUNCTION__);
+ ref_release(query);
+@@ -1052,7 +1129,7 @@ cmyth_channel_destroy(cmyth_channel_t pl)
+ if(pl->callsign)
+ ref_release(pl->callsign);
+ if(pl->icon)
+- ref_release(pl->callsign);
++ ref_release(pl->icon);
+ }
+
+ long
+@@ -1074,6 +1151,16 @@ cmyth_channel_channum(cmyth_channel_t channel)
+ }
+
+ char *
++cmyth_channel_channumstr(cmyth_channel_t channel)
++{
++ if (!channel) {
++ return NULL;
++ }
++ return channel->chanstr;
++}
++
++
++char *
+ cmyth_channel_name(cmyth_channel_t channel)
+ {
+ if (!channel) {
+@@ -1083,6 +1170,16 @@ cmyth_channel_name(cmyth_channel_t channel)
+ }
+
+ char *
++cmyth_channel_callsign(cmyth_channel_t channel)
++{
++ if (!channel) {
++ return NULL;
++ }
++ return ref_hold(channel->callsign);
++}
++
++
++char *
+ cmyth_channel_icon(cmyth_channel_t channel)
+ {
+ if (!channel) {
+@@ -1100,6 +1197,24 @@ cmyth_channel_visible(cmyth_channel_t channel)
+ return channel->visible;
+ }
+
++int
++cmyth_channel_sourceid(cmyth_channel_t channel)
++{
++ if (!channel) {
++ return -EINVAL;
++ }
++ return channel->sourceid;
++}
++
++int
++cmyth_channel_multiplex(cmyth_channel_t channel)
++{
++ if (!channel) {
++ return -EINVAL;
++ }
++ return channel->multiplex;
++}
++
+ cmyth_channel_t
+ cmyth_channel_create(void)
+ {
+@@ -1117,11 +1232,12 @@ cmyth_channel_create(void)
+ }
+
+
++
+ cmyth_chanlist_t cmyth_mysql_get_chanlist(cmyth_database_t db)
+ {
+ MYSQL_RES *res = NULL;
+ MYSQL_ROW row;
+- const char *query_str = "SELECT chanid, channum, name, icon, visible FROM channel;";
++ const char *query_str = "SELECT chanid, channum, name, icon, visible, sourceid, mplexid, callsign FROM channel;";
+ int rows = 0;
+ int i;
+ cmyth_mysql_query_t * query;
+@@ -1153,9 +1269,13 @@ cmyth_chanlist_t cmyth_mysql_get_chanlist(cmyth_database_t db)
+ channel = cmyth_channel_create();
+ channel->chanid = safe_atol(row[0]);
+ channel->channum = safe_atoi(row[1]);
++ strncpy(channel->chanstr, row[1], 10);
+ channel->name = ref_strdup(row[2]);
+ channel->icon = ref_strdup(row[3]);
+ channel->visible = safe_atoi(row[4]);
++ channel->sourceid = safe_atoi(row[5]);
++ channel->multiplex = safe_atoi(row[6]);
++ channel->callsign = ref_strdup(row[7]);
+ chanlist->chanlist_list[rows] = channel;
+ i = 0;
+ rows++;
+@@ -1259,3 +1379,1057 @@ int cmyth_livetv_keep_recording(cmyth_recorder_t rec, cmyth_database_t db, int k
+ return 1;
+ }
+
++
++int cmyth_mysql_is_radio(cmyth_database_t db, int chanid)
++{
++ cmyth_mysql_query_t * query;
++ MYSQL_RES *res = NULL;
++ MYSQL_ROW row;
++ int retval=-1;
++
++ if(cmyth_db_check_connection(db) != 0)
++ {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_db_check_connection failed\n", __FUNCTION__);
++ return -1;
++ }
++
++
++ query = cmyth_mysql_query_create(db,"SELECT is_audio_service FROM channelscan_channel INNER JOIN channel ON channelscan_channel.service_id=channel.serviceid WHERE channel.chanid = ? ORDER BY channelscan_channel.scanid DESC;");
++
++ if(cmyth_mysql_query_param_long(query,chanid) < 0)
++ {
++ cmyth_dbg(CMYTH_DBG_ERROR,"%s, binding of query parameters failed! Maybe we're out of memory?\n", __FUNCTION__);
++ ref_release(query);
++ return -1;
++ }
++
++ res = cmyth_mysql_query_result(query);
++ ref_release(query);
++
++ if (res == NULL) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s, finalisation/execution of query failed!\n", __FUNCTION__);
++ return -1;
++ }
++
++ if (row = mysql_fetch_row(res)) {
++ retval = safe_atoi(row[0]);
++ }
++ else
++ {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s, Channum %i not found\n", __FUNCTION__,chanid);
++ return -1;
++ }
++ mysql_free_result(res);
++ return retval;
++}
++
++
++static void
++cmyth_timer_destroy(cmyth_timer_t pl)
++{
++
++ cmyth_dbg(CMYTH_DBG_DEBUG, "%s\n", __FUNCTION__);
++ if (!pl) {
++ return;
++ }
++
++ if(pl->title)
++ ref_release(pl->title);
++ if(pl->description)
++ ref_release(pl->description);
++ if(pl->category)
++ ref_release(pl->category);
++ if(pl->rec_group)
++ ref_release(pl->rec_group);
++ if(pl->store_group)
++ ref_release(pl->store_group);
++ if(pl->play_group)
++ ref_release(pl->play_group);
++}
++
++
++cmyth_timer_t
++cmyth_timer_create(void)
++{
++ cmyth_timer_t ret = ref_alloc(sizeof(*ret));
++ memset(ret, 0, sizeof(*ret));
++
++ cmyth_dbg(CMYTH_DBG_DEBUG, "%s {\n", __FUNCTION__);
++ if (!ret) {
++ cmyth_dbg(CMYTH_DBG_DEBUG, "%s }!\n", __FUNCTION__);
++ return NULL;
++ }
++ ref_set_destroy(ret, (ref_destroy_t)cmyth_timer_destroy);
++
++ return ret;
++}
++
++
++static void
++cmyth_timerlist_destroy(cmyth_timerlist_t pl)
++{
++ int i;
++
++ cmyth_dbg(CMYTH_DBG_DEBUG, "%s\n", __FUNCTION__);
++ if (!pl) {
++ return;
++ }
++ for (i = 0; i < pl->timerlist_count; ++i) {
++ if (pl->timerlist_list[i]) {
++ ref_release(pl->timerlist_list[i]);
++ }
++ pl->timerlist_list[i] = NULL;
++ }
++ if (pl->timerlist_list) {
++ free(pl->timerlist_list);
++ }
++}
++
++cmyth_timerlist_t
++cmyth_timerlist_create(void)
++{
++ cmyth_timerlist_t ret;
++
++ cmyth_dbg(CMYTH_DBG_DEBUG, "%s\n", __FUNCTION__);
++ ret = ref_alloc(sizeof(*ret));
++ if (!ret) {
++ return(NULL);
++ }
++ ref_set_destroy(ret, (ref_destroy_t)cmyth_timerlist_destroy);
++
++ ret->timerlist_list = NULL;
++ ret->timerlist_count = 0;
++ return ret;
++}
++
++
++cmyth_timerlist_t
++cmyth_mysql_get_timers(cmyth_database_t db)
++{
++ MYSQL_RES *res= NULL;
++ MYSQL_ROW row;
++ const char *query_str = "SELECT recordid, chanid, UNIX_TIMESTAMP(ADDTIME(startdate,starttime)), UNIX_TIMESTAMP(ADDTIME(enddate,endtime)),title,description, type, category, subtitle, recpriority, startoffset, endoffset, search, inactive, station, dupmethod, dupin, recgroup, storagegroup, playgroup, autotranscode, (autouserjob1 | (autouserjob2 << 1) | (autouserjob3 << 2) | (autouserjob4 << 3)), autocommflag, autoexpire, maxepisodes, maxnewest, transcoder FROM record ORDER BY recordid";
++ int rows=0;
++ cmyth_timer_t timer;
++ cmyth_timerlist_t timerlist;
++
++ cmyth_mysql_query_t * query;
++ query = cmyth_mysql_query_create(db,query_str);
++
++ res = cmyth_mysql_query_result(query);
++ ref_release(query);
++ if(res == NULL)
++ {
++ cmyth_dbg(CMYTH_DBG_ERROR,"%s, finalisation/execution of query failed!\n", __FUNCTION__);
++ return NULL;
++ }
++
++
++ timerlist = cmyth_timerlist_create();
++
++ timerlist->timerlist_count = (int)mysql_num_rows(res);
++ timerlist->timerlist_list = malloc(timerlist->timerlist_count * sizeof(cmyth_timerlist_t));
++ if (!timerlist->timerlist_list) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: malloc() failed for list\n",
++ __FUNCTION__);
++ ref_release(timerlist);
++ return NULL;
++ }
++ memset(timerlist->timerlist_list, 0, timerlist->timerlist_count * sizeof(cmyth_timerlist_t));
++
++
++ while ((row = mysql_fetch_row(res))) {
++ timer = cmyth_timer_create();
++ timer->recordid = safe_atol(row[0]);
++ timer->chanid = safe_atoi(row[1]);
++ timer->starttime = (time_t)safe_atol(row[2]);
++ timer->endtime = (time_t)safe_atol(row[3]);
++ timer->title = ref_strdup(row[4]);
++ timer->description = ref_strdup(row[5]);
++ timer->type = safe_atoi(row[6]);
++ timer->category = ref_strdup(row[7]);
++ timer->subtitle = ref_strdup(row[8]);
++ timer->priority = safe_atoi(row[9]);
++ timer->startoffset = safe_atoi(row[10]);
++ timer->endoffset = safe_atoi(row[11]);
++ timer->searchtype = safe_atoi(row[12]);
++ timer->inactive = safe_atoi(row[13]);
++ timer->callsign = ref_strdup(row[14]);
++ timer->dup_method = safe_atoi(row[15]);
++ timer->dup_in = safe_atoi(row[16]);
++ timer->rec_group = ref_strdup(row[17]);
++ timer->store_group = ref_strdup(row[18]);
++ timer->play_group = ref_strdup(row[19]);
++ timer->autotranscode = safe_atoi(row[20]);
++ timer->userjobs = safe_atoi(row[21]);
++ timer->autocommflag = safe_atoi(row[22]);
++ timer->autoexpire = safe_atoi(row[23]);
++ timer->maxepisodes = safe_atoi(row[24]);
++ timer->maxnewest = safe_atoi(row[25]);
++ timer->transcoder = safe_atoi(row[26]);
++ timerlist->timerlist_list[rows] = timer;
++ rows++;
++ }
++
++ mysql_free_result(res);
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: rows= %d\n", __FUNCTION__, rows);
++ return timerlist;
++}
++
++
++
++int
++cmyth_mysql_add_timer(cmyth_database_t db, int chanid,char* callsign, char* description, time_t starttime, time_t endtime,char* title,char* category,int type,char* subtitle,int priority,int startoffset,int endoffset,int searchtype,int inactive,
++ int dup_method,
++ int dup_in,
++ char* rec_group,
++ char* store_group,
++ char* play_group,
++ int autotranscode,
++ int userjobs,
++ int autocommflag,
++ int autoexpire,
++ int maxepisodes,
++ int maxnewest,
++ int transcoder)
++{
++ int ret = -1;
++ int id=0;
++ MYSQL* sql=cmyth_db_get_connection(db);
++ const char *query_str = "INSERT INTO record (record.type, chanid, starttime, startdate, endtime, enddate,title, description, category, findid, findtime, station, subtitle , recpriority , startoffset , endoffset , search , inactive, dupmethod, dupin, recgroup, storagegroup, playgroup, autotranscode, autouserjob1, autouserjob2, autouserjob3, autouserjob4, autocommflag, autoexpire, maxepisodes, maxnewest, transcoder) VALUES (? , ? , TIME(FROM_UNIXTIME( ? )), DATE(FROM_UNIXTIME( ? )) , TIME(FROM_UNIXTIME( ? )), DATE(FROM_UNIXTIME( ? )) , ? , ?, ?, TO_DAYS(DATE(FROM_UNIXTIME( ? ))), TIME(FROM_UNIXTIME( ? )) , ?, ?, ?, ?, ?, ?, ? ,?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ,?, ?, ?, ?);";
++
++ char* esctitle = cmyth_mysql_escape_chars(db,title);
++ char* escdescription = cmyth_mysql_escape_chars(db,description);
++ char* esccategory = cmyth_mysql_escape_chars(db,category);
++ char* esccallsign=cmyth_mysql_escape_chars(db,callsign);
++ char* escsubtitle = cmyth_mysql_escape_chars(db,subtitle);
++
++ char* escrec_group = cmyth_mysql_escape_chars(db,rec_group);
++ char* escstore_group = cmyth_mysql_escape_chars(db,store_group);
++ char* escplay_group = cmyth_mysql_escape_chars(db,play_group);
++
++ cmyth_mysql_query_t * query;
++ query = cmyth_mysql_query_create(db,query_str);
++ if ( cmyth_mysql_query_param_long(query, type) < 0
++ || cmyth_mysql_query_param_long(query, chanid) < 0
++ || cmyth_mysql_query_param_long(query, starttime ) < 0
++ || cmyth_mysql_query_param_long(query, starttime ) < 0
++ || cmyth_mysql_query_param_long(query, endtime ) < 0
++ || cmyth_mysql_query_param_long(query, endtime ) < 0
++ || cmyth_mysql_query_param_str(query, title ) < 0
++ || cmyth_mysql_query_param_str(query, description ) < 0
++ || cmyth_mysql_query_param_str(query, category ) < 0
++ || cmyth_mysql_query_param_long(query, starttime ) < 0
++ || cmyth_mysql_query_param_long(query, starttime ) < 0
++ || cmyth_mysql_query_param_str(query, callsign ) < 0
++ || cmyth_mysql_query_param_str(query, subtitle ) < 0
++ || cmyth_mysql_query_param_long(query, priority ) < 0
++ || cmyth_mysql_query_param_long(query, startoffset ) < 0
++ || cmyth_mysql_query_param_long(query, endoffset ) < 0
++ || cmyth_mysql_query_param_long(query, searchtype ) < 0
++ || cmyth_mysql_query_param_long(query, inactive ) < 0
++
++ || cmyth_mysql_query_param_long(query, dup_method ) < 0
++ || cmyth_mysql_query_param_long(query, dup_in ) < 0
++ || cmyth_mysql_query_param_str(query, rec_group ) < 0
++ || cmyth_mysql_query_param_str(query, store_group ) < 0
++ || cmyth_mysql_query_param_str(query, play_group ) < 0
++ || cmyth_mysql_query_param_long(query, autotranscode ) < 0
++ || cmyth_mysql_query_param_long(query, userjobs & 1) < 0
++ || cmyth_mysql_query_param_long(query, userjobs & 2) < 0
++ || cmyth_mysql_query_param_long(query, userjobs & 4) < 0
++ || cmyth_mysql_query_param_long(query, userjobs & 8) < 0
++ || cmyth_mysql_query_param_long(query, autocommflag ) < 0
++ || cmyth_mysql_query_param_long(query, autoexpire ) < 0
++ || cmyth_mysql_query_param_long(query, maxepisodes ) < 0
++ || cmyth_mysql_query_param_long(query, maxnewest ) < 0
++ || cmyth_mysql_query_param_long(query, transcoder ) < 0
++ ) {
++ cmyth_dbg(CMYTH_DBG_ERROR,"%s, binding of query parameters failed! Maybe we're out of memory?\n", __FUNCTION__);
++ ref_release(query);
++ return -1;
++ }
++
++ ret = cmyth_mysql_query(query);
++
++ // int rrrr;
++ if(ret!=0)
++ return -1;
++
++ id = (int)mysql_insert_id(sql);
++
++ ref_release(query);
++ ref_release(esctitle);
++ ref_release(escdescription);
++ ref_release(esccategory);
++ ref_release(esccallsign);
++ ref_release(escsubtitle);
++
++ ref_release(escrec_group);
++ ref_release(escstore_group);
++ ref_release(escplay_group);
++
++ return id;
++}
++
++int
++cmyth_mysql_delete_timer(cmyth_database_t db, int recordid)
++{
++ int ret = -1;
++ int id=0;
++
++ const char *query_str = "DELETE FROM record WHERE recordid = ?;";
++
++ cmyth_mysql_query_t * query;
++ query = cmyth_mysql_query_create(db,query_str);
++ if (cmyth_mysql_query_param_long(query, recordid) < 0
++ ) {
++ cmyth_dbg(CMYTH_DBG_ERROR,"%s, binding of query parameters failed! Maybe we're out of memory?\n", __FUNCTION__);
++ ref_release(query);
++ return -1;
++ }
++
++ ret = cmyth_mysql_query(query);
++
++ // int rrrr;
++
++ if (ret != 0) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s, finalisation/execution of query failed!\n", __FUNCTION__);
++ return -1;
++ }
++
++ return 0;
++}
++
++int
++cmyth_mysql_update_timer(cmyth_database_t db, int recordid, int chanid,char* callsign,char* description, time_t starttime, time_t endtime,char* title,char* category,int type,char* subtitle,int priority,int startoffset,int endoffset,int searchtype,int inactive,
++ int dup_method,
++ int dup_in,
++ char* rec_group,
++ char* store_group,
++ char* play_group,
++ int autotranscode,
++ int userjobs,
++ int autocommflag,
++ int autoexpire,
++ int maxepisodes,
++ int maxnewest,
++ int transcoder)
++{
++ int ret = -1;
++ int id=0;
++
++ const char *query_str = "UPDATE record SET record.type = ?, `chanid` = ?, `starttime`= TIME(FROM_UNIXTIME( ? )), `startdate`= DATE(FROM_UNIXTIME( ? )), `endtime`= TIME(FROM_UNIXTIME( ? )), `enddate` = DATE(FROM_UNIXTIME( ? )) ,`title`= ?, `description`= ?, category = ?, subtitle = ?, recpriority = ?, startoffset = ?, endoffset = ?, search = ?, inactive = ?, station = ?, dupmethod = ?, dupin = ?, recgroup = ?, storagegroup = ?, playgroup = ?, autotranscode = ?, autouserjob1 = ?, autouserjob2 = ?, autouserjob3 = ?, autouserjob4 = ?, autocommflag = ?, autoexpire = ?, maxepisodes = ?, maxnewest = ?, transcoder = ? WHERE `recordid` = ? ;";
++
++ char* esctitle=cmyth_mysql_escape_chars(db,title);
++ char* escdescription=cmyth_mysql_escape_chars(db,description);
++ char* esccategory=cmyth_mysql_escape_chars(db,category);
++ char* esccallsign=cmyth_mysql_escape_chars(db,callsign);
++ char* escsubtitle=cmyth_mysql_escape_chars(db,subtitle);
++
++ char* escrec_group = cmyth_mysql_escape_chars(db,rec_group);
++ char* escstore_group = cmyth_mysql_escape_chars(db,store_group);
++ char* escplay_group = cmyth_mysql_escape_chars(db,play_group);
++
++ cmyth_mysql_query_t * query;
++ query = cmyth_mysql_query_create(db,query_str);
++ if ( cmyth_mysql_query_param_long(query, type) < 0
++ || cmyth_mysql_query_param_long(query, chanid) < 0
++ || cmyth_mysql_query_param_long(query, starttime ) < 0
++ || cmyth_mysql_query_param_long(query, starttime ) < 0
++ || cmyth_mysql_query_param_long(query, endtime ) < 0
++ || cmyth_mysql_query_param_long(query, endtime ) < 0
++ || cmyth_mysql_query_param_str(query, title ) < 0
++ || cmyth_mysql_query_param_str(query, description ) < 0
++ || cmyth_mysql_query_param_str(query, category ) < 0
++ || cmyth_mysql_query_param_str(query, subtitle ) < 0
++ || cmyth_mysql_query_param_long(query, priority ) < 0
++ || cmyth_mysql_query_param_long(query, startoffset ) < 0
++ || cmyth_mysql_query_param_long(query, endoffset ) < 0
++ || cmyth_mysql_query_param_long(query, searchtype ) < 0
++ || cmyth_mysql_query_param_long(query, inactive ) < 0
++ || cmyth_mysql_query_param_str(query, callsign ) < 0
++
++ || cmyth_mysql_query_param_long(query, dup_method ) < 0
++ || cmyth_mysql_query_param_long(query, dup_in ) < 0
++ || cmyth_mysql_query_param_str(query, rec_group ) < 0
++ || cmyth_mysql_query_param_str(query, store_group ) < 0
++ || cmyth_mysql_query_param_str(query, play_group ) < 0
++ || cmyth_mysql_query_param_long(query, autotranscode ) < 0
++ || cmyth_mysql_query_param_long(query, userjobs & 1) < 0
++ || cmyth_mysql_query_param_long(query, userjobs & 2) < 0
++ || cmyth_mysql_query_param_long(query, userjobs & 4) < 0
++ || cmyth_mysql_query_param_long(query, userjobs & 8) < 0
++ || cmyth_mysql_query_param_long(query, autocommflag ) < 0
++ || cmyth_mysql_query_param_long(query, autoexpire ) < 0
++ || cmyth_mysql_query_param_long(query, maxepisodes ) < 0
++ || cmyth_mysql_query_param_long(query, maxnewest ) < 0
++ || cmyth_mysql_query_param_long(query, transcoder ) < 0
++
++ || cmyth_mysql_query_param_long(query, recordid) < 0
++ ) {
++ cmyth_dbg(CMYTH_DBG_ERROR,"%s, binding of query parameters failed! Maybe we're out of memory?\n", __FUNCTION__);
++ ref_release(query);
++ return -1;
++ }
++
++ ret = cmyth_mysql_query(query);
++
++ ref_release(query);
++
++ if (ret == -1) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s, finalisation/execution of query failed!\n", __FUNCTION__);
++ return -1;
++ }
++ ref_release(esctitle);
++ ref_release(escdescription);
++ ref_release(esccategory);
++ ref_release(esccallsign);
++ ref_release(escsubtitle);
++
++ ref_release(escrec_group);
++ ref_release(escstore_group);
++ ref_release(escplay_group);
++
++ return 0;
++
++}
++
++
++int cmyth_timer_recordid(cmyth_timer_t timer)
++{
++ if (!timer) {
++ return -EINVAL;
++ }
++ return timer->recordid;
++}
++
++
++int cmyth_timer_chanid(cmyth_timer_t timer)
++ {
++ if (!timer) {
++ return -EINVAL;
++ }
++ return timer->chanid;
++}
++
++char* cmyth_timer_callsign(cmyth_timer_t timer)
++{
++ if (!timer) {
++ return NULL;
++ }
++ return ref_hold(timer->callsign);
++}
++
++time_t cmyth_timer_starttime(cmyth_timer_t timer)
++ {
++ if (!timer) {
++ return -EINVAL;
++ }
++ return timer->starttime;
++}
++
++time_t cmyth_timer_endtime(cmyth_timer_t timer)
++{
++ if (!timer) {
++ return -EINVAL;
++ }
++ return timer->endtime;
++}
++
++char* cmyth_timer_title(cmyth_timer_t timer)
++{
++ if (!timer) {
++ return NULL;
++ }
++ return ref_hold(timer->title);
++}
++
++char* cmyth_timer_description(cmyth_timer_t timer)
++{
++ if (!timer) {
++ return NULL;
++ }
++ return ref_hold(timer->description);
++}
++
++int cmyth_timer_type(cmyth_timer_t timer)
++ {
++ if (!timer) {
++ return -EINVAL;
++ }
++ return timer->type;
++}
++
++
++char* cmyth_timer_category(cmyth_timer_t timer)
++{
++ if (!timer) {
++ return NULL;
++ }
++ return ref_hold(timer->category);
++}
++
++char* cmyth_timer_subtitle(cmyth_timer_t timer)
++{
++ if (!timer) {
++ return NULL;
++ }
++ return ref_hold(timer->subtitle);
++}
++
++int cmyth_timer_priority(cmyth_timer_t timer)
++ {
++ if (!timer) {
++ return -EINVAL;
++ }
++ return timer->priority;
++}
++
++int cmyth_timer_startoffset(cmyth_timer_t timer)
++ {
++ if (!timer) {
++ return -EINVAL;
++ }
++ return timer->startoffset;
++}
++
++int cmyth_timer_endoffset(cmyth_timer_t timer)
++ {
++ if (!timer) {
++ return -EINVAL;
++ }
++ return timer->endoffset;
++}
++
++int cmyth_timer_searchtype(cmyth_timer_t timer)
++ {
++ if (!timer) {
++ return -EINVAL;
++ }
++ return timer->searchtype;
++}
++
++int cmyth_timer_inactive(cmyth_timer_t timer)
++ {
++ if (!timer) {
++ return -EINVAL;
++ }
++ return timer->inactive;
++}
++
++
++ int cmyth_timer_dup_method(cmyth_timer_t timer)
++ {
++ if (!timer) {
++ return -EINVAL;
++ }
++ return timer->dup_method;
++}
++
++ int cmyth_timer_dup_in(cmyth_timer_t timer)
++ {
++ if (!timer) {
++ return -EINVAL;
++ }
++ return timer->dup_in;
++}
++
++ char* cmyth_timer_rec_group(cmyth_timer_t timer)
++ {
++ if (!timer) {
++ return NULL;
++ }
++ return ref_hold(timer->rec_group);
++}
++
++ char* cmyth_timer_store_group(cmyth_timer_t timer)
++ {
++ if (!timer) {
++ return NULL;
++ }
++ return ref_hold(timer->store_group);
++}
++
++ char* cmyth_timer_play_group(cmyth_timer_t timer)
++ {
++ if (!timer) {
++ return NULL;
++ }
++ return ref_hold(timer->play_group);
++}
++
++ int cmyth_timer_autotranscode(cmyth_timer_t timer)
++ {
++ if (!timer) {
++ return -EINVAL;
++ }
++ return timer->autotranscode;
++}
++
++ int cmyth_timer_userjobs(cmyth_timer_t timer)
++ {
++ if (!timer) {
++ return -EINVAL;
++ }
++ return timer->userjobs;
++}
++
++ int cmyth_timer_autocommflag(cmyth_timer_t timer)
++ {
++ if (!timer) {
++ return -EINVAL;
++ }
++ return timer->autocommflag;
++}
++
++ int cmyth_timer_autoexpire(cmyth_timer_t timer)
++ {
++ if (!timer) {
++ return -EINVAL;
++ }
++ return timer->autoexpire;
++}
++
++ int cmyth_timer_maxepisodes(cmyth_timer_t timer)
++ {
++ if (!timer) {
++ return -EINVAL;
++ }
++ return timer->maxepisodes;
++}
++
++ int cmyth_timer_maxnewest(cmyth_timer_t timer)
++ {
++ if (!timer) {
++ return -EINVAL;
++ }
++ return timer->maxnewest;
++}
++
++int cmyth_timer_transcoder(cmyth_timer_t timer)
++ {
++ if (!timer) {
++ return -EINVAL;
++ }
++ return timer->transcoder;
++}
++
++cmyth_timer_t cmyth_timerlist_get_item(cmyth_timerlist_t pl, int index)
++{
++ if (!pl) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: NULL timer list\n",
++ __FUNCTION__);
++ return NULL;
++ }
++ if (!pl->timerlist_list) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: NULL list\n",
++ __FUNCTION__);
++ return NULL;
++ }
++ if ((index < 0) || (index >= pl->timerlist_count)) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: index %d out of range\n",
++ __FUNCTION__, index);
++ return NULL;
++ }
++ ref_hold(pl->timerlist_list[index]);
++ return pl->timerlist_list[index];
++}
++
++extern int cmyth_timerlist_get_count(cmyth_timerlist_t pl)
++{
++ if (!pl) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: NULL timer list\n",
++ __FUNCTION__);
++ return -EINVAL;
++ }
++ return pl->timerlist_count;
++}
++
++extern int cmyth_mysql_get_channelgroups(cmyth_database_t db,cmyth_channelgroups_t** changroups)
++{
++ MYSQL_RES *res= NULL;
++ MYSQL_ROW row;
++ const char *query_str = "SELECT grpid, name FROM channelgroupnames";
++ int rows=0;
++ cmyth_channelgroups_t* ret;
++
++
++ cmyth_mysql_query_t * query;
++ query = cmyth_mysql_query_create(db,query_str);
++
++ res = cmyth_mysql_query_result(query);
++ ref_release(query);
++ if(res == NULL)
++ {
++ cmyth_dbg(CMYTH_DBG_ERROR,"%s, finalisation/execution of query failed!\n", __FUNCTION__);
++ return 0;
++ }
++
++
++ ret = ref_alloc( sizeof( cmyth_channelgroups_t ) * (int)mysql_num_rows(res));
++
++
++ if (!ret) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: alloc() failed for list\n",
++ __FUNCTION__);
++ mysql_free_result(res);
++ return 0;
++ }
++
++ while ((row = mysql_fetch_row(res))) {
++ ret[rows].ID=safe_atoi(row[0]);
++ safe_strncpy(ret[rows].channelgroup, row[1], 65);
++
++ rows++;
++ }
++
++ mysql_free_result(res);
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: rows= %d\n", __FUNCTION__, rows);
++
++ *changroups=ret;
++ return rows;
++}
++
++extern int cmyth_mysql_get_channelids_in_group(cmyth_database_t db,unsigned int groupid,int** chanids)
++{
++
++ MYSQL_RES *res= NULL;
++ MYSQL_ROW row;
++ const char *query_str = "SELECT chanid FROM channelgroup WHERE grpid = ?";
++ int rows=0;
++ int* ret;
++
++ cmyth_mysql_query_t * query;
++ query = cmyth_mysql_query_create(db,query_str);
++ if (cmyth_mysql_query_param_long(query, groupid) < 0
++ ) {
++ cmyth_dbg(CMYTH_DBG_ERROR,"%s, binding of query parameters failed! Maybe we're out of memory?\n", __FUNCTION__);
++ ref_release(query);
++ return -1;
++ }
++
++ res = cmyth_mysql_query_result(query);
++ ref_release(query);
++ if(res == NULL)
++ {
++ cmyth_dbg(CMYTH_DBG_ERROR,"%s, finalisation/execution of query failed!\n", __FUNCTION__);
++ return 0;
++ }
++
++ ret = ref_alloc(sizeof(int)* (int)mysql_num_rows(res));
++
++
++ if (!ret) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: alloc() failed for list\n",
++ __FUNCTION__);
++ mysql_free_result(res);
++ return 0;
++ }
++
++ while ((row = mysql_fetch_row(res))) {
++ ret[rows]=safe_atoi(row[0]);
++ rows++;
++ }
++
++ mysql_free_result(res);
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: rows= %d\n", __FUNCTION__, rows);
++ *chanids=ret;
++ return rows;
++}
++
++
++int cmyth_mysql_get_recorder_list(cmyth_database_t db,cmyth_rec_t** reclist)
++{
++ MYSQL_RES *res= NULL;
++ MYSQL_ROW row;
++ const char *query_str = "SELECT cardid, sourceid FROM cardinput";
++ int rows=0;
++ cmyth_rec_t* ret;
++
++
++ cmyth_mysql_query_t * query;
++ query = cmyth_mysql_query_create(db,query_str);
++
++ res = cmyth_mysql_query_result(query);
++ ref_release(query);
++ if(res == NULL)
++ {
++ cmyth_dbg(CMYTH_DBG_ERROR,"%s, finalisation/execution of query failed!\n", __FUNCTION__);
++ return 0;
++ }
++
++
++ ret = ref_alloc( sizeof( cmyth_rec_t ) * (int)mysql_num_rows(res));
++
++
++ if (!ret) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: alloc() failed for list\n",
++ __FUNCTION__);
++ mysql_free_result(res);
++ return 0;
++ }
++
++ while ((row = mysql_fetch_row(res))) {
++ ret[rows].recid=safe_atoi(row[0]);
++ ret[rows].sourceid=safe_atoi(row[1]);
++
++ rows++;
++ }
++
++ mysql_free_result(res);
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: rows= %d\n", __FUNCTION__, rows);
++
++ *reclist=ret;
++ return rows;
++}
++
++int cmyth_mysql_get_prog_finder_time_title_chan(cmyth_database_t db,cmyth_program_t *prog,char* title,time_t starttime,int chanid)
++{
++ MYSQL_RES *res= NULL;
++ MYSQL_ROW row;
++ const char *query_str = "SELECT program.chanid,UNIX_TIMESTAMP(program.starttime),UNIX_TIMESTAMP(program.endtime),program.title,program.description,program.subtitle,program.programid,program.seriesid,program.category,channel.channum,channel.callsign,channel.name,channel.sourceid FROM program INNER JOIN channel ON program.chanid=channel.chanid WHERE program.chanid = ? AND program.title LIKE ? AND program.starttime = FROM_UNIXTIME( ? ) AND program.manualid = 0 ORDER BY (channel.channum + 0), program.starttime ASC ";
++ int rows=0;
++
++ char* esctitle=cmyth_mysql_escape_chars(db,title);
++
++ cmyth_mysql_query_t * query;
++ query = cmyth_mysql_query_create(db,query_str);
++
++ if ( cmyth_mysql_query_param_long(query, chanid) < 0
++ || cmyth_mysql_query_param_str(query, title ) < 0
++ || cmyth_mysql_query_param_long(query, starttime ) < 0
++ ) {
++ cmyth_dbg(CMYTH_DBG_ERROR,"%s, binding of query parameters failed! Maybe we're out of memory?\n", __FUNCTION__);
++ ref_release(query);
++ return -1;
++ }
++
++
++ res = cmyth_mysql_query_result(query);
++ ref_release(query);
++
++ if(res == NULL)
++ {
++ cmyth_dbg(CMYTH_DBG_ERROR,"%s, finalisation/execution of query failed!\n", __FUNCTION__);
++ return 0;
++ }
++
++ if ((row = mysql_fetch_row(res))) {
++ rows++;
++ if(prog)
++ {
++ prog->chanid = safe_atoi(row[0]);
++ prog->recording=0;
++ prog->starttime= (time_t)safe_atol(row[1]);
++ prog->endtime= (time_t)safe_atol(row[2]);
++ sizeof_strncpy(prog->title, row[3]);
++ sizeof_strncpy(prog->description, row[4]);
++ sizeof_strncpy(prog->subtitle, row[5]);
++ sizeof_strncpy(prog->programid, row[6]);
++ sizeof_strncpy(prog->seriesid, row[7]);
++ sizeof_strncpy(prog->category, row[8]);
++ prog->channum = safe_atoi(row[9]);
++ sizeof_strncpy(prog->callsign, row[10]);
++ sizeof_strncpy(prog->name, row[11]);
++ prog->sourceid = safe_atoi(row[12]);
++ prog->startoffset=0;
++ prog->endoffset=0;
++ }
++ }
++
++ mysql_free_result(res);
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: rows= %d\n", __FUNCTION__, rows);
++ return rows;
++}
++
++void destroy_char_array(void* p)
++{
++ char** ptr = (char**)p;
++ if(!ptr)
++ return;
++ while (*ptr)
++ {
++ ref_release(*ptr);
++ ptr++;
++ }
++}
++
++int cmyth_mysql_get_storagegroups(cmyth_database_t db, char** *profiles)
++{
++ MYSQL_RES *res= NULL;
++ MYSQL_ROW row;
++ const char *query_str = "SELECT groupname FROM storagegroup";
++ int rows=0;
++ char **ret;/* = profiles;*/
++
++
++ cmyth_mysql_query_t * query;
++ query = cmyth_mysql_query_create(db,query_str);
++
++ res = cmyth_mysql_query_result(query);
++ ref_release(query);
++ if(res == NULL)
++ {
++ cmyth_dbg(CMYTH_DBG_ERROR,"%s, finalisation/execution of query failed!\n", __FUNCTION__);
++ return 0;
++ }
++
++
++ ret = ref_alloc( sizeof( char*) * ((int) mysql_num_rows(res) +1 ) );
++
++
++ if (!ret) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: alloc() failed for list\n",
++ __FUNCTION__);
++ mysql_free_result(res);
++ return 0;
++ }
++
++ ref_set_destroy(ret,destroy_char_array);
++
++ while ((row = mysql_fetch_row(res))) {
++ ret[rows] = ref_strdup(row[0]);
++ rows++;
++ }
++ ret[rows] = NULL;
++
++ mysql_free_result(res);
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: rows= %d\n", __FUNCTION__, rows);
++ *profiles=ret;
++
++ return rows;
++}
++
++int cmyth_mysql_get_playgroups(cmyth_database_t db, char** *profiles)
++{
++
++
++ MYSQL_RES *res= NULL;
++ MYSQL_ROW row;
++ const char *query_str = "SELECT name FROM playgroup";
++ int rows=0;
++ char **ret;/* = profiles;*/
++
++
++ cmyth_mysql_query_t * query;
++ query = cmyth_mysql_query_create(db,query_str);
++
++ res = cmyth_mysql_query_result(query);
++ ref_release(query);
++ if(res == NULL)
++ {
++ cmyth_dbg(CMYTH_DBG_ERROR,"%s, finalisation/execution of query failed!\n", __FUNCTION__);
++ return 0;
++ }
++
++
++ ret = ref_alloc( sizeof( char*) * ((int) mysql_num_rows(res) +1 ) );
++
++
++ if (!ret) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: alloc() failed for list\n",
++ __FUNCTION__);
++ mysql_free_result(res);
++ return 0;
++ }
++
++ ref_set_destroy(ret,destroy_char_array);
++
++ while ((row = mysql_fetch_row(res))) {
++ ret[rows] = ref_strdup(row[0]);
++ rows++;
++ }
++ ret[rows] = NULL;
++
++ mysql_free_result(res);
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: rows= %d\n", __FUNCTION__, rows);
++ *profiles=ret;
++
++ return rows;
++}
++
++int cmyth_mysql_get_recprofiles(cmyth_database_t db, cmyth_recprofile_t **profiles)
++{
++ MYSQL_RES *res= NULL;
++ MYSQL_ROW row;
++ const char *query_str = "SELECT recordingprofiles.id, recordingprofiles.name, profilegroups.cardtype FROM recordingprofiles INNER JOIN profilegroups ON recordingprofiles.profilegroup = profilegroups.id";
++ int rows=0;
++ cmyth_recprofile_t *ret = NULL;
++
++
++ cmyth_mysql_query_t * query;
++ query = cmyth_mysql_query_create(db,query_str);
++
++ res = cmyth_mysql_query_result(query);
++ ref_release(query);
++ if(res == NULL)
++ {
++ cmyth_dbg(CMYTH_DBG_ERROR,"%s, finalisation/execution of query failed!\n", __FUNCTION__);
++ return 0;
++ }
++
++
++ ret = ref_alloc( sizeof( cmyth_recprofile_t ) * (int)mysql_num_rows(res));
++
++
++ if (!ret) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: alloc() failed for list\n",
++ __FUNCTION__);
++ mysql_free_result(res);
++ return 0;
++ }
++
++ while ((row = mysql_fetch_row(res))) {
++ ret[rows].id=safe_atoi(row[0]);
++ safe_strncpy(ret[rows].name, row[1], 128);
++ safe_strncpy(ret[rows].cardtype, row[2], 32);
++ rows++;
++ }
++
++ mysql_free_result(res);
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: rows= %d\n", __FUNCTION__, rows);
++ *profiles=ret;
++
++ return rows;
++}
++
++char* cmyth_mysql_get_cardtype(cmyth_database_t db, int chanid)
++{
++ /*"SELECT cardtype FROM channel LEFT JOIN cardinput ON channel.sourceid=cardinput.sourceid LEFT JOIN capturecard ON cardinput.cardid=capturecard.cardid WHERE channel.chanid = ?"*/
++
++ MYSQL_RES *res= NULL;
++ MYSQL_ROW row;
++ const char *query_str = "SELECT cardtype FROM channel LEFT JOIN cardinput ON channel.sourceid=cardinput.sourceid LEFT JOIN capturecard ON cardinput.cardid=capturecard.cardid WHERE channel.chanid = ?";
++
++ char* retval;
++
++ cmyth_mysql_query_t * query;
++ query = cmyth_mysql_query_create(db,query_str);
++
++ if ( cmyth_mysql_query_param_long(query, chanid) < 0
++ ) {
++ cmyth_dbg(CMYTH_DBG_ERROR,"%s, binding of query parameters failed! Maybe we're out of memory?\n", __FUNCTION__);
++ ref_release(query);
++ return NULL;
++ }
++
++
++ res = cmyth_mysql_query_result(query);
++ ref_release(query);
++
++ if(res == NULL)
++ {
++ cmyth_dbg(CMYTH_DBG_ERROR,"%s, finalisation/execution of query failed!\n", __FUNCTION__);
++ return NULL;
++ }
++
++ if ((row = mysql_fetch_row(res))) {
++ retval = ref_strdup(row[0]);
++ }
++
++ mysql_free_result(res);
++
++ return retval;
++
++}
+\ No newline at end of file
+diff --git a/lib/cmyth/libcmyth/proginfo.c b/lib/cmyth/libcmyth/proginfo.c
+index b175637..b94a7e8 100644
+--- a/lib/cmyth/libcmyth/proginfo.c
++++ b/lib/cmyth/libcmyth/proginfo.c
+@@ -1526,7 +1526,7 @@ cmyth_proginfo_t
+ cmyth_proginfo_get_detail(cmyth_conn_t control, cmyth_proginfo_t p)
+ {
+ cmyth_proginfo_t ret = cmyth_proginfo_dup(p);
+-
++ ref_release(p);
+ if (ret == NULL) {
+ cmyth_dbg(CMYTH_DBG_ERROR,
+ "%s: cmyth_proginfo_dup() failed\n",
+@@ -1540,7 +1540,7 @@ cmyth_proginfo_get_detail(cmyth_conn_t control, cmyth_proginfo_t p)
+ __FUNCTION__);
+ return NULL;
+ }
+- return ret;
++ return ret;
+ }
+
+ /*
+@@ -1636,6 +1636,30 @@ cmyth_proginfo_card_id(cmyth_proginfo_t prog)
+ return prog->proginfo_card_id;
+ }
+
++unsigned long
++cmyth_proginfo_recordid(cmyth_proginfo_t prog)
++{
++ if (!prog) {
++ cmyth_dbg(CMYTH_DBG_ERROR,
++ "%s: no program info\n", __FUNCTION__);
++ return -1;
++ }
++
++ return prog->proginfo_record_id;
++}
++
++long
++cmyth_proginfo_priority(cmyth_proginfo_t prog)
++{
++ if (!prog) {
++ cmyth_dbg(CMYTH_DBG_ERROR,
++ "%s: no program info\n", __FUNCTION__);
++ return -1;
++ }
++
++ return atoi(prog->proginfo_rec_priority);
++}
++
+ char *
+ cmyth_proginfo_recgroup(cmyth_proginfo_t prog)
+ {
+diff --git a/lib/cmyth/libcmyth/proglist.c b/lib/cmyth/libcmyth/proglist.c
+index 28e742c..fdc7861 100644
+--- a/lib/cmyth/libcmyth/proglist.c
++++ b/lib/cmyth/libcmyth/proglist.c
+@@ -33,6 +33,356 @@
+ #include <string.h>
+ #include <cmyth_local.h>
+
++extern void destroy_char_array2(void* p)
++{
++ char** ptr = (char**)p;
++ if(!ptr)
++ return;
++ while (*ptr)
++ {
++ ref_release(*ptr);
++ ptr++;
++ }
++}
++
++int cmyth_storagegroup_filelist(cmyth_conn_t control, char** *sgFilelist, char* sg2List, char* mythostname)
++{
++
++ char msg[256];
++ int res=0;
++ int count;
++ int err = 0;
++ int i = 0;
++ char **ret;
++ int consumed; /* = profiles;*/
++ char tmp_str[32768];
++
++ if (!control) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: no connection\n", __FUNCTION__);
++ return 0;
++ }
++
++ pthread_mutex_lock(&mutex);
++
++ snprintf(msg, sizeof(msg), "QUERY_SG_GETFILELIST[]:[]%s[]:[]%s[]:[][]:[]1", mythostname, sg2List);
++
++ err = cmyth_send_message(control, msg);
++ if (err < 0) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err);
++ res = 0;
++ goto out;
++ }
++
++ count = cmyth_rcv_length(control);
++ if (count < 0) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", __FUNCTION__, count);
++ res = 0;
++ goto out;
++ }
++
++ ret = (char**)ref_alloc( sizeof( char*) * (count + 1 ) );/*count + 1 ??*/
++ if (!ret) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: alloc() failed for list\n", __FUNCTION__);
++ res = 0;
++ goto out;
++ }
++
++ ref_set_destroy((void*)ret,destroy_char_array2);
++
++ while (i < count) {
++ consumed = cmyth_rcv_string(control, &err, tmp_str, sizeof(tmp_str) - 1, count);
++ count -= consumed;
++ if (err) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_string() failed (%d)\n", __FUNCTION__, count);
++ res = 0;
++ goto out;
++ }
++ ret[res] = ref_strdup(tmp_str);
++ res++;
++ }
++
++ ret[res] = NULL;
++
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: results= %d\n", __FUNCTION__, res);
++ *sgFilelist=ret;
++
++ out:
++ pthread_mutex_unlock(&mutex);
++ return res;
++
++}
++
++static void
++cmyth_storagegroup_file_destroy(cmyth_storagegroup_file_t pl)
++{
++
++ cmyth_dbg(CMYTH_DBG_DEBUG, "%s\n", __FUNCTION__);
++ if (!pl) {
++ return;
++ }
++ if(pl->filename)
++ ref_release(pl->filename);
++ if(pl->storagegroup)
++ ref_release(pl->storagegroup);
++ if(pl->hostname)
++ ref_release(pl->hostname);
++}
++
++
++cmyth_storagegroup_file_t
++cmyth_storagegroup_file_create(void)
++{
++ cmyth_storagegroup_file_t ret = ref_alloc(sizeof(*ret));
++ memset(ret, 0, sizeof(*ret));
++
++ cmyth_dbg(CMYTH_DBG_DEBUG, "%s {\n", __FUNCTION__);
++ if (!ret) {
++ cmyth_dbg(CMYTH_DBG_DEBUG, "%s }!\n", __FUNCTION__);
++ return NULL;
++ }
++ ref_set_destroy(ret, (ref_destroy_t)cmyth_storagegroup_file_destroy);
++
++ return ret;
++}
++
++static void
++cmyth_storagegroup_filelist_destroy(cmyth_storagegroup_filelist_t pl)
++{
++ int i;
++
++ cmyth_dbg(CMYTH_DBG_DEBUG, "%s\n", __FUNCTION__);
++ if (!pl) {
++ return;
++ }
++ for (i = 0; i < pl->storagegroup_filelist_count; ++i) {
++ if (pl->storagegroup_filelist_list[i]) {
++ ref_release(pl->storagegroup_filelist_list[i]);
++ }
++ pl->storagegroup_filelist_list[i] = NULL;
++ }
++ if (pl->storagegroup_filelist_list) {
++ free(pl->storagegroup_filelist_list);
++ }
++}
++
++cmyth_storagegroup_filelist_t
++cmyth_storagegroup_filelist_create(void)
++{
++ cmyth_storagegroup_filelist_t ret;
++
++ cmyth_dbg(CMYTH_DBG_DEBUG, "%s\n", __FUNCTION__);
++ ret = ref_alloc(sizeof(*ret));
++ if (!ret) {
++ return(NULL);
++ }
++ ref_set_destroy(ret, (ref_destroy_t)cmyth_storagegroup_filelist_destroy);
++
++ ret->storagegroup_filelist_list = NULL;
++ ret->storagegroup_filelist_count = 0;
++ return ret;
++}
++
++ int
++ cmyth_storagegroup_update_fileinfo(cmyth_conn_t control,cmyth_storagegroup_file_t file)
++ {
++
++ char msg[256];
++ int count;
++ int err = 0;
++ int consumed; /* = profiles;*/
++ char tmp_str[32768];
++
++ if (!control) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: no connection\n", __FUNCTION__);
++ return -1;
++ }
++
++ if (!file) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: no file specified\n", __FUNCTION__);
++ return -1;
++ }
++
++ snprintf(msg, sizeof(msg), "QUERY_SG_FILEQUERY[]:[]%s[]:[]%s[]:[]%s",file->hostname , file->storagegroup, file->filename);
++
++ err = cmyth_send_message(control, msg);
++ if (err < 0) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err);
++ return -1;
++ }
++
++ count = cmyth_rcv_length(control);
++ if (count < 0) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", __FUNCTION__, count);
++ return -1;
++ }
++
++ consumed = cmyth_rcv_string(control, &err, tmp_str, sizeof(tmp_str) - 1, count);
++ count -= consumed;
++
++ if (err) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_string() failed (%d)\n", __FUNCTION__, count);
++ return -1;
++ } else if (count == 0) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: QUERY_SG_FILEQUERY failed(%s)\n", __FUNCTION__, tmp_str);
++ return -1;
++ }
++ consumed = cmyth_rcv_ulong(control, &err, &(file->modified), count);
++ count -= consumed;
++ if (err) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_ulong() failed (%d)\n", __FUNCTION__, count);
++ return -1;
++ }
++
++ consumed = cmyth_rcv_ulong(control, &err, &(file->size), count);
++ count -= consumed;
++ if (err) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_ulong_long() failed (%d)\n", __FUNCTION__, count);
++ return -1;
++ }
++
++ return 0;
++
++ }
++
++
++ cmyth_storagegroup_filelist_t
++ cmyth_storagegroup_get_filelist(cmyth_conn_t control,char* storagegroup, char* hostname)
++ {
++ char msg[256];
++ int res=0;
++ int count = 0;
++ int err = 0;
++ int i = 0;
++ int listsize = 10;
++ cmyth_storagegroup_filelist_t ret = 0;
++ cmyth_storagegroup_file_t file = 0;
++ int consumed = 0; /* = profiles;*/
++ char tmp_str[32768];
++
++ if (!control) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: no connection\n", __FUNCTION__);
++ return 0;
++ }
++
++ pthread_mutex_lock(&mutex);
++
++ snprintf(msg, sizeof(msg), "QUERY_SG_GETFILELIST[]:[]%s[]:[]%s[]:[][]:[]1", hostname, storagegroup);
++
++ err = cmyth_send_message(control, msg);
++ if (err < 0) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err);
++ goto out;
++ }
++
++ count = cmyth_rcv_length(control);
++ if (count < 0) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", __FUNCTION__, count);
++ goto out;
++ }
++
++ ret = cmyth_storagegroup_filelist_create();
++
++
++ if (!ret) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: alloc() failed for list\n", __FUNCTION__);
++ ref_release(ret);
++ ret = 0;
++ goto out;
++ }
++
++ ret->storagegroup_filelist_count = 0;
++ ret->storagegroup_filelist_list = malloc(listsize * sizeof(cmyth_storagegroup_file_t));
++ if (!ret->storagegroup_filelist_list) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: alloc() failed for filelist list\n", __FUNCTION__);
++ ref_release(ret);
++ ret = 0;
++ goto out;
++ }
++ while (count) {
++ consumed = cmyth_rcv_string(control, &err, tmp_str, sizeof(tmp_str) - 1, count);
++ count -= consumed;
++ if (err) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_string() failed (%d)\n", __FUNCTION__, count);
++ ref_release(ret);
++ ret = 0;
++ goto out;
++ }
++ if(res>listsize-1)
++ {
++ listsize +=10;
++ ret->storagegroup_filelist_list = realloc(ret->storagegroup_filelist_list,listsize * sizeof(cmyth_storagegroup_file_t));
++ if (!ret->storagegroup_filelist_list) {
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: realloc() failed for filelist list\n", __FUNCTION__);
++ ref_release(ret);
++ ret = 0;
++ goto out;
++ }
++ }
++ file = cmyth_storagegroup_file_create();
++ file->filename = ref_strdup(tmp_str);
++ file->storagegroup = ref_strdup(storagegroup);
++ file->hostname = ref_strdup(hostname);
++ file->size = 0;
++ file->modified = 0;
++ ret->storagegroup_filelist_list[res] = file;
++ res++;
++ }
++ ret->storagegroup_filelist_count=res;
++
++ for(i = 0;i< ret->storagegroup_filelist_count;i++)
++ {
++ cmyth_storagegroup_update_fileinfo(control,ret->storagegroup_filelist_list[i]);
++ }
++
++ cmyth_dbg(CMYTH_DBG_ERROR, "%s: results= %d\n", __FUNCTION__, res);
++
++ out:
++ pthread_mutex_unlock(&mutex);
++ return ret;
++ }
++
++ int cmyth_storagegroup_filelist_count(cmyth_storagegroup_filelist_t fl)
++ {
++ if (!fl) {
++ return -EINVAL;
++ }
++ return fl->storagegroup_filelist_count;
++ }
++
++ cmyth_storagegroup_file_t cmyth_storagegroup_filelist_get_item(cmyth_storagegroup_filelist_t fl, int index)
++ {
++ if (!fl||index>=fl->storagegroup_filelist_count) {
++ return NULL;
++ }
++ ref_hold(fl->storagegroup_filelist_list[index]);
++ return fl->storagegroup_filelist_list[index];;
++ }
++
++char* cmyth_storagegroup_file_get_filename(cmyth_storagegroup_file_t file)
++{
++ if (!file) {
++ return NULL;
++ }
++ return ref_hold(file->filename);
++}
++
++unsigned long cmyth_storagegroup_file_get_lastmodified(cmyth_storagegroup_file_t file)
++{
++ if (!file) {
++ return 0;
++ }
++ return file->modified;
++}
++
++unsigned long long cmyth_storagegroup_file_get_size(cmyth_storagegroup_file_t file)
++{
++ if (!file) {
++ return 0;
++ }
++ return file->size;
++}
++
++
+ /*
+ * cmyth_proglist_destroy(void)
+ *
+diff --git a/lib/cmyth/libcmyth/socket.c b/lib/cmyth/libcmyth/socket.c
+index f1f9a45..4555860 100644
+--- a/lib/cmyth/libcmyth/socket.c
++++ b/lib/cmyth/libcmyth/socket.c
+@@ -148,6 +148,7 @@ cmyth_rcv_length(cmyth_conn_t conn)
+ char buf[16];
+ int rtot = 0;
+ int r;
++ int hangcount = 0;
+ int ret;
+ struct timeval tv;
+ fd_set fds;
+@@ -171,6 +172,9 @@ cmyth_rcv_length(cmyth_conn_t conn)
+ FD_SET(conn->conn_fd, &fds);
+ if ((r=select((int)conn->conn_fd+1, &fds, NULL, NULL, &tv)) == 0) {
+ conn->conn_hang = 1;
++ hangcount++;
++ if(++hangcount>6&&r>0)
++ return -1;
+ continue;
+ } else if (r > 0) {
+ conn->conn_hang = 0;
+
+diff --git a/lib/libhts/Win32/include/stdint.h b/lib/libhts/Win32/include/stdint.h
+deleted file mode 100644
+index 81ecedc..0000000
+--- a/lib/libhts/Win32/include/stdint.h
++++ /dev/null
+@@ -1,222 +0,0 @@
+-// ISO C9x compliant stdint.h for Microsoft Visual Studio
+-// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
+-//
+-// Copyright (c) 2006 Alexander Chemeris
+-//
+-// Redistribution and use in source and binary forms, with or without
+-// modification, are permitted provided that the following conditions are met:
+-//
+-// 1. Redistributions of source code must retain the above copyright notice,
+-// this list of conditions and the following disclaimer.
+-//
+-// 2. Redistributions in binary form must reproduce the above copyright
+-// notice, this list of conditions and the following disclaimer in the
+-// documentation and/or other materials provided with the distribution.
+-//
+-// 3. The name of the author may be used to endorse or promote products
+-// derived from this software without specific prior written permission.
+-//
+-// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+-// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+-// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+-// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+-// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+-// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+-// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+-// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-//
+-///////////////////////////////////////////////////////////////////////////////
+-
+-#ifndef _MSC_VER // [
+-#error "Use this header only with Microsoft Visual C++ compilers!"
+-#endif // _MSC_VER ]
+-
+-#ifndef _MSC_STDINT_H_ // [
+-#define _MSC_STDINT_H_
+-
+-#if _MSC_VER > 1000
+-#pragma once
+-#endif
+-
+-#include <limits.h>
+-
+-// For Visual Studio 6 in C++ mode wrap <wchar.h> include with 'extern "C++" {}'
+-// or compiler give many errors like this:
+-// error C2733: second C linkage of overloaded function 'wmemchr' not allowed
+-#if (_MSC_VER < 1300) && defined(__cplusplus)
+- extern "C++" {
+-#endif
+-# include <wchar.h>
+-#if (_MSC_VER < 1300) && defined(__cplusplus)
+- }
+-#endif
+-
+-// 7.18.1 Integer types
+-
+-// 7.18.1.1 Exact-width integer types
+-typedef __int8 int8_t;
+-typedef __int16 int16_t;
+-typedef __int32 int32_t;
+-typedef __int64 int64_t;
+-typedef unsigned __int8 uint8_t;
+-typedef unsigned __int16 uint16_t;
+-typedef unsigned __int32 uint32_t;
+-typedef unsigned __int64 uint64_t;
+-
+-// 7.18.1.2 Minimum-width integer types
+-typedef int8_t int_least8_t;
+-typedef int16_t int_least16_t;
+-typedef int32_t int_least32_t;
+-typedef int64_t int_least64_t;
+-typedef uint8_t uint_least8_t;
+-typedef uint16_t uint_least16_t;
+-typedef uint32_t uint_least32_t;
+-typedef uint64_t uint_least64_t;
+-
+-// 7.18.1.3 Fastest minimum-width integer types
+-typedef int8_t int_fast8_t;
+-typedef int16_t int_fast16_t;
+-typedef int32_t int_fast32_t;
+-typedef int64_t int_fast64_t;
+-typedef uint8_t uint_fast8_t;
+-typedef uint16_t uint_fast16_t;
+-typedef uint32_t uint_fast32_t;
+-typedef uint64_t uint_fast64_t;
+-
+-// 7.18.1.4 Integer types capable of holding object pointers
+-#ifdef _WIN64 // [
+- typedef __int64 intptr_t;
+- typedef unsigned __int64 uintptr_t;
+-#else // _WIN64 ][
+- typedef int intptr_t;
+- typedef unsigned int uintptr_t;
+-#endif // _WIN64 ]
+-
+-// 7.18.1.5 Greatest-width integer types
+-typedef int64_t intmax_t;
+-typedef uint64_t uintmax_t;
+-
+-
+-// 7.18.2 Limits of specified-width integer types
+-
+-#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259
+-
+-// 7.18.2.1 Limits of exact-width integer types
+-#define INT8_MIN ((int8_t)_I8_MIN)
+-#define INT8_MAX _I8_MAX
+-#define INT16_MIN ((int16_t)_I16_MIN)
+-#define INT16_MAX _I16_MAX
+-#define INT32_MIN ((int32_t)_I32_MIN)
+-#define INT32_MAX _I32_MAX
+-#define INT64_MIN ((int64_t)_I64_MIN)
+-#define INT64_MAX _I64_MAX
+-#define UINT8_MAX _UI8_MAX
+-#define UINT16_MAX _UI16_MAX
+-#define UINT32_MAX _UI32_MAX
+-#define UINT64_MAX _UI64_MAX
+-
+-// 7.18.2.2 Limits of minimum-width integer types
+-#define INT_LEAST8_MIN INT8_MIN
+-#define INT_LEAST8_MAX INT8_MAX
+-#define INT_LEAST16_MIN INT16_MIN
+-#define INT_LEAST16_MAX INT16_MAX
+-#define INT_LEAST32_MIN INT32_MIN
+-#define INT_LEAST32_MAX INT32_MAX
+-#define INT_LEAST64_MIN INT64_MIN
+-#define INT_LEAST64_MAX INT64_MAX
+-#define UINT_LEAST8_MAX UINT8_MAX
+-#define UINT_LEAST16_MAX UINT16_MAX
+-#define UINT_LEAST32_MAX UINT32_MAX
+-#define UINT_LEAST64_MAX UINT64_MAX
+-
+-// 7.18.2.3 Limits of fastest minimum-width integer types
+-#define INT_FAST8_MIN INT8_MIN
+-#define INT_FAST8_MAX INT8_MAX
+-#define INT_FAST16_MIN INT16_MIN
+-#define INT_FAST16_MAX INT16_MAX
+-#define INT_FAST32_MIN INT32_MIN
+-#define INT_FAST32_MAX INT32_MAX
+-#define INT_FAST64_MIN INT64_MIN
+-#define INT_FAST64_MAX INT64_MAX
+-#define UINT_FAST8_MAX UINT8_MAX
+-#define UINT_FAST16_MAX UINT16_MAX
+-#define UINT_FAST32_MAX UINT32_MAX
+-#define UINT_FAST64_MAX UINT64_MAX
+-
+-// 7.18.2.4 Limits of integer types capable of holding object pointers
+-#ifdef _WIN64 // [
+-# define INTPTR_MIN INT64_MIN
+-# define INTPTR_MAX INT64_MAX
+-# define UINTPTR_MAX UINT64_MAX
+-#else // _WIN64 ][
+-# define INTPTR_MIN INT32_MIN
+-# define INTPTR_MAX INT32_MAX
+-# define UINTPTR_MAX UINT32_MAX
+-#endif // _WIN64 ]
+-
+-// 7.18.2.5 Limits of greatest-width integer types
+-#define INTMAX_MIN INT64_MIN
+-#define INTMAX_MAX INT64_MAX
+-#define UINTMAX_MAX UINT64_MAX
+-
+-// 7.18.3 Limits of other integer types
+-
+-#ifdef _WIN64 // [
+-# define PTRDIFF_MIN _I64_MIN
+-# define PTRDIFF_MAX _I64_MAX
+-#else // _WIN64 ][
+-# define PTRDIFF_MIN _I32_MIN
+-# define PTRDIFF_MAX _I32_MAX
+-#endif // _WIN64 ]
+-
+-#define SIG_ATOMIC_MIN INT_MIN
+-#define SIG_ATOMIC_MAX INT_MAX
+-
+-#ifndef SIZE_MAX // [
+-# ifdef _WIN64 // [
+-# define SIZE_MAX _UI64_MAX
+-# else // _WIN64 ][
+-# define SIZE_MAX _UI32_MAX
+-# endif // _WIN64 ]
+-#endif // SIZE_MAX ]
+-
+-// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
+-#ifndef WCHAR_MIN // [
+-# define WCHAR_MIN 0
+-#endif // WCHAR_MIN ]
+-#ifndef WCHAR_MAX // [
+-# define WCHAR_MAX _UI16_MAX
+-#endif // WCHAR_MAX ]
+-
+-#define WINT_MIN 0
+-#define WINT_MAX _UI16_MAX
+-
+-#endif // __STDC_LIMIT_MACROS ]
+-
+-
+-// 7.18.4 Limits of other integer types
+-
+-#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
+-
+-// 7.18.4.1 Macros for minimum-width integer constants
+-
+-#define INT8_C(val) val##i8
+-#define INT16_C(val) val##i16
+-#define INT32_C(val) val##i32
+-//#define INT64_C(val) val##i64
+-
+-#define UINT8_C(val) val##ui8
+-#define UINT16_C(val) val##ui16
+-#define UINT32_C(val) val##ui32
+-//#define UINT64_C(val) val##ui64
+-
+-// 7.18.4.2 Macros for greatest-width integer constants
+-#define INTMAX_C INT64_C
+-#define UINTMAX_C UINT64_C
+-
+-#endif // __STDC_CONSTANT_MACROS ]
+-
+-
+-#endif // _MSC_STDINT_H_ ]
+diff --git a/lib/libhts/Win32/libhts_2003.sln b/lib/libhts/Win32/libhts_2003.sln
+deleted file mode 100644
+index 99b09b3..0000000
+--- a/lib/libhts/Win32/libhts_2003.sln
++++ /dev/null
+@@ -1,21 +0,0 @@
+-Microsoft Visual Studio Solution File, Format Version 8.00
+-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libhts_2003", "libhts_2003.vcproj", "{870DE8D3-B710-4CEF-9C73-36B37A9AF8A3}"
+- ProjectSection(ProjectDependencies) = postProject
+- EndProjectSection
+-EndProject
+-Global
+- GlobalSection(SolutionConfiguration) = preSolution
+- Debug = Debug
+- Release = Release
+- EndGlobalSection
+- GlobalSection(ProjectConfiguration) = postSolution
+- {870DE8D3-B710-4CEF-9C73-36B37A9AF8A3}.Debug.ActiveCfg = Debug|Win32
+- {870DE8D3-B710-4CEF-9C73-36B37A9AF8A3}.Debug.Build.0 = Debug|Win32
+- {870DE8D3-B710-4CEF-9C73-36B37A9AF8A3}.Release.ActiveCfg = Release|Win32
+- {870DE8D3-B710-4CEF-9C73-36B37A9AF8A3}.Release.Build.0 = Release|Win32
+- EndGlobalSection
+- GlobalSection(ExtensibilityGlobals) = postSolution
+- EndGlobalSection
+- GlobalSection(ExtensibilityAddIns) = postSolution
+- EndGlobalSection
+-EndGlobal
+diff --git a/lib/libhts/Win32/libhts_2003.vcproj b/lib/libhts/Win32/libhts_2003.vcproj
+deleted file mode 100644
+index 9173877..0000000
+--- a/lib/libhts/Win32/libhts_2003.vcproj
++++ /dev/null
+@@ -1,161 +0,0 @@
+-<?xml version="1.0" encoding="Windows-1252"?>
+-<VisualStudioProject
+- ProjectType="Visual C++"
+- Version="7.10"
+- Name="libhts_2003"
+- ProjectGUID="{870DE8D3-B710-4CEF-9C73-36B37A9AF8A3}"
+- RootNamespace="libhts_2003"
+- Keyword="Win32Proj">
+- <Platforms>
+- <Platform
+- Name="Win32"/>
+- </Platforms>
+- <Configurations>
+- <Configuration
+- Name="Debug|Win32"
+- OutputDirectory="Debug"
+- IntermediateDirectory="Debug"
+- ConfigurationType="4"
+- CharacterSet="2">
+- <Tool
+- Name="VCCLCompilerTool"
+- Optimization="0"
+- AdditionalIncludeDirectories="&quot;$(ProjectDir)include&quot;"
+- PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
+- MinimalRebuild="TRUE"
+- BasicRuntimeChecks="3"
+- RuntimeLibrary="1"
+- UsePrecompiledHeader="0"
+- WarningLevel="3"
+- Detect64BitPortabilityProblems="TRUE"
+- DebugInformationFormat="4"/>
+- <Tool
+- Name="VCCustomBuildTool"/>
+- <Tool
+- Name="VCLibrarianTool"
+- OutputFile="$(OutDir)/libhts_2003.lib"/>
+- <Tool
+- Name="VCMIDLTool"/>
+- <Tool
+- Name="VCPostBuildEventTool"/>
+- <Tool
+- Name="VCPreBuildEventTool"/>
+- <Tool
+- Name="VCPreLinkEventTool"/>
+- <Tool
+- Name="VCResourceCompilerTool"/>
+- <Tool
+- Name="VCWebServiceProxyGeneratorTool"/>
+- <Tool
+- Name="VCXMLDataGeneratorTool"/>
+- <Tool
+- Name="VCManagedWrapperGeneratorTool"/>
+- <Tool
+- Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+- </Configuration>
+- <Configuration
+- Name="Release|Win32"
+- OutputDirectory="Release"
+- IntermediateDirectory="Release"
+- ConfigurationType="4"
+- CharacterSet="2">
+- <Tool
+- Name="VCCLCompilerTool"
+- AdditionalIncludeDirectories="&quot;$(ProjectDir)include&quot;"
+- PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
+- RuntimeLibrary="0"
+- UsePrecompiledHeader="0"
+- WarningLevel="3"
+- Detect64BitPortabilityProblems="TRUE"
+- DebugInformationFormat="3"/>
+- <Tool
+- Name="VCCustomBuildTool"/>
+- <Tool
+- Name="VCLibrarianTool"
+- OutputFile="$(OutDir)/libhts_2003.lib"/>
+- <Tool
+- Name="VCMIDLTool"/>
+- <Tool
+- Name="VCPostBuildEventTool"/>
+- <Tool
+- Name="VCPreBuildEventTool"/>
+- <Tool
+- Name="VCPreLinkEventTool"/>
+- <Tool
+- Name="VCResourceCompilerTool"/>
+- <Tool
+- Name="VCWebServiceProxyGeneratorTool"/>
+- <Tool
+- Name="VCXMLDataGeneratorTool"/>
+- <Tool
+- Name="VCManagedWrapperGeneratorTool"/>
+- <Tool
+- Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+- </Configuration>
+- </Configurations>
+- <References>
+- </References>
+- <Files>
+- <Filter
+- Name="Source Files"
+- Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
+- UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
+- <File
+- RelativePath="..\config.h">
+- </File>
+- <File
+- RelativePath="..\hts_strtab.h">
+- </File>
+- <File
+- RelativePath="..\htsatomic.c">
+- </File>
+- <File
+- RelativePath="..\htsatomic.h">
+- </File>
+- <File
+- RelativePath="..\htsbuf.c">
+- </File>
+- <File
+- RelativePath="..\htsbuf.h">
+- </File>
+- <File
+- RelativePath="..\htsmsg.c">
+- </File>
+- <File
+- RelativePath="..\htsmsg.h">
+- </File>
+- <File
+- RelativePath="..\htsmsg_binary.c">
+- </File>
+- <File
+- RelativePath="..\htsmsg_binary.h">
+- </File>
+- <File
+- RelativePath="..\htsq.h">
+- </File>
+- <File
+- RelativePath="..\htsstr.c">
+- </File>
+- <File
+- RelativePath="..\htsstr.h">
+- </File>
+- <File
+- RelativePath="..\htsthreads.h">
+- </File>
+- <File
+- RelativePath="..\net.h">
+- </File>
+- <File
+- RelativePath="..\net_winsock.c">
+- </File>
+- <File
+- RelativePath="..\sha1.c">
+- </File>
+- <File
+- RelativePath="..\sha1.h">
+- </File>
+- </Filter>
+- </Files>
+- <Globals>
+- </Globals>
+-</VisualStudioProject>
+diff --git a/lib/libhts/Win32/libhts_2008.sln b/lib/libhts/Win32/libhts_2008.sln
+deleted file mode 100644
+index f2fb0a0..0000000
+--- a/lib/libhts/Win32/libhts_2008.sln
++++ /dev/null
+@@ -1,20 +0,0 @@
+-
+-Microsoft Visual Studio Solution File, Format Version 10.00
+-# Visual Studio 2008
+-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libhts", "libhts_2008.vcproj", "{00700E12-A63B-4E54-B962-4011A90584BD}"
+-EndProject
+-Global
+- GlobalSection(SolutionConfigurationPlatforms) = preSolution
+- Debug|Win32 = Debug|Win32
+- Release|Win32 = Release|Win32
+- EndGlobalSection
+- GlobalSection(ProjectConfigurationPlatforms) = postSolution
+- {00700E12-A63B-4E54-B962-4011A90584BD}.Debug|Win32.ActiveCfg = Debug|Win32
+- {00700E12-A63B-4E54-B962-4011A90584BD}.Debug|Win32.Build.0 = Debug|Win32
+- {00700E12-A63B-4E54-B962-4011A90584BD}.Release|Win32.ActiveCfg = Release|Win32
+- {00700E12-A63B-4E54-B962-4011A90584BD}.Release|Win32.Build.0 = Release|Win32
+- EndGlobalSection
+- GlobalSection(SolutionProperties) = preSolution
+- HideSolutionNode = FALSE
+- EndGlobalSection
+-EndGlobal
+diff --git a/lib/libhts/Win32/libhts_2008.vcproj b/lib/libhts/Win32/libhts_2008.vcproj
+deleted file mode 100644
+index ecbbe61..0000000
+--- a/lib/libhts/Win32/libhts_2008.vcproj
++++ /dev/null
+@@ -1,237 +0,0 @@
+-<?xml version="1.0" encoding="Windows-1252"?>
+-<VisualStudioProject
+- ProjectType="Visual C++"
+- Version="9,00"
+- Name="libhts"
+- ProjectGUID="{00700E12-A63B-4E54-B962-4011A90584BD}"
+- RootNamespace="libhts"
+- Keyword="Win32Proj"
+- TargetFrameworkVersion="196613"
+- >
+- <Platforms>
+- <Platform
+- Name="Win32"
+- />
+- </Platforms>
+- <ToolFiles>
+- </ToolFiles>
+- <Configurations>
+- <Configuration
+- Name="Debug|Win32"
+- OutputDirectory="$(SolutionDir)libs\$(ProjectName)\$(ConfigurationName)\"
+- IntermediateDirectory="$(SolutionDir)objs\$(ProjectName)\$(ConfigurationName)\"
+- ConfigurationType="4"
+- CharacterSet="1"
+- >
+- <Tool
+- Name="VCPreBuildEventTool"
+- />
+- <Tool
+- Name="VCCustomBuildTool"
+- />
+- <Tool
+- Name="VCXMLDataGeneratorTool"
+- />
+- <Tool
+- Name="VCWebServiceProxyGeneratorTool"
+- />
+- <Tool
+- Name="VCMIDLTool"
+- />
+- <Tool
+- Name="VCCLCompilerTool"
+- Optimization="0"
+- AdditionalIncludeDirectories="&quot;$(ProjectDir)include&quot;"
+- PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS"
+- MinimalRebuild="true"
+- BasicRuntimeChecks="0"
+- RuntimeLibrary="1"
+- UsePrecompiledHeader="0"
+- WarningLevel="3"
+- DebugInformationFormat="4"
+- />
+- <Tool
+- Name="VCManagedResourceCompilerTool"
+- />
+- <Tool
+- Name="VCResourceCompilerTool"
+- />
+- <Tool
+- Name="VCPreLinkEventTool"
+- />
+- <Tool
+- Name="VCLibrarianTool"
+- IgnoreAllDefaultLibraries="true"
+- IgnoreDefaultLibraryNames=""
+- />
+- <Tool
+- Name="VCALinkTool"
+- />
+- <Tool
+- Name="VCXDCMakeTool"
+- />
+- <Tool
+- Name="VCBscMakeTool"
+- />
+- <Tool
+- Name="VCFxCopTool"
+- />
+- <Tool
+- Name="VCPostBuildEventTool"
+- />
+- </Configuration>
+- <Configuration
+- Name="Release|Win32"
+- OutputDirectory="$(SolutionDir)libs\$(ProjectName)\$(ConfigurationName)\"
+- IntermediateDirectory="$(SolutionDir)objs\$(ProjectName)\$(ConfigurationName)\"
+- ConfigurationType="4"
+- CharacterSet="1"
+- WholeProgramOptimization="1"
+- >
+- <Tool
+- Name="VCPreBuildEventTool"
+- />
+- <Tool
+- Name="VCCustomBuildTool"
+- />
+- <Tool
+- Name="VCXMLDataGeneratorTool"
+- />
+- <Tool
+- Name="VCWebServiceProxyGeneratorTool"
+- />
+- <Tool
+- Name="VCMIDLTool"
+- />
+- <Tool
+- Name="VCCLCompilerTool"
+- Optimization="2"
+- EnableIntrinsicFunctions="true"
+- AdditionalIncludeDirectories="&quot;$(ProjectDir)include&quot;"
+- PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS"
+- ExceptionHandling="0"
+- RuntimeLibrary="0"
+- EnableFunctionLevelLinking="true"
+- UsePrecompiledHeader="0"
+- WarningLevel="3"
+- DebugInformationFormat="3"
+- />
+- <Tool
+- Name="VCManagedResourceCompilerTool"
+- />
+- <Tool
+- Name="VCResourceCompilerTool"
+- />
+- <Tool
+- Name="VCPreLinkEventTool"
+- />
+- <Tool
+- Name="VCLibrarianTool"
+- IgnoreAllDefaultLibraries="true"
+- />
+- <Tool
+- Name="VCALinkTool"
+- />
+- <Tool
+- Name="VCXDCMakeTool"
+- />
+- <Tool
+- Name="VCBscMakeTool"
+- />
+- <Tool
+- Name="VCFxCopTool"
+- />
+- <Tool
+- Name="VCPostBuildEventTool"
+- />
+- </Configuration>
+- </Configurations>
+- <References>
+- </References>
+- <Files>
+- <Filter
+- Name="Source Files"
+- Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+- UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+- >
+- <File
+- RelativePath="..\config.h"
+- >
+- </File>
+- <File
+- RelativePath="..\hts_strtab.h"
+- >
+- </File>
+- <File
+- RelativePath="..\htsatomic.c"
+- >
+- </File>
+- <File
+- RelativePath="..\htsatomic.h"
+- >
+- </File>
+- <File
+- RelativePath="..\htsbuf.c"
+- >
+- </File>
+- <File
+- RelativePath="..\htsbuf.h"
+- >
+- </File>
+- <File
+- RelativePath="..\htsmsg.c"
+- >
+- </File>
+- <File
+- RelativePath="..\htsmsg.h"
+- >
+- </File>
+- <File
+- RelativePath="..\htsmsg_binary.c"
+- >
+- </File>
+- <File
+- RelativePath="..\htsmsg_binary.h"
+- >
+- </File>
+- <File
+- RelativePath="..\htsq.h"
+- >
+- </File>
+- <File
+- RelativePath="..\htsstr.c"
+- >
+- </File>
+- <File
+- RelativePath="..\htsstr.h"
+- >
+- </File>
+- <File
+- RelativePath="..\htsthreads.h"
+- >
+- </File>
+- <File
+- RelativePath=".\include\msvc.h"
+- >
+- </File>
+- <File
+- RelativePath="..\net.h"
+- >
+- </File>
+- <File
+- RelativePath="..\net_winsock.c"
+- >
+- </File>
+- <File
+- RelativePath="..\sha1.c"
+- >
+- </File>
+- <File
+- RelativePath="..\sha1.h"
+- >
+- </File>
+- </Filter>
+- </Files>
+- <Globals>
+- </Globals>
+-</VisualStudioProject>
+diff --git a/lib/platform/os.h b/lib/platform/os.h
+new file mode 100644
+index 0000000..4913d7b
+--- /dev/null
++++ b/lib/platform/os.h
+@@ -0,0 +1,38 @@
++#pragma once
++/*
++ * This file is part of the libCEC(R) library.
++ *
++ * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
++ * libCEC(R) is an original work, containing original code.
++ *
++ * libCEC(R) is a trademark of Pulse-Eight Limited.
++ *
++ * This program is dual-licensed; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ *
++ * Alternatively, you can license this library under a commercial license,
++ * please contact Pulse-Eight Licensing for more information.
++ *
++ * For more information contact:
++ * Pulse-Eight Licensing <license@pulse-eight.com>
++ * http://www.pulse-eight.com/
++ * http://www.pulse-eight.net/
++ */
++
++#if (defined(_WIN32) || defined(_WIN64))
++#include "windows/os-types.h"
++#else
++#include "posix/os-types.h"
++#endif
+diff --git a/lib/platform/posix/os-socket.h b/lib/platform/posix/os-socket.h
+new file mode 100644
+index 0000000..036be3f
+--- /dev/null
++++ b/lib/platform/posix/os-socket.h
+@@ -0,0 +1,326 @@
++#pragma once
++/*
++ * This file is part of the libCEC(R) library.
++ *
++ * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
++ * libCEC(R) is an original work, containing original code.
++ *
++ * libCEC(R) is a trademark of Pulse-Eight Limited.
++ *
++ * This program is dual-licensed; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ *
++ * Alternatively, you can license this library under a commercial license,
++ * please contact Pulse-Eight Licensing for more information.
++ *
++ * For more information contact:
++ * Pulse-Eight Licensing <license@pulse-eight.com>
++ * http://www.pulse-eight.com/
++ * http://www.pulse-eight.net/
++ */
++
++
++#include "../os.h"
++#include "../util/timeutils.h"
++#include <stdio.h>
++#include <fcntl.h>
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <netinet/tcp.h>
++#include <arpa/inet.h>
++#include <netdb.h>
++#include <poll.h>
++
++/* Needed on Mac OS/X */
++#ifndef SOL_TCP
++#define SOL_TCP IPPROTO_TCP
++#endif
++
++namespace PLATFORM
++{
++ // Standard sockets
++ //@{
++ inline void SocketClose(socket_t socket)
++ {
++ if (socket != INVALID_SOCKET_VALUE)
++ close(socket);
++ }
++
++ inline void SocketSetBlocking(socket_t socket, bool bSetTo)
++ {
++ if (socket != INVALID_SOCKET_VALUE)
++ {
++ if (bSetTo)
++ fcntl(socket, F_SETFL, fcntl(socket, F_GETFL) & ~O_NONBLOCK);
++ else
++ fcntl(socket, F_SETFL, fcntl(socket, F_GETFL) | O_NONBLOCK);
++ }
++ }
++
++ inline ssize_t SocketWrite(socket_t socket, int *iError, void* data, size_t len)
++ {
++ fd_set port;
++
++ if (socket == INVALID_SOCKET_VALUE)
++ {
++ *iError = EINVAL;
++ return -EINVAL;
++ }
++
++ ssize_t iBytesWritten(0);
++ struct timeval *tv(NULL);
++
++ while (iBytesWritten < (ssize_t)len)
++ {
++ FD_ZERO(&port);
++ FD_SET(socket, &port);
++ int returnv = select(socket + 1, NULL, &port, NULL, tv);
++ if (returnv < 0)
++ {
++ *iError = errno;
++ return -errno;
++ }
++ else if (returnv == 0)
++ {
++ *iError = ETIMEDOUT;
++ return -ETIMEDOUT;
++ }
++
++ returnv = write(socket, (char*)data + iBytesWritten, len - iBytesWritten);
++ if (returnv == -1)
++ {
++ *iError = errno;
++ return -errno;
++ }
++ iBytesWritten += returnv;
++ }
++
++ return iBytesWritten;
++ }
++
++ inline ssize_t SocketRead(socket_t socket, int *iError, void* data, size_t len, uint64_t iTimeoutMs /*= 0*/)
++ {
++ fd_set port;
++ struct timeval timeout, *tv;
++ int64_t iNow(0), iTarget(0);
++ ssize_t iBytesRead(0);
++ *iError = 0;
++
++ if (socket == INVALID_SOCKET_VALUE)
++ {
++ *iError = EINVAL;
++ return -EINVAL;
++ }
++
++ if (iTimeoutMs > 0)
++ {
++ iNow = GetTimeMs();
++ iTarget = iNow + (int64_t) iTimeoutMs;
++ }
++
++ while (iBytesRead >= 0 && iBytesRead < (ssize_t)len && (iTimeoutMs == 0 || iTarget > iNow))
++ {
++ if (iTimeoutMs == 0)
++ {
++ tv = NULL;
++ }
++ else
++ {
++ timeout.tv_sec = ((long int)iTarget - (long int)iNow) / (long int)1000.;
++ timeout.tv_usec = ((long int)iTarget - (long int)iNow) % (long int)1000.;
++ tv = &timeout;
++ }
++
++ FD_ZERO(&port);
++ FD_SET(socket, &port);
++ int32_t returnv = select(socket + 1, &port, NULL, NULL, tv);
++
++ if (returnv == -1)
++ {
++ *iError = errno;
++ return -errno;
++ }
++ else if (returnv == 0)
++ {
++ break; //nothing to read
++ }
++
++ returnv = read(socket, (char*)data + iBytesRead, len - iBytesRead);
++ if (returnv == -1)
++ {
++ *iError = errno;
++ return -errno;
++ }
++
++ iBytesRead += returnv;
++
++ if (iTimeoutMs > 0)
++ iNow = GetTimeMs();
++ }
++
++ return iBytesRead;
++ }
++ //@}
++
++ // TCP
++ //@{
++ inline void TcpSocketClose(tcp_socket_t socket)
++ {
++ SocketClose(socket);
++ }
++
++ inline void TcpSocketShutdown(tcp_socket_t socket)
++ {
++ if (socket != INVALID_SOCKET_VALUE)
++ shutdown(socket, SHUT_RDWR);
++ }
++
++ inline ssize_t TcpSocketWrite(tcp_socket_t socket, int *iError, void* data, size_t len)
++ {
++ if (socket == INVALID_SOCKET_VALUE)
++ {
++ *iError = EINVAL;
++ return -1;
++ }
++
++ ssize_t iReturn = send(socket, data, len, 0);
++ if (iReturn < (ssize_t)len)
++ *iError = errno;
++ return iReturn;
++ }
++
++ inline ssize_t TcpSocketRead(tcp_socket_t socket, int *iError, void* data, size_t len, uint64_t iTimeoutMs /*= 0*/)
++ {
++ int64_t iNow(0), iTarget(0);
++ ssize_t iBytesRead(0);
++ *iError = 0;
++
++ if (socket == INVALID_SOCKET_VALUE)
++ {
++ *iError = EINVAL;
++ return -EINVAL;
++ }
++
++ if (iTimeoutMs > 0)
++ {
++ iNow = GetTimeMs();
++ iTarget = iNow + (int64_t) iTimeoutMs;
++ }
++
++ struct pollfd fds;
++ fds.fd = socket;
++ fds.events = POLLIN;
++ fds.revents = 0;
++
++ while (iBytesRead >= 0 && iBytesRead < (ssize_t)len && (iTimeoutMs == 0 || iTarget > iNow))
++ {
++ if (iTimeoutMs > 0)
++ {
++ int iPollResult = poll(&fds, 1, iTarget - iNow);
++ if (iPollResult == 0)
++ {
++ *iError = ETIMEDOUT;
++ return -ETIMEDOUT;
++ }
++ }
++
++ ssize_t iReadResult = (iTimeoutMs > 0) ?
++ recv(socket, (char*)data + iBytesRead, len - iBytesRead, MSG_DONTWAIT) :
++ recv(socket, data, len, MSG_WAITALL);
++ if (iReadResult < 0)
++ {
++ if (errno == EAGAIN && iTimeoutMs > 0)
++ continue;
++ *iError = errno;
++ return -errno;
++ }
++ else if (iReadResult == 0 || (iReadResult != (ssize_t)len && iTimeoutMs == 0))
++ {
++ *iError = ECONNRESET;
++ return -ECONNRESET;
++ }
++
++ iBytesRead += iReadResult;
++
++ if (iTimeoutMs > 0)
++ iNow = GetTimeMs();
++ }
++
++ if (iBytesRead < (ssize_t)len)
++ *iError = ETIMEDOUT;
++ return iBytesRead;
++ }
++
++ inline bool TcpResolveAddress(const char *strHost, uint16_t iPort, int *iError, struct addrinfo **info)
++ {
++ struct addrinfo hints;
++ char service[33];
++ memset(&hints, 0, sizeof(hints));
++ hints.ai_family = AF_UNSPEC;
++ hints.ai_socktype = SOCK_STREAM;
++ hints.ai_protocol = IPPROTO_TCP;
++ sprintf(service, "%d", iPort);
++
++ *iError = getaddrinfo(strHost, service, &hints, info);
++ return !(*iError);
++ }
++
++ inline int TcpGetSocketError(tcp_socket_t socket)
++ {
++ int iReturn(0);
++ socklen_t optLen = sizeof(socket_t);
++ getsockopt(socket, SOL_SOCKET, SO_ERROR, (void *)&iReturn, &optLen);
++ return iReturn;
++ }
++
++ inline bool TcpSetNoDelay(tcp_socket_t socket)
++ {
++ int iSetTo(1);
++ setsockopt(socket, SOL_TCP, TCP_NODELAY, &iSetTo, sizeof(iSetTo));
++ return true;
++ }
++
++ inline bool TcpConnectSocket(tcp_socket_t socket, struct addrinfo* addr, int *iError, uint64_t iTimeout = 0)
++ {
++ *iError = 0;
++ int iConnectResult = connect(socket, addr->ai_addr, addr->ai_addrlen);
++ if (iConnectResult == -1)
++ {
++ if (errno == EINPROGRESS)
++ {
++ struct pollfd pfd;
++ pfd.fd = socket;
++ pfd.events = POLLOUT;
++ pfd.revents = 0;
++
++ int iPollResult = poll(&pfd, 1, iTimeout);
++ if (iPollResult == 0)
++ *iError = ETIMEDOUT;
++ else if (iPollResult == -1)
++ *iError = errno;
++
++ socklen_t errlen = sizeof(int);
++ getsockopt(socket, SOL_SOCKET, SO_ERROR, (void *)iError, &errlen);
++ }
++ else
++ {
++ *iError = errno;
++ }
++ }
++
++ return *iError == 0;
++ }
++ //@}
++}
+diff --git a/lib/platform/posix/os-threads.h b/lib/platform/posix/os-threads.h
+new file mode 100644
+index 0000000..8b56731
+--- /dev/null
++++ b/lib/platform/posix/os-threads.h
+@@ -0,0 +1,119 @@
++#pragma once
++/*
++ * This file is part of the libCEC(R) library.
++ *
++ * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
++ * libCEC(R) is an original work, containing original code.
++ *
++ * libCEC(R) is a trademark of Pulse-Eight Limited.
++ *
++ * This program is dual-licensed; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ *
++ * Alternatively, you can license this library under a commercial license,
++ * please contact Pulse-Eight Licensing for more information.
++ *
++ * For more information contact:
++ * Pulse-Eight Licensing <license@pulse-eight.com>
++ * http://www.pulse-eight.com/
++ * http://www.pulse-eight.net/
++ */
++
++namespace PLATFORM
++{
++ inline pthread_mutexattr_t *GetRecursiveMutexAttribute(void)
++ {
++ static pthread_mutexattr_t g_mutexAttr;
++ static bool bAttributeInitialised = false;
++ if (!bAttributeInitialised)
++ {
++ pthread_mutexattr_init(&g_mutexAttr);
++ pthread_mutexattr_settype(&g_mutexAttr, PTHREAD_MUTEX_RECURSIVE);
++ bAttributeInitialised = true;
++ }
++ return &g_mutexAttr;
++ }
++
++ inline struct timespec GetAbsTime(uint64_t iIncreaseBy = 0)
++ {
++ struct timespec now;
++ #ifdef __APPLE__
++ struct timeval tv;
++ gettimeofday(&tv, NULL);
++ now.tv_sec = tv.tv_sec;
++ now.tv_nsec = tv.tv_usec * 1000;
++ #else
++ clock_gettime(CLOCK_REALTIME, &now);
++ #endif
++ now.tv_nsec += iIncreaseBy % 1000 * 1000000;
++ now.tv_sec += iIncreaseBy / 1000 + now.tv_nsec / 1000000000;
++ now.tv_nsec %= 1000000000;
++ return now;
++ }
++
++ typedef pthread_t thread_t;
++
++ #define ThreadsCreate(thread, func, arg) (pthread_create(&thread, NULL, (void *(*) (void *))func, (void *)arg) == 0)
++ #define ThreadsWait(thread, retval) (pthread_join(thread, retval) == 0)
++
++ typedef pthread_mutex_t mutex_t;
++ #define MutexCreate(mutex) pthread_mutex_init(&mutex, GetRecursiveMutexAttribute());
++ #define MutexDelete(mutex) pthread_mutex_destroy(&mutex);
++ #define MutexLock(mutex) (pthread_mutex_lock(&mutex) == 0)
++ #define MutexTryLock(mutex) (pthread_mutex_trylock(&mutex) == 0)
++ #define MutexUnlock(mutex) pthread_mutex_unlock(&mutex)
++
++ class CConditionImpl
++ {
++ public:
++ CConditionImpl(void)
++ {
++ pthread_cond_init(&m_condition, NULL);
++ }
++
++ virtual ~CConditionImpl(void)
++ {
++ pthread_cond_destroy(&m_condition);
++ }
++
++ void Signal(void)
++ {
++ pthread_cond_signal(&m_condition);
++ }
++
++ void Broadcast(void)
++ {
++ pthread_cond_broadcast(&m_condition);
++ }
++
++ bool Wait(mutex_t &mutex)
++ {
++ sched_yield();
++ return (pthread_cond_wait(&m_condition, &mutex) == 0);
++ }
++
++ bool Wait(mutex_t &mutex, uint32_t iTimeoutMs)
++ {
++ if (iTimeoutMs == 0)
++ return Wait(mutex);
++
++ sched_yield();
++ struct timespec timeout = GetAbsTime(iTimeoutMs);
++ return (pthread_cond_timedwait(&m_condition, &mutex, &timeout) == 0);
++ }
++
++ pthread_cond_t m_condition;
++ };
++}
+diff --git a/lib/platform/posix/os-types.h b/lib/platform/posix/os-types.h
+new file mode 100644
+index 0000000..b89b9cc
+--- /dev/null
++++ b/lib/platform/posix/os-types.h
+@@ -0,0 +1,54 @@
++#pragma once
++/*
++ * This file is part of the libCEC(R) library.
++ *
++ * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
++ * libCEC(R) is an original work, containing original code.
++ *
++ * libCEC(R) is a trademark of Pulse-Eight Limited.
++ *
++ * This program is dual-licensed; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ *
++ * Alternatively, you can license this library under a commercial license,
++ * please contact Pulse-Eight Licensing for more information.
++ *
++ * For more information contact:
++ * Pulse-Eight Licensing <license@pulse-eight.com>
++ * http://www.pulse-eight.com/
++ * http://www.pulse-eight.net/
++ */
++
++#define _FILE_OFFSET_BITS 64
++#include <unistd.h>
++#include <string.h>
++#include <errno.h>
++#include <sys/time.h>
++#if !defined(__APPLE__) && !defined(__FreeBSD__)
++#include <sys/prctl.h>
++#endif
++#include <pthread.h>
++#include <poll.h>
++#include <semaphore.h>
++#include <stdint.h>
++
++#define LIBTYPE
++#define DECLSPEC
++
++typedef int socket_t;
++typedef socket_t tcp_socket_t;
++#define INVALID_SOCKET_VALUE (-1)
++typedef socket_t serial_socket_t;
++#define INVALID_SERIAL_SOCKET_VALUE (-1)
+diff --git a/lib/platform/posix/serialport.cpp b/lib/platform/posix/serialport.cpp
+new file mode 100644
+index 0000000..cefc212
+--- /dev/null
++++ b/lib/platform/posix/serialport.cpp
+@@ -0,0 +1,193 @@
++/*
++ * This file is part of the libCEC(R) library.
++ *
++ * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
++ * libCEC(R) is an original work, containing original code.
++ *
++ * libCEC(R) is a trademark of Pulse-Eight Limited.
++ *
++ * This program is dual-licensed; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ *
++ * Alternatively, you can license this library under a commercial license,
++ * please contact Pulse-Eight Licensing for more information.
++ *
++ * For more information contact:
++ * Pulse-Eight Licensing <license@pulse-eight.com>
++ * http://www.pulse-eight.com/
++ * http://www.pulse-eight.net/
++ */
++
++#include "../os.h"
++#include <stdio.h>
++#include <fcntl.h>
++#include "../sockets/serialport.h"
++#include "../util/baudrate.h"
++#include "../posix/os-socket.h"
++
++#if defined(__APPLE__) || defined(__FreeBSD__)
++#ifndef XCASE
++#define XCASE 0
++#endif
++#ifndef OLCUC
++#define OLCUC 0
++#endif
++#ifndef IUCLC
++#define IUCLC 0
++#endif
++#endif
++using namespace std;
++using namespace PLATFORM;
++
++void CSerialSocket::Close(void)
++{
++ if (IsOpen())
++ SocketClose(m_socket);
++}
++
++void CSerialSocket::Shutdown(void)
++{
++ if (IsOpen())
++ SocketClose(m_socket);
++}
++
++ssize_t CSerialSocket::Write(void* data, size_t len)
++{
++ return IsOpen() ? SocketWrite(m_socket, &m_iError, data, len) : -1;
++}
++
++ssize_t CSerialSocket::Read(void* data, size_t len, uint64_t iTimeoutMs /* = 0 */)
++{
++ return IsOpen() ? SocketRead(m_socket, &m_iError, data, len, iTimeoutMs) : -1;
++}
++
++//setting all this stuff up is a pain in the ass
++bool CSerialSocket::Open(uint64_t iTimeoutMs /* = 0 */)
++{
++ iTimeoutMs = 0;
++ if (IsOpen())
++ return false;
++
++ if (m_iDatabits != SERIAL_DATA_BITS_FIVE && m_iDatabits != SERIAL_DATA_BITS_SIX &&
++ m_iDatabits != SERIAL_DATA_BITS_SEVEN && m_iDatabits != SERIAL_DATA_BITS_EIGHT)
++ {
++ m_strError = "Databits has to be between 5 and 8";
++ return false;
++ }
++
++ if (m_iStopbits != SERIAL_STOP_BITS_ONE && m_iStopbits != SERIAL_STOP_BITS_TWO)
++ {
++ m_strError = "Stopbits has to be 1 or 2";
++ return false;
++ }
++
++ if (m_iParity != SERIAL_PARITY_NONE && m_iParity != SERIAL_PARITY_EVEN && m_iParity != SERIAL_PARITY_ODD)
++ {
++ m_strError = "Parity has to be none, even or odd";
++ return false;
++ }
++
++ m_socket = open(m_strName.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
++
++ if (m_socket == INVALID_SERIAL_SOCKET_VALUE)
++ {
++ m_strError = strerror(errno);
++ return false;
++ }
++
++ SocketSetBlocking(m_socket, false);
++
++ if (!SetBaudRate(m_iBaudrate))
++ return false;
++
++ m_options.c_cflag |= (CLOCAL | CREAD);
++ m_options.c_cflag &= ~HUPCL;
++
++ m_options.c_cflag &= ~CSIZE;
++ if (m_iDatabits == SERIAL_DATA_BITS_FIVE) m_options.c_cflag |= CS5;
++ if (m_iDatabits == SERIAL_DATA_BITS_SIX) m_options.c_cflag |= CS6;
++ if (m_iDatabits == SERIAL_DATA_BITS_SEVEN) m_options.c_cflag |= CS7;
++ if (m_iDatabits == SERIAL_DATA_BITS_EIGHT) m_options.c_cflag |= CS8;
++
++ m_options.c_cflag &= ~PARENB;
++ if (m_iParity == SERIAL_PARITY_EVEN || m_iParity == SERIAL_PARITY_ODD)
++ m_options.c_cflag |= PARENB;
++ if (m_iParity == SERIAL_PARITY_ODD)
++ m_options.c_cflag |= PARODD;
++
++#ifdef CRTSCTS
++ m_options.c_cflag &= ~CRTSCTS;
++#elif defined(CNEW_RTSCTS)
++ m_options.c_cflag &= ~CNEW_RTSCTS;
++#endif
++
++ if (m_iStopbits == SERIAL_STOP_BITS_ONE) m_options.c_cflag &= ~CSTOPB;
++ else m_options.c_cflag |= CSTOPB;
++
++ //I guessed a little here
++ m_options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG | XCASE | ECHOK | ECHONL | ECHOCTL | ECHOPRT | ECHOKE | TOSTOP);
++
++ if (m_iParity == SERIAL_PARITY_NONE)
++ m_options.c_iflag &= ~INPCK;
++ else
++ m_options.c_iflag |= INPCK | ISTRIP;
++
++ m_options.c_iflag &= ~(IXON | IXOFF | IXANY | BRKINT | INLCR | IGNCR | ICRNL | IUCLC | IMAXBEL);
++ m_options.c_oflag &= ~(OPOST | ONLCR | OCRNL);
++
++ if (tcsetattr(m_socket, TCSANOW, &m_options) != 0)
++ {
++ m_strError = strerror(errno);
++ return false;
++ }
++
++ SocketSetBlocking(m_socket, true);
++ m_bIsOpen = true;
++
++ return true;
++}
++
++bool CSerialSocket::SetBaudRate(uint32_t baudrate)
++{
++ int rate = IntToBaudrate(baudrate);
++ if (rate == -1)
++ {
++ char buff[255];
++ sprintf(buff, "%i is not a valid baudrate", baudrate);
++ m_strError = buff;
++ return false;
++ }
++
++ //get the current port attributes
++ if (tcgetattr(m_socket, &m_options) != 0)
++ {
++ m_strError = strerror(errno);
++ return false;
++ }
++
++ if (cfsetispeed(&m_options, rate) != 0)
++ {
++ m_strError = strerror(errno);
++ return false;
++ }
++
++ if (cfsetospeed(&m_options, rate) != 0)
++ {
++ m_strError = strerror(errno);
++ return false;
++ }
++
++ return true;
++}
+diff --git a/lib/platform/sockets/serialport.h b/lib/platform/sockets/serialport.h
+new file mode 100644
+index 0000000..bdd05b9
+--- /dev/null
++++ b/lib/platform/sockets/serialport.h
+@@ -0,0 +1,115 @@
++#pragma once
++/*
++ * This file is part of the libCEC(R) library.
++ *
++ * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
++ * libCEC(R) is an original work, containing original code.
++ *
++ * libCEC(R) is a trademark of Pulse-Eight Limited.
++ *
++ * This program is dual-licensed; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ *
++ * Alternatively, you can license this library under a commercial license,
++ * please contact Pulse-Eight Licensing for more information.
++ *
++ * For more information contact:
++ * Pulse-Eight Licensing <license@pulse-eight.com>
++ * http://www.pulse-eight.com/
++ * http://www.pulse-eight.net/
++ */
++
++#include "../os.h"
++#include "../util/buffer.h"
++
++#include <string>
++#include <stdint.h>
++
++#if !defined(__WINDOWS__)
++#include <termios.h>
++#endif
++
++#include "socket.h"
++
++namespace PLATFORM
++{
++ enum SerialParity
++ {
++ SERIAL_PARITY_NONE = 0,
++ SERIAL_PARITY_EVEN,
++ SERIAL_PARITY_ODD
++ };
++
++ enum SerialStopBits
++ {
++ SERIAL_STOP_BITS_ONE = 1,
++ SERIAL_STOP_BITS_TWO = 2
++ };
++
++ enum SerialDataBits
++ {
++ SERIAL_DATA_BITS_FIVE = 5,
++ SERIAL_DATA_BITS_SIX = 6,
++ SERIAL_DATA_BITS_SEVEN = 7,
++ SERIAL_DATA_BITS_EIGHT = 8
++ };
++
++ class CSerialSocket : public CCommonSocket<serial_socket_t>
++ {
++ public:
++ CSerialSocket(const CStdString &strName, uint32_t iBaudrate, SerialDataBits iDatabits = SERIAL_DATA_BITS_EIGHT, SerialStopBits iStopbits = SERIAL_STOP_BITS_ONE, SerialParity iParity = SERIAL_PARITY_NONE) :
++ CCommonSocket<serial_socket_t>(INVALID_SERIAL_SOCKET_VALUE, strName),
++ m_bIsOpen(false),
++ m_iBaudrate(iBaudrate),
++ m_iDatabits(iDatabits),
++ m_iStopbits(iStopbits),
++ m_iParity(iParity) {}
++
++ virtual ~CSerialSocket(void) { Close(); }
++
++ virtual bool Open(uint64_t iTimeoutMs = 0);
++ virtual void Close(void);
++ virtual void Shutdown(void);
++ virtual ssize_t Write(void* data, size_t len);
++ virtual ssize_t Read(void* data, size_t len, uint64_t iTimeoutMs = 0);
++
++ virtual bool IsOpen(void)
++ {
++ return m_socket != INVALID_SERIAL_SOCKET_VALUE &&
++ m_bIsOpen;
++ }
++
++ virtual bool SetBaudRate(uint32_t baudrate);
++
++ protected:
++ #ifndef __WINDOWS__
++ struct termios m_options;
++ #endif
++
++ bool m_bIsOpen;
++ uint32_t m_iBaudrate;
++ SerialDataBits m_iDatabits;
++ SerialStopBits m_iStopbits;
++ SerialParity m_iParity;
++ };
++
++ class CSerialPort : public CProtectedSocket<CSerialSocket>
++ {
++ public:
++ CSerialPort(const CStdString &strName, uint32_t iBaudrate, SerialDataBits iDatabits = SERIAL_DATA_BITS_EIGHT, SerialStopBits iStopbits = SERIAL_STOP_BITS_ONE, SerialParity iParity = SERIAL_PARITY_NONE) :
++ CProtectedSocket<CSerialSocket> (new CSerialSocket(strName, iBaudrate, iDatabits, iStopbits, iParity)) {}
++ virtual ~CSerialPort(void) {}
++ };
++};
+diff --git a/lib/platform/sockets/socket.h b/lib/platform/sockets/socket.h
+new file mode 100644
+index 0000000..f56d515
+--- /dev/null
++++ b/lib/platform/sockets/socket.h
+@@ -0,0 +1,228 @@
++#pragma once
++/*
++ * This file is part of the libCEC(R) library.
++ *
++ * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
++ * libCEC(R) is an original work, containing original code.
++ *
++ * libCEC(R) is a trademark of Pulse-Eight Limited.
++ *
++ * This program is dual-licensed; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ *
++ * Alternatively, you can license this library under a commercial license,
++ * please contact Pulse-Eight Licensing for more information.
++ *
++ * For more information contact:
++ * Pulse-Eight Licensing <license@pulse-eight.com>
++ * http://www.pulse-eight.com/
++ * http://www.pulse-eight.net/
++ */
++
++#include "../threads/mutex.h"
++#include "../util/StdString.h"
++
++#if defined(__WINDOWS__)
++#include "../windows/os-socket.h"
++#else
++#include "../posix/os-socket.h"
++#endif
++
++// Common socket operations
++
++namespace PLATFORM
++{
++ class ISocket : public PreventCopy
++ {
++ public:
++ ISocket(void) {};
++ virtual ~ISocket(void) {}
++
++ virtual bool Open(uint64_t iTimeoutMs = 0) = 0;
++ virtual void Close(void) = 0;
++ virtual void Shutdown(void) = 0;
++ virtual bool IsOpen(void) = 0;
++ virtual ssize_t Write(void* data, size_t len) = 0;
++ virtual ssize_t Read(void* data, size_t len, uint64_t iTimeoutMs = 0) = 0;
++ virtual CStdString GetError(void) = 0;
++ virtual int GetErrorNumber(void) = 0;
++ virtual CStdString GetName(void) = 0;
++ };
++
++ template <typename _SType>
++ class CCommonSocket : public ISocket
++ {
++ public:
++ CCommonSocket(_SType initialSocketValue, const CStdString &strName) :
++ m_socket(initialSocketValue),
++ m_strName(strName),
++ m_iError(0) {}
++
++ virtual ~CCommonSocket(void) {}
++
++ virtual CStdString GetError(void)
++ {
++ CStdString strError;
++ strError = m_strError.IsEmpty() && m_iError != 0 ? strerror(m_iError) : m_strError;
++ return strError;
++ }
++
++ virtual int GetErrorNumber(void)
++ {
++ return m_iError;
++ }
++
++ virtual CStdString GetName(void)
++ {
++ CStdString strName;
++ strName = m_strName;
++ return strName;
++ }
++
++ protected:
++ _SType m_socket;
++ CStdString m_strError;
++ CStdString m_strName;
++ int m_iError;
++ CMutex m_mutex;
++ };
++
++ template <typename _Socket>
++ class CProtectedSocket : public ISocket
++ {
++ public:
++ CProtectedSocket(_Socket *socket) :
++ m_socket(socket),
++ m_bIsIdle(true) {}
++
++ virtual ~CProtectedSocket(void)
++ {
++ Close();
++ delete m_socket;
++ }
++
++ virtual bool Open(uint64_t iTimeoutMs = 0)
++ {
++ bool bReturn(false);
++ if (m_socket && WaitReady())
++ {
++ bReturn = m_socket->Open(iTimeoutMs);
++ MarkReady();
++ }
++ return bReturn;
++ }
++
++ virtual void Close(void)
++ {
++ if (m_socket && WaitReady())
++ {
++ m_socket->Close();
++ MarkReady();
++ }
++ }
++
++ virtual void Shutdown(void)
++ {
++ if (m_socket && WaitReady())
++ {
++ m_socket->Shutdown();
++ MarkReady();
++ }
++ }
++
++ virtual bool IsOpen(void)
++ {
++ CLockObject lock(m_mutex);
++ return m_socket && m_socket->IsOpen();
++ }
++
++ virtual bool IsBusy(void)
++ {
++ CLockObject lock(m_mutex);
++ return m_socket && !m_bIsIdle;
++ }
++
++ virtual bool IsIdle(void)
++ {
++ CLockObject lock(m_mutex);
++ return m_socket && m_bIsIdle;
++ }
++
++ virtual ssize_t Write(void* data, size_t len)
++ {
++ if (!m_socket || !WaitReady())
++ return -EINVAL;
++
++ ssize_t iReturn = m_socket->Write(data, len);
++ MarkReady();
++
++ return iReturn;
++ }
++
++ virtual ssize_t Read(void* data, size_t len, uint64_t iTimeoutMs = 0)
++ {
++ if (!m_socket || !WaitReady())
++ return -EINVAL;
++
++ ssize_t iReturn = m_socket->Read(data, len, iTimeoutMs);
++ MarkReady();
++
++ return iReturn;
++ }
++
++ virtual CStdString GetError(void)
++ {
++ CStdString strError;
++ CLockObject lock(m_mutex);
++ strError = m_socket ? m_socket->GetError() : "";
++ return strError;
++ }
++
++ virtual int GetErrorNumber(void)
++ {
++ CLockObject lock(m_mutex);
++ return m_socket ? m_socket->GetErrorNumber() : -EINVAL;
++ }
++
++ virtual CStdString GetName(void)
++ {
++ CStdString strName;
++ CLockObject lock(m_mutex);
++ strName = m_socket ? m_socket->GetName() : "";
++ return strName;
++ }
++
++ private:
++ bool WaitReady(void)
++ {
++ CLockObject lock(m_mutex);
++ m_condition.Wait(m_mutex, m_bIsIdle);
++ m_bIsIdle = false;
++ return true;
++ }
++
++ void MarkReady(void)
++ {
++ CLockObject lock(m_mutex);
++ m_bIsIdle = true;
++ m_condition.Signal();
++ }
++
++ _Socket * m_socket;
++ CMutex m_mutex;
++ CCondition<bool> m_condition;
++ bool m_bIsIdle;
++ };
++};
+diff --git a/lib/platform/sockets/tcp.h b/lib/platform/sockets/tcp.h
+new file mode 100644
+index 0000000..ffc372c
+--- /dev/null
++++ b/lib/platform/sockets/tcp.h
+@@ -0,0 +1,130 @@
++#pragma once
++/*
++ * This file is part of the libCEC(R) library.
++ *
++ * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
++ * libCEC(R) is an original work, containing original code.
++ *
++ * libCEC(R) is a trademark of Pulse-Eight Limited.
++ *
++ * This program is dual-licensed; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ *
++ * Alternatively, you can license this library under a commercial license,
++ * please contact Pulse-Eight Licensing for more information.
++ *
++ * For more information contact:
++ * Pulse-Eight Licensing <license@pulse-eight.com>
++ * http://www.pulse-eight.com/
++ * http://www.pulse-eight.net/
++ */
++
++#include "socket.h"
++
++using namespace std;
++
++namespace PLATFORM
++{
++ class CTcpSocket : public CCommonSocket<tcp_socket_t>
++ {
++ public:
++ CTcpSocket(const CStdString &strHostname, uint16_t iPort) :
++ CCommonSocket<tcp_socket_t>(INVALID_SOCKET_VALUE, strHostname),
++ m_iPort(iPort) {}
++
++ virtual ~CTcpSocket(void) {}
++
++ virtual bool Open(uint64_t iTimeoutMs = 0)
++ {
++ bool bReturn(false);
++ struct addrinfo *address(NULL), *addr(NULL);
++ if (!TcpResolveAddress(m_strName.c_str(), m_iPort, &m_iError, &address))
++ {
++ m_strError = strerror(m_iError);
++ return bReturn;
++ }
++
++ for(addr = address; !bReturn && addr; addr = addr->ai_next)
++ {
++ m_socket = TcpCreateSocket(addr, &m_iError, iTimeoutMs);
++ if (m_socket != INVALID_SOCKET_VALUE)
++ bReturn = true;
++ else
++ m_strError = strerror(m_iError);
++ }
++
++ freeaddrinfo(address);
++ return bReturn;
++ }
++
++ virtual void Close(void)
++ {
++ TcpSocketClose(m_socket);
++ m_socket = INVALID_SOCKET_VALUE;
++ }
++
++ virtual void Shutdown(void)
++ {
++ TcpSocketShutdown(m_socket);
++ m_socket = INVALID_SOCKET_VALUE;
++ }
++
++ virtual ssize_t Write(void* data, size_t len)
++ {
++ return TcpSocketWrite(m_socket, &m_iError, data, len);
++ }
++
++ virtual ssize_t Read(void* data, size_t len, uint64_t iTimeoutMs = 0)
++ {
++ return TcpSocketRead(m_socket, &m_iError, data, len, iTimeoutMs);
++ }
++
++ virtual bool IsOpen(void)
++ {
++ return m_socket != INVALID_SOCKET_VALUE;
++ }
++
++ protected:
++ virtual tcp_socket_t TcpCreateSocket(struct addrinfo* addr, int* iError, uint64_t iTimeout)
++ {
++ tcp_socket_t fdSock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
++ if (fdSock == INVALID_SOCKET_VALUE)
++ {
++ *iError = errno;
++ return (tcp_socket_t)INVALID_SOCKET_VALUE;
++ }
++
++ if (!TcpConnectSocket(fdSock, addr, iError, iTimeout))
++ {
++ TcpSocketClose(fdSock);
++ return (tcp_socket_t)INVALID_SOCKET_VALUE;
++ }
++
++ TcpSetNoDelay(fdSock);
++
++ return fdSock;
++ }
++
++ uint16_t m_iPort;
++ };
++
++ class CTcpConnection : public CProtectedSocket<CTcpSocket>
++ {
++ public:
++ CTcpConnection(const CStdString &strHostname, uint16_t iPort) :
++ CProtectedSocket<CTcpSocket> (new CTcpSocket(strHostname, iPort)) {}
++ virtual ~CTcpConnection(void) {}
++ };
++};
+diff --git a/lib/platform/threads/mutex.h b/lib/platform/threads/mutex.h
+new file mode 100644
+index 0000000..ed60ba8
+--- /dev/null
++++ b/lib/platform/threads/mutex.h
+@@ -0,0 +1,353 @@
++#pragma once
++/*
++ * This file is part of the libCEC(R) library.
++ *
++ * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
++ * libCEC(R) is an original work, containing original code.
++ *
++ * libCEC(R) is a trademark of Pulse-Eight Limited.
++ *
++ * This program is dual-licensed; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ *
++ * Alternatively, you can license this library under a commercial license,
++ * please contact Pulse-Eight Licensing for more information.
++ *
++ * For more information contact:
++ * Pulse-Eight Licensing <license@pulse-eight.com>
++ * http://www.pulse-eight.com/
++ * http://www.pulse-eight.net/
++ */
++
++#include "../os.h"
++
++#if defined(__WINDOWS__)
++#include "../windows/os-threads.h"
++#else
++#include "../posix/os-threads.h"
++#endif
++
++#include "../util/timeutils.h"
++
++namespace PLATFORM
++{
++ class PreventCopy
++ {
++ public:
++ inline PreventCopy(void) {}
++ inline ~PreventCopy(void) {}
++
++ private:
++ inline PreventCopy(const PreventCopy &c) { *this = c; }
++ inline PreventCopy &operator=(const PreventCopy &c){ *this = c; return *this; }
++ };
++
++ template <typename _Predicate>
++ class CCondition;
++
++ class CMutex : public PreventCopy
++ {
++ template <typename _Predicate>
++ friend class CCondition;
++ public:
++ inline CMutex(void) :
++ m_iLockCount(0)
++ {
++ MutexCreate(m_mutex);
++ }
++
++ inline ~CMutex(void)
++ {
++ Clear();
++ MutexDelete(m_mutex);
++ }
++
++ inline bool TryLock(void)
++ {
++ if (MutexTryLock(m_mutex))
++ {
++ ++m_iLockCount;
++ return true;
++ }
++ return false;
++ }
++
++ inline bool Lock(void)
++ {
++ MutexLock(m_mutex);
++ ++m_iLockCount;
++ return true;
++ }
++
++ inline void Unlock(void)
++ {
++ if (Lock())
++ {
++ if (m_iLockCount >= 2)
++ {
++ --m_iLockCount;
++ MutexUnlock(m_mutex);
++ }
++
++ --m_iLockCount;
++ MutexUnlock(m_mutex);
++ }
++ }
++
++ inline bool Clear(void)
++ {
++ bool bReturn(false);
++ if (TryLock())
++ {
++ unsigned int iLockCount = m_iLockCount;
++ for (unsigned int iPtr = 0; iPtr < iLockCount; iPtr++)
++ Unlock();
++ bReturn = true;
++ }
++ return bReturn;
++ }
++
++ private:
++ mutex_t m_mutex;
++ volatile unsigned int m_iLockCount;
++ };
++
++ class CLockObject : public PreventCopy
++ {
++ public:
++ inline CLockObject(CMutex &mutex, bool bClearOnExit = false) :
++ m_mutex(mutex),
++ m_bClearOnExit(bClearOnExit)
++ {
++ m_mutex.Lock();
++ }
++
++ inline ~CLockObject(void)
++ {
++ if (m_bClearOnExit)
++ Clear();
++ else
++ Unlock();
++ }
++
++ inline bool TryLock(void)
++ {
++ return m_mutex.TryLock();
++ }
++
++ inline void Unlock(void)
++ {
++ m_mutex.Unlock();
++ }
++
++ inline bool Clear(void)
++ {
++ return m_mutex.Clear();
++ }
++
++ inline bool Lock(void)
++ {
++ return m_mutex.Lock();
++ }
++
++ private:
++ CMutex &m_mutex;
++ bool m_bClearOnExit;
++ };
++
++ class CTryLockObject : public PreventCopy
++ {
++ public:
++ inline CTryLockObject(CMutex &mutex, bool bClearOnExit = false) :
++ m_mutex(mutex),
++ m_bClearOnExit(bClearOnExit),
++ m_bIsLocked(m_mutex.TryLock())
++ {
++ }
++
++ inline ~CTryLockObject(void)
++ {
++ if (m_bClearOnExit)
++ Clear();
++ else if (m_bIsLocked)
++ Unlock();
++ }
++
++ inline bool TryLock(void)
++ {
++ bool bReturn = m_mutex.TryLock();
++ m_bIsLocked |= bReturn;
++ return bReturn;
++ }
++
++ inline void Unlock(void)
++ {
++ if (m_bIsLocked)
++ {
++ m_bIsLocked = false;
++ m_mutex.Unlock();
++ }
++ }
++
++ inline bool Clear(void)
++ {
++ m_bIsLocked = false;
++ return m_mutex.Clear();
++ }
++
++ inline bool Lock(void)
++ {
++ bool bReturn = m_mutex.Lock();
++ m_bIsLocked |= bReturn;
++ return bReturn;
++ }
++
++ inline bool IsLocked(void) const
++ {
++ return m_bIsLocked;
++ }
++
++ private:
++ CMutex & m_mutex;
++ bool m_bClearOnExit;
++ volatile bool m_bIsLocked;
++ };
++
++ template <typename _Predicate>
++ class CCondition : public PreventCopy
++ {
++ public:
++ inline CCondition(void) {}
++ inline ~CCondition(void)
++ {
++ m_condition.Broadcast();
++ }
++
++ inline void Broadcast(void)
++ {
++ m_condition.Broadcast();
++ }
++
++ inline void Signal(void)
++ {
++ m_condition.Signal();
++ }
++
++ inline bool Wait(CMutex &mutex, _Predicate &predicate)
++ {
++ while(!predicate)
++ m_condition.Wait(mutex.m_mutex);
++ return true;
++ }
++
++ inline bool Wait(CMutex &mutex, _Predicate &predicate, uint32_t iTimeout)
++ {
++ if (iTimeout == 0)
++ return Wait(mutex, predicate);
++
++ if (predicate)
++ return true;
++
++ bool bReturn(false);
++ bool bBreak(false);
++ CTimeout timeout(iTimeout);
++ uint32_t iMsLeft(0);
++
++ while (!bReturn && !bBreak)
++ {
++ iMsLeft = timeout.TimeLeft();
++ if ((bReturn = predicate) == false && (bBreak = iMsLeft == 0) == false)
++ m_condition.Wait(mutex.m_mutex, iMsLeft);
++ }
++ return bReturn;
++ }
++
++ private:
++ CConditionImpl m_condition;
++ };
++
++ class CEvent
++ {
++ public:
++ CEvent(bool bAutoReset = true) :
++ m_bSignaled(false),
++ m_bBroadcast(false),
++ m_iWaitingThreads(0),
++ m_bAutoReset(bAutoReset) {}
++ virtual ~CEvent(void) {}
++
++ void Broadcast(void)
++ {
++ Set(true);
++ m_condition.Broadcast();
++ }
++
++ void Signal(void)
++ {
++ Set(false);
++ m_condition.Signal();
++ }
++
++ bool Wait(void)
++ {
++ CLockObject lock(m_mutex);
++ ++m_iWaitingThreads;
++
++ bool bReturn = m_condition.Wait(m_mutex, m_bSignaled);
++ return ResetAndReturn() && bReturn;
++ }
++
++ bool Wait(uint32_t iTimeout)
++ {
++ if (iTimeout == 0)
++ return Wait();
++
++ CLockObject lock(m_mutex);
++ ++m_iWaitingThreads;
++ bool bReturn = m_condition.Wait(m_mutex, m_bSignaled, iTimeout);
++ return ResetAndReturn() && bReturn;
++ }
++
++ static void Sleep(uint32_t iTimeout)
++ {
++ CEvent event;
++ event.Wait(iTimeout);
++ }
++
++ private:
++ void Set(bool bBroadcast = false)
++ {
++ CLockObject lock(m_mutex);
++ m_bSignaled = true;
++ m_bBroadcast = bBroadcast;
++ }
++
++ bool ResetAndReturn(void)
++ {
++ CLockObject lock(m_mutex);
++ bool bReturn(m_bSignaled);
++ if (bReturn && (--m_iWaitingThreads == 0 || !m_bBroadcast) && m_bAutoReset)
++ m_bSignaled = false;
++ return bReturn;
++ }
++
++ volatile bool m_bSignaled;
++ CCondition<volatile bool> m_condition;
++ CMutex m_mutex;
++ volatile bool m_bBroadcast;
++ unsigned int m_iWaitingThreads;
++ bool m_bAutoReset;
++ };
++}
+diff --git a/lib/platform/threads/threads.h b/lib/platform/threads/threads.h
+new file mode 100644
+index 0000000..97774b3
+--- /dev/null
++++ b/lib/platform/threads/threads.h
+@@ -0,0 +1,152 @@
++#pragma once
++/*
++ * This file is part of the libCEC(R) library.
++ *
++ * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
++ * libCEC(R) is an original work, containing original code.
++ *
++ * libCEC(R) is a trademark of Pulse-Eight Limited.
++ *
++ * This program is dual-licensed; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ *
++ * Alternatively, you can license this library under a commercial license,
++ * please contact Pulse-Eight Licensing for more information.
++ *
++ * For more information contact:
++ * Pulse-Eight Licensing <license@pulse-eight.com>
++ * http://www.pulse-eight.com/
++ * http://www.pulse-eight.net/
++ */
++
++#include "mutex.h"
++
++namespace PLATFORM
++{
++ class CThread
++ {
++ public:
++ CThread(void) :
++ m_bStop(false),
++ m_bRunning(false),
++ m_bStopped(false) {}
++
++ virtual ~CThread(void)
++ {
++ StopThread(0);
++ }
++
++ static void *ThreadHandler(CThread *thread)
++ {
++ void *retVal = NULL;
++
++ if (thread)
++ {
++ {
++ CLockObject lock(thread->m_threadMutex);
++ thread->m_bRunning = true;
++ thread->m_bStopped = false;
++ thread->m_threadCondition.Broadcast();
++ }
++
++ retVal = thread->Process();
++
++ {
++ CLockObject lock(thread->m_threadMutex);
++ thread->m_bRunning = false;
++ thread->m_bStopped = true;
++ thread->m_threadCondition.Broadcast();
++ }
++ }
++
++ return retVal;
++ }
++
++ virtual bool IsRunning(void)
++ {
++ CLockObject lock(m_threadMutex);
++ return m_bRunning;
++ }
++
++ virtual bool IsStopped(void)
++ {
++ CLockObject lock(m_threadMutex);
++ return m_bStop;
++ }
++
++ virtual bool CreateThread(bool bWait = true)
++ {
++ bool bReturn(false);
++ CLockObject lock(m_threadMutex);
++ if (!IsRunning())
++ {
++ m_bStop = false;
++ if (ThreadsCreate(m_thread, CThread::ThreadHandler, ((void*)static_cast<CThread *>(this))))
++ {
++ if (bWait)
++ m_threadCondition.Wait(m_threadMutex, m_bRunning);
++ bReturn = true;
++ }
++ }
++ return bReturn;
++ }
++
++ /*!
++ * @brief Stop the thread
++ * @param iWaitMs negative = don't wait, 0 = infinite, or the amount of ms to wait
++ */
++ virtual bool StopThread(int iWaitMs = 5000)
++ {
++ bool bReturn(true);
++ bool bRunning(false);
++ {
++ CLockObject lock(m_threadMutex);
++ bRunning = IsRunning();
++ m_bStop = true;
++ }
++
++ if (bRunning && iWaitMs >= 0)
++ {
++ CLockObject lock(m_threadMutex);
++ bReturn = m_threadCondition.Wait(m_threadMutex, m_bStopped, iWaitMs);
++ }
++ else
++ {
++ bReturn = true;
++ }
++
++ return bReturn;
++ }
++
++ virtual bool Sleep(uint32_t iTimeout)
++ {
++ CLockObject lock(m_threadMutex);
++ return m_bStop ? false : m_threadCondition.Wait(m_threadMutex, m_bStopped, iTimeout);
++ }
++
++ virtual void *Process(void) = 0;
++
++ protected:
++ void SetRunning(bool bSetTo);
++
++ private:
++ bool m_bStop;
++ bool m_bRunning;
++ bool m_bStopped;
++ CCondition<bool> m_threadCondition;
++ CMutex m_threadMutex;
++ thread_t m_thread;
++ };
++};
+diff --git a/lib/platform/util/StdString.h b/lib/platform/util/StdString.h
+new file mode 100644
+index 0000000..496dae7
+--- /dev/null
++++ b/lib/platform/util/StdString.h
+@@ -0,0 +1,4337 @@
++#pragma once
++#include "../os.h"
++#include <string>
++#include <stdint.h>
++#include <vector>
++
++#if defined(_WIN32) && !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) (((unsigned __int64)(_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;
++ SSCodeCvt::result res = SSCodeCvt::ok;
++ const SSCodeCvt& conv = SS_USE_FACET(loc, SSCodeCvt);
++ SSCodeCvt::state_type st= { 0 };
++ res = conv.in(st,
++ pSrcA, pSrcA + nSrc, pNextSrcA,
++ pDstW, pDstW + nDst, pNextDstW);
++#ifdef _LINUX
++#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;
++ SSCodeCvt::result res = SSCodeCvt::ok;
++ const SSCodeCvt& conv = SS_USE_FACET(loc, SSCodeCvt);
++ SSCodeCvt::state_type st= { 0 };
++ res = conv.out(st,
++ pSrcW, pSrcW + nSrc, pNextSrcW,
++ pDstA, pDstA + nDst, pNextDstA);
++#ifdef _LINUX
++#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)
++ {
++ }
++
++#ifdef SS_UNSIGNED
++ CStdStr(PCUSTR pU)
++ {
++ *this = reinterpret_cast<PCSTR>(pU);
++ }
++#endif
++
++ 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(uint16_t* pW)
++ {
++ #ifdef SS_ANSI
++ *this = pW;
++ #else
++ if ( !TryLoad(pW) )
++ *this = pW;
++ #endif
++ }
++
++ CStdStr(uint32_t* 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;
++ }
++
++#ifdef SS_UNSIGNED
++ MYTYPE& operator=(PCUSTR pU)
++ {
++ ssasn(*this, reinterpret_cast<PCSTR>(pU));
++ return *this;
++ }
++#endif
++
++ MYTYPE& operator=(uint16_t* pA)
++ {
++ ssasn(*this, pA);
++ return *this;
++ }
++
++ MYTYPE& operator=(uint32_t* pA)
++ {
++ ssasn(*this, pA);
++ return *this;
++ }
++
++ MYTYPE& operator=(CT t)
++ {
++ Q172398(*this);
++ this->assign(1, t);
++ return *this;
++ }
++
++ #ifdef SS_INC_COMDEF
++ MYTYPE& operator=(const _bstr_t& bstr)
++ {
++ if ( bstr.length() > 0 )
++ {
++ this->assign(static_cast<PCMYSTR>(bstr), bstr.length());
++ return *this;
++ }
++ else
++ {
++ this->erase();
++ return *this;
++ }
++ }
++ #endif
++
++
++ // 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+=(uint16_t* pW)
++ {
++ ssadd(*this, pW);
++ return *this;
++ }
++
++ MYTYPE& operator+=(uint32_t* pW)
++ {
++ ssadd(*this, pW);
++ return *this;
++ }
++
++ MYTYPE& operator+=(CT t)
++ {
++ this->append(1, t);
++ return *this;
++ }
++ #ifdef SS_INC_COMDEF // if we have _bstr_t, define a += for it too.
++ MYTYPE& operator+=(const _bstr_t& bstr)
++ {
++ return this->operator+=(static_cast<PCMYSTR>(bstr));
++ }
++ #endif
++
++
++ // -------------------------------------------------------------------------
++ // Case changing functions
++ // -------------------------------------------------------------------------
++
++ MYTYPE& ToUpper(const std::locale& loc=std::locale())
++ {
++ // Note -- if there are any MBCS character sets in which the lowercase
++ // form a character takes up a different number of bytes than the
++ // uppercase form, this would probably not work...
++
++ std::transform(this->begin(),
++ this->end(),
++ this->begin(),
++#ifdef SS_NO_LOCALE
++ SSToUpper<CT>());
++#else
++ std::bind2nd(SSToUpper<CT>(), loc));
++#endif
++
++ // ...but if it were, this would probably work better. Also, this way
++ // seems to be a bit faster when anything other then the "C" locale is
++ // used...
++
++// if ( !empty() )
++// {
++// ssupr(this->GetBuf(), this->size(), loc);
++// this->RelBuf();
++// }
++
++ return *this;
++ }
++
++ MYTYPE& ToLower(const std::locale& loc=std::locale())
++ {
++ // Note -- if there are any MBCS character sets in which the lowercase
++ // form a character takes up a different number of bytes than the
++ // uppercase form, this would probably not work...
++
++ std::transform(this->begin(),
++ this->end(),
++ this->begin(),
++#ifdef SS_NO_LOCALE
++ SSToLower<CT>());
++#else
++ std::bind2nd(SSToLower<CT>(), loc));
++#endif
++
++ // ...but if it were, this would probably work better. Also, this way
++ // seems to be a bit faster when anything other then the "C" locale is
++ // used...
++
++// if ( !empty() )
++// {
++// sslwr(this->GetBuf(), this->size(), loc);
++// this->RelBuf();
++// }
++ return *this;
++ }
++
++
++ MYTYPE& Normalize()
++ {
++ return Trim().ToLower();
++ }
++
++
++ // -------------------------------------------------------------------------
++ // 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
++
++ // -------------------------------------------------------------------------
++ // FUNCTION: CStdStr::Format
++ // void _cdecl Formst(CStdStringA& PCSTR szFormat, ...)
++ // void _cdecl Format(PCSTR szFormat);
++ //
++ // DESCRIPTION:
++ // This function does sprintf/wsprintf style formatting on CStdStringA
++ // objects. It looks a lot like MFC's CString::Format. Some people
++ // might even call this identical. Fortunately, these people are now
++ // dead... heh heh.
++ //
++ // PARAMETERS:
++ // nId - ID of string resource holding the format string
++ // szFormat - a PCSTR holding the format specifiers
++ // argList - a va_list holding the arguments for the format specifiers.
++ //
++ // RETURN VALUE: None.
++ // -------------------------------------------------------------------------
++ // formatting (using wsprintf style formatting)
++
++ // If they want a Format() function that safely handles string objects
++ // without casting
++
++#ifdef SS_SAFE_FORMAT
++
++ // Question: Joe, you wacky coder you, why do you have so many overloads
++ // of the Format() function
++ // Answer: One reason only - CString compatability. In short, by making
++ // the Format() function a template this way, I can do strong typing
++ // and allow people to pass CStdString arguments as fillers for
++ // "%s" format specifiers without crashing their program! The downside
++ // is that I need to overload on the number of arguments. If you are
++ // passing more arguments than I have listed below in any of my
++ // overloads, just add another one.
++ //
++ // Yes, yes, this is really ugly. In essence what I am doing here is
++ // protecting people from a bad (and incorrect) programming practice
++ // that they should not be doing anyway. I am protecting them from
++ // themselves. Why am I doing this? Well, if you had any idea the
++ // number of times I've been emailed by people about this
++ // "incompatability" in my code, you wouldn't ask.
++
++ void Fmt(const CT* szFmt, ...)
++ {
++ va_list argList;
++ va_start(argList, szFmt);
++ FormatV(szFmt, argList);
++ va_end(argList);
++ }
++
++#ifndef SS_ANSI
++
++ void Format(UINT nId)
++ {
++ MYTYPE strFmt;
++ if ( strFmt.Load(nId) )
++ this->swap(strFmt);
++ }
++ template<class A1>
++ void Format(UINT nId, const A1& v)
++ {
++ MYTYPE strFmt;
++ if ( strFmt.Load(nId) )
++ Fmt(strFmt, FmtArg<A1>(v)());
++ }
++ template<class A1, class A2>
++ void Format(UINT nId, const A1& v1, const A2& v2)
++ {
++ MYTYPE strFmt;
++ if ( strFmt.Load(nId) )
++ Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)());
++ }
++ template<class A1, class A2, class A3>
++ void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3)
++ {
++ MYTYPE strFmt;
++ if ( strFmt.Load(nId) )
++ {
++ Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)());
++ }
++ }
++ template<class A1, class A2, class A3, class A4>
++ void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
++ const A4& v4)
++ {
++ MYTYPE strFmt;
++ if ( strFmt.Load(nId) )
++ {
++ Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)(), FmtArg<A4>(v4)());
++ }
++ }
++ template<class A1, class A2, class A3, class A4, class A5>
++ void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
++ const A4& v4, const A5& v5)
++ {
++ MYTYPE strFmt;
++ if ( strFmt.Load(nId) )
++ {
++ Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)());
++ }
++ }
++ template<class A1, class A2, class A3, class A4, class A5, class A6>
++ void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
++ const A4& v4, const A5& v5, const A6& v6)
++ {
++ MYTYPE strFmt;
++ if ( strFmt.Load(nId) )
++ {
++ Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)(), FmtArg<A4>(v4)(),FmtArg<A5>(v5)(),
++ FmtArg<A6>(v6)());
++ }
++ }
++ template<class A1, class A2, class A3, class A4, class A5, class A6,
++ class A7>
++ void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
++ const A4& v4, const A5& v5, const A6& v6, const A7& v7)
++ {
++ MYTYPE strFmt;
++ if ( strFmt.Load(nId) )
++ {
++ Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)(), FmtArg<A4>(v4)(),FmtArg<A5>(v5)(),
++ FmtArg<A6>(v6)(), FmtArg<A7>(v7)());
++ }
++ }
++ template<class A1, class A2, class A3, class A4, class A5, class A6,
++ class A7, class A8>
++ void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
++ const A4& v4, const A5& v5, const A6& v6, const A7& v7,
++ const A8& v8)
++ {
++ MYTYPE strFmt;
++ if ( strFmt.Load(nId) )
++ {
++ Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
++ FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)());
++ }
++ }
++ template<class A1, class A2, class A3, class A4, class A5, class A6,
++ class A7, class A8, class A9>
++ void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
++ const A4& v4, const A5& v5, const A6& v6, const A7& v7,
++ const A8& v8, const A9& v9)
++ {
++ MYTYPE strFmt;
++ if ( strFmt.Load(nId) )
++ {
++ Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
++ FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
++ FmtArg<A9>(v9)());
++ }
++ }
++ template<class A1, class A2, class A3, class A4, class A5, class A6,
++ class A7, class A8, class A9, class A10>
++ void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
++ const A4& v4, const A5& v5, const A6& v6, const A7& v7,
++ const A8& v8, const A9& v9, const A10& v10)
++ {
++ MYTYPE strFmt;
++ if ( strFmt.Load(nId) )
++ {
++ Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
++ FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
++ FmtArg<A9>(v9)(), FmtArg<A10>(v10)());
++ }
++ }
++ template<class A1, class A2, class A3, class A4, class A5, class A6,
++ class A7, class A8, class A9, class A10, class A11>
++ void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
++ const A4& v4, const A5& v5, const A6& v6, const A7& v7,
++ const A8& v8, const A9& v9, const A10& v10, const A11& v11)
++ {
++ MYTYPE strFmt;
++ if ( strFmt.Load(nId) )
++ {
++ Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
++ FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
++ FmtArg<A9>(v9)(),FmtArg<A10>(v10)(),FmtArg<A11>(v11)());
++ }
++ }
++ template<class A1, class A2, class A3, class A4, class A5, class A6,
++ class A7, class A8, class A9, class A10, class A11, class A12>
++ void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
++ const A4& v4, const A5& v5, const A6& v6, const A7& v7,
++ const A8& v8, const A9& v9, const A10& v10, const A11& v11,
++ const A12& v12)
++ {
++ MYTYPE strFmt;
++ if ( strFmt.Load(nId) )
++ {
++ Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
++ FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
++ FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
++ FmtArg<A12>(v12)());
++ }
++ }
++ template<class A1, class A2, class A3, class A4, class A5, class A6,
++ class A7, class A8, class A9, class A10, class A11, class A12,
++ class A13>
++ void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
++ const A4& v4, const A5& v5, const A6& v6, const A7& v7,
++ const A8& v8, const A9& v9, const A10& v10, const A11& v11,
++ const A12& v12, const A13& v13)
++ {
++ MYTYPE strFmt;
++ if ( strFmt.Load(nId) )
++ {
++ Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
++ FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
++ FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
++ FmtArg<A12>(v12)(), FmtArg<A13>(v13)());
++ }
++ }
++ template<class A1, class A2, class A3, class A4, class A5, class A6,
++ class A7, class A8, class A9, class A10, class A11, class A12,
++ class A13, class A14>
++ void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
++ const A4& v4, const A5& v5, const A6& v6, const A7& v7,
++ const A8& v8, const A9& v9, const A10& v10, const A11& v11,
++ const A12& v12, const A13& v13, const A14& v14)
++ {
++ MYTYPE strFmt;
++ if ( strFmt.Load(nId) )
++ {
++ Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
++ FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
++ FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
++ FmtArg<A12>(v12)(), FmtArg<A13>(v13)(),FmtArg<A14>(v14)());
++ }
++ }
++ template<class A1, class A2, class A3, class A4, class A5, class A6,
++ class A7, class A8, class A9, class A10, class A11, class A12,
++ class A13, class A14, class A15>
++ void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
++ const A4& v4, const A5& v5, const A6& v6, const A7& v7,
++ const A8& v8, const A9& v9, const A10& v10, const A11& v11,
++ const A12& v12, const A13& v13, const A14& v14, const A15& v15)
++ {
++ MYTYPE strFmt;
++ if ( strFmt.Load(nId) )
++ {
++ Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
++ FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
++ FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
++ FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
++ FmtArg<A15>(v15)());
++ }
++ }
++ template<class A1, class A2, class A3, class A4, class A5, class A6,
++ class A7, class A8, class A9, class A10, class A11, class A12,
++ class A13, class A14, class A15, class A16>
++ void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
++ const A4& v4, const A5& v5, const A6& v6, const A7& v7,
++ const A8& v8, const A9& v9, const A10& v10, const A11& v11,
++ const A12& v12, const A13& v13, const A14& v14, const A15& v15,
++ const A16& v16)
++ {
++ MYTYPE strFmt;
++ if ( strFmt.Load(nId) )
++ {
++ Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
++ FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
++ FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
++ FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
++ FmtArg<A15>(v15)(), FmtArg<A16>(v16)());
++ }
++ }
++ template<class A1, class A2, class A3, class A4, class A5, class A6,
++ class A7, class A8, class A9, class A10, class A11, class A12,
++ class A13, class A14, class A15, class A16, class A17>
++ void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
++ const A4& v4, const A5& v5, const A6& v6, const A7& v7,
++ const A8& v8, const A9& v9, const A10& v10, const A11& v11,
++ const A12& v12, const A13& v13, const A14& v14, const A15& v15,
++ const A16& v16, const A17& v17)
++ {
++ MYTYPE strFmt;
++ if ( strFmt.Load(nId) )
++ {
++ Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
++ FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
++ FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
++ FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
++ FmtArg<A15>(v15)(),FmtArg<A16>(v16)(),FmtArg<A17>(v17)());
++ }
++ }
++
++#endif // #ifndef SS_ANSI
++
++ // ...now the other overload of Format: the one that takes a string literal
++
++ void Format(const CT* szFmt)
++ {
++ *this = szFmt;
++ }
++ template<class A1>
++ void Format(const CT* szFmt, const A1& v)
++ {
++ Fmt(szFmt, FmtArg<A1>(v)());
++ }
++ template<class A1, class A2>
++ void Format(const CT* szFmt, const A1& v1, const A2& v2)
++ {
++ Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)());
++ }
++ template<class A1, class A2, class A3>
++ void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3)
++ {
++ Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)());
++ }
++ template<class A1, class A2, class A3, class A4>
++ void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
++ const A4& v4)
++ {
++ Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)(), FmtArg<A4>(v4)());
++ }
++ template<class A1, class A2, class A3, class A4, class A5>
++ void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
++ const A4& v4, const A5& v5)
++ {
++ Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)());
++ }
++ template<class A1, class A2, class A3, class A4, class A5, class A6>
++ void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
++ const A4& v4, const A5& v5, const A6& v6)
++ {
++ Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
++ FmtArg<A6>(v6)());
++ }
++ template<class A1, class A2, class A3, class A4, class A5, class A6,
++ class A7>
++ void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
++ const A4& v4, const A5& v5, const A6& v6, const A7& v7)
++ {
++ Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
++ FmtArg<A6>(v6)(), FmtArg<A7>(v7)());
++ }
++ template<class A1, class A2, class A3, class A4, class A5, class A6,
++ class A7, class A8>
++ void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
++ const A4& v4, const A5& v5, const A6& v6, const A7& v7,
++ const A8& v8)
++ {
++ Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
++ FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)());
++ }
++ template<class A1, class A2, class A3, class A4, class A5, class A6,
++ class A7, class A8, class A9>
++ void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
++ const A4& v4, const A5& v5, const A6& v6, const A7& v7,
++ const A8& v8, const A9& v9)
++ {
++ Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
++ FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
++ FmtArg<A9>(v9)());
++ }
++ template<class A1, class A2, class A3, class A4, class A5, class A6,
++ class A7, class A8, class A9, class A10>
++ void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
++ const A4& v4, const A5& v5, const A6& v6, const A7& v7,
++ const A8& v8, const A9& v9, const A10& v10)
++ {
++ Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
++ FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
++ FmtArg<A9>(v9)(), FmtArg<A10>(v10)());
++ }
++ template<class A1, class A2, class A3, class A4, class A5, class A6,
++ class A7, class A8, class A9, class A10, class A11>
++ void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
++ const A4& v4, const A5& v5, const A6& v6, const A7& v7,
++ const A8& v8, const A9& v9, const A10& v10, const A11& v11)
++ {
++ Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
++ FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
++ FmtArg<A9>(v9)(),FmtArg<A10>(v10)(),FmtArg<A11>(v11)());
++ }
++ template<class A1, class A2, class A3, class A4, class A5, class A6,
++ class A7, class A8, class A9, class A10, class A11, class A12>
++ void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
++ const A4& v4, const A5& v5, const A6& v6, const A7& v7,
++ const A8& v8, const A9& v9, const A10& v10, const A11& v11,
++ const A12& v12)
++ {
++ Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
++ FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
++ FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
++ FmtArg<A12>(v12)());
++ }
++ template<class A1, class A2, class A3, class A4, class A5, class A6,
++ class A7, class A8, class A9, class A10, class A11, class A12,
++ class A13>
++ void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
++ const A4& v4, const A5& v5, const A6& v6, const A7& v7,
++ const A8& v8, const A9& v9, const A10& v10, const A11& v11,
++ const A12& v12, const A13& v13)
++ {
++ Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
++ FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
++ FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
++ FmtArg<A12>(v12)(), FmtArg<A13>(v13)());
++ }
++ template<class A1, class A2, class A3, class A4, class A5, class A6,
++ class A7, class A8, class A9, class A10, class A11, class A12,
++ class A13, class A14>
++ void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
++ const A4& v4, const A5& v5, const A6& v6, const A7& v7,
++ const A8& v8, const A9& v9, const A10& v10, const A11& v11,
++ const A12& v12, const A13& v13, const A14& v14)
++ {
++ Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
++ FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
++ FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
++ FmtArg<A12>(v12)(), FmtArg<A13>(v13)(),FmtArg<A14>(v14)());
++ }
++ template<class A1, class A2, class A3, class A4, class A5, class A6,
++ class A7, class A8, class A9, class A10, class A11, class A12,
++ class A13, class A14, class A15>
++ void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
++ const A4& v4, const A5& v5, const A6& v6, const A7& v7,
++ const A8& v8, const A9& v9, const A10& v10, const A11& v11,
++ const A12& v12, const A13& v13, const A14& v14, const A15& v15)
++ {
++ Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
++ FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
++ FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
++ FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
++ FmtArg<A15>(v15)());
++ }
++ template<class A1, class A2, class A3, class A4, class A5, class A6,
++ class A7, class A8, class A9, class A10, class A11, class A12,
++ class A13, class A14, class A15, class A16>
++ void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
++ const A4& v4, const A5& v5, const A6& v6, const A7& v7,
++ const A8& v8, const A9& v9, const A10& v10, const A11& v11,
++ const A12& v12, const A13& v13, const A14& v14, const A15& v15,
++ const A16& v16)
++ {
++ Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
++ FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
++ FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
++ FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
++ FmtArg<A15>(v15)(), FmtArg<A16>(v16)());
++ }
++ template<class A1, class A2, class A3, class A4, class A5, class A6,
++ class A7, class A8, class A9, class A10, class A11, class A12,
++ class A13, class A14, class A15, class A16, class A17>
++ void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
++ const A4& v4, const A5& v5, const A6& v6, const A7& v7,
++ const A8& v8, const A9& v9, const A10& v10, const A11& v11,
++ const A12& v12, const A13& v13, const A14& v14, const A15& v15,
++ const A16& v16, const A17& v17)
++ {
++ Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
++ FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
++ FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
++ FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
++ FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
++ FmtArg<A15>(v15)(),FmtArg<A16>(v16)(),FmtArg<A17>(v17)());
++ }
++
++#else // #ifdef SS_SAFE_FORMAT
++
++
++#ifndef SS_ANSI
++
++ void Format(UINT nId, ...)
++ {
++ va_list argList;
++ va_start(argList, nId);
++
++ MYTYPE strFmt;
++ if ( strFmt.Load(nId) )
++ FormatV(strFmt, argList);
++
++ va_end(argList);
++ }
++
++#endif // #ifdef SS_ANSI
++
++ void Format(const CT* szFmt, ...)
++ {
++ va_list argList;
++ va_start(argList, szFmt);
++ FormatV(szFmt, argList);
++ va_end(argList);
++ }
++
++#endif // #ifdef SS_SAFE_FORMAT
++
++ void AppendFormat(const CT* szFmt, ...)
++ {
++ va_list argList;
++ va_start(argList, szFmt);
++ AppendFormatV(szFmt, argList);
++ va_end(argList);
++ }
++
++ #define MAX_FMT_TRIES 5 // #of times we try
++ #define FMT_BLOCK_SIZE 2048 // # of bytes to increment per try
++ #define BUFSIZE_1ST 256
++ #define BUFSIZE_2ND 512
++ #define STD_BUF_SIZE 1024
++
++ // an efficient way to add formatted characters to the string. You may only
++ // add up to STD_BUF_SIZE characters at a time, though
++ void AppendFormatV(const CT* szFmt, va_list argList)
++ {
++ CT szBuf[STD_BUF_SIZE];
++ int nLen = ssnprintf(szBuf, STD_BUF_SIZE-1, szFmt, argList);
++
++ if ( 0 < nLen )
++ this->append(szBuf, nLen);
++ }
++
++ // -------------------------------------------------------------------------
++ // FUNCTION: FormatV
++ // void FormatV(PCSTR szFormat, va_list, argList);
++ //
++ // DESCRIPTION:
++ // This function formats the string with sprintf style format-specs.
++ // It makes a general guess at required buffer size and then tries
++ // successively larger buffers until it finds one big enough or a
++ // threshold (MAX_FMT_TRIES) is exceeded.
++ //
++ // PARAMETERS:
++ // szFormat - a PCSTR holding the format of the output
++ // argList - a Microsoft specific va_list for variable argument lists
++ //
++ // RETURN VALUE:
++ // -------------------------------------------------------------------------
++
++ // NOTE: Changed by JM to actually function under non-win32,
++ // and to remove the upper limit on size.
++ void FormatV(const CT* szFormat, va_list argList)
++ {
++ // try and grab a sufficient buffersize
++ int nChars = FMT_BLOCK_SIZE;
++ va_list argCopy;
++
++ CT *p = reinterpret_cast<CT*>(malloc(sizeof(CT)*nChars));
++ if (!p) return;
++
++ while (1)
++ {
++ va_copy(argCopy, argList);
++
++ int nActual = ssvsprintf(p, nChars, szFormat, argCopy);
++ /* If that worked, return the string. */
++ if (nActual > -1 && nActual < nChars)
++ { /* make sure it's NULL terminated */
++ p[nActual] = '\0';
++ this->assign(p, nActual);
++ free(p);
++ va_end(argCopy);
++ return;
++ }
++ /* Else try again with more space. */
++ if (nActual > -1) /* glibc 2.1 */
++ nChars = nActual + 1; /* precisely what is needed */
++ else /* glibc 2.0 */
++ nChars *= 2; /* twice the old size */
++
++ CT *np = reinterpret_cast<CT*>(realloc(p, sizeof(CT)*nChars));
++ if (np == NULL)
++ {
++ free(p);
++ va_end(argCopy);
++ return; // failed :(
++ }
++ p = np;
++ va_end(argCopy);
++ }
++ }
++
++ // -------------------------------------------------------------------------
++ // 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 Compare(PCMYSTR szThat) const
++ {
++ return this->compare(szThat);
++ }
++
++ int CompareNoCase(PCMYSTR szThat) const
++ {
++ return ssicmp(this->c_str(), szThat);
++ }
++
++ int Delete(int nIdx, int nCount=1)
++ {
++ if ( nIdx < 0 )
++ nIdx = 0;
++
++ if ( nIdx < this->GetLength() )
++ this->erase(static_cast<MYSIZE>(nIdx), static_cast<MYSIZE>(nCount));
++
++ return GetLength();
++ }
++
++ void Empty()
++ {
++ this->erase();
++ }
++
++ int Find(CT ch) const
++ {
++ MYSIZE nIdx = this->find_first_of(ch);
++ return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
++ }
++
++ int Find(PCMYSTR szSub) const
++ {
++ MYSIZE nIdx = this->find(szSub);
++ return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
++ }
++
++ int Find(CT ch, int nStart) const
++ {
++ // CString::Find docs say add 1 to nStart when it's not zero
++ // CString::Find code doesn't do that however. We'll stick
++ // with what the code does
++
++ MYSIZE nIdx = this->find_first_of(ch, static_cast<MYSIZE>(nStart));
++ return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
++ }
++
++ int Find(PCMYSTR szSub, int nStart) const
++ {
++ // CString::Find docs say add 1 to nStart when it's not zero
++ // CString::Find code doesn't do that however. We'll stick
++ // with what the code does
++
++ MYSIZE nIdx = this->find(szSub, static_cast<MYSIZE>(nStart));
++ return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
++ }
++
++ 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 GetAt(int nIdx) const
++ {
++ return this->at(static_cast<MYSIZE>(nIdx));
++ }
++
++ CT* GetBuffer(int nMinLen=-1)
++ {
++ return GetBuf(nMinLen);
++ }
++
++ CT* GetBufferSetLength(int nLen)
++ {
++ return BufferSet(nLen);
++ }
++
++ // GetLength() -- MFC docs say this is the # of BYTES but
++ // in truth it is the number of CHARACTERs (chars or wchar_ts)
++ int GetLength() const
++ {
++ return static_cast<int>(this->length());
++ }
++
++ int Insert(int nIdx, CT ch)
++ {
++ if ( static_cast<MYSIZE>(nIdx) > this->size()-1 )
++ this->append(1, ch);
++ else
++ this->insert(static_cast<MYSIZE>(nIdx), 1, ch);
++
++ return GetLength();
++ }
++ int Insert(int nIdx, PCMYSTR sz)
++ {
++ if ( static_cast<MYSIZE>(nIdx) >= this->size() )
++ this->append(sz, static_cast<MYSIZE>(sslen(sz)));
++ else
++ this->insert(static_cast<MYSIZE>(nIdx), sz);
++
++ return GetLength();
++ }
++
++ bool IsEmpty() const
++ {
++ return this->empty();
++ }
++
++ MYTYPE Left(int nCount) const
++ {
++ // Range check the count.
++
++ nCount = SSMAX(0, SSMIN(nCount, static_cast<int>(this->size())));
++ return this->substr(0, static_cast<MYSIZE>(nCount));
++ }
++
++#ifndef SS_ANSI
++ bool LoadString(UINT nId)
++ {
++ return this->Load(nId);
++ }
++#endif
++
++ void MakeLower()
++ {
++ ToLower();
++ }
++
++ void MakeReverse()
++ {
++ std::reverse(this->begin(), this->end());
++ }
++
++ void MakeUpper()
++ {
++ ToUpper();
++ }
++
++ MYTYPE Mid(int nFirst) const
++ {
++ return Mid(nFirst, this->GetLength()-nFirst);
++ }
++
++ MYTYPE Mid(int nFirst, int nCount) const
++ {
++ // CString does range checking here. Since we're trying to emulate it,
++ // we must check too.
++
++ if ( nFirst < 0 )
++ nFirst = 0;
++ if ( nCount < 0 )
++ nCount = 0;
++
++ int nSize = static_cast<int>(this->size());
++
++ if ( nFirst + nCount > nSize )
++ nCount = nSize - nFirst;
++
++ if ( nFirst > nSize )
++ return MYTYPE();
++
++ ASSERT(nFirst >= 0);
++ ASSERT(nFirst + nCount <= nSize);
++
++ return this->substr(static_cast<MYSIZE>(nFirst),
++ static_cast<MYSIZE>(nCount));
++ }
++
++ void ReleaseBuffer(int nNewLen=-1)
++ {
++ RelBuf(nNewLen);
++ }
++
++ int Remove(CT ch)
++ {
++ MYSIZE nIdx = 0;
++ int nRemoved = 0;
++ while ( (nIdx=this->find_first_of(ch)) != MYBASE::npos )
++ {
++ this->erase(nIdx, 1);
++ nRemoved++;
++ }
++ return nRemoved;
++ }
++
++ int Replace(CT chOld, CT chNew)
++ {
++ int nReplaced = 0;
++
++ for ( MYITER iter=this->begin(); iter != this->end(); iter++ )
++ {
++ if ( *iter == chOld )
++ {
++ *iter = chNew;
++ nReplaced++;
++ }
++ }
++
++ return nReplaced;
++ }
++
++ int Replace(PCMYSTR szOld, PCMYSTR szNew)
++ {
++ int nReplaced = 0;
++ MYSIZE nIdx = 0;
++ MYSIZE nOldLen = sslen(szOld);
++
++ if ( 0 != nOldLen )
++ {
++ // If the replacement string is longer than the one it replaces, this
++ // string is going to have to grow in size, Figure out how much
++ // and grow it all the way now, rather than incrementally
++
++ MYSIZE nNewLen = sslen(szNew);
++ if ( nNewLen > nOldLen )
++ {
++ int nFound = 0;
++ while ( nIdx < this->length() &&
++ (nIdx=this->find(szOld, nIdx)) != MYBASE::npos )
++ {
++ nFound++;
++ nIdx += nOldLen;
++ }
++ this->reserve(this->size() + nFound * (nNewLen - nOldLen));
++ }
++
++
++ static const CT ch = CT(0);
++ PCMYSTR szRealNew = szNew == 0 ? &ch : szNew;
++ nIdx = 0;
++
++ while ( nIdx < this->length() &&
++ (nIdx=this->find(szOld, nIdx)) != MYBASE::npos )
++ {
++ this->replace(this->begin()+nIdx, this->begin()+nIdx+nOldLen,
++ szRealNew);
++
++ nReplaced++;
++ nIdx += nNewLen;
++ }
++ }
++
++ return nReplaced;
++ }
++
++ int ReverseFind(CT ch) const
++ {
++ MYSIZE nIdx = this->find_last_of(ch);
++ return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
++ }
++
++ // ReverseFind overload that's not in CString but might be useful
++ int ReverseFind(PCMYSTR szFind, MYSIZE pos=MYBASE::npos) const
++ {
++ //yuvalt - this does not compile with g++ since MYTTYPE() is different type
++ //MYSIZE nIdx = this->rfind(0 == szFind ? MYTYPE() : szFind, pos);
++ MYSIZE nIdx = this->rfind(0 == szFind ? "" : szFind, pos);
++ return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
++ }
++
++ MYTYPE Right(int nCount) const
++ {
++ // Range check the count.
++
++ nCount = SSMAX(0, SSMIN(nCount, static_cast<int>(this->size())));
++ return this->substr(this->size()-static_cast<MYSIZE>(nCount));
++ }
++
++ void SetAt(int nIndex, CT ch)
++ {
++ ASSERT(this->size() > static_cast<MYSIZE>(nIndex));
++ this->at(static_cast<MYSIZE>(nIndex)) = ch;
++ }
++
++#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
++
++
++ // -------------------------------------------------------------------------
++ // Trim and its variants
++ // -------------------------------------------------------------------------
++ MYTYPE& Trim()
++ {
++ return TrimLeft().TrimRight();
++ }
++
++ MYTYPE& TrimLeft()
++ {
++ this->erase(this->begin(),
++ std::find_if(this->begin(), this->end(), NotSpace<CT>()));
++
++ return *this;
++ }
++
++ MYTYPE& TrimLeft(CT tTrim)
++ {
++ this->erase(0, this->find_first_not_of(tTrim));
++ return *this;
++ }
++
++ MYTYPE& TrimLeft(PCMYSTR szTrimChars)
++ {
++ this->erase(0, this->find_first_not_of(szTrimChars));
++ return *this;
++ }
++
++ MYTYPE& TrimRight()
++ {
++ // NOTE: When comparing reverse_iterators here (MYRITER), I avoid using
++ // operator!=. This is because namespace rel_ops also has a template
++ // operator!= which conflicts with the global operator!= already defined
++ // for reverse_iterator in the header <utility>.
++ // Thanks to John James for alerting me to this.
++
++ MYRITER it = std::find_if(this->rbegin(), this->rend(), NotSpace<CT>());
++ if ( !(this->rend() == it) )
++ this->erase(this->rend() - it);
++
++ this->erase(!(it == this->rend()) ? this->find_last_of(*it) + 1 : 0);
++ return *this;
++ }
++
++ MYTYPE& TrimRight(CT tTrim)
++ {
++ MYSIZE nIdx = this->find_last_not_of(tTrim);
++ this->erase(MYBASE::npos == nIdx ? 0 : ++nIdx);
++ return *this;
++ }
++
++ MYTYPE& TrimRight(PCMYSTR szTrimChars)
++ {
++ MYSIZE nIdx = this->find_last_not_of(szTrimChars);
++ this->erase(MYBASE::npos == nIdx ? 0 : ++nIdx);
++ return *this;
++ }
++
++ 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?)
++
++
++
++// -----------------------------------------------------------------------------
++// GLOBAL FUNCTION: WUFormat
++// CStdStringA WUFormat(UINT nId, ...);
++// CStdStringA WUFormat(PCSTR szFormat, ...);
++//
++// REMARKS:
++// This function allows the caller for format and return a CStdStringA
++// object with a single line of code.
++// -----------------------------------------------------------------------------
++#ifdef SS_ANSI
++#else
++ inline CStdStringA WUFormatA(UINT nId, ...)
++ {
++ va_list argList;
++ va_start(argList, nId);
++
++ CStdStringA strFmt;
++ CStdStringA strOut;
++ if ( strFmt.Load(nId) )
++ strOut.FormatV(strFmt, argList);
++
++ va_end(argList);
++ return strOut;
++ }
++ inline CStdStringA WUFormatA(PCSTR szFormat, ...)
++ {
++ va_list argList;
++ va_start(argList, szFormat);
++ CStdStringA strOut;
++ strOut.FormatV(szFormat, argList);
++ va_end(argList);
++ return strOut;
++ }
++ inline CStdStringW WUFormatW(UINT nId, ...)
++ {
++ va_list argList;
++ va_start(argList, nId);
++
++ CStdStringW strFmt;
++ CStdStringW strOut;
++ if ( strFmt.Load(nId) )
++ strOut.FormatV(strFmt, argList);
++
++ va_end(argList);
++ return strOut;
++ }
++ inline CStdStringW WUFormatW(PCWSTR szwFormat, ...)
++ {
++ va_list argList;
++ va_start(argList, szwFormat);
++ CStdStringW strOut;
++ strOut.FormatV(szwFormat, argList);
++ va_end(argList);
++ return strOut;
++ }
++#endif // #ifdef SS_ANSI
++
++
++
++#if defined(SS_WIN32) && !defined (SS_ANSI)
++ // -------------------------------------------------------------------------
++ // FUNCTION: WUSysMessage
++ // CStdStringA WUSysMessageA(DWORD dwError, DWORD dwLangId=SS_DEFLANGID);
++ // CStdStringW WUSysMessageW(DWORD dwError, DWORD dwLangId=SS_DEFLANGID);
++ //
++ // DESCRIPTION:
++ // This function simplifies the process of obtaining a string equivalent
++ // of a system error code returned from GetLastError(). You simply
++ // supply the value returned by GetLastError() to this function and the
++ // corresponding system string is returned in the form of a CStdStringA.
++ //
++ // PARAMETERS:
++ // dwError - a DWORD value representing the error code to be translated
++ // dwLangId - the language id to use. defaults to english.
++ //
++ // RETURN VALUE:
++ // a CStdStringA equivalent of the error code. Currently, this function
++ // only returns either English of the system default language strings.
++ // -------------------------------------------------------------------------
++ #define SS_DEFLANGID MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT)
++ inline CStdStringA WUSysMessageA(DWORD dwError, DWORD dwLangId=SS_DEFLANGID)
++ {
++ CHAR szBuf[512];
++
++ if ( 0 != ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError,
++ dwLangId, szBuf, 511, NULL) )
++ return WUFormatA("%s (0x%X)", szBuf, dwError);
++ else
++ return WUFormatA("Unknown error (0x%X)", dwError);
++ }
++ inline CStdStringW WUSysMessageW(DWORD dwError, DWORD dwLangId=SS_DEFLANGID)
++ {
++ WCHAR szBuf[512];
++
++ if ( 0 != ::FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError,
++ dwLangId, szBuf, 511, NULL) )
++ return WUFormatW(L"%s (0x%X)", szBuf, dwError);
++ else
++ return WUFormatW(L"Unknown error (0x%X)", dwError);
++ }
++#endif
++
++// Define TCHAR based friendly names for some of these functions
++
++#ifdef UNICODE
++ //#define CStdString CStdStringW
++ typedef CStdStringW CStdString;
++ #define WUSysMessage WUSysMessageW
++ #define WUFormat WUFormatW
++#else
++ //#define CStdString CStdStringA
++ typedef CStdStringA CStdString;
++ #define WUSysMessage WUSysMessageA
++ #define WUFormat WUFormatA
++#endif
++
++// ...and some shorter names for the space-efficient
++
++#define WUSysMsg WUSysMessage
++#define WUSysMsgA WUSysMessageA
++#define WUSysMsgW WUSysMessageW
++#define WUFmtA WUFormatA
++#define WUFmtW WUFormatW
++#define WUFmt WUFormat
++#define WULastErrMsg() WUSysMessage(::GetLastError())
++#define WULastErrMsgA() WUSysMessageA(::GetLastError())
++#define WULastErrMsgW() WUSysMessageW(::GetLastError())
++
++
++// -----------------------------------------------------------------------------
++// 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
++
++typedef std::vector<CStdString> CStdStringArray;
++
++#endif // #ifndef STDSTRING_H
+diff --git a/lib/platform/util/baudrate.h b/lib/platform/util/baudrate.h
+new file mode 100644
+index 0000000..1411452
+--- /dev/null
++++ b/lib/platform/util/baudrate.h
+@@ -0,0 +1,211 @@
++#pragma once
++/*
++ * This file is part of the libCEC(R) library.
++ *
++ * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
++ * libCEC(R) is an original work, containing original code.
++ *
++ * libCEC(R) is a trademark of Pulse-Eight Limited.
++ *
++ * This program is dual-licensed; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ *
++ * Alternatively, you can license this library under a commercial license,
++ * please contact Pulse-Eight Licensing for more information.
++ *
++ * For more information contact:
++ * Pulse-Eight Licensing <license@pulse-eight.com>
++ * http://www.pulse-eight.com/
++ * http://www.pulse-eight.net/
++ */
++
++//every baudrate I could find is in here in an #ifdef block
++//so it should compile on everything
++
++#ifndef __WINDOWS__
++#include <termios.h>
++#endif
++
++namespace PLATFORM
++{
++ static struct sbaudrate
++ {
++ int32_t rate;
++ int32_t symbol;
++ }
++
++ baudrates[] =
++ {
++ #ifdef B50
++ { 50, B50 },
++ #endif
++ #ifdef B75
++ { 75, B75 },
++ #endif
++ #ifdef B110
++ { 110, B110 },
++ #endif
++ #ifdef B134
++ { 134, B134 },
++ #endif
++ #ifdef B150
++ { 150, B150 },
++ #endif
++ #ifdef B200
++ { 200, B200 },
++ #endif
++ #ifdef B300
++ { 300, B300 },
++ #endif
++ #ifdef B600
++ { 600, B600 },
++ #endif
++ #ifdef B1200
++ { 1200, B1200 },
++ #endif
++ #ifdef B1800
++ { 1800, B1800 },
++ #endif
++ #ifdef B2400
++ { 2400, B2400 },
++ #endif
++ #ifdef B4800
++ { 4800, B4800 },
++ #endif
++ #ifdef B9600
++ { 9600, B9600 },
++ #endif
++ #ifdef B14400
++ { 14400, B14400 },
++ #endif
++ #ifdef B19200
++ { 19200, B19200 },
++ #endif
++ #ifdef B28800
++ { 28800, B28800 },
++ #endif
++ #ifdef B38400
++ { 38400, B38400 },
++ #endif
++ #ifdef B57600
++ { 57600, B57600 },
++ #endif
++ #ifdef B76800
++ { 76800, B76800 },
++ #endif
++ #ifdef B115200
++ { 115200, B115200 },
++ #endif
++ #ifdef B230400
++ { 230400, B230400 },
++ #endif
++ #ifdef B250000
++ { 250000, B250000 },
++ #endif
++ #ifdef B460800
++ { 460800, B460800 },
++ #endif
++ #ifdef B500000
++ { 500000, B500000 },
++ #endif
++ #ifdef B576000
++ { 576000, B576000 },
++ #endif
++ #ifdef B921600
++ { 921600, B921600 },
++ #endif
++ #ifdef B1000000
++ { 1000000, B1000000 },
++ #endif
++ #ifdef B1152000
++ { 1152000, B1152000 },
++ #endif
++ #ifdef B1500000
++ { 1500000, B1500000 },
++ #endif
++ #ifdef B2000000
++ { 2000000, B2000000 },
++ #endif
++ #ifdef B2500000
++ { 2500000, B2500000 },
++ #endif
++ #ifdef B3000000
++ { 3000000, B3000000 },
++ #endif
++ #ifdef B3500000
++ { 3500000, B3500000 },
++ #endif
++ #ifdef B4000000
++ { 4000000, B4000000 },
++ #endif
++ #ifdef CBR_110
++ { 110, CBR_110 },
++ #endif
++ #ifdef CBR_300
++ { 300, CBR_300 },
++ #endif
++ #ifdef CBR_600
++ { 600, CBR_600 },
++ #endif
++ #ifdef CBR_1200
++ { 1200, CBR_1200 },
++ #endif
++ #ifdef CBR_2400
++ { 2400, CBR_2400 },
++ #endif
++ #ifdef CBR_4800
++ { 4800, CBR_4800 },
++ #endif
++ #ifdef CBR_9600
++ { 9600, CBR_9600 },
++ #endif
++ #ifdef CBR_11400
++ { 11400, CBR_14400 },
++ #endif
++ #ifdef CBR_19200
++ { 19200, CBR_19200 },
++ #endif
++ #ifdef CBR_38400
++ { 38400, CBR_38400 },
++ #endif
++ #ifdef CBR_56000
++ { 56000, CBR_56000 },
++ #endif
++ #ifdef CBR_57600
++ { 57600, CBR_57600 },
++ #endif
++ #ifdef CBR_115200
++ { 115200, CBR_115200 },
++ #endif
++ #ifdef CBR_128000
++ { 128000, CBR_128000 },
++ #endif
++ #ifdef CBR_256000
++ { 256000, CBR_256000 },
++ #endif
++ { -1, -1}
++ };
++
++ inline int32_t IntToBaudrate(uint32_t baudrate)
++ {
++ for (unsigned int i = 0; i < sizeof(baudrates) / sizeof(PLATFORM::sbaudrate) - 1; i++)
++ {
++ if (baudrates[i].rate == (int32_t) baudrate)
++ return baudrates[i].symbol;
++ }
++
++ return -1;
++ };
++};
+diff --git a/lib/platform/util/buffer.h b/lib/platform/util/buffer.h
+new file mode 100644
+index 0000000..56ffd64
+--- /dev/null
++++ b/lib/platform/util/buffer.h
+@@ -0,0 +1,98 @@
++#pragma once
++/*
++ * This file is part of the libCEC(R) library.
++ *
++ * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
++ * libCEC(R) is an original work, containing original code.
++ *
++ * libCEC(R) is a trademark of Pulse-Eight Limited.
++ *
++ * This program is dual-licensed; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ *
++ * Alternatively, you can license this library under a commercial license,
++ * please contact Pulse-Eight Licensing for more information.
++ *
++ * For more information contact:
++ * Pulse-Eight Licensing <license@pulse-eight.com>
++ * http://www.pulse-eight.com/
++ * http://www.pulse-eight.net/
++ */
++
++#include "../threads/mutex.h"
++#include <queue>
++
++namespace PLATFORM
++{
++ template<typename _BType>
++ struct SyncedBuffer
++ {
++ public:
++ SyncedBuffer(size_t iMaxSize = 100) :
++ m_maxSize(iMaxSize) {}
++
++ virtual ~SyncedBuffer(void)
++ {
++ Clear();
++ }
++
++ void Clear(void)
++ {
++ CLockObject lock(m_mutex);
++ while (!m_buffer.empty())
++ m_buffer.pop();
++ }
++
++ size_t Size(void)
++ {
++ CLockObject lock(m_mutex);
++ return m_buffer.size();
++ }
++
++ bool IsEmpty(void)
++ {
++ CLockObject lock(m_mutex);
++ return m_buffer.empty();
++ }
++
++ bool Push(_BType entry)
++ {
++ CLockObject lock(m_mutex);
++ if (m_buffer.size() == m_maxSize)
++ return false;
++
++ m_buffer.push(entry);
++ return true;
++ }
++
++ bool Pop(_BType &entry)
++ {
++ bool bReturn(false);
++ CLockObject lock(m_mutex);
++ if (!m_buffer.empty())
++ {
++ entry = m_buffer.front();
++ m_buffer.pop();
++ bReturn = true;
++ }
++ return bReturn;
++ }
++
++ private:
++ size_t m_maxSize;
++ std::queue<_BType> m_buffer;
++ CMutex m_mutex;
++ };
++};
+diff --git a/lib/platform/util/timeutils.h b/lib/platform/util/timeutils.h
+new file mode 100644
+index 0000000..5f2d27a
+--- /dev/null
++++ b/lib/platform/util/timeutils.h
+@@ -0,0 +1,122 @@
++#pragma once
++/*
++ * This file is part of the libCEC(R) library.
++ *
++ * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
++ * libCEC(R) is an original work, containing original code.
++ *
++ * libCEC(R) is a trademark of Pulse-Eight Limited.
++ *
++ * This program is dual-licensed; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ *
++ * Alternatively, you can license this library under a commercial license,
++ * please contact Pulse-Eight Licensing for more information.
++ *
++ * For more information contact:
++ * Pulse-Eight Licensing <license@pulse-eight.com>
++ * http://www.pulse-eight.com/
++ * http://www.pulse-eight.net/
++ */
++
++#include "../os.h"
++
++#if defined(__APPLE__)
++#include <mach/mach_time.h>
++#include <CoreVideo/CVHostTime.h>
++#elif defined(__WINDOWS__)
++#include <time.h>
++#else
++#include <sys/time.h>
++#endif
++
++namespace PLATFORM
++{
++ #if defined(__WINDOWS__)
++ struct timezone
++ {
++ int tz_minuteswest;
++ int tz_dsttime;
++ };
++
++ #define usleep(t) Sleep((DWORD)(t)/1000)
++
++ inline int gettimeofday(struct timeval *pcur_time, struct timezone *tz)
++ {
++ if (pcur_time == NULL)
++ {
++ SetLastError(EFAULT);
++ return -1;
++ }
++ struct _timeb current;
++
++ _ftime(&current);
++
++ pcur_time->tv_sec = (long) current.time;
++ pcur_time->tv_usec = current.millitm * 1000L;
++ if (tz)
++ {
++ tz->tz_minuteswest = current.timezone; /* minutes west of Greenwich */
++ tz->tz_dsttime = current.dstflag; /* type of dst correction */
++ }
++ return 0;
++ }
++ #endif
++
++ inline int64_t GetTimeMs()
++ {
++ #if defined(__APPLE__)
++ return (int64_t) (CVGetCurrentHostTime() / (int64_t)(CVGetHostClockFrequency() * 0.001));
++ #elif defined(__WINDOWS__)
++ LARGE_INTEGER tickPerSecond;
++ LARGE_INTEGER tick;
++ if (QueryPerformanceFrequency(&tickPerSecond))
++ {
++ QueryPerformanceCounter(&tick);
++ return (int64_t) (tick.QuadPart / (tickPerSecond.QuadPart / 1000.));
++ }
++ return -1;
++ #else
++ timespec time;
++ clock_gettime(CLOCK_MONOTONIC, &time);
++ return (int64_t)time.tv_sec * 1000 + time.tv_nsec / 1000000;
++ #endif
++ }
++
++ template <class T>
++ inline T GetTimeSec()
++ {
++ return (T)GetTimeMs() / (T)1000.0;
++ }
++
++ class CTimeout
++ {
++ public:
++ CTimeout(void) : m_iTarget(0) {}
++ CTimeout(uint32_t iTimeout) { Init(iTimeout); }
++
++ bool IsSet(void) const { return m_iTarget > 0; }
++ void Init(uint32_t iTimeout) { m_iTarget = GetTimeMs() + iTimeout; }
++
++ uint32_t TimeLeft(void) const
++ {
++ uint64_t iNow = GetTimeMs();
++ return (iNow > m_iTarget) ? 0 : (uint32_t)(m_iTarget - iNow);
++ }
++
++ private:
++ uint64_t m_iTarget;
++ };
++};
+diff --git a/lib/platform/windows/dlfcn-win32.cpp b/lib/platform/windows/dlfcn-win32.cpp
+new file mode 100644
+index 0000000..5839921
+--- /dev/null
++++ b/lib/platform/windows/dlfcn-win32.cpp
+@@ -0,0 +1,263 @@
++/*
++ * dlfcn-win32
++ * Copyright (c) 2007 Ramiro Polla
++ *
++ * 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 <windows.h>
++#include <stdio.h>
++
++#include "dlfcn-win32.h"
++
++/* Note:
++ * MSDN says these functions are not thread-safe. We make no efforts to have
++ * any kind of thread safety.
++ */
++
++/* I have no special reason to have set MAX_GLOBAL_OBJECTS to this value. Any
++ * comments are welcome.
++ */
++#define MAX_OBJECTS 255
++
++static HMODULE global_objects[MAX_OBJECTS];
++
++/* This function adds an object to the list of global objects.
++ * The implementation is very simple and slow.
++ * TODO: should failing this function be enough to fail the call to dlopen( )?
++ */
++static void global_object_add( HMODULE hModule )
++{
++ int i;
++
++ for( i = 0 ; i < MAX_OBJECTS ; i++ )
++ {
++ if( !global_objects[i] )
++ {
++ global_objects[i] = hModule;
++ break;
++ }
++ }
++}
++
++static void global_object_rem( HMODULE hModule )
++{
++ int i;
++
++ for( i = 0 ; i < MAX_OBJECTS ; i++ )
++ {
++ if( global_objects[i] == hModule )
++ {
++ global_objects[i] = 0;
++ break;
++ }
++ }
++}
++
++/* Argument to last function. Used in dlerror( ) */
++static char last_name[MAX_PATH];
++
++static int copy_string( char *dest, int dest_size, const char *src )
++{
++ int i = 0;
++
++ if( src && dest )
++ {
++ for( i = 0 ; i < dest_size-1 ; i++ )
++ {
++ if( !src[i] )
++ break;
++ else
++ dest[i] = src[i];
++ }
++ }
++ dest[i] = '\0';
++
++ return i;
++}
++
++void *dlopen( const char *file, int mode )
++{
++ HMODULE hModule;
++ UINT uMode;
++
++ /* Do not let Windows display the critical-error-handler message box */
++ uMode = SetErrorMode( SEM_FAILCRITICALERRORS );
++
++ if( file == 0 )
++ {
++ /* Save NULL pointer for error message */
++ _snprintf_s( last_name, MAX_PATH, MAX_PATH, "0x%p", file );
++
++ /* POSIX says that if the value of file is 0, a handle on a global
++ * symbol object must be provided. That object must be able to access
++ * all symbols from the original program file, and any objects loaded
++ * with the RTLD_GLOBAL flag.
++ * The return value from GetModuleHandle( ) allows us to retrieve
++ * symbols only from the original program file. For objects loaded with
++ * the RTLD_GLOBAL flag, we create our own list later on.
++ */
++ hModule = GetModuleHandle( NULL );
++ }
++ else
++ {
++ char lpFileName[MAX_PATH];
++ int i;
++
++ /* MSDN says backslashes *must* be used instead of forward slashes. */
++ for( i = 0 ; i < sizeof(lpFileName)-1 ; i++ )
++ {
++ if( !file[i] )
++ break;
++ else if( file[i] == '/' )
++ lpFileName[i] = '\\';
++ else
++ lpFileName[i] = file[i];
++ }
++ lpFileName[i] = '\0';
++
++ /* Save file name for error message */
++ copy_string( last_name, sizeof(last_name), lpFileName );
++
++ /* POSIX says the search path is implementation-defined.
++ * LOAD_WITH_ALTERED_SEARCH_PATH is used to make it behave more closely
++ * to UNIX's search paths (start with system folders instead of current
++ * folder).
++ */
++ hModule = LoadLibraryEx( (LPSTR) lpFileName, NULL,
++ LOAD_WITH_ALTERED_SEARCH_PATH );
++ /* If the object was loaded with RTLD_GLOBAL, add it to list of global
++ * objects, so that its symbols may be retrieved even if the handle for
++ * the original program file is passed. POSIX says that if the same
++ * file is specified in multiple invocations, and any of them are
++ * RTLD_GLOBAL, even if any further invocations use RTLD_LOCAL, the
++ * symbols will remain global.
++ */
++
++ if( hModule && (mode & RTLD_GLOBAL) )
++ global_object_add( hModule );
++ }
++
++ /* Return to previous state of the error-mode bit flags. */
++ SetErrorMode( uMode );
++
++ return (void *) hModule;
++}
++
++int dlclose( void *handle )
++{
++ HMODULE hModule = (HMODULE) handle;
++ BOOL ret;
++
++ /* Save handle for error message */
++ _snprintf_s( last_name, MAX_PATH, MAX_PATH, "0x%p", handle );
++
++ ret = FreeLibrary( hModule );
++
++ /* If the object was loaded with RTLD_GLOBAL, remove it from list of global
++ * objects.
++ */
++ if( ret )
++ global_object_rem( hModule );
++
++ /* dlclose's return value in inverted in relation to FreeLibrary's. */
++ ret = !ret;
++
++ return (int) ret;
++}
++
++void *dlsym( void *handle, const char *name )
++{
++ FARPROC symbol;
++ HMODULE myhandle = (HMODULE) handle;
++
++ /* Save symbol name for error message */
++ copy_string( last_name, sizeof(last_name), name );
++
++ symbol = GetProcAddress( myhandle, name );
++#if 0
++ if( symbol == NULL )
++ {
++ HMODULE hModule;
++
++ /* If the handle for the original program file is passed, also search
++ * in all globally loaded objects.
++ */
++
++ hModule = GetModuleHandle( NULL );
++
++ if( hModule == handle )
++ {
++ int i;
++
++ for( i = 0 ; i < MAX_OBJECTS ; i++ )
++ {
++ if( global_objects[i] != 0 )
++ {
++ symbol = GetProcAddress( global_objects[i], name );
++ if( symbol != NULL )
++ break;
++ }
++ }
++ }
++
++
++ CloseHandle( hModule );
++ }
++#endif
++ return (void*) symbol;
++}
++
++char *dlerror( void )
++{
++ DWORD dwMessageId;
++ /* POSIX says this function doesn't have to be thread-safe, so we use one
++ * static buffer.
++ * MSDN says the buffer cannot be larger than 64K bytes, so we set it to
++ * the limit.
++ */
++ static char lpBuffer[65535];
++ DWORD ret;
++
++ dwMessageId = GetLastError( );
++
++ if( dwMessageId == 0 )
++ return NULL;
++
++ /* Format error message to:
++ * "<argument to function that failed>": <Windows localized error message>
++ */
++ ret = copy_string( lpBuffer, sizeof(lpBuffer), "\"" );
++ ret += copy_string( lpBuffer+ret, sizeof(lpBuffer)-ret, last_name );
++ ret += copy_string( lpBuffer+ret, sizeof(lpBuffer)-ret, "\": " );
++ ret += FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwMessageId,
++ MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
++ lpBuffer+ret, sizeof(lpBuffer)-ret, NULL );
++
++ if( ret > 1 )
++ {
++ /* POSIX says the string must not have trailing <newline> */
++ if( lpBuffer[ret-2] == '\r' && lpBuffer[ret-1] == '\n' )
++ lpBuffer[ret-2] = '\0';
++ }
++
++ /* POSIX says that invoking dlerror( ) a second time, immediately following
++ * a prior invocation, shall result in NULL being returned.
++ */
++ SetLastError(0);
++
++ return lpBuffer;
++}
++
+diff --git a/lib/platform/windows/dlfcn-win32.h b/lib/platform/windows/dlfcn-win32.h
+new file mode 100644
+index 0000000..b93a029
+--- /dev/null
++++ b/lib/platform/windows/dlfcn-win32.h
+@@ -0,0 +1,46 @@
++#pragma once
++/*
++ * dlfcn-win32
++ * Copyright (c) 2007 Ramiro Polla
++ *
++ * 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
++ */
++
++#ifndef DLFCN_H
++#define DLFCN_H
++
++/* POSIX says these are implementation-defined.
++ * To simplify use with Windows API, we treat them the same way.
++ */
++
++#define RTLD_LAZY 0
++#define RTLD_NOW 0
++
++#define RTLD_GLOBAL (1 << 1)
++#define RTLD_LOCAL (1 << 2)
++
++/* These two were added in The Open Group Base Specifications Issue 6.
++ * Note: All other RTLD_* flags in any dlfcn.h are not standard compliant.
++ */
++
++#define RTLD_DEFAULT 0
++#define RTLD_NEXT 0
++
++void *dlopen ( const char *file, int mode );
++int dlclose( void *handle );
++void *dlsym ( void *handle, const char *name );
++char *dlerror( void );
++
++#endif /* DLFCN-WIN32_H */
+diff --git a/lib/platform/windows/os-socket.h b/lib/platform/windows/os-socket.h
+new file mode 100644
+index 0000000..5174cba
+--- /dev/null
++++ b/lib/platform/windows/os-socket.h
+@@ -0,0 +1,297 @@
++#pragma once
++/*
++ * This file is part of the libCEC(R) library.
++ *
++ * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
++ * libCEC(R) is an original work, containing original code.
++ *
++ * libCEC(R) is a trademark of Pulse-Eight Limited.
++ *
++ * This program is dual-licensed; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ *
++ * Alternatively, you can license this library under a commercial license,
++ * please contact Pulse-Eight Licensing for more information.
++ *
++ * For more information contact:
++ * Pulse-Eight Licensing <license@pulse-eight.com>
++ * http://www.pulse-eight.com/
++ * http://www.pulse-eight.net/
++ */
++
++#include "../os.h"
++#include "../util/timeutils.h"
++
++#include <ws2spi.h>
++#include <ws2ipdef.h>
++#include <ws2tcpip.h>
++
++#define SHUT_RDWR SD_BOTH
++
++#ifndef ETIMEDOUT
++#define ETIMEDOUT 138
++#endif
++
++namespace PLATFORM
++{
++ #ifndef MSG_WAITALL
++ #define MSG_WAITALL 0x8
++ #endif
++
++ inline int GetSocketError(void)
++ {
++ int error = WSAGetLastError();
++ switch(error)
++ {
++ case WSAEINPROGRESS: return EINPROGRESS;
++ case WSAECONNRESET : return ECONNRESET;
++ case WSAETIMEDOUT : return ETIMEDOUT;
++ case WSAEWOULDBLOCK: return EAGAIN;
++ default : return error;
++ }
++ }
++
++ // Serial port
++ //@{
++ inline void SerialSocketClose(serial_socket_t socket)
++ {
++ if (socket != INVALID_HANDLE_VALUE)
++ CloseHandle(socket);
++ }
++
++ inline ssize_t SerialSocketWrite(serial_socket_t socket, int *iError, void* data, size_t len)
++ {
++ if (len != (DWORD)len)
++ {
++ *iError = EINVAL;
++ return -1;
++ }
++
++ DWORD iBytesWritten(0);
++ if (socket != INVALID_HANDLE_VALUE)
++ {
++ if (!WriteFile(socket, data, (DWORD)len, &iBytesWritten, NULL))
++ {
++ *iError = GetLastError();
++ return -1;
++ }
++ return (ssize_t)iBytesWritten;
++ }
++
++ return -1;
++ }
++
++ inline ssize_t SerialSocketRead(serial_socket_t socket, int *iError, void* data, size_t len, uint64_t iTimeoutMs /*= 0*/)
++ {
++ if (len != (DWORD)len)
++ {
++ *iError = EINVAL;
++ return -1;
++ }
++
++ DWORD iBytesRead(0);
++ if (socket != INVALID_HANDLE_VALUE)
++ {
++ if(!ReadFile(socket, data, (DWORD)len, &iBytesRead, NULL) != 0)
++ {
++ *iError = GetLastError();
++ return -1;
++ }
++ return (ssize_t)iBytesRead;
++ }
++ return -1;
++ }
++ //@}
++
++ // TCP
++ //@{
++ inline void TcpSocketSetBlocking(tcp_socket_t socket, bool bSetTo)
++ {
++ u_long iSetTo = bSetTo ? 0 : 1;
++ ioctlsocket(socket, FIONBIO, &iSetTo);
++ }
++
++ inline void TcpSocketClose(tcp_socket_t socket)
++ {
++ closesocket(socket);
++ }
++
++ inline void TcpSocketShutdown(tcp_socket_t socket)
++ {
++ if (socket != INVALID_SOCKET &&
++ socket != SOCKET_ERROR)
++ shutdown(socket, SHUT_RDWR);
++ }
++
++ inline ssize_t TcpSocketWrite(tcp_socket_t socket, int *iError, void* data, size_t len)
++ {
++ if (socket == INVALID_SOCKET ||
++ socket == SOCKET_ERROR ||
++ len != (int)len)
++ {
++ *iError = EINVAL;
++ return -1;
++ }
++
++ ssize_t iReturn = send(socket, (char*)data, (int)len, 0);
++ if (iReturn < (ssize_t)len)
++ *iError = GetSocketError();
++ return iReturn;
++ }
++
++ inline ssize_t TcpSocketRead(tcp_socket_t socket, int *iError, void* data, size_t len, uint64_t iTimeoutMs /*= 0*/)
++ {
++ int64_t iNow(0), iTarget(0);
++ ssize_t iBytesRead(0);
++ *iError = 0;
++
++ if (socket == INVALID_SOCKET ||
++ socket == SOCKET_ERROR ||
++ len != (int)len)
++ {
++ *iError = EINVAL;
++ return -1;
++ }
++
++ if (iTimeoutMs > 0)
++ {
++ iNow = GetTimeMs();
++ iTarget = iNow + (int64_t) iTimeoutMs;
++ }
++
++ fd_set fd_read;
++ struct timeval tv;
++ while (iBytesRead >= 0 && iBytesRead < (ssize_t)len && (iTimeoutMs == 0 || iTarget > iNow))
++ {
++ if (iTimeoutMs > 0)
++ {
++ tv.tv_sec = (long)(iTimeoutMs / 1000);
++ tv.tv_usec = 1000 * (long)(iTimeoutMs % 1000);
++
++ FD_ZERO(&fd_read);
++ FD_SET(socket, &fd_read);
++
++ if (select((int)socket + 1, &fd_read, NULL, NULL, &tv) == 0)
++ {
++ *iError = ETIMEDOUT;
++ return ETIMEDOUT;
++ }
++ TcpSocketSetBlocking(socket, false);
++ }
++
++ ssize_t iReadResult = (iTimeoutMs > 0) ?
++ recv(socket, (char*)data + iBytesRead, (int)(len - iBytesRead), 0) :
++ recv(socket, (char*)data, (int)len, MSG_WAITALL);
++ *iError = GetSocketError();
++
++ if (iTimeoutMs > 0)
++ {
++ TcpSocketSetBlocking(socket, true);
++ iNow = GetTimeMs();
++ }
++
++ if (iReadResult < 0)
++ {
++ if (*iError == EAGAIN && iTimeoutMs > 0)
++ continue;
++ return -1;
++ }
++ else if (iReadResult == 0 || (iReadResult != (ssize_t)len && iTimeoutMs == 0))
++ {
++ *iError = ECONNRESET;
++ return -1;
++ }
++
++ iBytesRead += iReadResult;
++ }
++
++ if (iBytesRead < (ssize_t)len && *iError == 0)
++ *iError = ETIMEDOUT;
++
++ return iBytesRead;
++ }
++
++ inline bool TcpResolveAddress(const char *strHost, uint16_t iPort, int *iError, struct addrinfo **info)
++ {
++ struct addrinfo hints;
++ char service[33];
++ memset(&hints, 0, sizeof(hints));
++ hints.ai_family = AF_UNSPEC;
++ hints.ai_socktype = SOCK_STREAM;
++ hints.ai_protocol = IPPROTO_TCP;
++ sprintf(service, "%d", iPort);
++
++ *iError = getaddrinfo(strHost, service, &hints, info);
++ return !(*iError);
++ }
++
++ inline int TcpGetSocketError(tcp_socket_t socket)
++ {
++ int iReturn(0);
++ socklen_t optLen = sizeof(tcp_socket_t);
++ getsockopt(socket, SOL_SOCKET, SO_ERROR, (char *)&iReturn, &optLen);
++ return iReturn;
++ }
++
++ inline bool TcpSetNoDelay(tcp_socket_t socket)
++ {
++ int iSetTo(1);
++ setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (const char*)&iSetTo, sizeof(iSetTo));
++ return true;
++ }
++
++ inline bool TcpConnectSocket(tcp_socket_t socket, struct addrinfo* addr, int *iError, uint64_t iTimeout = 0)
++ {
++ TcpSocketSetBlocking(socket, false);
++
++ *iError = 0;
++ int iConnectResult = connect(socket, addr->ai_addr, (int)addr->ai_addrlen);
++ if (iConnectResult == -1)
++ {
++ if (GetSocketError() == EINPROGRESS ||
++ GetSocketError() == EAGAIN)
++ {
++ fd_set fd_write, fd_except;
++ struct timeval tv;
++ tv.tv_sec = (long)(iTimeout / 1000);
++ tv.tv_usec = 1000 * (long)(iTimeout % 1000);
++
++ FD_ZERO(&fd_write);
++ FD_ZERO(&fd_except);
++ FD_SET(socket, &fd_write);
++ FD_SET(socket, &fd_except);
++
++ int iPollResult = select(sizeof(socket)*8, NULL, &fd_write, &fd_except, &tv);
++ if (iPollResult == 0)
++ *iError = ETIMEDOUT;
++ else if (iPollResult == -1)
++ *iError = GetSocketError();
++ else
++ {
++ socklen_t errlen = sizeof(int);
++ getsockopt(socket, SOL_SOCKET, SO_ERROR, (char *)iError, &errlen);
++ }
++ }
++ else
++ {
++ *iError = GetSocketError();
++ }
++ }
++
++ TcpSocketSetBlocking(socket, true);
++
++ return *iError == 0;
++ }
++}
+diff --git a/lib/platform/windows/os-threads.cpp b/lib/platform/windows/os-threads.cpp
+new file mode 100644
+index 0000000..7c06d41
+--- /dev/null
++++ b/lib/platform/windows/os-threads.cpp
+@@ -0,0 +1,136 @@
++/*
++ * This file is part of the libCEC(R) library.
++ *
++ * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
++ * libCEC(R) is an original work, containing original code.
++ *
++ * libCEC(R) is a trademark of Pulse-Eight Limited.
++ *
++ * This program is dual-licensed; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ *
++ * Alternatively, you can license this library under a commercial license,
++ * please contact Pulse-Eight Licensing for more information.
++ *
++ * For more information contact:
++ * Pulse-Eight Licensing <license@pulse-eight.com>
++ * http://www.pulse-eight.com/
++ * http://www.pulse-eight.net/
++ */
++
++#include "../os.h"
++#include "os-threads.h"
++using namespace PLATFORM;
++
++static ConditionArg g_InitializeConditionVariable;
++static ConditionArg g_WakeConditionVariable;
++static ConditionArg g_WakeAllConditionVariable;
++static ConditionMutexArg g_SleepConditionVariableCS;
++
++// check whether vista+ conditions are available at runtime
++static bool CheckVistaConditionFunctions(void)
++{
++ static int iHasVistaConditionFunctions(-1);
++ if (iHasVistaConditionFunctions == -1)
++ {
++ HMODULE handle = GetModuleHandle("Kernel32");
++ if (handle == NULL)
++ {
++ iHasVistaConditionFunctions = 0;
++ }
++ else
++ {
++ g_InitializeConditionVariable = (ConditionArg) GetProcAddress(handle,"InitializeConditionVariable");
++ g_WakeConditionVariable = (ConditionArg) GetProcAddress(handle,"WakeConditionVariable");
++ g_WakeAllConditionVariable = (ConditionArg) GetProcAddress(handle,"WakeAllConditionVariable");
++ g_SleepConditionVariableCS = (ConditionMutexArg)GetProcAddress(handle,"SleepConditionVariableCS");
++
++ // 1 when everything is resolved, 0 otherwise
++ iHasVistaConditionFunctions = g_InitializeConditionVariable &&
++ g_WakeConditionVariable &&
++ g_WakeAllConditionVariable &&
++ g_SleepConditionVariableCS ? 1 : 0;
++ }
++ }
++ return iHasVistaConditionFunctions == 1;
++}
++
++CConditionImpl::CConditionImpl(void)
++{
++ m_bOnVista = CheckVistaConditionFunctions();
++ if (m_bOnVista)
++ (*g_InitializeConditionVariable)(m_conditionVista = new CONDITION_VARIABLE);
++ else
++ m_conditionPreVista = ::CreateEvent(NULL, TRUE, FALSE, NULL);
++}
++
++CConditionImpl::~CConditionImpl(void)
++{
++ if (m_bOnVista)
++ delete m_conditionVista;
++ else
++ ::CloseHandle(m_conditionPreVista);
++}
++
++void CConditionImpl::Signal(void)
++{
++ if (m_bOnVista)
++ (*g_WakeConditionVariable)(m_conditionVista);
++ else
++ ::SetEvent(m_conditionPreVista);
++}
++
++void CConditionImpl::Broadcast(void)
++{
++ if (m_bOnVista)
++ (*g_WakeAllConditionVariable)(m_conditionVista);
++ else
++ ::SetEvent(m_conditionPreVista);
++}
++
++bool CConditionImpl::Wait(mutex_t &mutex)
++{
++ if (m_bOnVista)
++ {
++ return ((*g_SleepConditionVariableCS)(m_conditionVista, mutex, INFINITE) ? true : false);
++ }
++ else
++ {
++ ::ResetEvent(m_conditionPreVista);
++ MutexUnlock(mutex);
++ DWORD iWaitReturn = ::WaitForSingleObject(m_conditionPreVista, 1000);
++ MutexLock(mutex);
++ return (iWaitReturn == 0);
++ }
++}
++
++bool CConditionImpl::Wait(mutex_t &mutex, uint32_t iTimeoutMs)
++{
++ if (iTimeoutMs == 0)
++ return Wait(mutex);
++
++ if (m_bOnVista)
++ {
++ return ((*g_SleepConditionVariableCS)(m_conditionVista, mutex, iTimeoutMs) ? true : false);
++ }
++ else
++ {
++ ::ResetEvent(m_conditionPreVista);
++ MutexUnlock(mutex);
++ DWORD iWaitReturn = ::WaitForSingleObject(m_conditionPreVista, iTimeoutMs);
++ MutexLock(mutex);
++ return (iWaitReturn == 0);
++ }
++}
+diff --git a/lib/platform/windows/os-threads.h b/lib/platform/windows/os-threads.h
+new file mode 100644
+index 0000000..3714c16
+--- /dev/null
++++ b/lib/platform/windows/os-threads.h
+@@ -0,0 +1,65 @@
++#pragma once
++/*
++ * This file is part of the libCEC(R) library.
++ *
++ * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
++ * libCEC(R) is an original work, containing original code.
++ *
++ * libCEC(R) is a trademark of Pulse-Eight Limited.
++ *
++ * This program is dual-licensed; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ *
++ * Alternatively, you can license this library under a commercial license,
++ * please contact Pulse-Eight Licensing for more information.
++ *
++ * For more information contact:
++ * Pulse-Eight Licensing <license@pulse-eight.com>
++ * http://www.pulse-eight.com/
++ * http://www.pulse-eight.net/
++ */
++
++namespace PLATFORM
++{
++ #define thread_t HANDLE
++ #define ThreadsWait(thread, retVal) (::WaitForSingleObject(thread, INFINITE) < 0)
++ #define ThreadsCreate(thread, func, arg) ((thread = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func, arg, 0, NULL)) == NULL ? false : true)
++
++ typedef CRITICAL_SECTION* mutex_t;
++ #define MutexCreate(mutex) ::InitializeCriticalSection(mutex = new CRITICAL_SECTION)
++ #define MutexDelete(mutex) ::DeleteCriticalSection(mutex); delete mutex
++ #define MutexLock(mutex) ::EnterCriticalSection(mutex)
++ #define MutexTryLock(mutex) (::TryEnterCriticalSection(mutex) != 0)
++ #define MutexUnlock(mutex) ::LeaveCriticalSection(mutex)
++
++ // windows vista+ conditions
++ typedef VOID (WINAPI *ConditionArg) (CONDITION_VARIABLE*);
++ typedef BOOL (WINAPI *ConditionMutexArg)(CONDITION_VARIABLE*, CRITICAL_SECTION*, DWORD);
++
++ class CConditionImpl
++ {
++ public:
++ CConditionImpl(void);
++ virtual ~CConditionImpl(void);
++ void Signal(void);
++ void Broadcast(void);
++ bool Wait(mutex_t &mutex);
++ bool Wait(mutex_t &mutex, uint32_t iTimeoutMs);
++
++ bool m_bOnVista;
++ CONDITION_VARIABLE *m_conditionVista;
++ HANDLE m_conditionPreVista;
++ };
++}
+diff --git a/lib/platform/windows/os-types.h b/lib/platform/windows/os-types.h
+new file mode 100644
+index 0000000..9b0dca0
+--- /dev/null
++++ b/lib/platform/windows/os-types.h
+@@ -0,0 +1,80 @@
++#pragma once
++/*
++ * This file is part of the libCEC(R) library.
++ *
++ * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
++ * libCEC(R) is an original work, containing original code.
++ *
++ * libCEC(R) is a trademark of Pulse-Eight Limited.
++ *
++ * This program is dual-licensed; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ *
++ * Alternatively, you can license this library under a commercial license,
++ * please contact Pulse-Eight Licensing for more information.
++ *
++ * For more information contact:
++ * Pulse-Eight Licensing <license@pulse-eight.com>
++ * http://www.pulse-eight.com/
++ * http://www.pulse-eight.net/
++ */
++
++#if !defined(__WINDOWS__)
++#define __WINDOWS__
++#endif
++
++#ifndef _WINSOCKAPI_
++#define _WINSOCKAPI_
++#endif
++
++#pragma warning(disable:4005) // Disable "warning C4005: '_WINSOCKAPI_' : macro redefinition"
++#include <winsock2.h>
++#pragma warning(default:4005)
++
++#include <sys/timeb.h>
++#include <io.h>
++#include <stdlib.h>
++#include <errno.h>
++#include <stddef.h>
++#include <process.h>
++#include <stdint.h>
++
++typedef SOCKET tcp_socket_t;
++#define INVALID_SOCKET_VALUE INVALID_SOCKET
++typedef HANDLE serial_socket_t;
++#define INVALID_SERIAL_SOCKET_VALUE INVALID_HANDLE_VALUE
++
++#ifndef _SSIZE_T_DEFINED
++#ifdef _WIN64
++typedef __int64 ssize_t;
++#else
++typedef _W64 int ssize_t;
++#endif
++#define _SSIZE_T_DEFINED
++#endif
++
++#define snprintf _snprintf
++
++#if defined(_MSC_VER)
++#pragma warning (push)
++#endif
++
++#define NOGDI
++#if defined(_MSC_VER) /* prevent inclusion of wingdi.h */
++#pragma warning (pop)
++#endif
++
++#pragma warning(disable:4189) /* disable 'defined but not used' */
++#pragma warning(disable:4100) /* disable 'unreferenced formal parameter' */
+diff --git a/lib/platform/windows/serialport.cpp b/lib/platform/windows/serialport.cpp
+new file mode 100644
+index 0000000..c0cdd93
+--- /dev/null
++++ b/lib/platform/windows/serialport.cpp
+@@ -0,0 +1,211 @@
++/*
++ * This file is part of the libCEC(R) library.
++ *
++ * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
++ * libCEC(R) is an original work, containing original code.
++ *
++ * libCEC(R) is a trademark of Pulse-Eight Limited.
++ *
++ * This program is dual-licensed; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ *
++ * Alternatively, you can license this library under a commercial license,
++ * please contact Pulse-Eight Licensing for more information.
++ *
++ * For more information contact:
++ * Pulse-Eight Licensing <license@pulse-eight.com>
++ * http://www.pulse-eight.com/
++ * http://www.pulse-eight.net/
++ */
++
++#include "../sockets/serialport.h"
++#include "../util/baudrate.h"
++#include "../util/timeutils.h"
++
++using namespace std;
++using namespace PLATFORM;
++
++void FormatWindowsError(int iErrorCode, CStdString &strMessage)
++{
++ if (iErrorCode != ERROR_SUCCESS)
++ {
++ char strAddMessage[1024];
++ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, iErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), strAddMessage, 1024, NULL);
++ strMessage.append(": ");
++ strMessage.append(strAddMessage);
++ }
++}
++
++bool SetTimeouts(serial_socket_t socket, int* iError, bool bBlocking)
++{
++ if (socket == INVALID_HANDLE_VALUE)
++ return false;
++
++ COMMTIMEOUTS cto;
++ if (!GetCommTimeouts(socket, &cto))
++ {
++ *iError = GetLastError();
++ return false;
++ }
++
++ if (bBlocking)
++ {
++ cto.ReadIntervalTimeout = 0;
++ cto.ReadTotalTimeoutConstant = 0;
++ cto.ReadTotalTimeoutMultiplier = 0;
++ }
++ else
++ {
++ cto.ReadIntervalTimeout = MAXDWORD;
++ cto.ReadTotalTimeoutConstant = 0;
++ cto.ReadTotalTimeoutMultiplier = 0;
++ }
++
++ if (!SetCommTimeouts(socket, &cto))
++ {
++ *iError = GetLastError();
++ return false;
++ }
++
++ return true;
++}
++
++void CSerialSocket::Close(void)
++{
++ if (IsOpen())
++ SerialSocketClose(m_socket);
++ m_socket = INVALID_SERIAL_SOCKET_VALUE;
++}
++
++void CSerialSocket::Shutdown(void)
++{
++ if (IsOpen())
++ SerialSocketClose(m_socket);
++ m_socket = INVALID_SERIAL_SOCKET_VALUE;
++}
++
++ssize_t CSerialSocket::Write(void* data, size_t len)
++{
++ return IsOpen() ? SerialSocketWrite(m_socket, &m_iError, data, len) : -1;
++}
++
++ssize_t CSerialSocket::Read(void* data, size_t len, uint64_t iTimeoutMs /* = 0 */)
++{
++ return IsOpen() ? SerialSocketRead(m_socket, &m_iError, data, len, iTimeoutMs) : -1;
++}
++
++bool CSerialSocket::Open(uint64_t iTimeoutMs /* = 0 */)
++{
++ iTimeoutMs = 0;
++ if (IsOpen())
++ return false;
++
++ CStdString strComPath = "\\\\.\\" + m_strName;
++ CLockObject lock(m_mutex);
++ m_socket = CreateFile(strComPath.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
++ if (m_socket == INVALID_HANDLE_VALUE)
++ {
++ m_strError = "Unable to open COM port";
++ FormatWindowsError(GetLastError(), m_strError);
++ return false;
++ }
++
++ COMMCONFIG commConfig = {0};
++ DWORD dwSize = sizeof(commConfig);
++ commConfig.dwSize = dwSize;
++ if (GetDefaultCommConfig(strComPath.c_str(), &commConfig,&dwSize))
++ {
++ if (!SetCommConfig(m_socket, &commConfig,dwSize))
++ {
++ m_strError = "unable to set default config";
++ FormatWindowsError(GetLastError(), m_strError);
++ }
++ }
++ else
++ {
++ m_strError = "unable to get default config";
++ FormatWindowsError(GetLastError(), m_strError);
++ }
++
++ if (!SetupComm(m_socket, 64, 64))
++ {
++ m_strError = "unable to set up the com port";
++ FormatWindowsError(GetLastError(), m_strError);
++ }
++
++ if (!SetBaudRate(m_iBaudrate))
++ {
++ m_strError = "unable to set baud rate";
++ FormatWindowsError(GetLastError(), m_strError);
++ Close();
++ return false;
++ }
++
++ if (!SetTimeouts(m_socket, &m_iError, false))
++ {
++ m_strError = "unable to set timeouts";
++ FormatWindowsError(GetLastError(), m_strError);
++ Close();
++ return false;
++ }
++
++ m_bIsOpen = true;
++ return m_bIsOpen;
++}
++
++bool CSerialSocket::SetBaudRate(uint32_t baudrate)
++{
++ int32_t rate = IntToBaudrate(baudrate);
++ if (rate < 0)
++ m_iBaudrate = baudrate > 0 ? baudrate : 0;
++ else
++ m_iBaudrate = rate;
++
++ DCB dcb;
++ memset(&dcb,0,sizeof(dcb));
++ dcb.DCBlength = sizeof(dcb);
++ dcb.BaudRate = IntToBaudrate(m_iBaudrate);
++ dcb.fBinary = true;
++ dcb.fDtrControl = DTR_CONTROL_DISABLE;
++ dcb.fRtsControl = RTS_CONTROL_DISABLE;
++ dcb.fOutxCtsFlow = false;
++ dcb.fOutxDsrFlow = false;
++ dcb.fOutX = false;
++ dcb.fInX = false;
++ dcb.fAbortOnError = true;
++
++ if (m_iParity == SERIAL_PARITY_NONE)
++ dcb.Parity = NOPARITY;
++ else if (m_iParity == SERIAL_PARITY_EVEN)
++ dcb.Parity = EVENPARITY;
++ else
++ dcb.Parity = ODDPARITY;
++
++ if (m_iStopbits == SERIAL_STOP_BITS_TWO)
++ dcb.StopBits = TWOSTOPBITS;
++ else
++ dcb.StopBits = ONESTOPBIT;
++
++ dcb.ByteSize = (BYTE)m_iDatabits;
++
++ if(!SetCommState(m_socket,&dcb))
++ {
++ m_strError = "SetCommState failed";
++ FormatWindowsError(GetLastError(), m_strError);
++ return false;
++ }
++
++ return true;
++}
+diff --git a/lib/platform/windows/stdint.h b/lib/platform/windows/stdint.h
+new file mode 100644
+index 0000000..d02608a
+--- /dev/null
++++ b/lib/platform/windows/stdint.h
+@@ -0,0 +1,247 @@
++// ISO C9x compliant stdint.h for Microsoft Visual Studio
++// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
++//
++// Copyright (c) 2006-2008 Alexander Chemeris
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are met:
++//
++// 1. Redistributions of source code must retain the above copyright notice,
++// this list of conditions and the following disclaimer.
++//
++// 2. Redistributions in binary form must reproduce the above copyright
++// notice, this list of conditions and the following disclaimer in the
++// documentation and/or other materials provided with the distribution.
++//
++// 3. The name of the author may be used to endorse or promote products
++// derived from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
++// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
++// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
++// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
++// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
++// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
++// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
++// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
++// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++//
++///////////////////////////////////////////////////////////////////////////////
++
++#ifndef _MSC_VER // [
++#error "Use this header only with Microsoft Visual C++ compilers!"
++#endif // _MSC_VER ]
++
++#ifndef _MSC_STDINT_H_ // [
++#define _MSC_STDINT_H_
++
++#if _MSC_VER > 1000
++#pragma once
++#endif
++
++#include <limits.h>
++
++// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
++// compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}'
++// or compiler give many errors like this:
++// error C2733: second C linkage of overloaded function 'wmemchr' not allowed
++#ifdef __cplusplus
++extern "C" {
++#endif
++# include <wchar.h>
++#ifdef __cplusplus
++}
++#endif
++
++// Define _W64 macros to mark types changing their size, like intptr_t.
++#ifndef _W64
++# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
++# define _W64 __w64
++# else
++# define _W64
++# endif
++#endif
++
++
++// 7.18.1 Integer types
++
++// 7.18.1.1 Exact-width integer types
++
++// Visual Studio 6 and Embedded Visual C++ 4 doesn't
++// realize that, e.g. char has the same size as __int8
++// so we give up on __intX for them.
++#if (_MSC_VER < 1300)
++ typedef signed char int8_t;
++ typedef signed short int16_t;
++ typedef signed int int32_t;
++ typedef unsigned char uint8_t;
++ typedef unsigned short uint16_t;
++ typedef unsigned int uint32_t;
++#else
++ typedef signed __int8 int8_t;
++ typedef signed __int16 int16_t;
++ typedef signed __int32 int32_t;
++ typedef unsigned __int8 uint8_t;
++ typedef unsigned __int16 uint16_t;
++ typedef unsigned __int32 uint32_t;
++#endif
++typedef signed __int64 int64_t;
++typedef unsigned __int64 uint64_t;
++
++
++// 7.18.1.2 Minimum-width integer types
++typedef int8_t int_least8_t;
++typedef int16_t int_least16_t;
++typedef int32_t int_least32_t;
++typedef int64_t int_least64_t;
++typedef uint8_t uint_least8_t;
++typedef uint16_t uint_least16_t;
++typedef uint32_t uint_least32_t;
++typedef uint64_t uint_least64_t;
++
++// 7.18.1.3 Fastest minimum-width integer types
++typedef int8_t int_fast8_t;
++typedef int16_t int_fast16_t;
++typedef int32_t int_fast32_t;
++typedef int64_t int_fast64_t;
++typedef uint8_t uint_fast8_t;
++typedef uint16_t uint_fast16_t;
++typedef uint32_t uint_fast32_t;
++typedef uint64_t uint_fast64_t;
++
++// 7.18.1.4 Integer types capable of holding object pointers
++#ifdef _WIN64 // [
++ typedef signed __int64 intptr_t;
++ typedef unsigned __int64 uintptr_t;
++#else // _WIN64 ][
++ typedef _W64 signed int intptr_t;
++ typedef _W64 unsigned int uintptr_t;
++#endif // _WIN64 ]
++
++// 7.18.1.5 Greatest-width integer types
++typedef int64_t intmax_t;
++typedef uint64_t uintmax_t;
++
++
++// 7.18.2 Limits of specified-width integer types
++
++#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259
++
++// 7.18.2.1 Limits of exact-width integer types
++#define INT8_MIN ((int8_t)_I8_MIN)
++#define INT8_MAX _I8_MAX
++#define INT16_MIN ((int16_t)_I16_MIN)
++#define INT16_MAX _I16_MAX
++#define INT32_MIN ((int32_t)_I32_MIN)
++#define INT32_MAX _I32_MAX
++#define INT64_MIN ((int64_t)_I64_MIN)
++#define INT64_MAX _I64_MAX
++#define UINT8_MAX _UI8_MAX
++#define UINT16_MAX _UI16_MAX
++#define UINT32_MAX _UI32_MAX
++#define UINT64_MAX _UI64_MAX
++
++// 7.18.2.2 Limits of minimum-width integer types
++#define INT_LEAST8_MIN INT8_MIN
++#define INT_LEAST8_MAX INT8_MAX
++#define INT_LEAST16_MIN INT16_MIN
++#define INT_LEAST16_MAX INT16_MAX
++#define INT_LEAST32_MIN INT32_MIN
++#define INT_LEAST32_MAX INT32_MAX
++#define INT_LEAST64_MIN INT64_MIN
++#define INT_LEAST64_MAX INT64_MAX
++#define UINT_LEAST8_MAX UINT8_MAX
++#define UINT_LEAST16_MAX UINT16_MAX
++#define UINT_LEAST32_MAX UINT32_MAX
++#define UINT_LEAST64_MAX UINT64_MAX
++
++// 7.18.2.3 Limits of fastest minimum-width integer types
++#define INT_FAST8_MIN INT8_MIN
++#define INT_FAST8_MAX INT8_MAX
++#define INT_FAST16_MIN INT16_MIN
++#define INT_FAST16_MAX INT16_MAX
++#define INT_FAST32_MIN INT32_MIN
++#define INT_FAST32_MAX INT32_MAX
++#define INT_FAST64_MIN INT64_MIN
++#define INT_FAST64_MAX INT64_MAX
++#define UINT_FAST8_MAX UINT8_MAX
++#define UINT_FAST16_MAX UINT16_MAX
++#define UINT_FAST32_MAX UINT32_MAX
++#define UINT_FAST64_MAX UINT64_MAX
++
++// 7.18.2.4 Limits of integer types capable of holding object pointers
++#ifdef _WIN64 // [
++# define INTPTR_MIN INT64_MIN
++# define INTPTR_MAX INT64_MAX
++# define UINTPTR_MAX UINT64_MAX
++#else // _WIN64 ][
++# define INTPTR_MIN INT32_MIN
++# define INTPTR_MAX INT32_MAX
++# define UINTPTR_MAX UINT32_MAX
++#endif // _WIN64 ]
++
++// 7.18.2.5 Limits of greatest-width integer types
++#define INTMAX_MIN INT64_MIN
++#define INTMAX_MAX INT64_MAX
++#define UINTMAX_MAX UINT64_MAX
++
++// 7.18.3 Limits of other integer types
++
++#ifdef _WIN64 // [
++# define PTRDIFF_MIN _I64_MIN
++# define PTRDIFF_MAX _I64_MAX
++#else // _WIN64 ][
++# define PTRDIFF_MIN _I32_MIN
++# define PTRDIFF_MAX _I32_MAX
++#endif // _WIN64 ]
++
++#define SIG_ATOMIC_MIN INT_MIN
++#define SIG_ATOMIC_MAX INT_MAX
++
++#ifndef SIZE_MAX // [
++# ifdef _WIN64 // [
++# define SIZE_MAX _UI64_MAX
++# else // _WIN64 ][
++# define SIZE_MAX _UI32_MAX
++# endif // _WIN64 ]
++#endif // SIZE_MAX ]
++
++// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
++#ifndef WCHAR_MIN // [
++# define WCHAR_MIN 0
++#endif // WCHAR_MIN ]
++#ifndef WCHAR_MAX // [
++# define WCHAR_MAX _UI16_MAX
++#endif // WCHAR_MAX ]
++
++#define WINT_MIN 0
++#define WINT_MAX _UI16_MAX
++
++#endif // __STDC_LIMIT_MACROS ]
++
++
++// 7.18.4 Limits of other integer types
++
++#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
++
++// 7.18.4.1 Macros for minimum-width integer constants
++
++#define INT8_C(val) val##i8
++#define INT16_C(val) val##i16
++#define INT32_C(val) val##i32
++#define INT64_C(val) val##i64
++
++#define UINT8_C(val) val##ui8
++#define UINT16_C(val) val##ui16
++#define UINT32_C(val) val##ui32
++#define UINT64_C(val) val##ui64
++
++// 7.18.4.2 Macros for greatest-width integer constants
++#define INTMAX_C INT64_C
++#define UINTMAX_C UINT64_C
++
++#endif // __STDC_CONSTANT_MACROS ]
++
++
++#endif // _MSC_STDINT_H_ ]
+diff --git a/project/VS2010Express/XBMC for Windows.sln b/project/VS2010Express/XBMC for Windows.sln
+index 9834bfc..ece2861 100644
+--- a/project/VS2010Express/XBMC for Windows.sln
++++ b/project/VS2010Express/XBMC for Windows.sln
+@@ -10,6 +10,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libhts", "..\..\lib\libhts\
+ EndProject
+ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libass_dll", "..\..\lib\libass\xbmc\libass_win32\libass_win32_vs2010.vcxproj", "{BA5B08FC-2ECB-4571-9F25-F8054522FC65}"
+ EndProject
++Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pvrclient_vnsi", "..\..\xbmc\pvrclients\vdr-vnsi\project\VS2010Express\XBMC_VDR_vnsi.vcxproj", "{3AD3147B-8E7F-4C70-B049-19FA1916BF12}"
++EndProject
+ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "visMilkdrop", "..\..\xbmc\visualizations\Milkdrop\Plugin.vcxproj", "{5E479372-4F34-426D-AA1E-9879E94C105D}"
+ EndProject
+ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcmyth_dll", "..\..\lib\cmyth\Win32\libcmyth.vcxproj", "{F9E6874D-60A8-49BA-9393-A2105E63ABCF}"
+@@ -72,298 +74,543 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmp3lame_dll", "..\..\lib
+ EndProject
+ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libhdhomerun_dll", "..\..\lib\libhdhomerun\hdhomerun\hdhomerun.vcxproj", "{1E2FB608-3DD2-4021-A598-90008FA6DE85}"
+ EndProject
++Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libXBMC_addon", "..\..\lib\addons\library.xbmc.addon\project\VS2010Express\libXBMC_addon.vcxproj", "{2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}"
++EndProject
++Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libXBMC_gui", "..\..\lib\addons\library.xbmc.gui\project\VS2010Express\libXBMC_gui.vcxproj", "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}"
++EndProject
++Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libXBMC_pvr", "..\..\lib\addons\library.xbmc.pvr\project\VS2010Express\libXBMC_pvr.vcxproj", "{6D8C91F8-992F-4C83-9DE3-485D64EF8420}"
++EndProject
++Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pvrclient_mptv", "..\..\xbmc\pvrclients\MediaPortal\project\VS2010Express\XBMC_MPTV.vcxproj", "{74C9642E-1988-48DC-8404-11717C355378}"
++EndProject
++Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pvrclient_tvheadend", "..\..\xbmc\pvrclients\tvheadend\project\VS2010Express\XBMC_tvheadend.vcxproj", "{C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}"
++ ProjectSection(ProjectDependencies) = postProject
++ {00700E12-A63B-4E54-B962-4011A90584BD} = {00700E12-A63B-4E54-B962-4011A90584BD}
++ EndProjectSection
++EndProject
++Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pvrclient_mythtv_cmyth", "..\..\xbmc\pvrclients\mythtv-cmyth\project\VS2010Express\XBMC_mythtc_cmyth.vcxproj", "{A55ACC6B-837D-4784-8E41-66947B1D9B8C}"
++EndProject
+ Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug (DirectX)|Win32 = Debug (DirectX)|Win32
+ Debug (OpenGL)|Win32 = Debug (OpenGL)|Win32
++ Debug|Win32 = Debug|Win32
+ Release (DirectX)|Win32 = Release (DirectX)|Win32
+ Release (OpenGL)|Win32 = Release (OpenGL)|Win32
++ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {3A68081D-E8F9-4523-9436-530DE9E5530A}.Debug (DirectX)|Win32.ActiveCfg = Debug (DirectX)|Win32
+ {3A68081D-E8F9-4523-9436-530DE9E5530A}.Debug (DirectX)|Win32.Build.0 = Debug (DirectX)|Win32
+ {3A68081D-E8F9-4523-9436-530DE9E5530A}.Debug (OpenGL)|Win32.ActiveCfg = Debug (OpenGL)|Win32
+ {3A68081D-E8F9-4523-9436-530DE9E5530A}.Debug (OpenGL)|Win32.Build.0 = Debug (OpenGL)|Win32
++ {3A68081D-E8F9-4523-9436-530DE9E5530A}.Debug|Win32.ActiveCfg = Debug (OpenGL)|Win32
++ {3A68081D-E8F9-4523-9436-530DE9E5530A}.Debug|Win32.Build.0 = Debug (OpenGL)|Win32
+ {3A68081D-E8F9-4523-9436-530DE9E5530A}.Release (DirectX)|Win32.ActiveCfg = Release (DirectX)|Win32
+ {3A68081D-E8F9-4523-9436-530DE9E5530A}.Release (DirectX)|Win32.Build.0 = Release (DirectX)|Win32
+ {3A68081D-E8F9-4523-9436-530DE9E5530A}.Release (OpenGL)|Win32.ActiveCfg = Release (OpenGL)|Win32
+ {3A68081D-E8F9-4523-9436-530DE9E5530A}.Release (OpenGL)|Win32.Build.0 = Release (OpenGL)|Win32
++ {3A68081D-E8F9-4523-9436-530DE9E5530A}.Release|Win32.ActiveCfg = Release (OpenGL)|Win32
++ {3A68081D-E8F9-4523-9436-530DE9E5530A}.Release|Win32.Build.0 = Release (OpenGL)|Win32
+ {B2975495-FBE4-4F94-AAC5-B21A9842BF50}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {B2975495-FBE4-4F94-AAC5-B21A9842BF50}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {B2975495-FBE4-4F94-AAC5-B21A9842BF50}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {B2975495-FBE4-4F94-AAC5-B21A9842BF50}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {B2975495-FBE4-4F94-AAC5-B21A9842BF50}.Debug|Win32.ActiveCfg = Debug|Win32
++ {B2975495-FBE4-4F94-AAC5-B21A9842BF50}.Debug|Win32.Build.0 = Debug|Win32
+ {B2975495-FBE4-4F94-AAC5-B21A9842BF50}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {B2975495-FBE4-4F94-AAC5-B21A9842BF50}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {B2975495-FBE4-4F94-AAC5-B21A9842BF50}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+ {B2975495-FBE4-4F94-AAC5-B21A9842BF50}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {B2975495-FBE4-4F94-AAC5-B21A9842BF50}.Release|Win32.ActiveCfg = Release|Win32
++ {B2975495-FBE4-4F94-AAC5-B21A9842BF50}.Release|Win32.Build.0 = Release|Win32
+ {FE0A91C0-E30A-47CD-8A92-A508C9292452}.Debug (DirectX)|Win32.ActiveCfg = Debug (DirectX)|Win32
+ {FE0A91C0-E30A-47CD-8A92-A508C9292452}.Debug (DirectX)|Win32.Build.0 = Debug (DirectX)|Win32
+ {FE0A91C0-E30A-47CD-8A92-A508C9292452}.Debug (OpenGL)|Win32.ActiveCfg = Debug (OpenGL)|Win32
+ {FE0A91C0-E30A-47CD-8A92-A508C9292452}.Debug (OpenGL)|Win32.Build.0 = Debug (OpenGL)|Win32
++ {FE0A91C0-E30A-47CD-8A92-A508C9292452}.Debug|Win32.ActiveCfg = Debug (OpenGL)|Win32
++ {FE0A91C0-E30A-47CD-8A92-A508C9292452}.Debug|Win32.Build.0 = Debug (OpenGL)|Win32
+ {FE0A91C0-E30A-47CD-8A92-A508C9292452}.Release (DirectX)|Win32.ActiveCfg = Release (DirectX)|Win32
+ {FE0A91C0-E30A-47CD-8A92-A508C9292452}.Release (DirectX)|Win32.Build.0 = Release (DirectX)|Win32
+ {FE0A91C0-E30A-47CD-8A92-A508C9292452}.Release (OpenGL)|Win32.ActiveCfg = Release (OpenGL)|Win32
+ {FE0A91C0-E30A-47CD-8A92-A508C9292452}.Release (OpenGL)|Win32.Build.0 = Release (OpenGL)|Win32
++ {FE0A91C0-E30A-47CD-8A92-A508C9292452}.Release|Win32.ActiveCfg = Release (OpenGL)|Win32
++ {FE0A91C0-E30A-47CD-8A92-A508C9292452}.Release|Win32.Build.0 = Release (OpenGL)|Win32
+ {00700E12-A63B-4E54-B962-4011A90584BD}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {00700E12-A63B-4E54-B962-4011A90584BD}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {00700E12-A63B-4E54-B962-4011A90584BD}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {00700E12-A63B-4E54-B962-4011A90584BD}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {00700E12-A63B-4E54-B962-4011A90584BD}.Debug|Win32.ActiveCfg = Debug|Win32
++ {00700E12-A63B-4E54-B962-4011A90584BD}.Debug|Win32.Build.0 = Debug|Win32
+ {00700E12-A63B-4E54-B962-4011A90584BD}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {00700E12-A63B-4E54-B962-4011A90584BD}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {00700E12-A63B-4E54-B962-4011A90584BD}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+ {00700E12-A63B-4E54-B962-4011A90584BD}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {00700E12-A63B-4E54-B962-4011A90584BD}.Release|Win32.ActiveCfg = Release|Win32
++ {00700E12-A63B-4E54-B962-4011A90584BD}.Release|Win32.Build.0 = Release|Win32
+ {BA5B08FC-2ECB-4571-9F25-F8054522FC65}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {BA5B08FC-2ECB-4571-9F25-F8054522FC65}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {BA5B08FC-2ECB-4571-9F25-F8054522FC65}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {BA5B08FC-2ECB-4571-9F25-F8054522FC65}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {BA5B08FC-2ECB-4571-9F25-F8054522FC65}.Debug|Win32.ActiveCfg = Debug|Win32
++ {BA5B08FC-2ECB-4571-9F25-F8054522FC65}.Debug|Win32.Build.0 = Debug|Win32
+ {BA5B08FC-2ECB-4571-9F25-F8054522FC65}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {BA5B08FC-2ECB-4571-9F25-F8054522FC65}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {BA5B08FC-2ECB-4571-9F25-F8054522FC65}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+ {BA5B08FC-2ECB-4571-9F25-F8054522FC65}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {BA5B08FC-2ECB-4571-9F25-F8054522FC65}.Release|Win32.ActiveCfg = Release|Win32
++ {BA5B08FC-2ECB-4571-9F25-F8054522FC65}.Release|Win32.Build.0 = Release|Win32
++ {3AD3147B-8E7F-4C70-B049-19FA1916BF12}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
++ {3AD3147B-8E7F-4C70-B049-19FA1916BF12}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
++ {3AD3147B-8E7F-4C70-B049-19FA1916BF12}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
++ {3AD3147B-8E7F-4C70-B049-19FA1916BF12}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {3AD3147B-8E7F-4C70-B049-19FA1916BF12}.Debug|Win32.ActiveCfg = Debug|Win32
++ {3AD3147B-8E7F-4C70-B049-19FA1916BF12}.Debug|Win32.Build.0 = Debug|Win32
++ {3AD3147B-8E7F-4C70-B049-19FA1916BF12}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
++ {3AD3147B-8E7F-4C70-B049-19FA1916BF12}.Release (DirectX)|Win32.Build.0 = Release|Win32
++ {3AD3147B-8E7F-4C70-B049-19FA1916BF12}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
++ {3AD3147B-8E7F-4C70-B049-19FA1916BF12}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {3AD3147B-8E7F-4C70-B049-19FA1916BF12}.Release|Win32.ActiveCfg = Release|Win32
++ {3AD3147B-8E7F-4C70-B049-19FA1916BF12}.Release|Win32.Build.0 = Release|Win32
+ {5E479372-4F34-426D-AA1E-9879E94C105D}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {5E479372-4F34-426D-AA1E-9879E94C105D}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {5E479372-4F34-426D-AA1E-9879E94C105D}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
++ {5E479372-4F34-426D-AA1E-9879E94C105D}.Debug|Win32.ActiveCfg = Debug|Win32
++ {5E479372-4F34-426D-AA1E-9879E94C105D}.Debug|Win32.Build.0 = Debug|Win32
+ {5E479372-4F34-426D-AA1E-9879E94C105D}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {5E479372-4F34-426D-AA1E-9879E94C105D}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {5E479372-4F34-426D-AA1E-9879E94C105D}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
++ {5E479372-4F34-426D-AA1E-9879E94C105D}.Release|Win32.ActiveCfg = Release|Win32
++ {5E479372-4F34-426D-AA1E-9879E94C105D}.Release|Win32.Build.0 = Release|Win32
+ {F9E6874D-60A8-49BA-9393-A2105E63ABCF}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {F9E6874D-60A8-49BA-9393-A2105E63ABCF}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {F9E6874D-60A8-49BA-9393-A2105E63ABCF}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {F9E6874D-60A8-49BA-9393-A2105E63ABCF}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {F9E6874D-60A8-49BA-9393-A2105E63ABCF}.Debug|Win32.ActiveCfg = Debug|Win32
++ {F9E6874D-60A8-49BA-9393-A2105E63ABCF}.Debug|Win32.Build.0 = Debug|Win32
+ {F9E6874D-60A8-49BA-9393-A2105E63ABCF}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {F9E6874D-60A8-49BA-9393-A2105E63ABCF}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {F9E6874D-60A8-49BA-9393-A2105E63ABCF}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+ {F9E6874D-60A8-49BA-9393-A2105E63ABCF}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {F9E6874D-60A8-49BA-9393-A2105E63ABCF}.Release|Win32.ActiveCfg = Release|Win32
++ {F9E6874D-60A8-49BA-9393-A2105E63ABCF}.Release|Win32.Build.0 = Release|Win32
+ {D8097C41-605D-4917-8957-9DF7F44A18CD}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {D8097C41-605D-4917-8957-9DF7F44A18CD}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {D8097C41-605D-4917-8957-9DF7F44A18CD}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {D8097C41-605D-4917-8957-9DF7F44A18CD}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {D8097C41-605D-4917-8957-9DF7F44A18CD}.Debug|Win32.ActiveCfg = Debug|Win32
++ {D8097C41-605D-4917-8957-9DF7F44A18CD}.Debug|Win32.Build.0 = Debug|Win32
+ {D8097C41-605D-4917-8957-9DF7F44A18CD}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {D8097C41-605D-4917-8957-9DF7F44A18CD}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {D8097C41-605D-4917-8957-9DF7F44A18CD}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+ {D8097C41-605D-4917-8957-9DF7F44A18CD}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {D8097C41-605D-4917-8957-9DF7F44A18CD}.Release|Win32.ActiveCfg = Release|Win32
++ {D8097C41-605D-4917-8957-9DF7F44A18CD}.Release|Win32.Build.0 = Release|Win32
+ {19B16CD0-3B47-47B7-AB0E-81EF2BF1B187}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {19B16CD0-3B47-47B7-AB0E-81EF2BF1B187}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {19B16CD0-3B47-47B7-AB0E-81EF2BF1B187}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {19B16CD0-3B47-47B7-AB0E-81EF2BF1B187}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {19B16CD0-3B47-47B7-AB0E-81EF2BF1B187}.Debug|Win32.ActiveCfg = Debug|Win32
++ {19B16CD0-3B47-47B7-AB0E-81EF2BF1B187}.Debug|Win32.Build.0 = Debug|Win32
+ {19B16CD0-3B47-47B7-AB0E-81EF2BF1B187}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {19B16CD0-3B47-47B7-AB0E-81EF2BF1B187}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {19B16CD0-3B47-47B7-AB0E-81EF2BF1B187}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+ {19B16CD0-3B47-47B7-AB0E-81EF2BF1B187}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {19B16CD0-3B47-47B7-AB0E-81EF2BF1B187}.Release|Win32.ActiveCfg = Release|Win32
++ {19B16CD0-3B47-47B7-AB0E-81EF2BF1B187}.Release|Win32.Build.0 = Release|Win32
+ {22B25AEC-7223-46FC-8356-4418327EFDE1}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {22B25AEC-7223-46FC-8356-4418327EFDE1}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {22B25AEC-7223-46FC-8356-4418327EFDE1}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {22B25AEC-7223-46FC-8356-4418327EFDE1}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {22B25AEC-7223-46FC-8356-4418327EFDE1}.Debug|Win32.ActiveCfg = Debug|Win32
++ {22B25AEC-7223-46FC-8356-4418327EFDE1}.Debug|Win32.Build.0 = Debug|Win32
+ {22B25AEC-7223-46FC-8356-4418327EFDE1}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {22B25AEC-7223-46FC-8356-4418327EFDE1}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {22B25AEC-7223-46FC-8356-4418327EFDE1}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+ {22B25AEC-7223-46FC-8356-4418327EFDE1}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {22B25AEC-7223-46FC-8356-4418327EFDE1}.Release|Win32.ActiveCfg = Release|Win32
++ {22B25AEC-7223-46FC-8356-4418327EFDE1}.Release|Win32.Build.0 = Release|Win32
+ {3B424C94-2005-44CC-BFB1-4B6C89090732}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {3B424C94-2005-44CC-BFB1-4B6C89090732}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {3B424C94-2005-44CC-BFB1-4B6C89090732}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {3B424C94-2005-44CC-BFB1-4B6C89090732}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {3B424C94-2005-44CC-BFB1-4B6C89090732}.Debug|Win32.ActiveCfg = Debug|Win32
++ {3B424C94-2005-44CC-BFB1-4B6C89090732}.Debug|Win32.Build.0 = Debug|Win32
+ {3B424C94-2005-44CC-BFB1-4B6C89090732}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {3B424C94-2005-44CC-BFB1-4B6C89090732}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {3B424C94-2005-44CC-BFB1-4B6C89090732}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+ {3B424C94-2005-44CC-BFB1-4B6C89090732}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {3B424C94-2005-44CC-BFB1-4B6C89090732}.Release|Win32.ActiveCfg = Release|Win32
++ {3B424C94-2005-44CC-BFB1-4B6C89090732}.Release|Win32.Build.0 = Release|Win32
+ {3843C3D4-E5A6-4030-87EC-E7EE57242106}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {3843C3D4-E5A6-4030-87EC-E7EE57242106}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {3843C3D4-E5A6-4030-87EC-E7EE57242106}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {3843C3D4-E5A6-4030-87EC-E7EE57242106}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {3843C3D4-E5A6-4030-87EC-E7EE57242106}.Debug|Win32.ActiveCfg = Debug|Win32
++ {3843C3D4-E5A6-4030-87EC-E7EE57242106}.Debug|Win32.Build.0 = Debug|Win32
+ {3843C3D4-E5A6-4030-87EC-E7EE57242106}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {3843C3D4-E5A6-4030-87EC-E7EE57242106}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {3843C3D4-E5A6-4030-87EC-E7EE57242106}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+ {3843C3D4-E5A6-4030-87EC-E7EE57242106}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {3843C3D4-E5A6-4030-87EC-E7EE57242106}.Release|Win32.ActiveCfg = Release|Win32
++ {3843C3D4-E5A6-4030-87EC-E7EE57242106}.Release|Win32.Build.0 = Release|Win32
+ {88E7E431-3752-4D58-BCD2-A7E6A1B74247}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {88E7E431-3752-4D58-BCD2-A7E6A1B74247}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {88E7E431-3752-4D58-BCD2-A7E6A1B74247}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {88E7E431-3752-4D58-BCD2-A7E6A1B74247}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {88E7E431-3752-4D58-BCD2-A7E6A1B74247}.Debug|Win32.ActiveCfg = Debug|Win32
++ {88E7E431-3752-4D58-BCD2-A7E6A1B74247}.Debug|Win32.Build.0 = Debug|Win32
+ {88E7E431-3752-4D58-BCD2-A7E6A1B74247}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {88E7E431-3752-4D58-BCD2-A7E6A1B74247}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {88E7E431-3752-4D58-BCD2-A7E6A1B74247}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+ {88E7E431-3752-4D58-BCD2-A7E6A1B74247}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {88E7E431-3752-4D58-BCD2-A7E6A1B74247}.Release|Win32.ActiveCfg = Release|Win32
++ {88E7E431-3752-4D58-BCD2-A7E6A1B74247}.Release|Win32.Build.0 = Release|Win32
+ {145287C8-24EA-42FE-8D7D-C13D5E4B054C}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {145287C8-24EA-42FE-8D7D-C13D5E4B054C}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {145287C8-24EA-42FE-8D7D-C13D5E4B054C}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {145287C8-24EA-42FE-8D7D-C13D5E4B054C}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {145287C8-24EA-42FE-8D7D-C13D5E4B054C}.Debug|Win32.ActiveCfg = Debug|Win32
++ {145287C8-24EA-42FE-8D7D-C13D5E4B054C}.Debug|Win32.Build.0 = Debug|Win32
+ {145287C8-24EA-42FE-8D7D-C13D5E4B054C}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {145287C8-24EA-42FE-8D7D-C13D5E4B054C}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {145287C8-24EA-42FE-8D7D-C13D5E4B054C}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+ {145287C8-24EA-42FE-8D7D-C13D5E4B054C}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {145287C8-24EA-42FE-8D7D-C13D5E4B054C}.Release|Win32.ActiveCfg = Release|Win32
++ {145287C8-24EA-42FE-8D7D-C13D5E4B054C}.Release|Win32.Build.0 = Release|Win32
+ {8E5F7DBE-2E8B-4FD2-BFFE-1960CE7EDC09}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {8E5F7DBE-2E8B-4FD2-BFFE-1960CE7EDC09}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {8E5F7DBE-2E8B-4FD2-BFFE-1960CE7EDC09}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {8E5F7DBE-2E8B-4FD2-BFFE-1960CE7EDC09}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {8E5F7DBE-2E8B-4FD2-BFFE-1960CE7EDC09}.Debug|Win32.ActiveCfg = Debug|Win32
++ {8E5F7DBE-2E8B-4FD2-BFFE-1960CE7EDC09}.Debug|Win32.Build.0 = Debug|Win32
+ {8E5F7DBE-2E8B-4FD2-BFFE-1960CE7EDC09}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {8E5F7DBE-2E8B-4FD2-BFFE-1960CE7EDC09}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {8E5F7DBE-2E8B-4FD2-BFFE-1960CE7EDC09}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+ {8E5F7DBE-2E8B-4FD2-BFFE-1960CE7EDC09}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {8E5F7DBE-2E8B-4FD2-BFFE-1960CE7EDC09}.Release|Win32.ActiveCfg = Release|Win32
++ {8E5F7DBE-2E8B-4FD2-BFFE-1960CE7EDC09}.Release|Win32.Build.0 = Release|Win32
+ {783701E9-4A65-4505-97B0-39E580AA680D}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {783701E9-4A65-4505-97B0-39E580AA680D}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {783701E9-4A65-4505-97B0-39E580AA680D}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {783701E9-4A65-4505-97B0-39E580AA680D}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {783701E9-4A65-4505-97B0-39E580AA680D}.Debug|Win32.ActiveCfg = Debug|Win32
++ {783701E9-4A65-4505-97B0-39E580AA680D}.Debug|Win32.Build.0 = Debug|Win32
+ {783701E9-4A65-4505-97B0-39E580AA680D}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {783701E9-4A65-4505-97B0-39E580AA680D}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {783701E9-4A65-4505-97B0-39E580AA680D}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+ {783701E9-4A65-4505-97B0-39E580AA680D}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {783701E9-4A65-4505-97B0-39E580AA680D}.Release|Win32.ActiveCfg = Release|Win32
++ {783701E9-4A65-4505-97B0-39E580AA680D}.Release|Win32.Build.0 = Release|Win32
+ {44BF83C4-F73A-4093-A29A-11B9016318C4}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {44BF83C4-F73A-4093-A29A-11B9016318C4}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {44BF83C4-F73A-4093-A29A-11B9016318C4}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {44BF83C4-F73A-4093-A29A-11B9016318C4}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {44BF83C4-F73A-4093-A29A-11B9016318C4}.Debug|Win32.ActiveCfg = Debug|Win32
++ {44BF83C4-F73A-4093-A29A-11B9016318C4}.Debug|Win32.Build.0 = Debug|Win32
+ {44BF83C4-F73A-4093-A29A-11B9016318C4}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {44BF83C4-F73A-4093-A29A-11B9016318C4}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {44BF83C4-F73A-4093-A29A-11B9016318C4}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+ {44BF83C4-F73A-4093-A29A-11B9016318C4}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {44BF83C4-F73A-4093-A29A-11B9016318C4}.Release|Win32.ActiveCfg = Release|Win32
++ {44BF83C4-F73A-4093-A29A-11B9016318C4}.Release|Win32.Build.0 = Release|Win32
+ {8735F1ED-317D-4F7A-A512-B2BF9DAEA25A}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {8735F1ED-317D-4F7A-A512-B2BF9DAEA25A}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {8735F1ED-317D-4F7A-A512-B2BF9DAEA25A}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {8735F1ED-317D-4F7A-A512-B2BF9DAEA25A}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {8735F1ED-317D-4F7A-A512-B2BF9DAEA25A}.Debug|Win32.ActiveCfg = Debug|Win32
++ {8735F1ED-317D-4F7A-A512-B2BF9DAEA25A}.Debug|Win32.Build.0 = Debug|Win32
+ {8735F1ED-317D-4F7A-A512-B2BF9DAEA25A}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {8735F1ED-317D-4F7A-A512-B2BF9DAEA25A}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {8735F1ED-317D-4F7A-A512-B2BF9DAEA25A}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+ {8735F1ED-317D-4F7A-A512-B2BF9DAEA25A}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {8735F1ED-317D-4F7A-A512-B2BF9DAEA25A}.Release|Win32.ActiveCfg = Release|Win32
++ {8735F1ED-317D-4F7A-A512-B2BF9DAEA25A}.Release|Win32.Build.0 = Release|Win32
+ {96798038-06CE-4382-BD5B-F9C366724DEB}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {96798038-06CE-4382-BD5B-F9C366724DEB}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {96798038-06CE-4382-BD5B-F9C366724DEB}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {96798038-06CE-4382-BD5B-F9C366724DEB}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {96798038-06CE-4382-BD5B-F9C366724DEB}.Debug|Win32.ActiveCfg = Debug|Win32
++ {96798038-06CE-4382-BD5B-F9C366724DEB}.Debug|Win32.Build.0 = Debug|Win32
+ {96798038-06CE-4382-BD5B-F9C366724DEB}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {96798038-06CE-4382-BD5B-F9C366724DEB}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {96798038-06CE-4382-BD5B-F9C366724DEB}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+ {96798038-06CE-4382-BD5B-F9C366724DEB}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {96798038-06CE-4382-BD5B-F9C366724DEB}.Release|Win32.ActiveCfg = Release|Win32
++ {96798038-06CE-4382-BD5B-F9C366724DEB}.Release|Win32.Build.0 = Release|Win32
+ {AD20A3E2-09CB-42DB-9A70-27F7CDC886CE}.Debug (DirectX)|Win32.ActiveCfg = Release (DLL)|Win32
+ {AD20A3E2-09CB-42DB-9A70-27F7CDC886CE}.Debug (DirectX)|Win32.Build.0 = Release (DLL)|Win32
+ {AD20A3E2-09CB-42DB-9A70-27F7CDC886CE}.Debug (OpenGL)|Win32.ActiveCfg = Release (DLL)|Win32
+ {AD20A3E2-09CB-42DB-9A70-27F7CDC886CE}.Debug (OpenGL)|Win32.Build.0 = Release (DLL)|Win32
++ {AD20A3E2-09CB-42DB-9A70-27F7CDC886CE}.Debug|Win32.ActiveCfg = Debug|Win32
++ {AD20A3E2-09CB-42DB-9A70-27F7CDC886CE}.Debug|Win32.Build.0 = Debug|Win32
+ {AD20A3E2-09CB-42DB-9A70-27F7CDC886CE}.Release (DirectX)|Win32.ActiveCfg = Release (DLL)|Win32
+ {AD20A3E2-09CB-42DB-9A70-27F7CDC886CE}.Release (DirectX)|Win32.Build.0 = Release (DLL)|Win32
+ {AD20A3E2-09CB-42DB-9A70-27F7CDC886CE}.Release (OpenGL)|Win32.ActiveCfg = Release (DLL)|Win32
+ {AD20A3E2-09CB-42DB-9A70-27F7CDC886CE}.Release (OpenGL)|Win32.Build.0 = Release (DLL)|Win32
++ {AD20A3E2-09CB-42DB-9A70-27F7CDC886CE}.Release|Win32.ActiveCfg = Release|Win32
++ {AD20A3E2-09CB-42DB-9A70-27F7CDC886CE}.Release|Win32.Build.0 = Release|Win32
+ {0D91724A-E6F6-4708-AF47-9F88BBE2114C}.Debug (DirectX)|Win32.ActiveCfg = Debug (DirectX)|Win32
+ {0D91724A-E6F6-4708-AF47-9F88BBE2114C}.Debug (DirectX)|Win32.Build.0 = Debug (DirectX)|Win32
+ {0D91724A-E6F6-4708-AF47-9F88BBE2114C}.Debug (OpenGL)|Win32.ActiveCfg = Debug (DirectX)|Win32
++ {0D91724A-E6F6-4708-AF47-9F88BBE2114C}.Debug|Win32.ActiveCfg = Debug (DirectX)|Win32
++ {0D91724A-E6F6-4708-AF47-9F88BBE2114C}.Debug|Win32.Build.0 = Debug (DirectX)|Win32
+ {0D91724A-E6F6-4708-AF47-9F88BBE2114C}.Release (DirectX)|Win32.ActiveCfg = Release (DirectX)|Win32
+ {0D91724A-E6F6-4708-AF47-9F88BBE2114C}.Release (DirectX)|Win32.Build.0 = Release (DirectX)|Win32
+ {0D91724A-E6F6-4708-AF47-9F88BBE2114C}.Release (OpenGL)|Win32.ActiveCfg = Release (DirectX)|Win32
++ {0D91724A-E6F6-4708-AF47-9F88BBE2114C}.Release|Win32.ActiveCfg = Release (DirectX)|Win32
++ {0D91724A-E6F6-4708-AF47-9F88BBE2114C}.Release|Win32.Build.0 = Release (DirectX)|Win32
+ {C15B374E-7126-48FF-B618-A375D7B17FCF}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {C15B374E-7126-48FF-B618-A375D7B17FCF}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {C15B374E-7126-48FF-B618-A375D7B17FCF}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {C15B374E-7126-48FF-B618-A375D7B17FCF}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {C15B374E-7126-48FF-B618-A375D7B17FCF}.Debug|Win32.ActiveCfg = Debug|Win32
++ {C15B374E-7126-48FF-B618-A375D7B17FCF}.Debug|Win32.Build.0 = Debug|Win32
+ {C15B374E-7126-48FF-B618-A375D7B17FCF}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {C15B374E-7126-48FF-B618-A375D7B17FCF}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {C15B374E-7126-48FF-B618-A375D7B17FCF}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+ {C15B374E-7126-48FF-B618-A375D7B17FCF}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {C15B374E-7126-48FF-B618-A375D7B17FCF}.Release|Win32.ActiveCfg = Release|Win32
++ {C15B374E-7126-48FF-B618-A375D7B17FCF}.Release|Win32.Build.0 = Release|Win32
+ {BDD2CB99-93C5-4A70-ACBF-396FFB961AD3}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {BDD2CB99-93C5-4A70-ACBF-396FFB961AD3}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {BDD2CB99-93C5-4A70-ACBF-396FFB961AD3}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {BDD2CB99-93C5-4A70-ACBF-396FFB961AD3}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {BDD2CB99-93C5-4A70-ACBF-396FFB961AD3}.Debug|Win32.ActiveCfg = Debug|Win32
++ {BDD2CB99-93C5-4A70-ACBF-396FFB961AD3}.Debug|Win32.Build.0 = Debug|Win32
+ {BDD2CB99-93C5-4A70-ACBF-396FFB961AD3}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {BDD2CB99-93C5-4A70-ACBF-396FFB961AD3}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {BDD2CB99-93C5-4A70-ACBF-396FFB961AD3}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+ {BDD2CB99-93C5-4A70-ACBF-396FFB961AD3}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {BDD2CB99-93C5-4A70-ACBF-396FFB961AD3}.Release|Win32.ActiveCfg = Release|Win32
++ {BDD2CB99-93C5-4A70-ACBF-396FFB961AD3}.Release|Win32.Build.0 = Release|Win32
+ {DD4818AE-7E35-40B7-A6A0-0FF83AA1C916}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {DD4818AE-7E35-40B7-A6A0-0FF83AA1C916}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {DD4818AE-7E35-40B7-A6A0-0FF83AA1C916}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {DD4818AE-7E35-40B7-A6A0-0FF83AA1C916}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {DD4818AE-7E35-40B7-A6A0-0FF83AA1C916}.Debug|Win32.ActiveCfg = Debug|Win32
++ {DD4818AE-7E35-40B7-A6A0-0FF83AA1C916}.Debug|Win32.Build.0 = Debug|Win32
+ {DD4818AE-7E35-40B7-A6A0-0FF83AA1C916}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {DD4818AE-7E35-40B7-A6A0-0FF83AA1C916}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {DD4818AE-7E35-40B7-A6A0-0FF83AA1C916}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+ {DD4818AE-7E35-40B7-A6A0-0FF83AA1C916}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {DD4818AE-7E35-40B7-A6A0-0FF83AA1C916}.Release|Win32.ActiveCfg = Release|Win32
++ {DD4818AE-7E35-40B7-A6A0-0FF83AA1C916}.Release|Win32.Build.0 = Release|Win32
+ {D450FE9A-CE56-4496-B4AB-379094E642F2}.Debug (DirectX)|Win32.ActiveCfg = Debug (DirectX)|Win32
+ {D450FE9A-CE56-4496-B4AB-379094E642F2}.Debug (DirectX)|Win32.Build.0 = Debug (DirectX)|Win32
+ {D450FE9A-CE56-4496-B4AB-379094E642F2}.Debug (OpenGL)|Win32.ActiveCfg = Debug (OpenGL)|Win32
+ {D450FE9A-CE56-4496-B4AB-379094E642F2}.Debug (OpenGL)|Win32.Build.0 = Debug (OpenGL)|Win32
++ {D450FE9A-CE56-4496-B4AB-379094E642F2}.Debug|Win32.ActiveCfg = Debug (OpenGL)|Win32
++ {D450FE9A-CE56-4496-B4AB-379094E642F2}.Debug|Win32.Build.0 = Debug (OpenGL)|Win32
+ {D450FE9A-CE56-4496-B4AB-379094E642F2}.Release (DirectX)|Win32.ActiveCfg = Release (DirectX)|Win32
+ {D450FE9A-CE56-4496-B4AB-379094E642F2}.Release (DirectX)|Win32.Build.0 = Release (DirectX)|Win32
+ {D450FE9A-CE56-4496-B4AB-379094E642F2}.Release (OpenGL)|Win32.ActiveCfg = Release (OpenGL)|Win32
+ {D450FE9A-CE56-4496-B4AB-379094E642F2}.Release (OpenGL)|Win32.Build.0 = Release (OpenGL)|Win32
++ {D450FE9A-CE56-4496-B4AB-379094E642F2}.Release|Win32.ActiveCfg = Release (OpenGL)|Win32
++ {D450FE9A-CE56-4496-B4AB-379094E642F2}.Release|Win32.Build.0 = Release (OpenGL)|Win32
+ {6A8518C3-D81A-4428-BD7F-C37933088AC1}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {6A8518C3-D81A-4428-BD7F-C37933088AC1}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {6A8518C3-D81A-4428-BD7F-C37933088AC1}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {6A8518C3-D81A-4428-BD7F-C37933088AC1}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {6A8518C3-D81A-4428-BD7F-C37933088AC1}.Debug|Win32.ActiveCfg = Debug|Win32
++ {6A8518C3-D81A-4428-BD7F-C37933088AC1}.Debug|Win32.Build.0 = Debug|Win32
+ {6A8518C3-D81A-4428-BD7F-C37933088AC1}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {6A8518C3-D81A-4428-BD7F-C37933088AC1}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {6A8518C3-D81A-4428-BD7F-C37933088AC1}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+ {6A8518C3-D81A-4428-BD7F-C37933088AC1}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {6A8518C3-D81A-4428-BD7F-C37933088AC1}.Release|Win32.ActiveCfg = Release|Win32
++ {6A8518C3-D81A-4428-BD7F-C37933088AC1}.Release|Win32.Build.0 = Release|Win32
+ {D9885434-4B9D-41FB-B5FC-5E89D41AEFF0}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {D9885434-4B9D-41FB-B5FC-5E89D41AEFF0}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {D9885434-4B9D-41FB-B5FC-5E89D41AEFF0}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {D9885434-4B9D-41FB-B5FC-5E89D41AEFF0}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {D9885434-4B9D-41FB-B5FC-5E89D41AEFF0}.Debug|Win32.ActiveCfg = Debug|Win32
++ {D9885434-4B9D-41FB-B5FC-5E89D41AEFF0}.Debug|Win32.Build.0 = Debug|Win32
+ {D9885434-4B9D-41FB-B5FC-5E89D41AEFF0}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {D9885434-4B9D-41FB-B5FC-5E89D41AEFF0}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {D9885434-4B9D-41FB-B5FC-5E89D41AEFF0}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+ {D9885434-4B9D-41FB-B5FC-5E89D41AEFF0}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {D9885434-4B9D-41FB-B5FC-5E89D41AEFF0}.Release|Win32.ActiveCfg = Release|Win32
++ {D9885434-4B9D-41FB-B5FC-5E89D41AEFF0}.Release|Win32.Build.0 = Release|Win32
+ {AF6C8945-5DDC-4F62-A48C-86B11B7ED996}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {AF6C8945-5DDC-4F62-A48C-86B11B7ED996}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {AF6C8945-5DDC-4F62-A48C-86B11B7ED996}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {AF6C8945-5DDC-4F62-A48C-86B11B7ED996}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {AF6C8945-5DDC-4F62-A48C-86B11B7ED996}.Debug|Win32.ActiveCfg = Debug|Win32
++ {AF6C8945-5DDC-4F62-A48C-86B11B7ED996}.Debug|Win32.Build.0 = Debug|Win32
+ {AF6C8945-5DDC-4F62-A48C-86B11B7ED996}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {AF6C8945-5DDC-4F62-A48C-86B11B7ED996}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {AF6C8945-5DDC-4F62-A48C-86B11B7ED996}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+ {AF6C8945-5DDC-4F62-A48C-86B11B7ED996}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {AF6C8945-5DDC-4F62-A48C-86B11B7ED996}.Release|Win32.ActiveCfg = Release|Win32
++ {AF6C8945-5DDC-4F62-A48C-86B11B7ED996}.Release|Win32.Build.0 = Release|Win32
+ {3600E1C5-FECA-468C-83F3-FE467DBE2A66}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {3600E1C5-FECA-468C-83F3-FE467DBE2A66}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {3600E1C5-FECA-468C-83F3-FE467DBE2A66}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {3600E1C5-FECA-468C-83F3-FE467DBE2A66}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {3600E1C5-FECA-468C-83F3-FE467DBE2A66}.Debug|Win32.ActiveCfg = Debug|Win32
++ {3600E1C5-FECA-468C-83F3-FE467DBE2A66}.Debug|Win32.Build.0 = Debug|Win32
+ {3600E1C5-FECA-468C-83F3-FE467DBE2A66}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {3600E1C5-FECA-468C-83F3-FE467DBE2A66}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {3600E1C5-FECA-468C-83F3-FE467DBE2A66}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+ {3600E1C5-FECA-468C-83F3-FE467DBE2A66}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {3600E1C5-FECA-468C-83F3-FE467DBE2A66}.Release|Win32.ActiveCfg = Release|Win32
++ {3600E1C5-FECA-468C-83F3-FE467DBE2A66}.Release|Win32.Build.0 = Release|Win32
+ {AEABB6CE-8F0F-4507-8DC4-5E3BCE5962DA}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {AEABB6CE-8F0F-4507-8DC4-5E3BCE5962DA}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {AEABB6CE-8F0F-4507-8DC4-5E3BCE5962DA}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {AEABB6CE-8F0F-4507-8DC4-5E3BCE5962DA}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {AEABB6CE-8F0F-4507-8DC4-5E3BCE5962DA}.Debug|Win32.ActiveCfg = Debug|Win32
++ {AEABB6CE-8F0F-4507-8DC4-5E3BCE5962DA}.Debug|Win32.Build.0 = Debug|Win32
+ {AEABB6CE-8F0F-4507-8DC4-5E3BCE5962DA}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {AEABB6CE-8F0F-4507-8DC4-5E3BCE5962DA}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {AEABB6CE-8F0F-4507-8DC4-5E3BCE5962DA}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+ {AEABB6CE-8F0F-4507-8DC4-5E3BCE5962DA}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {AEABB6CE-8F0F-4507-8DC4-5E3BCE5962DA}.Release|Win32.ActiveCfg = Release|Win32
++ {AEABB6CE-8F0F-4507-8DC4-5E3BCE5962DA}.Release|Win32.Build.0 = Release|Win32
+ {2A8CBFB5-C226-4BB3-8C03-7C75D511A4A2}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {2A8CBFB5-C226-4BB3-8C03-7C75D511A4A2}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {2A8CBFB5-C226-4BB3-8C03-7C75D511A4A2}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {2A8CBFB5-C226-4BB3-8C03-7C75D511A4A2}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {2A8CBFB5-C226-4BB3-8C03-7C75D511A4A2}.Debug|Win32.ActiveCfg = Debug|Win32
++ {2A8CBFB5-C226-4BB3-8C03-7C75D511A4A2}.Debug|Win32.Build.0 = Debug|Win32
+ {2A8CBFB5-C226-4BB3-8C03-7C75D511A4A2}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {2A8CBFB5-C226-4BB3-8C03-7C75D511A4A2}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {2A8CBFB5-C226-4BB3-8C03-7C75D511A4A2}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+ {2A8CBFB5-C226-4BB3-8C03-7C75D511A4A2}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {2A8CBFB5-C226-4BB3-8C03-7C75D511A4A2}.Release|Win32.ActiveCfg = Release|Win32
++ {2A8CBFB5-C226-4BB3-8C03-7C75D511A4A2}.Release|Win32.Build.0 = Release|Win32
+ {17238C64-04D6-4B51-B205-4A5A84ADB9FA}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {17238C64-04D6-4B51-B205-4A5A84ADB9FA}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {17238C64-04D6-4B51-B205-4A5A84ADB9FA}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {17238C64-04D6-4B51-B205-4A5A84ADB9FA}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {17238C64-04D6-4B51-B205-4A5A84ADB9FA}.Debug|Win32.ActiveCfg = Debug|Win32
++ {17238C64-04D6-4B51-B205-4A5A84ADB9FA}.Debug|Win32.Build.0 = Debug|Win32
+ {17238C64-04D6-4B51-B205-4A5A84ADB9FA}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {17238C64-04D6-4B51-B205-4A5A84ADB9FA}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {17238C64-04D6-4B51-B205-4A5A84ADB9FA}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+ {17238C64-04D6-4B51-B205-4A5A84ADB9FA}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {17238C64-04D6-4B51-B205-4A5A84ADB9FA}.Release|Win32.ActiveCfg = Release|Win32
++ {17238C64-04D6-4B51-B205-4A5A84ADB9FA}.Release|Win32.Build.0 = Release|Win32
+ {FB3AB83A-C37A-4636-87FD-827F8506A8FC}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {FB3AB83A-C37A-4636-87FD-827F8506A8FC}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {FB3AB83A-C37A-4636-87FD-827F8506A8FC}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {FB3AB83A-C37A-4636-87FD-827F8506A8FC}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {FB3AB83A-C37A-4636-87FD-827F8506A8FC}.Debug|Win32.ActiveCfg = Debug|Win32
++ {FB3AB83A-C37A-4636-87FD-827F8506A8FC}.Debug|Win32.Build.0 = Debug|Win32
+ {FB3AB83A-C37A-4636-87FD-827F8506A8FC}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {FB3AB83A-C37A-4636-87FD-827F8506A8FC}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {FB3AB83A-C37A-4636-87FD-827F8506A8FC}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+ {FB3AB83A-C37A-4636-87FD-827F8506A8FC}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {FB3AB83A-C37A-4636-87FD-827F8506A8FC}.Release|Win32.ActiveCfg = Release|Win32
++ {FB3AB83A-C37A-4636-87FD-827F8506A8FC}.Release|Win32.Build.0 = Release|Win32
+ {88968763-3D6B-48A8-B495-CC8C187FAC02}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {88968763-3D6B-48A8-B495-CC8C187FAC02}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {88968763-3D6B-48A8-B495-CC8C187FAC02}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {88968763-3D6B-48A8-B495-CC8C187FAC02}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {88968763-3D6B-48A8-B495-CC8C187FAC02}.Debug|Win32.ActiveCfg = Debug|Win32
++ {88968763-3D6B-48A8-B495-CC8C187FAC02}.Debug|Win32.Build.0 = Debug|Win32
+ {88968763-3D6B-48A8-B495-CC8C187FAC02}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {88968763-3D6B-48A8-B495-CC8C187FAC02}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {88968763-3D6B-48A8-B495-CC8C187FAC02}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+ {88968763-3D6B-48A8-B495-CC8C187FAC02}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {88968763-3D6B-48A8-B495-CC8C187FAC02}.Release|Win32.ActiveCfg = Release|Win32
++ {88968763-3D6B-48A8-B495-CC8C187FAC02}.Release|Win32.Build.0 = Release|Win32
+ {92BD50AA-04D6-4FBF-ACE1-468FAF6778F2}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {92BD50AA-04D6-4FBF-ACE1-468FAF6778F2}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {92BD50AA-04D6-4FBF-ACE1-468FAF6778F2}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {92BD50AA-04D6-4FBF-ACE1-468FAF6778F2}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {92BD50AA-04D6-4FBF-ACE1-468FAF6778F2}.Debug|Win32.ActiveCfg = Debug|Win32
++ {92BD50AA-04D6-4FBF-ACE1-468FAF6778F2}.Debug|Win32.Build.0 = Debug|Win32
+ {92BD50AA-04D6-4FBF-ACE1-468FAF6778F2}.Release (DirectX)|Win32.ActiveCfg = ReleaseNASM|Win32
+ {92BD50AA-04D6-4FBF-ACE1-468FAF6778F2}.Release (DirectX)|Win32.Build.0 = ReleaseNASM|Win32
+ {92BD50AA-04D6-4FBF-ACE1-468FAF6778F2}.Release (OpenGL)|Win32.ActiveCfg = ReleaseNASM|Win32
+ {92BD50AA-04D6-4FBF-ACE1-468FAF6778F2}.Release (OpenGL)|Win32.Build.0 = ReleaseNASM|Win32
++ {92BD50AA-04D6-4FBF-ACE1-468FAF6778F2}.Release|Win32.ActiveCfg = Release|Win32
++ {92BD50AA-04D6-4FBF-ACE1-468FAF6778F2}.Release|Win32.Build.0 = Release|Win32
+ {1E2FB608-3DD2-4021-A598-90008FA6DE85}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+ {1E2FB608-3DD2-4021-A598-90008FA6DE85}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+ {1E2FB608-3DD2-4021-A598-90008FA6DE85}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+ {1E2FB608-3DD2-4021-A598-90008FA6DE85}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {1E2FB608-3DD2-4021-A598-90008FA6DE85}.Debug|Win32.ActiveCfg = Debug|Win32
++ {1E2FB608-3DD2-4021-A598-90008FA6DE85}.Debug|Win32.Build.0 = Debug|Win32
+ {1E2FB608-3DD2-4021-A598-90008FA6DE85}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+ {1E2FB608-3DD2-4021-A598-90008FA6DE85}.Release (DirectX)|Win32.Build.0 = Release|Win32
+ {1E2FB608-3DD2-4021-A598-90008FA6DE85}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+ {1E2FB608-3DD2-4021-A598-90008FA6DE85}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {1E2FB608-3DD2-4021-A598-90008FA6DE85}.Release|Win32.ActiveCfg = Release|Win32
++ {1E2FB608-3DD2-4021-A598-90008FA6DE85}.Release|Win32.Build.0 = Release|Win32
++ {2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
++ {2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
++ {2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
++ {2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}.Debug|Win32.ActiveCfg = Debug|Win32
++ {2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}.Debug|Win32.Build.0 = Debug|Win32
++ {2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
++ {2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}.Release (DirectX)|Win32.Build.0 = Release|Win32
++ {2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
++ {2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}.Release|Win32.ActiveCfg = Release|Win32
++ {2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}.Release|Win32.Build.0 = Release|Win32
++ {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
++ {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
++ {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
++ {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug|Win32.ActiveCfg = Debug|Win32
++ {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug|Win32.Build.0 = Debug|Win32
++ {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
++ {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Release (DirectX)|Win32.Build.0 = Release|Win32
++ {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
++ {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Release|Win32.ActiveCfg = Release|Win32
++ {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Release|Win32.Build.0 = Release|Win32
++ {6D8C91F8-992F-4C83-9DE3-485D64EF8420}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
++ {6D8C91F8-992F-4C83-9DE3-485D64EF8420}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
++ {6D8C91F8-992F-4C83-9DE3-485D64EF8420}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
++ {6D8C91F8-992F-4C83-9DE3-485D64EF8420}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {6D8C91F8-992F-4C83-9DE3-485D64EF8420}.Debug|Win32.ActiveCfg = Debug|Win32
++ {6D8C91F8-992F-4C83-9DE3-485D64EF8420}.Debug|Win32.Build.0 = Debug|Win32
++ {6D8C91F8-992F-4C83-9DE3-485D64EF8420}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
++ {6D8C91F8-992F-4C83-9DE3-485D64EF8420}.Release (DirectX)|Win32.Build.0 = Release|Win32
++ {6D8C91F8-992F-4C83-9DE3-485D64EF8420}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
++ {6D8C91F8-992F-4C83-9DE3-485D64EF8420}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {6D8C91F8-992F-4C83-9DE3-485D64EF8420}.Release|Win32.ActiveCfg = Release|Win32
++ {6D8C91F8-992F-4C83-9DE3-485D64EF8420}.Release|Win32.Build.0 = Release|Win32
++ {74C9642E-1988-48DC-8404-11717C355378}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
++ {74C9642E-1988-48DC-8404-11717C355378}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
++ {74C9642E-1988-48DC-8404-11717C355378}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
++ {74C9642E-1988-48DC-8404-11717C355378}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {74C9642E-1988-48DC-8404-11717C355378}.Debug|Win32.ActiveCfg = Debug|Win32
++ {74C9642E-1988-48DC-8404-11717C355378}.Debug|Win32.Build.0 = Debug|Win32
++ {74C9642E-1988-48DC-8404-11717C355378}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
++ {74C9642E-1988-48DC-8404-11717C355378}.Release (DirectX)|Win32.Build.0 = Release|Win32
++ {74C9642E-1988-48DC-8404-11717C355378}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
++ {74C9642E-1988-48DC-8404-11717C355378}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {74C9642E-1988-48DC-8404-11717C355378}.Release|Win32.ActiveCfg = Release|Win32
++ {74C9642E-1988-48DC-8404-11717C355378}.Release|Win32.Build.0 = Release|Win32
++ {C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
++ {C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
++ {C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
++ {C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}.Debug|Win32.ActiveCfg = Debug|Win32
++ {C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}.Debug|Win32.Build.0 = Debug|Win32
++ {C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
++ {C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}.Release (DirectX)|Win32.Build.0 = Release|Win32
++ {C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
++ {C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}.Release|Win32.ActiveCfg = Release|Win32
++ {C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}.Release|Win32.Build.0 = Release|Win32
++ {A55ACC6B-837D-4784-8E41-66947B1D9B8C}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
++ {A55ACC6B-837D-4784-8E41-66947B1D9B8C}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
++ {A55ACC6B-837D-4784-8E41-66947B1D9B8C}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
++ {A55ACC6B-837D-4784-8E41-66947B1D9B8C}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
++ {A55ACC6B-837D-4784-8E41-66947B1D9B8C}.Debug|Win32.ActiveCfg = Debug|Win32
++ {A55ACC6B-837D-4784-8E41-66947B1D9B8C}.Debug|Win32.Build.0 = Debug|Win32
++ {A55ACC6B-837D-4784-8E41-66947B1D9B8C}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
++ {A55ACC6B-837D-4784-8E41-66947B1D9B8C}.Release (DirectX)|Win32.Build.0 = Release|Win32
++ {A55ACC6B-837D-4784-8E41-66947B1D9B8C}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
++ {A55ACC6B-837D-4784-8E41-66947B1D9B8C}.Release (OpenGL)|Win32.Build.0 = Release|Win32
++ {A55ACC6B-837D-4784-8E41-66947B1D9B8C}.Release|Win32.ActiveCfg = Release|Win32
++ {A55ACC6B-837D-4784-8E41-66947B1D9B8C}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+diff --git a/project/VS2010Express/XBMC.vcxproj b/project/VS2010Express/XBMC.vcxproj
+index f3b44da..993eb6c 100644
+--- a/project/VS2010Express/XBMC.vcxproj
++++ b/project/VS2010Express/XBMC.vcxproj
+@@ -143,6 +143,10 @@
+ <AdditionalManifestFiles>VC90.CRT.x86.manifest;%(AdditionalManifestFiles)</AdditionalManifestFiles>
+ <EnableDPIAwareness>true</EnableDPIAwareness>
+ </Manifest>
++ <PostBuildEvent>
++ <Command>copy "$(TargetPath)" "$(ProjectDir)..\.."
++copy "$(TargetDir)*.dll" "$(ProjectDir)..\.."</Command>
++ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug (DirectX)|Win32'">
+ <ClCompile>
+@@ -186,6 +190,10 @@
+ <AdditionalManifestFiles>VC90.CRT.x86.manifest;%(AdditionalManifestFiles)</AdditionalManifestFiles>
+ <EnableDPIAwareness>true</EnableDPIAwareness>
+ </Manifest>
++ <PostBuildEvent>
++ <Command>copy "$(TargetPath)" "$(ProjectDir)..\.."
++copy "$(TargetDir)*.dll" "$(ProjectDir)..\.."</Command>
++ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release (DirectX)|Win32'">
+ <ClCompile>
+@@ -234,6 +242,9 @@
+ <AdditionalManifestFiles>VC90.CRT.x86.manifest;%(AdditionalManifestFiles)</AdditionalManifestFiles>
+ <EnableDPIAwareness>true</EnableDPIAwareness>
+ </Manifest>
++ <PostBuildEvent>
++ <Command>copy "$(TargetPath)" "$(ProjectDir)..\.."</Command>
++ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug (OpenGL)|Win32'">
+ <ClCompile>
+@@ -278,6 +289,16 @@
+ <AdditionalManifestFiles>VC90.CRT.x86.manifest;%(AdditionalManifestFiles)</AdditionalManifestFiles>
+ <EnableDPIAwareness>true</EnableDPIAwareness>
+ </Manifest>
++ <PostBuildEvent>
++ <Command>copy "$(TargetPath)" "$(ProjectDir)..\.."
++copy "$(TargetDir)*.dll" "$(ProjectDir)..\.."</Command>
++ </PostBuildEvent>
++ </ItemDefinitionGroup>
++ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Template|Win32'">
++ <PostBuildEvent>
++ <Command>copy "$(TargetPath)" "$(ProjectDir)..\.."
++copy "$(TargetDir)*.dll" "$(ProjectDir)..\.."</Command>
++ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\lib\SlingboxLib\SlingboxLib.cpp" />
+@@ -285,6 +306,10 @@
+ <ClCompile Include="..\..\lib\tinyXML\tinyxml.cpp" />
+ <ClCompile Include="..\..\lib\tinyXML\tinyxmlerror.cpp" />
+ <ClCompile Include="..\..\lib\tinyXML\tinyxmlparser.cpp" />
++ <ClCompile Include="..\..\xbmc\addons\AddonCallbacks.cpp" />
++ <ClCompile Include="..\..\xbmc\addons\AddonCallbacksAddon.cpp" />
++ <ClCompile Include="..\..\xbmc\addons\AddonCallbacksGUI.cpp" />
++ <ClCompile Include="..\..\xbmc\addons\AddonCallbacksPVR.cpp" />
+ <ClCompile Include="..\..\xbmc\addons\AddonDatabase.cpp" />
+ <ClCompile Include="..\..\xbmc\addons\AddonInstaller.cpp" />
+ <ClCompile Include="..\..\xbmc\addons\AddonVersion.cpp" />
+@@ -302,7 +327,9 @@
+ <ClCompile Include="..\..\xbmc\AutoSwitch.cpp" />
+ <ClCompile Include="..\..\xbmc\BackgroundInfoLoader.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Video\CrystalHD.cpp" />
++ <ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDDemuxers\DVDDemuxPVRClient.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDInputStreams\DVDInputStreamBluray.cpp" />
++ <ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDInputStreams\DVDInputStreamPVRManager.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\paplayer\BXAcodec.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\paplayer\PCMCodec.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\VideoRenderers\RenderCapture.cpp" />
+@@ -318,6 +345,7 @@
+ <ClCompile Include="..\..\xbmc\dialogs\GUIDialogButtonMenu.cpp" />
+ <ClCompile Include="..\..\xbmc\dialogs\GUIDialogCache.cpp" />
+ <ClCompile Include="..\..\xbmc\dialogs\GUIDialogContextMenu.cpp" />
++ <ClCompile Include="..\..\xbmc\dialogs\GUIDialogExtendedProgressBar.cpp" />
+ <ClCompile Include="..\..\xbmc\dialogs\GUIDialogFavourites.cpp" />
+ <ClCompile Include="..\..\xbmc\dialogs\GUIDialogFileBrowser.cpp" />
+ <ClCompile Include="..\..\xbmc\dialogs\GUIDialogGamepad.cpp" />
+@@ -340,6 +368,12 @@
+ <ClCompile Include="..\..\xbmc\dialogs\GUIDialogVolumeBar.cpp" />
+ <ClCompile Include="..\..\xbmc\dialogs\GUIDialogYesNo.cpp" />
+ <ClCompile Include="..\..\xbmc\DynamicDll.cpp" />
++ <ClCompile Include="..\..\xbmc\epg\Epg.cpp" />
++ <ClCompile Include="..\..\xbmc\epg\EpgContainer.cpp" />
++ <ClCompile Include="..\..\xbmc\epg\EpgDatabase.cpp" />
++ <ClCompile Include="..\..\xbmc\epg\EpgInfoTag.cpp" />
++ <ClCompile Include="..\..\xbmc\epg\EpgSearchFilter.cpp" />
++ <ClCompile Include="..\..\xbmc\epg\GUIEPGGridContainer.cpp" />
+ <ClCompile Include="..\..\xbmc\Favourites.cpp" />
+ <ClCompile Include="..\..\xbmc\FileItem.cpp" />
+ <ClCompile Include="..\..\xbmc\filesystem\CacheCircular.cpp" />
+@@ -349,6 +383,8 @@
+ <ClCompile Include="..\..\xbmc\FileSystem\iso9660.cpp" />
+ <ClCompile Include="..\..\xbmc\FileSystem\ISO9660Directory.cpp" />
+ <ClCompile Include="..\..\xbmc\FileSystem\FileUDF.cpp" />
++ <ClCompile Include="..\..\xbmc\filesystem\PVRDirectory.cpp" />
++ <ClCompile Include="..\..\xbmc\filesystem\PVRFile.cpp" />
+ <ClCompile Include="..\..\xbmc\filesystem\NFSDirectory.cpp" />
+ <ClCompile Include="..\..\xbmc\filesystem\PipesManager.cpp" />
+ <ClCompile Include="..\..\xbmc\filesystem\Slingbox.cpp" />
+@@ -473,6 +509,7 @@
+ <ClCompile Include="..\..\xbmc\interfaces\json-rpc\JSONServiceDescription.cpp" />
+ <ClCompile Include="..\..\xbmc\interfaces\json-rpc\PlayerOperations.cpp" />
+ <ClCompile Include="..\..\xbmc\interfaces\json-rpc\PlaylistOperations.cpp" />
++ <ClCompile Include="..\..\xbmc\interfaces\json-rpc\PVROperations.cpp" />
+ <ClCompile Include="..\..\xbmc\interfaces\json-rpc\SystemOperations.cpp" />
+ <ClCompile Include="..\..\xbmc\interfaces\json-rpc\VideoLibrary.cpp" />
+ <ClCompile Include="..\..\xbmc\interfaces\json-rpc\XBMCOperations.cpp" />
+@@ -719,6 +756,38 @@
+ <ClCompile Include="..\..\xbmc\programs\GUIWindowPrograms.cpp" />
+ <ClCompile Include="..\..\xbmc\programs\ProgramDatabase.cpp" />
+ <ClCompile Include="..\..\xbmc\programs\Shortcut.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\addons\PVRClient.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\addons\PVRClients.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\channels\PVRChannel.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\channels\PVRChannelGroup.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\channels\PVRChannelGroupInternal.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\channels\PVRChannelGroups.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\channels\PVRChannelGroupsContainer.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRChannelManager.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRChannelsOSD.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRCutterOSD.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRDirectorOSD.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGroupManager.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGuideInfo.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGuideOSD.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGuideSearch.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRRecordingInfo.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRTimerSettings.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\PVRDatabase.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\PVRGUIInfo.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\PVRManager.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\recordings\PVRRecording.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\recordings\PVRRecordings.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\timers\PVRTimerInfoTag.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\timers\PVRTimers.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\windows\GUIViewStatePVR.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\windows\GUIWindowPVR.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\windows\GUIWindowPVRChannels.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\windows\GUIWindowPVRCommon.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\windows\GUIWindowPVRGuide.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\windows\GUIWindowPVRRecordings.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\windows\GUIWindowPVRSearch.cpp" />
++ <ClCompile Include="..\..\xbmc\pvr\windows\GUIWindowPVRTimers.cpp" />
+ <ClCompile Include="..\..\xbmc\rendering\dx\GUIWindowTestPatternDX.cpp" />
+ <ClCompile Include="..\..\xbmc\rendering\dx\RenderSystemDX.cpp" />
+ <ClCompile Include="..\..\xbmc\rendering\gl\GUIWindowTestPatternGL.cpp" />
+@@ -758,6 +827,7 @@
+ <ClInclude Include="..\..\xbmc\cores\AudioRenderers\IAudioRenderer.h" />
+ <ClInclude Include="..\..\xbmc\cores\paplayer\PCMCodec.h" />
+ <ClInclude Include="..\..\xbmc\filesystem\FileUPnP.h" />
++ <ClInclude Include="..\..\xbmc\interfaces\json-rpc\PVROperations.h" />
+ <ClInclude Include="..\..\xbmc\interfaces\python\xbmcmodule\pythreadstate.h" />
+ <ClInclude Include="..\..\xbmc\threads\platform\win\Implementation.cpp" />
+ <ClCompile Include="..\..\xbmc\threads\SystemClock.cpp" />
+@@ -802,6 +872,7 @@
+ <ClCompile Include="..\..\xbmc\utils\LCD.cpp" />
+ <ClCompile Include="..\..\xbmc\utils\log.cpp" />
+ <ClCompile Include="..\..\xbmc\utils\md5.cpp" />
++ <ClCompile Include="..\..\xbmc\utils\Observer.cpp" />
+ <ClCompile Include="..\..\xbmc\utils\PCMAmplifier.cpp" />
+ <ClCompile Include="..\..\xbmc\utils\PerformanceSample.cpp" />
+ <ClCompile Include="..\..\xbmc\utils\PerformanceStats.cpp" />
+@@ -818,6 +889,7 @@
+ <ClCompile Include="..\..\xbmc\utils\StreamUtils.cpp" />
+ <ClCompile Include="..\..\xbmc\utils\StringUtils.cpp" />
+ <ClCompile Include="..\..\xbmc\utils\SystemInfo.cpp" />
++ <ClCompile Include="..\..\xbmc\utils\TextSearch.cpp" />
+ <ClCompile Include="..\..\xbmc\utils\TimeSmoother.cpp" />
+ <ClCompile Include="..\..\xbmc\utils\TimeUtils.cpp" />
+ <ClCompile Include="..\..\xbmc\utils\TuxBoxUtil.cpp" />
+@@ -1204,10 +1276,15 @@
+ <ClInclude Include="..\..\lib\SlingboxLib\SlingboxLib.h" />
+ <ClInclude Include="..\..\lib\tinyXML\tinystr.h" />
+ <ClInclude Include="..\..\lib\tinyXML\tinyxml.h" />
++ <ClInclude Include="..\..\xbmc\addons\AddonCallbacks.h" />
++ <ClInclude Include="..\..\xbmc\addons\AddonCallbacksAddon.h" />
++ <ClInclude Include="..\..\xbmc\addons\AddonCallbacksGUI.h" />
++ <ClInclude Include="..\..\xbmc\addons\AddonCallbacksPVR.h" />
+ <ClInclude Include="..\..\xbmc\addons\AddonDatabase.h" />
+ <ClInclude Include="..\..\xbmc\addons\AddonInstaller.h" />
+ <ClInclude Include="..\..\xbmc\addons\AddonVersion.h" />
+ <ClInclude Include="..\..\xbmc\addons\DllLibCPluff.h" />
++ <ClInclude Include="..\..\xbmc\addons\DllPVRClient.h" />
+ <ClInclude Include="..\..\xbmc\addons\GUIDialogAddonInfo.h" />
+ <ClInclude Include="..\..\xbmc\addons\GUIDialogAddonSettings.h" />
+ <ClInclude Include="..\..\xbmc\addons\GUIViewStateAddonBrowser.h" />
+@@ -1222,7 +1299,9 @@
+ <ClInclude Include="..\..\xbmc\AutoSwitch.h" />
+ <ClInclude Include="..\..\xbmc\BackgroundInfoLoader.h" />
+ <ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Video\CrystalHD.h" />
++ <ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDDemuxers\DVDDemuxPVRClient.h" />
+ <ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDInputStreams\DVDInputStreamBluray.h" />
++ <ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDInputStreams\DVDInputStreamPVRManager.h" />
+ <ClInclude Include="..\..\xbmc\cores\paplayer\BXAcodec.h" />
+ <ClInclude Include="..\..\xbmc\cores\VideoRenderers\RenderCapture.h" />
+ <ClInclude Include="..\..\xbmc\cores\VideoRenderers\VideoShaders\WinVideoFilter.h" />
+@@ -1237,6 +1316,7 @@
+ <ClInclude Include="..\..\xbmc\dialogs\GUIDialogButtonMenu.h" />
+ <ClInclude Include="..\..\xbmc\dialogs\GUIDialogCache.h" />
+ <ClInclude Include="..\..\xbmc\dialogs\GUIDialogContextMenu.h" />
++ <ClInclude Include="..\..\xbmc\dialogs\GUIDialogExtendedProgressBar.h" />
+ <ClInclude Include="..\..\xbmc\dialogs\GUIDialogFavourites.h" />
+ <ClInclude Include="..\..\xbmc\dialogs\GUIDialogFileBrowser.h" />
+ <ClInclude Include="..\..\xbmc\dialogs\GUIDialogGamepad.h" />
+@@ -1259,6 +1339,12 @@
+ <ClInclude Include="..\..\xbmc\dialogs\GUIDialogVolumeBar.h" />
+ <ClInclude Include="..\..\xbmc\dialogs\GUIDialogYesNo.h" />
+ <ClInclude Include="..\..\xbmc\DynamicDll.h" />
++ <ClInclude Include="..\..\xbmc\epg\Epg.h" />
++ <ClInclude Include="..\..\xbmc\epg\EpgContainer.h" />
++ <ClInclude Include="..\..\xbmc\epg\EpgDatabase.h" />
++ <ClInclude Include="..\..\xbmc\epg\EpgInfoTag.h" />
++ <ClInclude Include="..\..\xbmc\epg\EpgSearchFilter.h" />
++ <ClInclude Include="..\..\xbmc\epg\GUIEPGGridContainer.h" />
+ <ClInclude Include="..\..\xbmc\Favourites.h" />
+ <ClInclude Include="..\..\xbmc\FileItem.h" />
+ <ClInclude Include="..\..\xbmc\filesystem\CacheCircular.h" />
+@@ -1278,6 +1364,8 @@
+ <ClInclude Include="..\..\xbmc\filesystem\iso9660.h" />
+ <ClInclude Include="..\..\xbmc\FileSystem\ISO9660Directory.h" />
+ <ClInclude Include="..\..\xbmc\FileSystem\FileUDF.h" />
++ <ClInclude Include="..\..\xbmc\filesystem\PVRDirectory.h" />
++ <ClInclude Include="..\..\xbmc\filesystem\PVRFile.h" />
+ <ClInclude Include="..\..\xbmc\filesystem\NFSDirectory.h" />
+ <ClInclude Include="..\..\xbmc\filesystem\PipesManager.h" />
+ <ClInclude Include="..\..\xbmc\filesystem\Slingbox.h" />
+@@ -1586,6 +1674,38 @@
+ <ClInclude Include="..\..\xbmc\programs\GUIWindowPrograms.h" />
+ <ClInclude Include="..\..\xbmc\programs\ProgramDatabase.h" />
+ <ClInclude Include="..\..\xbmc\programs\Shortcut.h" />
++ <ClInclude Include="..\..\xbmc\pvr\addons\PVRClient.h" />
++ <ClInclude Include="..\..\xbmc\pvr\addons\PVRClients.h" />
++ <ClInclude Include="..\..\xbmc\pvr\channels\PVRChannel.h" />
++ <ClInclude Include="..\..\xbmc\pvr\channels\PVRChannelGroup.h" />
++ <ClInclude Include="..\..\xbmc\pvr\channels\PVRChannelGroupInternal.h" />
++ <ClInclude Include="..\..\xbmc\pvr\channels\PVRChannelGroups.h" />
++ <ClInclude Include="..\..\xbmc\pvr\channels\PVRChannelGroupsContainer.h" />
++ <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRChannelManager.h" />
++ <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRChannelsOSD.h" />
++ <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRCutterOSD.h" />
++ <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRDirectorOSD.h" />
++ <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGroupManager.h" />
++ <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGuideInfo.h" />
++ <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGuideOSD.h" />
++ <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGuideSearch.h" />
++ <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRRecordingInfo.h" />
++ <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRTimerSettings.h" />
++ <ClInclude Include="..\..\xbmc\pvr\PVRDatabase.h" />
++ <ClInclude Include="..\..\xbmc\pvr\PVRGUIInfo.h" />
++ <ClInclude Include="..\..\xbmc\pvr\PVRManager.h" />
++ <ClInclude Include="..\..\xbmc\pvr\recordings\PVRRecording.h" />
++ <ClInclude Include="..\..\xbmc\pvr\recordings\PVRRecordings.h" />
++ <ClInclude Include="..\..\xbmc\pvr\timers\PVRTimerInfoTag.h" />
++ <ClInclude Include="..\..\xbmc\pvr\timers\PVRTimers.h" />
++ <ClInclude Include="..\..\xbmc\pvr\windows\GUIViewStatePVR.h" />
++ <ClInclude Include="..\..\xbmc\pvr\windows\GUIWindowPVR.h" />
++ <ClInclude Include="..\..\xbmc\pvr\windows\GUIWindowPVRChannels.h" />
++ <ClInclude Include="..\..\xbmc\pvr\windows\GUIWindowPVRCommon.h" />
++ <ClInclude Include="..\..\xbmc\pvr\windows\GUIWindowPVRGuide.h" />
++ <ClInclude Include="..\..\xbmc\pvr\windows\GUIWindowPVRRecordings.h" />
++ <ClInclude Include="..\..\xbmc\pvr\windows\GUIWindowPVRSearch.h" />
++ <ClInclude Include="..\..\xbmc\pvr\windows\GUIWindowPVRTimers.h" />
+ <ClInclude Include="..\..\xbmc\rendering\dx\GUIWindowTestPatternDX.h" />
+ <ClInclude Include="..\..\xbmc\rendering\dx\RenderSystemDX.h" />
+ <ClInclude Include="..\..\xbmc\rendering\gl\GUIWindowTestPatternGL.h" />
+@@ -1674,6 +1794,7 @@
+ <ClInclude Include="..\..\xbmc\utils\log.h" />
+ <ClInclude Include="..\..\xbmc\utils\MathUtils.h" />
+ <ClInclude Include="..\..\xbmc\utils\md5.h" />
++ <ClInclude Include="..\..\xbmc\utils\Observer.h" />
+ <ClInclude Include="..\..\xbmc\utils\PCMAmplifier.h" />
+ <ClInclude Include="..\..\xbmc\utils\PerformanceSample.h" />
+ <ClInclude Include="..\..\xbmc\utils\PerformanceStats.h" />
+@@ -1692,6 +1813,7 @@
+ <ClInclude Include="..\..\xbmc\utils\StreamUtils.h" />
+ <ClInclude Include="..\..\xbmc\utils\StringUtils.h" />
+ <ClInclude Include="..\..\xbmc\utils\SystemInfo.h" />
++ <ClInclude Include="..\..\xbmc\utils\TextSearch.h" />
+ <ClInclude Include="..\..\xbmc\utils\TimeSmoother.h" />
+ <ClInclude Include="..\..\xbmc\utils\TimeUtils.h" />
+ <ClInclude Include="..\..\xbmc\utils\TuxBoxUtil.h" />
+diff --git a/project/VS2010Express/XBMC.vcxproj.filters b/project/VS2010Express/XBMC.vcxproj.filters
+index 62a6f25..e340659 100644
+--- a/project/VS2010Express/XBMC.vcxproj.filters
++++ b/project/VS2010Express/XBMC.vcxproj.filters
+@@ -226,6 +226,30 @@
+ <Filter Include="guilib\Rendering\GL">
+ <UniqueIdentifier>{9592a005-d33b-48d1-8462-d95c42c383c2}</UniqueIdentifier>
+ </Filter>
++ <Filter Include="pvr">
++ <UniqueIdentifier>{5d07f015-2e93-4085-bda3-2be566fb07a8}</UniqueIdentifier>
++ </Filter>
++ <Filter Include="pvr\dialogs">
++ <UniqueIdentifier>{5fb53298-e501-4f23-bf84-fc61acf9b814}</UniqueIdentifier>
++ </Filter>
++ <Filter Include="epg">
++ <UniqueIdentifier>{ef8e21c9-b588-4255-ba38-57c6ae82d0aa}</UniqueIdentifier>
++ </Filter>
++ <Filter Include="pvr\windows">
++ <UniqueIdentifier>{43455925-2158-4eff-97ce-1fa3f6597a3a}</UniqueIdentifier>
++ </Filter>
++ <Filter Include="pvr\timers">
++ <UniqueIdentifier>{14af7c50-6457-48ec-87b2-4efb3986bdd8}</UniqueIdentifier>
++ </Filter>
++ <Filter Include="pvr\recordings">
++ <UniqueIdentifier>{eab084ef-b5b5-4a61-b2a5-eac88bbcc73a}</UniqueIdentifier>
++ </Filter>
++ <Filter Include="pvr\channels">
++ <UniqueIdentifier>{7be58f63-0e53-4a26-9894-e52c2bd78709}</UniqueIdentifier>
++ </Filter>
++ <Filter Include="pvr\addons">
++ <UniqueIdentifier>{dbfd4898-7df3-4393-8b04-ab0cc1265c33}</UniqueIdentifier>
++ </Filter>
+ <Filter Include="libs\SlingboxLib">
+ <UniqueIdentifier>{dfa70c36-927b-4540-b505-35919e64eb3d}</UniqueIdentifier>
+ </Filter>
+@@ -2408,6 +2432,39 @@
+ <ClCompile Include="..\..\xbmc\addons\AddonInstaller.cpp">
+ <Filter>addons</Filter>
+ </ClCompile>
++ <ClCompile Include="..\..\xbmc\epg\Epg.cpp">
++ <Filter>epg</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\epg\EpgContainer.cpp">
++ <Filter>epg</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\epg\EpgDatabase.cpp">
++ <Filter>epg</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\epg\EpgInfoTag.cpp">
++ <Filter>epg</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\epg\EpgSearchFilter.cpp">
++ <Filter>epg</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\filesystem\PVRDirectory.cpp">
++ <Filter>filesystem</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\filesystem\PVRFile.cpp">
++ <Filter>filesystem</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\utils\Observer.cpp">
++ <Filter>utils</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDInputStreams\DVDInputStreamPVRManager.cpp">
++ <Filter>cores\dvdplayer\DVDInputStreams</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDDemuxers\DVDDemuxPVRClient.cpp">
++ <Filter>cores\dvdplayer\DVDDemuxers</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\utils\TextSearch.cpp">
++ <Filter>utils</Filter>
++ </ClCompile>
+ <ClCompile Include="..\..\xbmc\utils\GLUtils.cpp">
+ <Filter>utils</Filter>
+ </ClCompile>
+@@ -2417,12 +2474,120 @@
+ <ClCompile Include="..\..\xbmc\win32\stat_utf8.cpp">
+ <Filter>win32</Filter>
+ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\windows\GUIWindowPVR.cpp">
++ <Filter>pvr\windows</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\timers\PVRTimerInfoTag.cpp">
++ <Filter>pvr\timers</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\timers\PVRTimers.cpp">
++ <Filter>pvr\timers</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\recordings\PVRRecording.cpp">
++ <Filter>pvr\recordings</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\recordings\PVRRecordings.cpp">
++ <Filter>pvr\recordings</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRChannelManager.cpp">
++ <Filter>pvr\dialogs</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRChannelsOSD.cpp">
++ <Filter>pvr\dialogs</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRCutterOSD.cpp">
++ <Filter>pvr\dialogs</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRDirectorOSD.cpp">
++ <Filter>pvr\dialogs</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGroupManager.cpp">
++ <Filter>pvr\dialogs</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGuideInfo.cpp">
++ <Filter>pvr\dialogs</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGuideOSD.cpp">
++ <Filter>pvr\dialogs</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGuideSearch.cpp">
++ <Filter>pvr\dialogs</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRRecordingInfo.cpp">
++ <Filter>pvr\dialogs</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRTimerSettings.cpp">
++ <Filter>pvr\dialogs</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\channels\PVRChannel.cpp">
++ <Filter>pvr\channels</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\channels\PVRChannelGroup.cpp">
++ <Filter>pvr\channels</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\channels\PVRChannelGroupInternal.cpp">
++ <Filter>pvr\channels</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\channels\PVRChannelGroups.cpp">
++ <Filter>pvr\channels</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\channels\PVRChannelGroupsContainer.cpp">
++ <Filter>pvr\channels</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\addons\PVRClient.cpp">
++ <Filter>pvr\addons</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\dialogs\GUIDialogExtendedProgressBar.cpp">
++ <Filter>dialogs</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\PVRDatabase.cpp">
++ <Filter>pvr</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\PVRManager.cpp">
++ <Filter>pvr</Filter>
++ </ClCompile>
+ <ClCompile Include="..\..\xbmc\interfaces\python\xbmcmodule\xbmcvfsmodule.cpp">
+ <Filter>interfaces\python\xbmcmodule</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\xbmc\cores\VideoRenderers\RenderCapture.cpp">
+ <Filter>cores\VideoRenderers</Filter>
+ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\windows\GUIViewStatePVR.cpp">
++ <Filter>pvr\windows</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\windows\GUIWindowPVRChannels.cpp">
++ <Filter>pvr\windows</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\windows\GUIWindowPVRCommon.cpp">
++ <Filter>pvr\windows</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\windows\GUIWindowPVRGuide.cpp">
++ <Filter>pvr\windows</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\windows\GUIWindowPVRRecordings.cpp">
++ <Filter>pvr\windows</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\windows\GUIWindowPVRSearch.cpp">
++ <Filter>pvr\windows</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\windows\GUIWindowPVRTimers.cpp">
++ <Filter>pvr\windows</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\addons\PVRClients.cpp">
++ <Filter>pvr\addons</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\addons\AddonCallbacks.cpp">
++ <Filter>addons</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\addons\AddonCallbacksAddon.cpp">
++ <Filter>addons</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\addons\AddonCallbacksGUI.cpp">
++ <Filter>addons</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\xbmc\addons\AddonCallbacksPVR.cpp">
++ <Filter>addons</Filter>
++ </ClCompile>
+ <ClCompile Include="..\..\lib\SlingboxLib\SlingboxLib.cpp">
+ <Filter>libs\SlingboxLib</Filter>
+ </ClCompile>
+@@ -2432,6 +2597,9 @@
+ <ClCompile Include="..\..\xbmc\dialogs\GUIDialogPlayEject.cpp">
+ <Filter>dialogs</Filter>
+ </ClCompile>
++ <ClCompile Include="..\..\xbmc\pvr\PVRGUIInfo.cpp">
++ <Filter>pvr</Filter>
++ </ClCompile>
+ <ClCompile Include="..\..\xbmc\interfaces\json-rpc\JSONServiceDescription.cpp">
+ <Filter>interfaces\json-rpc</Filter>
+ </ClCompile>
+@@ -2451,6 +2619,9 @@
+ <ClCompile Include="..\..\xbmc\filesystem\CacheCircular.cpp">
+ <Filter>filesystem</Filter>
+ </ClCompile>
++ <ClCompile Include="..\..\xbmc\epg\GUIEPGGridContainer.cpp">
++ <Filter>epg</Filter>
++ </ClCompile>
+ <ClCompile Include="..\..\xbmc\input\XBMC_keytable.cpp">
+ <Filter>input</Filter>
+ </ClCompile>
+@@ -2565,9 +2736,15 @@
+ <ClCompile Include="..\..\xbmc\filesystem\FileUPnP.cpp">
+ <Filter>filesystem</Filter>
+ </ClCompile>
++ <ClCompile Include="..\..\xbmc\interfaces\json-rpc\PVROperations.cpp">
++ <Filter>interfaces\json-rpc</Filter>
++ </ClCompile>
++<<<<<<< HEAD
+ <ClCompile Include="..\..\xbmc\cores\paplayer\PCMCodec.cpp">
+ <Filter>cores\paplayer</Filter>
+ </ClCompile>
++=======
++>>>>>>> FIX: VS compile errors.
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\xbmc\win32\pch.h">
+@@ -4960,8 +5137,41 @@
+ <ClInclude Include="..\..\xbmc\addons\AddonInstaller.h">
+ <Filter>addons</Filter>
+ </ClInclude>
+- <ClInclude Include="..\..\lib\ffmpeg\include-xbmc-win32\libavutil\avconfig.h">
+- <Filter>cores\dvdplayer\DVDHeaders</Filter>
++ <ClInclude Include="..\..\xbmc\epg\EpgSearchFilter.h">
++ <Filter>epg</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\epg\Epg.h">
++ <Filter>epg</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\epg\EpgContainer.h">
++ <Filter>epg</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\epg\EpgDatabase.h">
++ <Filter>epg</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\epg\EpgInfoTag.h">
++ <Filter>epg</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\filesystem\PVRFile.h">
++ <Filter>filesystem</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\filesystem\PVRDirectory.h">
++ <Filter>filesystem</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\addons\DllPVRClient.h">
++ <Filter>addons</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\utils\Observer.h">
++ <Filter>utils</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDInputStreams\DVDInputStreamPVRManager.h">
++ <Filter>cores\dvdplayer\DVDInputStreams</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDDemuxers\DVDDemuxPVRClient.h">
++ <Filter>cores\dvdplayer\DVDDemuxers</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\utils\TextSearch.h">
++ <Filter>utils</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\utils\GLUtils.h">
+ <Filter>utils</Filter>
+@@ -4972,12 +5182,121 @@
+ <ClInclude Include="..\..\xbmc\win32\stat_utf8.h">
+ <Filter>win32</Filter>
+ </ClInclude>
++ <ClInclude Include="..\..\lib\ffmpeg\include-xbmc-win32\libavutil\avconfig.h" />
++ <ClInclude Include="..\..\xbmc\pvr\windows\GUIWindowPVR.h">
++ <Filter>pvr\windows</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\timers\PVRTimers.h">
++ <Filter>pvr\timers</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\timers\PVRTimerInfoTag.h">
++ <Filter>pvr\timers</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\recordings\PVRRecordings.h">
++ <Filter>pvr\recordings</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\recordings\PVRRecording.h">
++ <Filter>pvr\recordings</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRTimerSettings.h">
++ <Filter>pvr\dialogs</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRChannelManager.h">
++ <Filter>pvr\dialogs</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRChannelsOSD.h">
++ <Filter>pvr\dialogs</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRCutterOSD.h">
++ <Filter>pvr\dialogs</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRDirectorOSD.h">
++ <Filter>pvr\dialogs</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGroupManager.h">
++ <Filter>pvr\dialogs</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGuideInfo.h">
++ <Filter>pvr\dialogs</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGuideOSD.h">
++ <Filter>pvr\dialogs</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGuideSearch.h">
++ <Filter>pvr\dialogs</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRRecordingInfo.h">
++ <Filter>pvr\dialogs</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\channels\PVRChannelGroupsContainer.h">
++ <Filter>pvr\channels</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\channels\PVRChannel.h">
++ <Filter>pvr\channels</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\channels\PVRChannelGroup.h">
++ <Filter>pvr\channels</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\channels\PVRChannelGroupInternal.h">
++ <Filter>pvr\channels</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\channels\PVRChannelGroups.h">
++ <Filter>pvr\channels</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\addons\PVRClient.h">
++ <Filter>pvr\addons</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\dialogs\GUIDialogExtendedProgressBar.h">
++ <Filter>dialogs</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\PVRDatabase.h">
++ <Filter>pvr</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\PVRManager.h">
++ <Filter>pvr</Filter>
++ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\VideoRenderers\RenderCapture.h">
+ <Filter>cores\VideoRenderers</Filter>
+ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\windows\GUIWindowPVRTimers.h">
++ <Filter>pvr\windows</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\windows\GUIViewStatePVR.h">
++ <Filter>pvr\windows</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\windows\GUIWindowPVRChannels.h">
++ <Filter>pvr\windows</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\windows\GUIWindowPVRCommon.h">
++ <Filter>pvr\windows</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\windows\GUIWindowPVRGuide.h">
++ <Filter>pvr\windows</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\windows\GUIWindowPVRRecordings.h">
++ <Filter>pvr\windows</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\windows\GUIWindowPVRSearch.h">
++ <Filter>pvr\windows</Filter>
++ </ClInclude>
+ <ClInclude Include="..\..\xbmc\utils\GlobalsHandling.h">
+ <Filter>utils</Filter>
+ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\addons\PVRClients.h">
++ <Filter>pvr\addons</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\addons\AddonCallbacks.h">
++ <Filter>addons</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\addons\AddonCallbacksAddon.h">
++ <Filter>addons</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\addons\AddonCallbacksGUI.h">
++ <Filter>addons</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\xbmc\addons\AddonCallbacksPVR.h">
++ <Filter>addons</Filter>
++ </ClInclude>
+ <ClInclude Include="..\..\lib\SlingboxLib\SlingboxLib.h">
+ <Filter>libs\SlingboxLib</Filter>
+ </ClInclude>
+@@ -4990,6 +5309,9 @@
+ <ClInclude Include="..\..\xbmc\dialogs\GUIDialogPlayEject.h">
+ <Filter>dialogs</Filter>
+ </ClInclude>
++ <ClInclude Include="..\..\xbmc\pvr\PVRGUIInfo.h">
++ <Filter>pvr</Filter>
++ </ClInclude>
+ <ClInclude Include="..\..\xbmc\interfaces\json-rpc\JSONServiceDescription.h">
+ <Filter>interfaces\json-rpc</Filter>
+ </ClInclude>
+@@ -5003,6 +5325,9 @@
+ <ClInclude Include="..\..\xbmc\interfaces\json-rpc\InputOperations.h">
+ <Filter>interfaces\json-rpc</Filter>
+ </ClInclude>
++ <ClInclude Include="..\..\xbmc\epg\GUIEPGGridContainer.h">
++ <Filter>epg</Filter>
++ </ClInclude>
+ <ClInclude Include="..\..\xbmc\input\XBMC_keytable.h">
+ <Filter>input</Filter>
+ </ClInclude>
+@@ -5153,6 +5478,10 @@
+ <ClInclude Include="..\..\xbmc\filesystem\FileUPnP.h">
+ <Filter>filesystem</Filter>
+ </ClInclude>
++ <ClInclude Include="..\..\xbmc\interfaces\json-rpc\PVROperations.h">
++ <Filter>interfaces\json-rpc</Filter>
++ </ClInclude>
++<<<<<<< HEAD
+ <ClInclude Include="..\..\xbmc\cores\AudioRenderers\IAudioRenderer.h" />
+ <ClInclude Include="..\..\xbmc\interfaces\python\xbmcmodule\pythreadstate.h">
+ <Filter>interfaces\python\xbmcmodule</Filter>
+@@ -5160,6 +5489,8 @@
+ <ClInclude Include="..\..\xbmc\cores\paplayer\PCMCodec.h">
+ <Filter>cores\paplayer</Filter>
+ </ClInclude>
++=======
++>>>>>>> FIX: VS compile errors.
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="..\..\xbmc\win32\XBMC_PC.rc">
+@@ -5171,4 +5502,4 @@
+ <Filter>win32</Filter>
+ </CustomBuild>
+ </ItemGroup>
+-</Project>
+\ No newline at end of file
++</Project>
+diff --git a/system/PVRDemoAddonSettings.xml b/system/PVRDemoAddonSettings.xml
+new file mode 100644
+index 0000000..008930a
+--- /dev/null
++++ b/system/PVRDemoAddonSettings.xml
+@@ -0,0 +1,460 @@
++<demo>
++ <channels>
++ <channel>
++ <name>Demo TV Channel 1</name>
++ <radio>0</radio>
++ <number>1</number>
++ <encryption>0</encryption>
++ <icon></icon>
++ <stream></stream>
++ </channel>
++ <channel>
++ <name>Demo TV Channel 2</name>
++ <radio>0</radio>
++ <number>2</number>
++ <encryption>0</encryption>
++ <icon></icon>
++ <stream></stream>
++ </channel>
++ <channel>
++ <name>Demo TV Channel 3</name>
++ <radio>0</radio>
++ <number>3</number>
++ <encryption>0</encryption>
++ <icon></icon>
++ <stream></stream>
++ </channel>
++ <channel>
++ <name>Demo TV Channel 4</name>
++ <radio>0</radio>
++ <number>4</number>
++ <encryption>0</encryption>
++ <icon></icon>
++ <stream></stream>
++ </channel>
++ <channel>
++ <name>Demo TV Channel 5</name>
++ <radio>0</radio>
++ <number>5</number>
++ <encryption>0</encryption>
++ <icon></icon>
++ <stream></stream>
++ </channel>
++ <channel>
++ <name>Demo TV Channel 10</name>
++ <radio>0</radio>
++ <number>10</number>
++ <encryption>0</encryption>
++ <icon></icon>
++ <stream></stream>
++ </channel>
++ <channel>
++ <name>Demo TV Channel 11</name>
++ <radio>0</radio>
++ <number>11</number>
++ <encryption>0</encryption>
++ <icon></icon>
++ <stream></stream>
++ </channel>
++ <channel>
++ <name>Demo TV Channel 12</name>
++ <radio>0</radio>
++ <number>12</number>
++ <encryption>0</encryption>
++ <icon></icon>
++ <stream></stream>
++ </channel>
++ <channel>
++ <name>Demo TV Channel 13</name>
++ <radio>0</radio>
++ <number>13</number>
++ <encryption>0</encryption>
++ <icon></icon>
++ <stream></stream>
++ </channel>
++ <channel>
++ <name>Demo TV Channel 14</name>
++ <radio>0</radio>
++ <number>14</number>
++ <encryption>0</encryption>
++ <icon></icon>
++ <stream></stream>
++ </channel>
++ <channel>
++ <name>Demo TV Channel 15</name>
++ <radio>0</radio>
++ <number>15</number>
++ <encryption>0</encryption>
++ <icon></icon>
++ <stream></stream>
++ </channel>
++
++ <channel>
++ <name>Demo Radio Channel #1</name>
++ <radio>1</radio>
++ <number>1</number>
++ <encryption>0</encryption>
++ <icon></icon>
++ <stream></stream>
++ </channel>
++ <channel>
++ <name>Demo Radio Channel #2</name>
++ <radio>1</radio>
++ <number>2</number>
++ <encryption>0</encryption>
++ <icon></icon>
++ <stream></stream>
++ </channel>
++ <channel>
++ <name>Demo Radio Channel #3</name>
++ <radio>1</radio>
++ <number>3</number>
++ <encryption>0</encryption>
++ <icon></icon>
++ <stream></stream>
++ </channel>
++ <channel>
++ <name>Demo Radio Channel #4</name>
++ <radio>1</radio>
++ <number>4</number>
++ <encryption>0</encryption>
++ <icon></icon>
++ <stream></stream>
++ </channel>
++ <channel>
++ <name>Demo Radio Channel #5</name>
++ <radio>1</radio>
++ <number>5</number>
++ <encryption>0</encryption>
++ <icon></icon>
++ <stream></stream>
++ </channel>
++ <channel>
++ <name>Demo Radio Channel #6</name>
++ <radio>1</radio>
++ <number>6</number>
++ <encryption>0</encryption>
++ <icon></icon>
++ <stream></stream>
++ </channel>
++ <channel>
++ <name>Demo Radio Channel #7</name>
++ <radio>1</radio>
++ <number>7</number>
++ <encryption>0</encryption>
++ <icon></icon>
++ <stream></stream>
++ </channel>
++ <channel>
++ <name>Demo Radio Channel #8</name>
++ <radio>1</radio>
++ <number>8</number>
++ <encryption>0</encryption>
++ <icon></icon>
++ <stream></stream>
++ </channel>
++ <channel>
++ <name>Demo Radio Channel #9</name>
++ <radio>1</radio>
++ <number>9</number>
++ <encryption>0</encryption>
++ <icon></icon>
++ <stream></stream>
++ </channel>
++ <channel>
++ <name>Demo Radio Channel #10</name>
++ <radio>1</radio>
++ <number>10</number>
++ <encryption>0</encryption>
++ <icon></icon>
++ <stream></stream>
++ </channel>
++ </channels>
++ <channelgroups>
++ <group>
++ <name>Demo Group #1</name>
++ <radio>0</radio>
++ <members>
++ <member>1</member>
++ <member>2</member>
++ <member>3</member>
++ <member>4</member>
++ <member>5</member>
++ <member>6</member>
++ <member>7</member>
++ <member>8</member>
++ <member>9</member>
++ <member>10</member>
++ <member>11</member>
++ </members>
++ </group>
++ <group>
++ <name>Demo Group #2</name>
++ <radio>0</radio>
++ <members>
++ <member>1</member>
++ <member>3</member>
++ <member>5</member>
++ <member>7</member>
++ <member>9</member>
++ <member>11</member>
++ </members>
++ </group>
++ </channelgroups>
++ <epg>
++ <entry>
++ <broadcastid>100</broadcastid>
++ <title>Demo EPG entry #1</title>
++ <channelid>1</channelid>
++ <start>0</start>
++ <end>7200</end>
++ <plotoutline>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed. </plotoutline>
++ <plot>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed. </plot>
++ <icon></icon>
++ <genretype>10</genretype>
++ <genresubtype>0</genresubtype>
++ </entry>
++ <entry>
++ <broadcastid>200</broadcastid>
++ <title>Demo EPG entry #2</title>
++ <channelid>2</channelid>
++ <start>0</start>
++ <end>7200</end>
++ <plotoutline>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed. </plotoutline>
++ <plot>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed. </plot>
++ <icon></icon>
++ <genretype>20</genretype>
++ <genresubtype>0</genresubtype>
++ </entry>
++ <entry>
++ <broadcastid>300</broadcastid>
++ <title>Demo EPG entry #3</title>
++ <channelid>3</channelid>
++ <start>0</start>
++ <end>7200</end>
++ <plotoutline>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plotoutline>
++ <plot>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plot>
++ <icon></icon>
++ <genretype></genretype>
++ <genresubtype>0</genresubtype>
++ </entry>
++ <entry>
++ <broadcastid>400</broadcastid>
++ <title>Demo EPG entry #4</title>
++ <channelid>4</channelid>
++ <start>0</start>
++ <end>7200</end>
++ <plotoutline>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plotoutline>
++ <plot>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plot>
++ <icon></icon>
++ <genretype>30</genretype>
++ <genresubtype>0</genresubtype>
++ </entry>
++ <entry>
++ <broadcastid>500</broadcastid>
++ <title>Demo EPG entry #5</title>
++ <channelid>5</channelid>
++ <start>0</start>
++ <end>7200</end>
++ <plotoutline>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plotoutline>
++ <plot>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plot>
++ <icon></icon>
++ <genretype></genretype>
++ <genresubtype>0</genresubtype>
++ </entry>
++ <entry>
++ <broadcastid>600</broadcastid>
++ <title>Demo EPG entry #6</title>
++ <channelid>6</channelid>
++ <start>0</start>
++ <end>7200</end>
++ <plotoutline>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plotoutline>
++ <plot>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plot>
++ <icon></icon>
++ <genretype>10</genretype>
++ <genresubtype>0</genresubtype>
++ </entry>
++ <entry>
++ <broadcastid>700</broadcastid>
++ <title>Demo EPG entry #7</title>
++ <channelid>7</channelid>
++ <start>0</start>
++ <end>7200</end>
++ <plotoutline>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plotoutline>
++ <plot>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plot>
++ <icon></icon>
++ <genretype>10</genretype>
++ <genresubtype>0</genresubtype>
++ </entry>
++ <entry>
++ <broadcastid>800</broadcastid>
++ <title>Demo EPG entry #8</title>
++ <channelid>8</channelid>
++ <start>0</start>
++ <end>7200</end>
++ <plotoutline>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plotoutline>
++ <plot>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plot>
++ <icon></icon>
++ <genretype>20</genretype>
++ <genresubtype>0</genresubtype>
++ </entry>
++ <entry>
++ <broadcastid>900</broadcastid>
++ <title>Demo EPG entry #9</title>
++ <channelid>9</channelid>
++ <start>0</start>
++ <end>7200</end>
++ <plotoutline>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plotoutline>
++ <plot>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plot>
++ <icon></icon>
++ <genretype>30</genretype>
++ <genresubtype>0</genresubtype>
++ </entry>
++ <entry>
++ <broadcastid>1000</broadcastid>
++ <title>Demo EPG entry #10</title>
++ <channelid>10</channelid>
++ <start>0</start>
++ <end>7200</end>
++ <plotoutline>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plotoutline>
++ <plot>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plot>
++ <icon></icon>
++ <genretype>10</genretype>
++ <genresubtype>0</genresubtype>
++ </entry>
++ <entry>
++ <broadcastid>1100</broadcastid>
++ <title>Demo EPG entry #11</title>
++ <channelid>11</channelid>
++ <start>0</start>
++ <end>7200</end>
++ <plotoutline>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plotoutline>
++ <plot>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plot>
++ <icon></icon>
++ <genretype>20</genretype>
++ <genresubtype>0</genresubtype>
++ </entry>
++
++ <entry>
++ <broadcastid>1200</broadcastid>
++ <title>Demo Radio EPG entry #1</title>
++ <channelid>12</channelid>
++ <start>0</start>
++ <end>7200</end>
++ <plotoutline>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plotoutline>
++ <plot>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plot>
++ <icon></icon>
++ <genretype>30</genretype>
++ <genresubtype>0</genresubtype>
++ </entry>
++ <entry>
++ <broadcastid>1300</broadcastid>
++ <title>Demo Radio EPG entry #2</title>
++ <channelid>13</channelid>
++ <start>0</start>
++ <end>7200</end>
++ <plotoutline>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plotoutline>
++ <plot>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plot>
++ <icon></icon>
++ <genretype>10</genretype>
++ <genresubtype>0</genresubtype>
++ </entry>
++ <entry>
++ <broadcastid>1400</broadcastid>
++ <title>Demo Radio EPG entry #3</title>
++ <channelid>14</channelid>
++ <start>0</start>
++ <end>7200</end>
++ <plotoutline>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plotoutline>
++ <plot>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plot>
++ <icon></icon>
++ <genretype>20</genretype>
++ <genresubtype>0</genresubtype>
++ </entry>
++ <entry>
++ <broadcastid>1500</broadcastid>
++ <title>Demo Radio EPG entry #4</title>
++ <channelid>15</channelid>
++ <start>0</start>
++ <end>7200</end>
++ <plotoutline>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plotoutline>
++ <plot>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plot>
++ <icon></icon>
++ <genretype>30</genretype>
++ <genresubtype>0</genresubtype>
++ </entry>
++ <entry>
++ <broadcastid>1600</broadcastid>
++ <title>Demo Radio EPG entry #5</title>
++ <channelid>16</channelid>
++ <start>0</start>
++ <end>7200</end>
++ <plotoutline>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plotoutline>
++ <plot>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plot>
++ <icon></icon>
++ <genretype>10</genretype>
++ <genresubtype>0</genresubtype>
++ </entry>
++ <entry>
++ <broadcastid>1700</broadcastid>
++ <title>Demo Radio EPG entry #6</title>
++ <channelid>17</channelid>
++ <start>0</start>
++ <end>7200</end>
++ <plotoutline>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plotoutline>
++ <plot>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plot>
++ <icon></icon>
++ <genretype>20</genretype>
++ <genresubtype>0</genresubtype>
++ </entry>
++ <entry>
++ <broadcastid>1800</broadcastid>
++ <title>Demo Radio EPG entry #7</title>
++ <channelid>18</channelid>
++ <start>0</start>
++ <end>7200</end>
++ <plotoutline>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plotoutline>
++ <plot>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plot>
++ <icon></icon>
++ <genretype>30</genretype>
++ <genresubtype>0</genresubtype>
++ </entry>
++ <entry>
++ <broadcastid>1900</broadcastid>
++ <title>Demo Radio EPG entry #8</title>
++ <channelid>19</channelid>
++ <start>0</start>
++ <end>7200</end>
++ <plotoutline>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plotoutline>
++ <plot>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plot>
++ <icon></icon>
++ <genretype>10</genretype>
++ <genresubtype>0</genresubtype>
++ </entry>
++ <entry>
++ <broadcastid>2000</broadcastid>
++ <title>Demo Radio EPG entry #9</title>
++ <channelid>20</channelid>
++ <start>0</start>
++ <end>7200</end>
++ <plotoutline>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plotoutline>
++ <plot>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plot>
++ <icon></icon>
++ <genretype>20</genretype>
++ <genresubtype>0</genresubtype>
++ </entry>
++ <entry>
++ <broadcastid>2100</broadcastid>
++ <title>Demo Radio EPG entry #10</title>
++ <channelid>21</channelid>
++ <start>0</start>
++ <end>7200</end>
++ <plotoutline>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plotoutline>
++ <plot>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam cursus consectetur ipsum, eu tincidunt dui aliquam ac. Sed scelerisque, augue eu lacinia ultrices, libero ante ullamcorper augue, vel malesuada justo risus ac nulla. Quisque ac libero libero. Sed tincidunt, orci eu condimentum laoreet, felis odio mattis est, et lacinia metus enim in leo. Fusce faucibus tristique risus in varius. Etiam sagittis venenatis ligula nec rutrum. Etiam gravida dictum hendrerit. Sed sodales felis in sapien rutrum non malesuada nisi lobortis. Mauris iaculis ante odio. Nunc gravida erat convallis purus dignissim et ultricies orci dapibus. Aliquam erat volutpat. Vestibulum mi felis, malesuada ac tincidunt sit amet, pulvinar nec dolor. Pellentesque vehicula est vulputate mi adipiscing euismod. Donec ac mauris nulla. Nullam suscipit felis eu quam sodales ac bibendum nisi interdum. Curabitur non lectus a ante venenatis semper eget id justo. Ut facilisis, ligula pretium dictum congue, lacus dolor commodo nibh, sit amet sodales sed.</plot>
++ <icon></icon>
++ <genretype>30</genretype>
++ <genresubtype>0</genresubtype>
++ </entry>
++
++ </epg>
++</demo>
+diff --git a/system/keymaps/keyboard.xml b/system/keymaps/keyboard.xml
+index fe15ce2..f35c79c 100644
+--- a/system/keymaps/keyboard.xml
++++ b/system/keymaps/keyboard.xml
+@@ -94,6 +94,12 @@
+ <backslash>ToggleFullScreen</backslash>
+ <home>FirstPage</home>
+ <end>LastPage</end>
++ <!-- PVR windows -->
++ <e>XBMC.ActivateWindowAndFocus(MyPVR, 31,0, 10,0)</e>
++ <h>XBMC.ActivateWindowAndFocus(MyPVR, 32,0, 11,0)</h>
++ <j>XBMC.ActivateWindowAndFocus(MyPVR, 33,0, 12,0)</j>
++ <k>XBMC.ActivateWindowAndFocus(MyPVR, 34,0, 13,0)</k>
++ <b>XBMC.ActivateWindowAndFocus(MyPVR, 35,0, 14,0)</b>
+ <!-- Multimedia keyboard keys -->
+ <browser_back>Back</browser_back>
+ <browser_forward/>
+@@ -159,6 +165,13 @@
+ <backspace>Backspace</backspace>
+ </keyboard>
+ </VirtualKeyboard>
++ <MyTV>
++ <keyboard>
++ <delete>Delete</delete>
++ <m>Move</m>
++ <r>Rename</r>
++ </keyboard>
++ </MyTV>
+ <MyFiles>
+ <keyboard>
+ <space>Highlight</space>
+@@ -220,6 +233,7 @@
+ <down>BigStepBack</down>
+ <a>AudioDelay</a>
+ <escape>Fullscreen</escape>
++ <c>Playlist</c>
+ <v>XBMC.ActivateWindow(Teletext)</v>
+ <up mod="ctrl">SubtitleShiftUp</up>
+ <down mod="ctrl">SubtitleShiftDown</down>
+@@ -267,6 +281,8 @@
+ <o>CodecInfo</o>
+ <l>LockPreset</l>
+ <escape>FullScreen</escape>
++ <g>XBMC.ActivateWindow(PVROSDGuide)</g>
++ <c>XBMC.ActivateWindow(PVROSDChannels)</c>
+ </keyboard>
+ </Visualisation>
+ <MusicOSD>
+@@ -454,6 +470,46 @@
+ <v>Back</v>
+ </keyboard>
+ </Teletext>
++ <Favourites>
++ <keyboard>
++ <backspace>Close</backspace>
++ </keyboard>
++ </Favourites>
++ <NumericInput>
++ <keyboard>
++ <backspace>Close</backspace>
++ </keyboard>
++ </NumericInput>
++ <PVROSDChannels>
++ <keyboard>
++ <backspace>Close</backspace>
++ <escape>Close</escape>
++ <c>Close</c>
++ </keyboard>
++ </PVROSDChannels>
++ <PVROSDGuide>
++ <keyboard>
++ <backspace>Close</backspace>
++ <escape>Close</escape>
++ </keyboard>
++ </PVROSDGuide>
++ <PVROSDDirector>
++ <keyboard>
++ <backspace>Close</backspace>
++ <escape>Close</escape>
++ </keyboard>
++ </PVROSDDirector>
++ <PVROSDCutter>
++ <keyboard>
++ <backspace>Close</backspace>
++ <escape>Close</escape>
++ </keyboard>
++ </PVROSDCutter>
++ <MyTVSettings>
++ <keyboard>
++ <backspace>PreviousMenu</backspace>
++ </keyboard>
++ </MyTVSettings>
+ <FileBrowser>
+ <keyboard>
+ <space>Highlight</space>
+diff --git a/system/keymaps/remote.xml b/system/keymaps/remote.xml
+index 2a9ab3c..4001a3b 100644
+--- a/system/keymaps/remote.xml
++++ b/system/keymaps/remote.xml
+@@ -66,8 +66,13 @@
+ <myvideo>XBMC.ActivateWindow(MyVideos)</myvideo>
+ <mymusic>XBMC.ActivateWindow(MyMusic)</mymusic>
+ <mypictures>XBMC.ActivateWindow(MyPictures)</mypictures>
+- <mytv>XBMC.ActivateWindow(VideoLibrary,TvShows)</mytv>
+- <red>XBMC.ActivateWindow(Home)</red>
++ <mytv>XBMC.ActivateWindow(MyPVR)</mytv>
++ <guide>XBMC.ActivateWindowAndFocus(MyPVR, 31,0, 10,0)</guide>
++ <livetv>XBMC.ActivateWindowAndFocus(MyPVR, 32,0, 11,0)</livetv>
++ <liveradio>XBMC.ActivateWindowAndFocus(MyPVR, 33,0, 12,0)</liveradio>
++ <recordedtv>XBMC.ActivateWindowAndFocus(MyPVR, 34,0, 13,0)</recordedtv>
++ <epgsearch>XBMC.ActivateWindowAndFocus(MyPVR, 35,0, 14,0)</epgsearch>
++ <red>XBMC.ActivateWindow(MyPVR)</red>
+ <green>XBMC.ActivateWindow(MyVideos)</green>
+ <yellow>XBMC.ActivateWindow(MyMusic)</yellow>
+ <blue>XBMC.ActivateWindow(MyPictures)</blue>
+@@ -90,6 +95,11 @@
+ <hash>XBMC.ActivateWindow(Settings)</hash>
+ </remote>
+ </Home>
++ <MyTV>
++ <remote>
++ <clear>Delete</clear>
++ </remote>
++ </MyTV>
+ <MyFiles>
+ <remote>
+ <clear>Delete</clear>
+@@ -143,10 +153,13 @@
+ <select>AspectRatio</select>
+ <title>CodecInfo</title>
+ <info>Info</info>
++ <guide>XBMC.ActivateWindow(PVROSDGuide)</guide>
+ <teletext>XBMC.ActivateWindow(Teletext)</teletext>
+ <subtitle>NextSubtitle</subtitle>
+ <star>NextSubtitle</star>
+ <language>AudioNextLanguage</language>
++ <playlist>Playlist</playlist>
++ <language>Language</language>
+ <hash>AudioNextLanguage</hash>
+ </remote>
+ </FullscreenVideo>
+@@ -180,6 +193,8 @@
+ <menu>XBMC.ActivateWindow(MusicOSD)</menu>
+ <start>XBMC.ActivateWindow(MusicOSD)</start>
+ <info>Info</info>
++ <guide>XBMC.ActivateWindow(PVROSDGuide)</guide>
++ <playlist>XBMC.ActivateWindow(PVROSDChannels)</playlist>
+ </remote>
+ </Visualisation>
+ <MusicOSD>
+@@ -339,6 +354,103 @@
+ <back>BackSpace</back>
+ </remote>
+ </NumericInput>
++ <Weather>
++ <remote>
++ <back>PreviousMenu</back>
++ </remote>
++ </Weather>
++ <TV>
++ <remote>
++ <red>Red</red>
++ <green>Green</green>
++ <yellow>Yellow</yellow>
++ <blue>Blue</blue>
++ </remote>
++ </TV>
++ <Settings>
++ <remote>
++ <back>PreviousMenu</back>
++ </remote>
++ </Settings>
++ <AddonBrowser>
++ <remote>
++ </remote>
++ </AddonBrowser>
++ <AddonInformation>
++ <remote>
++ <back>Close</back>
++ </remote>
++ </AddonInformation>
++ <AddonSettings>
++ <remote>
++ <back>Close</back>
++ </remote>
++ </AddonSettings>
++ <TextViewer>
++ <remote>
++ <back>Close</back>
++ </remote>
++ </TextViewer>
++ <MyPicturesSettings>
++ <remote>
++ <back>PreviousMenu</back>
++ </remote>
++ </MyPicturesSettings>
++ <MyProgramsSettings>
++ <remote>
++ <back>PreviousMenu</back>
++ </remote>
++ </MyProgramsSettings>
++ <MyWeatherSettings>
++ <remote>
++ <back>PreviousMenu</back>
++ </remote>
++ </MyWeatherSettings>
++ <MyMusicSettings>
++ <remote>
++ <back>PreviousMenu</back>
++ </remote>
++ </MyMusicSettings>
++ <SystemSettings>
++ <remote>
++ <back>PreviousMenu</back>
++ </remote>
++ </SystemSettings>
++ <MyVideosSettings>
++ <remote>
++ <back>PreviousMenu</back>
++ </remote>
++ </MyVideosSettings>
++ <NetworkSettings>
++ <remote>
++ <back>PreviousMenu</back>
++ </remote>
++ </NetworkSettings>
++ <AppearanceSettings>
++ <remote>
++ <back>PreviousMenu</back>
++ </remote>
++ </AppearanceSettings>
++ <Profiles>
++ <remote>
++ <back>PreviousMenu</back>
++ </remote>
++ </Profiles>
++ <systeminfo>
++ <remote>
++ <back>PreviousMenu</back>
++ </remote>
++ </systeminfo>
++ <shutdownmenu>
++ <remote>
++ <back>PreviousMenu</back>
++ </remote>
++ </shutdownmenu>
++ <submenu>
++ <remote>
++ <back>PreviousMenu</back>
++ </remote>
++ </submenu>
+ <MusicInformation>
+ <remote>
+ <info>Back</info>
+@@ -388,6 +500,44 @@
+ <teletext>Back</teletext>
+ </remote>
+ </Teletext>
++ <Favourites>
++ <remote>
++ <back>Close</back>
++ </remote>
++ </Favourites>
++ <PVROSDChannels>
++ <remote>
++ <back>Close</back>
++ <menu>Close</menu>
++ <start>Close</start>
++ <playlist>Close</playlist>
++ </remote>
++ </PVROSDChannels>
++ <PVROSDGuide>
++ <remote>
++ <back>Close</back>
++ <menu>Close</menu>
++ <start>Close</start>
++ <guide>Close</guide>
++ </remote>
++ </PVROSDGuide>
++ <PVROSDDirector>
++ <remote>
++ <back>Close</back>
++ <menu>Close</menu>
++ </remote>
++ </PVROSDDirector>
++ <PVROSDCutter>
++ <remote>
++ <back>Close</back>
++ <menu>Close</menu>
++ </remote>
++ </PVROSDCutter>
++ <MyTVSettings>
++ <remote>
++ <back>PreviousMenu</back>
++ </remote>
++ </MyTVSettings>
+ <AddonSettings>
+ <remote>
+ <clear>Delete</clear>
+diff --git a/system/playercorefactory.xml b/system/playercorefactory.xml
+index 66784de..6fc8c20 100644
+--- a/system/playercorefactory.xml
++++ b/system/playercorefactory.xml
+@@ -33,5 +33,8 @@
+
+ <!-- Pass these to dvdplayer as we do not know if they are audio or video -->
+ <rule name="nsv" filetypes="nsv" player="DVDPlayer" />
++
++ <!-- pvr radio channels should be played by dvdplayer because they need buffering -->
++ <rule name="radio" filetypes="pvr" filename=".*/radio/.*" player="DVDPlayer" />
+ </rules>
+ </playercorefactory>
+diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp
+index df7de6e..d170340 100644
+--- a/xbmc/Application.cpp
++++ b/xbmc/Application.cpp
+@@ -225,6 +225,7 @@
+ #include "dialogs/GUIDialogYesNo.h"
+ #include "dialogs/GUIDialogOK.h"
+ #include "dialogs/GUIDialogProgress.h"
++#include "dialogs/GUIDialogExtendedProgressBar.h"
+ #include "dialogs/GUIDialogSelect.h"
+ #include "dialogs/GUIDialogSeekBar.h"
+ #include "dialogs/GUIDialogKaiToast.h"
+@@ -248,6 +249,24 @@
+ #ifdef HAS_LINUX_NETWORK
+ #include "network/GUIDialogAccessPoints.h"
+ #endif
++
++/* PVR related include Files */
++#include "pvr/PVRManager.h"
++#include "pvr/timers/PVRTimers.h"
++#include "pvr/windows/GUIWindowPVR.h"
++#include "pvr/dialogs/GUIDialogPVRChannelManager.h"
++#include "pvr/dialogs/GUIDialogPVRChannelsOSD.h"
++#include "pvr/dialogs/GUIDialogPVRCutterOSD.h"
++#include "pvr/dialogs/GUIDialogPVRDirectorOSD.h"
++#include "pvr/dialogs/GUIDialogPVRGroupManager.h"
++#include "pvr/dialogs/GUIDialogPVRGuideInfo.h"
++#include "pvr/dialogs/GUIDialogPVRGuideOSD.h"
++#include "pvr/dialogs/GUIDialogPVRGuideSearch.h"
++#include "pvr/dialogs/GUIDialogPVRRecordingInfo.h"
++#include "pvr/dialogs/GUIDialogPVRTimerSettings.h"
++
++#include "epg/EpgContainer.h"
++
+ #include "video/dialogs/GUIDialogFullScreenInfo.h"
+ #include "video/dialogs/GUIDialogTeletext.h"
+ #include "dialogs/GUIDialogSlider.h"
+@@ -323,6 +342,8 @@ using namespace EVENTSERVER;
+ using namespace JSONRPC;
+ #endif
+ using namespace ANNOUNCEMENT;
++using namespace PVR;
++using namespace EPG;
+ using namespace PERIPHERALS;
+
+ using namespace XbmcThreads;
+@@ -344,6 +365,7 @@ CApplication::CApplication(void)
+ , m_progressTrackingItem(new CFileItem)
+ {
+ m_iPlaySpeed = 1;
++ m_bInhibitIdleShutdown = false;
+ m_bScreenSave = false;
+ m_dpms = NULL;
+ m_dpmsIsActive = false;
+@@ -1089,7 +1111,6 @@ bool CApplication::Initialize()
+ #ifdef HAS_DX
+ g_windowManager.Add(new CGUIWindowTestPatternDX); // window id = 8
+ #endif
+- g_windowManager.Add(new CGUIDialogTeletext); // window id =
+ g_windowManager.Add(new CGUIWindowSettingsScreenCalibration); // window id = 11
+ g_windowManager.Add(new CGUIWindowSettingsCategory); // window id = 12 slideshow:window id 2007
+ g_windowManager.Add(new CGUIWindowVideoNav); // window id = 36
+@@ -1102,6 +1123,7 @@ bool CApplication::Initialize()
+ g_windowManager.Add(new CGUIWindowPointer); // window id = 99
+ g_windowManager.Add(new CGUIDialogYesNo); // window id = 100
+ g_windowManager.Add(new CGUIDialogProgress); // window id = 101
++ g_windowManager.Add(new CGUIDialogExtendedProgressBar); // window id = 148
+ g_windowManager.Add(new CGUIDialogKeyboard); // window id = 103
+ g_windowManager.Add(new CGUIDialogVolumeBar); // window id = 104
+ g_windowManager.Add(new CGUIDialogSeekBar); // window id = 115
+@@ -1140,7 +1162,6 @@ bool CApplication::Initialize()
+ #ifdef HAS_LINUX_NETWORK
+ g_windowManager.Add(new CGUIDialogAccessPoints); // window id = 141
+ #endif
+-
+ g_windowManager.Add(new CGUIDialogLockSettings); // window id = 131
+
+ g_windowManager.Add(new CGUIDialogContentSettings); // window id = 132
+@@ -1155,6 +1176,20 @@ bool CApplication::Initialize()
+ g_windowManager.Add(new CGUIWindowMusicNav); // window id = 502
+ g_windowManager.Add(new CGUIWindowMusicPlaylistEditor); // window id = 503
+
++ /* Load PVR related Windows and Dialogs */
++ g_windowManager.Add(new CGUIWindowPVR); // window id = 600
++ g_windowManager.Add(new CGUIDialogPVRGuideInfo); // window id = 601
++ g_windowManager.Add(new CGUIDialogPVRRecordingInfo); // window id = 602
++ g_windowManager.Add(new CGUIDialogPVRTimerSettings); // window id = 603
++ g_windowManager.Add(new CGUIDialogPVRGroupManager); // window id = 604
++ g_windowManager.Add(new CGUIDialogPVRChannelManager); // window id = 605
++ g_windowManager.Add(new CGUIDialogPVRGuideSearch); // window id = 606
++ g_windowManager.Add(new CGUIDialogPVRChannelsOSD); // window id = 609
++ g_windowManager.Add(new CGUIDialogPVRGuideOSD); // window id = 610
++ g_windowManager.Add(new CGUIDialogPVRDirectorOSD); // window id = 611
++ g_windowManager.Add(new CGUIDialogPVRCutterOSD); // window id = 612
++ g_windowManager.Add(new CGUIDialogTeletext); // window id = 613
++
+ g_windowManager.Add(new CGUIDialogSelect); // window id = 2000
+ g_windowManager.Add(new CGUIDialogMusicInfo); // window id = 2001
+ g_windowManager.Add(new CGUIDialogOK); // window id = 2002
+@@ -1184,6 +1219,9 @@ bool CApplication::Initialize()
+ FatalErrorHandler(true, true, true);
+ }
+
++ StartEPGManager();
++ StartPVRManager();
++
+ if (g_advancedSettings.m_splashImage)
+ SAFE_DELETE(m_splash);
+
+@@ -1556,6 +1594,29 @@ void CApplication::StopZeroconf()
+ #endif
+ }
+
++void CApplication::StartPVRManager()
++{
++ if (g_guiSettings.GetBool("pvrmanager.enabled"))
++ g_PVRManager.Start();
++}
++
++void CApplication::StartEPGManager(void)
++{
++ g_EpgContainer.Start();
++}
++
++void CApplication::StopPVRManager()
++{
++ CLog::Log(LOGINFO, "stopping PVRManager");
++ StopPlaying();
++ g_PVRManager.Stop();
++}
++
++void CApplication::StopEPGManager(void)
++{
++ g_EpgContainer.Stop();
++}
++
+ void CApplication::DimLCDOnPlayback(bool dim)
+ {
+ #ifdef HAS_LCD
+@@ -2476,7 +2537,7 @@ bool CApplication::OnAction(const CAction &action)
+ return true;
+ }
+
+- if ( IsPlaying())
++ if (IsPlaying() && !CurrentFileItem().IsLiveTV())
+ {
+ // pause : pauses current audio song
+ if (action.GetID() == ACTION_PAUSE && m_iPlaySpeed == 1)
+@@ -2556,6 +2617,7 @@ bool CApplication::OnAction(const CAction &action)
+ }
+ }
+ }
++
+ if (action.GetID() == ACTION_MUTE)
+ {
+ ToggleMute();
+@@ -3184,11 +3246,24 @@ bool CApplication::Cleanup()
+ g_windowManager.Delete(WINDOW_DIALOG_ACCESS_POINTS);
+ g_windowManager.Delete(WINDOW_DIALOG_SLIDER);
+
++ /* Delete PVR related windows and dialogs */
++ g_windowManager.Delete(WINDOW_PVR);
++ g_windowManager.Delete(WINDOW_DIALOG_PVR_GUIDE_INFO);
++ g_windowManager.Delete(WINDOW_DIALOG_PVR_RECORDING_INFO);
++ g_windowManager.Delete(WINDOW_DIALOG_PVR_TIMER_SETTING);
++ g_windowManager.Delete(WINDOW_DIALOG_PVR_GROUP_MANAGER);
++ g_windowManager.Delete(WINDOW_DIALOG_PVR_CHANNEL_MANAGER);
++ g_windowManager.Delete(WINDOW_DIALOG_PVR_GUIDE_SEARCH);
++ g_windowManager.Delete(WINDOW_DIALOG_PVR_CHANNEL_SCAN);
++ g_windowManager.Delete(WINDOW_DIALOG_PVR_UPDATE_PROGRESS);
++ g_windowManager.Delete(WINDOW_DIALOG_PVR_OSD_CHANNELS);
++ g_windowManager.Delete(WINDOW_DIALOG_PVR_OSD_GUIDE);
++ g_windowManager.Delete(WINDOW_DIALOG_PVR_OSD_DIRECTOR);
++ g_windowManager.Delete(WINDOW_DIALOG_PVR_OSD_CUTTER);
+ g_windowManager.Delete(WINDOW_DIALOG_OSD_TELETEXT);
+- g_windowManager.Delete(WINDOW_DIALOG_TEXT_VIEWER);
+
++ g_windowManager.Delete(WINDOW_DIALOG_TEXT_VIEWER);
+ g_windowManager.Delete(WINDOW_DIALOG_PLAY_EJECT);
+-
+ g_windowManager.Delete(WINDOW_STARTUP_ANIM);
+ g_windowManager.Delete(WINDOW_LOGIN_SCREEN);
+ g_windowManager.Delete(WINDOW_VISUALISATION);
+@@ -3204,6 +3279,7 @@ bool CApplication::Cleanup()
+ g_windowManager.Delete(WINDOW_DIALOG_MUSIC_OVERLAY);
+ g_windowManager.Delete(WINDOW_DIALOG_VIDEO_OVERLAY);
+ g_windowManager.Delete(WINDOW_SLIDESHOW);
++ g_windowManager.Delete(WINDOW_ADDON_BROWSER);
+
+ g_windowManager.Delete(WINDOW_HOME);
+ g_windowManager.Delete(WINDOW_PROGRAMS);
+@@ -3218,6 +3294,7 @@ bool CApplication::Cleanup()
+ g_windowManager.Remove(WINDOW_SETTINGS_MYVIDEOS);
+ g_windowManager.Remove(WINDOW_SETTINGS_NETWORK);
+ g_windowManager.Remove(WINDOW_SETTINGS_APPEARANCE);
++ g_windowManager.Remove(WINDOW_SETTINGS_MYPVR);
+ g_windowManager.Remove(WINDOW_DIALOG_KAI_TOAST);
+
+ g_windowManager.Remove(WINDOW_DIALOG_SEEK_BAR);
+@@ -3337,6 +3414,8 @@ void CApplication::Stop(int exitCode)
+
+ m_applicationMessenger.Cleanup();
+
++ StopPVRManager();
++ StopEPGManager();
+ StopServices();
+ //Sleep(5000);
+
+@@ -4144,7 +4223,7 @@ bool CApplication::IsPlayingFullScreenVideo() const
+
+ void CApplication::SaveFileState()
+ {
+- if (!g_settings.GetCurrentProfile().canWriteDatabases())
++ if (m_progressTrackingItem->IsPVRChannel() || !g_settings.GetCurrentProfile().canWriteDatabases())
+ return;
+ CJob* job = new CSaveFileStateJob(*m_progressTrackingItem,
+ m_progressTrackingVideoResumeBookmark,
+@@ -4226,6 +4305,9 @@ void CApplication::StopPlaying()
+ m_pKaraokeMgr->Stop();
+ #endif
+
++ if (g_PVRManager.IsPlayingTV() || g_PVRManager.IsPlayingRadio())
++ g_PVRManager.SaveCurrentChannelSettings();
++
+ if (m_pPlayer)
+ m_pPlayer->CloseFile();
+
+@@ -4445,7 +4527,7 @@ void CApplication::ActivateScreenSaver(bool forceType /*= false */)
+ if (!forceType)
+ {
+ // set to Dim in the case of a dialog on screen or playing video
+- if (g_windowManager.HasModalDialog() || (IsPlayingVideo() && g_guiSettings.GetBool("screensaver.usedimonpause")))
++ if (g_windowManager.HasModalDialog() || (IsPlayingVideo() && g_guiSettings.GetBool("screensaver.usedimonpause")) || g_PVRManager.IsRunningChannelScan())
+ {
+ if (!CAddonMgr::Get().GetAddon("screensaver.xbmc.builtin.dim", m_screenSaver))
+ m_screenSaver.reset(new CScreenSaver(""));
+@@ -4487,7 +4569,8 @@ void CApplication::CheckShutdown()
+ CGUIDialogVideoScan *pVideoScan = (CGUIDialogVideoScan *)g_windowManager.GetWindow(WINDOW_DIALOG_VIDEO_SCAN);
+
+ // first check if we should reset the timer
+- bool resetTimer = false;
++ bool resetTimer = m_bInhibitIdleShutdown;
++
+ if (IsPlaying() || IsPaused()) // is something playing?
+ resetTimer = true;
+
+@@ -4500,6 +4583,9 @@ void CApplication::CheckShutdown()
+ if (g_windowManager.IsWindowActive(WINDOW_DIALOG_PROGRESS)) // progress dialog is onscreen
+ resetTimer = true;
+
++ if (g_guiSettings.GetBool("pvrmanager.enabled") && !g_PVRManager.IsIdle())
++ resetTimer = true;
++
+ if (resetTimer)
+ {
+ m_shutdownTimer.StartZero();
+@@ -4516,6 +4602,16 @@ void CApplication::CheckShutdown()
+ }
+ }
+
++void CApplication::InhibitIdleShutdown(bool inhibit)
++{
++ m_bInhibitIdleShutdown = inhibit;
++}
++
++bool CApplication::IsIdleShutdownInhibited() const
++{
++ return m_bInhibitIdleShutdown;
++}
++
+ bool CApplication::OnMessage(CGUIMessage& message)
+ {
+ switch ( message.GetMessage() )
+@@ -5427,7 +5523,8 @@ bool CApplication::ProcessAndStartPlaylist(const CStdString& strPlayList, CPlayL
+
+ void CApplication::SaveCurrentFileSettings()
+ {
+- if (m_itemCurrentFile->IsVideo())
++ // don't store settings for PVR in video database
++ if (m_itemCurrentFile->IsVideo() && !m_itemCurrentFile->IsPVRChannel())
+ {
+ // save video settings
+ if (g_settings.m_currentVideoSettings != g_settings.m_defaultVideoSettings)
+@@ -5438,6 +5535,10 @@ void CApplication::SaveCurrentFileSettings()
+ dbs.Close();
+ }
+ }
++ else if (m_itemCurrentFile->IsPVRChannel())
++ {
++ g_PVRManager.SaveCurrentChannelSettings();
++ }
+ }
+
+ bool CApplication::AlwaysProcess(const CAction& action)
+diff --git a/xbmc/Application.h b/xbmc/Application.h
+index 6e6a005..1be029e 100644
+--- a/xbmc/Application.h
++++ b/xbmc/Application.h
+@@ -108,6 +108,10 @@ public:
+ void StopUPnPRenderer();
+ void StartUPnPServer();
+ void StopUPnPServer();
++ void StartPVRManager();
++ void StartEPGManager(void);
++ void StopPVRManager();
++ void StopEPGManager(void);
+ bool StartEventServer();
+ bool StopEventServer(bool bWait, bool promptuser);
+ void RefreshEventServer();
+@@ -153,6 +157,8 @@ public:
+ bool OnAppCommand(const CAction &action);
+ bool OnAction(const CAction &action);
+ void CheckShutdown();
++ void InhibitIdleShutdown(bool inhibit);
++ bool IsIdleShutdownInhibited() const;
+ // Checks whether the screensaver and / or DPMS should become active.
+ void CheckScreenSaverAndDPMS();
+ void CheckPlayingProgress();
+@@ -311,6 +317,8 @@ protected:
+ CStopWatch m_screenSaverTimer;
+ CStopWatch m_shutdownTimer;
+
++ bool m_bInhibitIdleShutdown;
++
+ DPMSSupport* m_dpms;
+ bool m_dpmsIsActive;
+ bool m_dpmsIsManual;
+diff --git a/xbmc/ApplicationMessenger.cpp b/xbmc/ApplicationMessenger.cpp
+index 74a0a6b..a920952 100644
+--- a/xbmc/ApplicationMessenger.cpp
++++ b/xbmc/ApplicationMessenger.cpp
+@@ -53,6 +53,8 @@
+ #elif defined __APPLE__
+ #include "CocoaInterface.h"
+ #endif
++#include "addons/AddonCallbacks.h"
++#include "addons/AddonCallbacksGUI.h"
+ #include "storage/MediaManager.h"
+ #include "guilib/LocalizeStrings.h"
+ #include "threads/SingleLock.h"
+@@ -244,7 +246,7 @@ void CApplicationMessenger::ProcessMessage(ThreadMessage *pMsg)
+ }
+ break;
+
+-case TMSG_POWERDOWN:
++ case TMSG_POWERDOWN:
+ {
+ g_application.Stop(EXITCODE_POWERDOWN);
+ g_powerManager.Powerdown();
+@@ -286,6 +288,12 @@ case TMSG_POWERDOWN:
+ }
+ break;
+
++ case TMSG_INHIBITIDLESHUTDOWN:
++ {
++ g_application.InhibitIdleShutdown((bool)pMsg->dwParam1);
++ }
++ break;
++
+ case TMSG_MEDIA_PLAY:
+ {
+ // first check if we were called from the PlayFile() function
+@@ -675,6 +683,15 @@ case TMSG_POWERDOWN:
+ }
+ break;
+
++ case TMSG_GUI_ADDON_DIALOG:
++ {
++ if (pMsg->lpVoid)
++ { // TODO: This is ugly - really these python dialogs should just be normal XBMC dialogs
++ ((ADDON::CGUIAddonWindowDialog *) pMsg->lpVoid)->Show_Internal(pMsg->dwParam2 > 0);
++ }
++ }
++ break;
++
+ case TMSG_GUI_PYTHON_DIALOG:
+ {
+ if (pMsg->lpVoid)
+@@ -868,10 +885,10 @@ void CApplicationMessenger::PlayFile(const CFileItem &item, bool bRestart /*= fa
+ SendMessage(tMsg, false);
+ }
+
+-void CApplicationMessenger::MediaStop()
++void CApplicationMessenger::MediaStop(bool bWait /* = true */)
+ {
+ ThreadMessage tMsg = {TMSG_MEDIA_STOP};
+- SendMessage(tMsg, true);
++ SendMessage(tMsg, bWait);
+ }
+
+ void CApplicationMessenger::MediaPause()
+@@ -1076,6 +1093,12 @@ void CApplicationMessenger::RestartApp()
+ SendMessage(tMsg);
+ }
+
++void CApplicationMessenger::InhibitIdleShutdown(bool inhibit)
++{
++ ThreadMessage tMsg = {TMSG_INHIBITIDLESHUTDOWN, (DWORD)inhibit};
++ SendMessage(tMsg);
++}
++
+ void CApplicationMessenger::NetworkMessage(DWORD dwMessage, DWORD dwParam)
+ {
+ ThreadMessage tMsg = {TMSG_NETWORKMESSAGE, dwMessage, dwParam};
+diff --git a/xbmc/ApplicationMessenger.h b/xbmc/ApplicationMessenger.h
+index 393ee4d..87ee93a 100644
+--- a/xbmc/ApplicationMessenger.h
++++ b/xbmc/ApplicationMessenger.h
+@@ -78,6 +78,7 @@ class CGUIMessage;
+ #define TMSG_TOGGLEFULLSCREEN 310
+ #define TMSG_SETLANGUAGE 311
+ #define TMSG_RENDERER_FLUSH 312
++#define TMSG_INHIBITIDLESHUTDOWN 313
+
+ #define TMSG_HTTPAPI 400
+
+@@ -91,7 +92,8 @@ class CGUIMessage;
+ #define TMSG_GUI_ACTION 607
+ #define TMSG_GUI_INFOLABEL 608
+ #define TMSG_GUI_INFOBOOL 609
+-#define TMSG_GUI_MESSAGE 610
++#define TMSG_GUI_ADDON_DIALOG 610
++#define TMSG_GUI_MESSAGE 611
+
+ #define TMSG_CALLBACK 800
+
+@@ -144,7 +146,7 @@ public:
+ void MediaPlay(const CFileItem &item);
+ void MediaPlay(const CFileItemList &item, int song = 0);
+ void MediaPlay(int playlistid, int song = -1);
+- void MediaStop();
++ void MediaStop(bool bWait = true);
+ void MediaPause();
+ void MediaRestart(bool bWait);
+
+@@ -176,6 +178,7 @@ public:
+ void Restart();
+ void RestartApp();
+ void Reset();
++ void InhibitIdleShutdown(bool inhibit);
+ void SwitchToFullscreen(); //
+ void Minimize(bool wait = false);
+ void ExecOS(const CStdString command, bool waitExit = false);
+diff --git a/xbmc/FileItem.cpp b/xbmc/FileItem.cpp
+index 9db1df6..21a10f9 100644
+--- a/xbmc/FileItem.cpp
++++ b/xbmc/FileItem.cpp
+@@ -40,6 +40,11 @@
+ #include "music/MusicDatabase.h"
+ #include "SortFileItem.h"
+ #include "utils/TuxBoxUtil.h"
++#include "epg/Epg.h"
++#include "pvr/channels/PVRChannel.h"
++#include "pvr/recordings/PVRRecording.h"
++#include "pvr/timers/PVRTimerInfoTag.h"
++#include "utils/Observer.h"
+ #include "video/VideoInfoTag.h"
+ #include "threads/SingleLock.h"
+ #include "music/tags/MusicInfoTag.h"
+@@ -61,11 +66,17 @@ using namespace std;
+ using namespace XFILE;
+ using namespace PLAYLIST;
+ using namespace MUSIC_INFO;
++using namespace PVR;
++using namespace EPG;
+
+ CFileItem::CFileItem(const CSong& song)
+ {
+ m_musicInfoTag = NULL;
+ m_videoInfoTag = NULL;
++ m_epgInfoTag = NULL;
++ m_pvrChannelInfoTag = NULL;
++ m_pvrRecordingInfoTag = NULL;
++ m_pvrTimerInfoTag = NULL;
+ m_pictureInfoTag = NULL;
+ Reset();
+ SetLabel(song.strTitle);
+@@ -80,6 +91,10 @@ CFileItem::CFileItem(const CStdString &path, const CAlbum& album)
+ {
+ m_musicInfoTag = NULL;
+ m_videoInfoTag = NULL;
++ m_epgInfoTag = NULL;
++ m_pvrChannelInfoTag = NULL;
++ m_pvrRecordingInfoTag = NULL;
++ m_pvrTimerInfoTag = NULL;
+ m_pictureInfoTag = NULL;
+ Reset();
+ SetLabel(album.strAlbum);
+@@ -100,6 +115,10 @@ CFileItem::CFileItem(const CVideoInfoTag& movie)
+ {
+ m_musicInfoTag = NULL;
+ m_videoInfoTag = NULL;
++ m_epgInfoTag = NULL;
++ m_pvrChannelInfoTag = NULL;
++ m_pvrRecordingInfoTag = NULL;
++ m_pvrTimerInfoTag = NULL;
+ m_pictureInfoTag = NULL;
+ Reset();
+ SetLabel(movie.m_strTitle);
+@@ -120,10 +139,145 @@ CFileItem::CFileItem(const CVideoInfoTag& movie)
+ SetCachedVideoThumb();
+ }
+
++CFileItem::CFileItem(const CEpgInfoTag& tag)
++{
++ m_musicInfoTag = NULL;
++ m_videoInfoTag = NULL;
++ m_epgInfoTag = NULL;
++ m_pvrChannelInfoTag = NULL;
++ m_pvrRecordingInfoTag = NULL;
++ m_pvrTimerInfoTag = NULL;
++ m_pictureInfoTag = NULL;
++
++ Reset();
++
++ m_strPath = tag.Path();
++ m_bIsFolder = false;
++ *GetEPGInfoTag() = tag;
++ SetLabel(tag.Title());
++ m_strLabel2 = tag.Plot();
++ m_dateTime = tag.StartAsLocalTime();
++
++ if (!tag.Icon().IsEmpty())
++ {
++ SetThumbnailImage(tag.Icon());
++ SetIconImage(tag.Icon());
++ }
++}
++
++CFileItem::CFileItem(const CPVRChannel& channel)
++{
++ m_musicInfoTag = NULL;
++ m_videoInfoTag = NULL;
++ m_epgInfoTag = NULL;
++ m_pvrChannelInfoTag = NULL;
++ m_pvrRecordingInfoTag = NULL;
++ m_pvrTimerInfoTag = NULL;
++ m_pictureInfoTag = NULL;
++
++ Reset();
++ CEpgInfoTag epgNow;
++ bool bHasEpgNow = channel.GetEPGNow(epgNow);
++
++ m_strPath = channel.Path();
++ m_bIsFolder = false;
++ *GetPVRChannelInfoTag() = channel;
++ SetLabel(channel.ChannelName());
++ m_strLabel2 = bHasEpgNow ? epgNow.Title() : g_localizeStrings.Get(19055);
++
++ if (channel.IsRadio() && bHasEpgNow)
++ {
++ CMusicInfoTag* musictag = GetMusicInfoTag();
++ if (musictag)
++ {
++ musictag->SetURL(channel.Path());
++ musictag->SetTitle(bHasEpgNow ? epgNow.Title() : g_localizeStrings.Get(19055));
++ musictag->SetArtist(channel.ChannelName());
++ musictag->SetAlbumArtist(channel.ChannelName());
++ musictag->SetGenre(bHasEpgNow ? epgNow.Genre() : "");
++ musictag->SetDuration(bHasEpgNow ? epgNow.GetDuration() : 3600);
++ musictag->SetLoaded(true);
++ musictag->SetComment("");
++ musictag->SetLyrics("");
++ }
++ }
++
++ if (!channel.IconPath().IsEmpty())
++ {
++ SetThumbnailImage(channel.IconPath());
++ SetIconImage(channel.IconPath());
++ }
++
++ SetProperty("channelid", channel.ChannelID());
++ SetProperty("path", channel.Path());
++}
++
++CFileItem::CFileItem(const CPVRRecording& record)
++{
++ m_musicInfoTag = NULL;
++ m_videoInfoTag = NULL;
++ m_epgInfoTag = NULL;
++ m_pvrChannelInfoTag = NULL;
++ m_pvrRecordingInfoTag = NULL;
++ m_pvrTimerInfoTag = NULL;
++ m_pictureInfoTag = NULL;
++
++ Reset();
++
++ m_strPath = record.m_strFileNameAndPath;
++ m_bIsFolder = false;
++ *GetPVRRecordingInfoTag() = record;
++ SetLabel(record.m_strTitle);
++ m_strLabel2 = record.m_strPlot;
++ if ((!record.m_defualt_icon.IsEmpty()) &&
++ ((record.m_defualt_icon.Left(1).CompareNoCase("/") == 0) ||
++ (record.m_defualt_icon.Left(8).CompareNoCase("special:") == 0)))
++ {
++ SetIconImage(record.m_defualt_icon.c_str());
++ }
++ if ((!record.m_fanart_image.IsEmpty()) &&
++ ((record.m_fanart_image.Left(1).CompareNoCase("/") == 0) ||
++ (record.m_fanart_image.Left(8).CompareNoCase("special:") == 0)))
++ {
++ SetProperty("Fanart_Image",record.m_fanart_image.c_str());
++ }
++
++}
++
++CFileItem::CFileItem(const CPVRTimerInfoTag& timer)
++{
++ m_musicInfoTag = NULL;
++ m_videoInfoTag = NULL;
++ m_epgInfoTag = NULL;
++ m_pvrChannelInfoTag = NULL;
++ m_pvrRecordingInfoTag = NULL;
++ m_pvrTimerInfoTag = NULL;
++ m_pictureInfoTag = NULL;
++
++ Reset();
++
++ m_strPath = timer.m_strFileNameAndPath;
++ m_bIsFolder = false;
++ *GetPVRTimerInfoTag() = timer;
++ SetLabel(timer.m_strTitle);
++ m_strLabel2 = timer.m_strSummary;
++ m_dateTime = timer.StartAsLocalTime();
++
++ if (!timer.ChannelIcon().IsEmpty())
++ {
++ SetThumbnailImage(timer.ChannelIcon());
++ SetIconImage(timer.ChannelIcon());
++ }
++}
++
+ CFileItem::CFileItem(const CArtist& artist)
+ {
+ m_musicInfoTag = NULL;
+ m_videoInfoTag = NULL;
++ m_epgInfoTag = NULL;
++ m_pvrChannelInfoTag = NULL;
++ m_pvrRecordingInfoTag = NULL;
++ m_pvrTimerInfoTag = NULL;
+ m_pictureInfoTag = NULL;
+ Reset();
+ SetLabel(artist.strArtist);
+@@ -137,6 +291,10 @@ CFileItem::CFileItem(const CGenre& genre)
+ {
+ m_musicInfoTag = NULL;
+ m_videoInfoTag = NULL;
++ m_epgInfoTag = NULL;
++ m_pvrChannelInfoTag = NULL;
++ m_pvrRecordingInfoTag = NULL;
++ m_pvrTimerInfoTag = NULL;
+ m_pictureInfoTag = NULL;
+ Reset();
+ SetLabel(genre.strGenre);
+@@ -150,6 +308,10 @@ CFileItem::CFileItem(const CFileItem& item): CGUIListItem()
+ {
+ m_musicInfoTag = NULL;
+ m_videoInfoTag = NULL;
++ m_epgInfoTag = NULL;
++ m_pvrChannelInfoTag = NULL;
++ m_pvrRecordingInfoTag = NULL;
++ m_pvrTimerInfoTag = NULL;
+ m_pictureInfoTag = NULL;
+ *this = item;
+ }
+@@ -158,6 +320,10 @@ CFileItem::CFileItem(const CGUIListItem& item)
+ {
+ m_musicInfoTag = NULL;
+ m_videoInfoTag = NULL;
++ m_epgInfoTag = NULL;
++ m_pvrChannelInfoTag = NULL;
++ m_pvrRecordingInfoTag = NULL;
++ m_pvrTimerInfoTag = NULL;
+ m_pictureInfoTag = NULL;
+ Reset();
+ // not particularly pretty, but it gets around the issue of Reset() defaulting
+@@ -169,6 +335,10 @@ CFileItem::CFileItem(void)
+ {
+ m_musicInfoTag = NULL;
+ m_videoInfoTag = NULL;
++ m_epgInfoTag = NULL;
++ m_pvrChannelInfoTag = NULL;
++ m_pvrRecordingInfoTag = NULL;
++ m_pvrTimerInfoTag = NULL;
+ m_pictureInfoTag = NULL;
+ Reset();
+ }
+@@ -178,6 +348,10 @@ CFileItem::CFileItem(const CStdString& strLabel)
+ {
+ m_musicInfoTag = NULL;
+ m_videoInfoTag = NULL;
++ m_epgInfoTag = NULL;
++ m_pvrChannelInfoTag = NULL;
++ m_pvrRecordingInfoTag = NULL;
++ m_pvrTimerInfoTag = NULL;
+ m_pictureInfoTag = NULL;
+ Reset();
+ SetLabel(strLabel);
+@@ -187,6 +361,10 @@ CFileItem::CFileItem(const CStdString& strPath, bool bIsFolder)
+ {
+ m_musicInfoTag = NULL;
+ m_videoInfoTag = NULL;
++ m_epgInfoTag = NULL;
++ m_pvrChannelInfoTag = NULL;
++ m_pvrRecordingInfoTag = NULL;
++ m_pvrTimerInfoTag = NULL;
+ m_pictureInfoTag = NULL;
+ Reset();
+ m_strPath = strPath;
+@@ -200,6 +378,10 @@ CFileItem::CFileItem(const CMediaSource& share)
+ {
+ m_musicInfoTag = NULL;
+ m_videoInfoTag = NULL;
++ m_epgInfoTag = NULL;
++ m_pvrChannelInfoTag = NULL;
++ m_pvrRecordingInfoTag = NULL;
++ m_pvrTimerInfoTag = NULL;
+ m_pictureInfoTag = NULL;
+ Reset();
+ m_bIsFolder = true;
+@@ -225,10 +407,18 @@ CFileItem::~CFileItem(void)
+ {
+ delete m_musicInfoTag;
+ delete m_videoInfoTag;
++ delete m_epgInfoTag;
++ delete m_pvrChannelInfoTag;
++ delete m_pvrRecordingInfoTag;
++ delete m_pvrTimerInfoTag;
+ delete m_pictureInfoTag;
+
+ m_musicInfoTag = NULL;
+ m_videoInfoTag = NULL;
++ m_epgInfoTag = NULL;
++ m_pvrChannelInfoTag = NULL;
++ m_pvrRecordingInfoTag = NULL;
++ m_pvrTimerInfoTag = NULL;
+ m_pictureInfoTag = NULL;
+ }
+
+@@ -268,6 +458,62 @@ const CFileItem& CFileItem::operator=(const CFileItem& item)
+ m_videoInfoTag = NULL;
+ }
+
++ if (item.HasEPGInfoTag())
++ {
++ m_epgInfoTag = GetEPGInfoTag();
++ if (m_epgInfoTag)
++ *m_epgInfoTag = *item.m_epgInfoTag;
++ }
++ else
++ {
++ if (m_epgInfoTag)
++ delete m_epgInfoTag;
++
++ m_epgInfoTag = NULL;
++ }
++
++ if (item.HasPVRChannelInfoTag())
++ {
++ m_pvrChannelInfoTag = GetPVRChannelInfoTag();
++ if (m_pvrChannelInfoTag)
++ *m_pvrChannelInfoTag = *item.m_pvrChannelInfoTag;
++ }
++ else
++ {
++ if (m_pvrChannelInfoTag)
++ delete m_pvrChannelInfoTag;
++
++ m_pvrChannelInfoTag = NULL;
++ }
++
++ if (item.HasPVRRecordingInfoTag())
++ {
++ m_pvrRecordingInfoTag = GetPVRRecordingInfoTag();
++ if (m_pvrRecordingInfoTag)
++ *m_pvrRecordingInfoTag = *item.m_pvrRecordingInfoTag;
++ }
++ else
++ {
++ if (m_pvrRecordingInfoTag)
++ delete m_pvrRecordingInfoTag;
++
++ m_pvrRecordingInfoTag = NULL;
++ }
++
++ if (item.HasPVRTimerInfoTag())
++ {
++ m_pvrTimerInfoTag = GetPVRTimerInfoTag();
++ if (m_pvrTimerInfoTag)
++ *m_pvrTimerInfoTag = *item.m_pvrTimerInfoTag;
++ }
++ else
++ {
++ if (m_pvrTimerInfoTag)
++ delete m_pvrTimerInfoTag;
++
++ m_pvrTimerInfoTag = NULL;
++ }
++
+ if (item.HasPictureInfoTag())
+ {
+ m_pictureInfoTag = GetPictureInfoTag();
+@@ -330,6 +576,14 @@ void CFileItem::Reset()
+ m_musicInfoTag=NULL;
+ delete m_videoInfoTag;
+ m_videoInfoTag=NULL;
++ delete m_epgInfoTag;
++ m_epgInfoTag=NULL;
++ delete m_pvrChannelInfoTag;
++ m_pvrChannelInfoTag=NULL;
++ delete m_pvrRecordingInfoTag;
++ m_pvrRecordingInfoTag=NULL;
++ delete m_pvrTimerInfoTag;
++ m_pvrTimerInfoTag=NULL;
+ delete m_pictureInfoTag;
+ m_pictureInfoTag=NULL;
+ m_extrainfo.Empty();
+@@ -492,6 +746,7 @@ bool CFileItem::IsVideo() const
+ if (HasVideoInfoTag()) return true;
+ if (HasMusicInfoTag()) return false;
+ if (HasPictureInfoTag()) return false;
++ if (IsPVRRecording()) return true;
+
+ if (IsHDHomeRun() || IsTuxBox() || URIUtils::IsDVD(m_strPath) || IsSlingbox())
+ return true;
+@@ -516,6 +771,30 @@ bool CFileItem::IsVideo() const
+ return (g_settings.m_videoExtensions.Find(extension) != -1);
+ }
+
++bool CFileItem::IsEPG() const
++{
++ if (HasEPGInfoTag()) return true; /// is this enough?
++ return false;
++}
++
++bool CFileItem::IsPVRChannel() const
++{
++ if (HasPVRChannelInfoTag()) return true; /// is this enough?
++ return false;
++}
++
++bool CFileItem::IsPVRRecording() const
++{
++ if (HasPVRRecordingInfoTag()) return true; /// is this enough?
++ return false;
++}
++
++bool CFileItem::IsPVRTimer() const
++{
++ if (HasPVRTimerInfoTag()) return true; /// is this enough?
++ return false;
++}
++
+ bool CFileItem::IsDiscStub() const
+ {
+ CStdString strExtension;
+@@ -836,6 +1115,11 @@ bool CFileItem::IsVTP() const
+ return URIUtils::IsVTP(m_strPath);
+ }
+
++bool CFileItem::IsPVR() const
++{
++ return CUtil::IsPVR(m_strPath);
++}
++
+ bool CFileItem::IsLiveTV() const
+ {
+ return URIUtils::IsLiveTV(m_strPath);
+@@ -900,7 +1184,19 @@ void CFileItem::FillInDefaultIcon()
+ * in mind the complexity of the code behind the check in the
+ * case of IsWhatater() returns false.
+ */
+- if ( IsAudio() )
++ if (IsPVRChannel())
++ {
++ if (GetPVRChannelInfoTag()->IsRadio())
++ SetIconImage("DefaultAudio.png");
++ else
++ SetIconImage("DefaultVideo.png");
++ }
++ else if ( IsLiveTV() )
++ {
++ // Live TV Channel
++ SetIconImage("DefaultVideo.png");
++ }
++ else if ( IsAudio() )
+ {
+ // audio
+ SetIconImage("DefaultAudio.png");
+@@ -910,6 +1206,14 @@ void CFileItem::FillInDefaultIcon()
+ // video
+ SetIconImage("DefaultVideo.png");
+ }
++ else if (IsPVRRecording())
++ {
++ SetIconImage("DefaultVideo.png");
++ }
++ else if (IsPVRTimer())
++ {
++ SetIconImage("DefaultVideo.png");
++ }
+ else if ( IsPicture() )
+ {
+ // picture
+@@ -1046,6 +1350,12 @@ void CFileItem::SetLabel(const CStdString &strLabel)
+ CGUIListItem::SetLabel(strLabel);
+ }
+
++void CFileItem::SetLabel2(const CStdString &strLabel)
++{
++ m_strLabel2 = strLabel;
++}
++
++
+ void CFileItem::SetFileSizeLabel()
+ {
+ if( m_bIsFolder && m_dwSize == 0 )
+@@ -1629,6 +1939,9 @@ void CFileItemList::Sort(SORT_METHOD sortMethod, SORT_ORDER sortOrder)
+ case SORT_METHOD_LISTENERS:
+ FillSortFields(SSortFileItem::ByListeners);
+ break;
++ case SORT_METHOD_CHANNEL:
++ FillSortFields(SSortFileItem::ByChannel);
++ break;
+ default:
+ break;
+ }
+@@ -2377,6 +2690,8 @@ bool CFileItemList::AlwaysCache() const
+ return CMusicDatabaseDirectory::CanCache(GetPath());
+ if (IsVideoDb())
+ return CVideoDatabaseDirectory::CanCache(GetPath());
++ if (IsEPG())
++ return true; // always cache
+ return false;
+ }
+
+@@ -3051,6 +3366,38 @@ CVideoInfoTag* CFileItem::GetVideoInfoTag()
+ return m_videoInfoTag;
+ }
+
++CEpgInfoTag* CFileItem::GetEPGInfoTag()
++{
++ if (!m_epgInfoTag)
++ m_epgInfoTag = new CEpgInfoTag;
++
++ return m_epgInfoTag;
++}
++
++CPVRChannel* CFileItem::GetPVRChannelInfoTag()
++{
++ if (!m_pvrChannelInfoTag)
++ m_pvrChannelInfoTag = new CPVRChannel;
++
++ return m_pvrChannelInfoTag;
++}
++
++CPVRRecording* CFileItem::GetPVRRecordingInfoTag()
++{
++ if (!m_pvrRecordingInfoTag)
++ m_pvrRecordingInfoTag = new CPVRRecording;
++
++ return m_pvrRecordingInfoTag;
++}
++
++CPVRTimerInfoTag* CFileItem::GetPVRTimerInfoTag()
++{
++ if (!m_pvrTimerInfoTag)
++ m_pvrTimerInfoTag = new CPVRTimerInfoTag;
++
++ return m_pvrTimerInfoTag;
++}
++
+ CPictureInfoTag* CFileItem::GetPictureInfoTag()
+ {
+ if (!m_pictureInfoTag)
+diff --git a/xbmc/FileItem.h b/xbmc/FileItem.h
+index 73faf0d..e781823 100644
+--- a/xbmc/FileItem.h
++++ b/xbmc/FileItem.h
+@@ -42,6 +42,16 @@ namespace MUSIC_INFO
+ class CMusicInfoTag;
+ }
+ class CVideoInfoTag;
++namespace EPG
++{
++ class CEpgInfoTag;
++}
++namespace PVR
++{
++ class CPVRChannel;
++ class CPVRRecording;
++ class CPVRTimerInfoTag;
++}
+ class CPictureInfoTag;
+
+ class CAlbum;
+@@ -74,6 +84,10 @@ public:
+ CFileItem(const CArtist& artist);
+ CFileItem(const CGenre& genre);
+ CFileItem(const CVideoInfoTag& movie);
++ CFileItem(const EPG::CEpgInfoTag& tag);
++ CFileItem(const PVR::CPVRChannel& channel);
++ CFileItem(const PVR::CPVRRecording& record);
++ CFileItem(const PVR::CPVRTimerInfoTag& timer);
+ CFileItem(const CMediaSource& share);
+ virtual ~CFileItem(void);
+ virtual CGUIListItem *Clone() const { return new CFileItem(*this); };
+@@ -131,6 +145,10 @@ public:
+ bool IsMultiPath() const;
+ bool IsMusicDb() const;
+ bool IsVideoDb() const;
++ bool IsEPG() const;
++ bool IsPVRChannel() const;
++ bool IsPVRRecording() const;
++ bool IsPVRTimer() const;
+ bool IsType(const char *ext) const;
+ bool IsVirtualDirectoryRoot() const;
+ bool IsReadOnly() const;
+@@ -144,6 +162,7 @@ public:
+ bool IsHDHomeRun() const;
+ bool IsSlingbox() const;
+ bool IsVTP() const;
++ bool IsPVR() const;
+ bool IsLiveTV() const;
+ bool IsRSS() const;
+
+@@ -153,6 +172,7 @@ public:
+ void SetMusicThumb(bool alwaysCheckRemote = false);
+ void SetFileSizeLabel();
+ virtual void SetLabel(const CStdString &strLabel);
++ virtual void SetLabel2(const CStdString &strLabel);
+ CURL GetAsUrl() const;
+ int GetVideoContentType() const; /* return VIDEODB_CONTENT_TYPE, but don't want to include videodb in this header */
+ bool IsLabelPreformated() const { return m_bLabelPreformated; }
+@@ -185,6 +205,54 @@ public:
+ return m_videoInfoTag;
+ }
+
++ inline bool HasEPGInfoTag() const
++ {
++ return m_epgInfoTag != NULL;
++ }
++
++ EPG::CEpgInfoTag* GetEPGInfoTag();
++
++ inline const EPG::CEpgInfoTag* GetEPGInfoTag() const
++ {
++ return m_epgInfoTag;
++ }
++
++ inline bool HasPVRChannelInfoTag() const
++ {
++ return m_pvrChannelInfoTag != NULL;
++ }
++
++ PVR::CPVRChannel* GetPVRChannelInfoTag();
++
++ inline const PVR::CPVRChannel* GetPVRChannelInfoTag() const
++ {
++ return m_pvrChannelInfoTag;
++ }
++
++ inline bool HasPVRRecordingInfoTag() const
++ {
++ return m_pvrRecordingInfoTag != NULL;
++ }
++
++ PVR::CPVRRecording* GetPVRRecordingInfoTag();
++
++ inline const PVR::CPVRRecording* GetPVRRecordingInfoTag() const
++ {
++ return m_pvrRecordingInfoTag;
++ }
++
++ inline bool HasPVRTimerInfoTag() const
++ {
++ return m_pvrTimerInfoTag != NULL;
++ }
++
++ PVR::CPVRTimerInfoTag* GetPVRTimerInfoTag();
++
++ inline const PVR::CPVRTimerInfoTag* GetPVRTimerInfoTag() const
++ {
++ return m_pvrTimerInfoTag;
++ }
++
+ inline bool HasPictureInfoTag() const
+ {
+ return m_pictureInfoTag != NULL;
+@@ -333,6 +401,10 @@ private:
+ CStdString m_extrainfo;
+ MUSIC_INFO::CMusicInfoTag* m_musicInfoTag;
+ CVideoInfoTag* m_videoInfoTag;
++ EPG::CEpgInfoTag* m_epgInfoTag;
++ PVR::CPVRChannel* m_pvrChannelInfoTag;
++ PVR::CPVRRecording* m_pvrRecordingInfoTag;
++ PVR::CPVRTimerInfoTag * m_pvrTimerInfoTag;
+ CPictureInfoTag* m_pictureInfoTag;
+ bool m_bIsAlbum;
+ };
+diff --git a/xbmc/GUIInfoManager.cpp b/xbmc/GUIInfoManager.cpp
+index 72f5f95..2e4f7b5 100644
+--- a/xbmc/GUIInfoManager.cpp
++++ b/xbmc/GUIInfoManager.cpp
+@@ -74,6 +74,12 @@
+ #include "threads/SingleLock.h"
+ #include "utils/log.h"
+
++#include "pvr/PVRManager.h"
++#include "pvr/channels/PVRChannelGroupsContainer.h"
++#include "epg/EpgInfoTag.h"
++#include "pvr/timers/PVRTimers.h"
++#include "pvr/recordings/PVRRecording.h"
++
+ #include "addons/AddonManager.h"
+ #include "interfaces/info/InfoBool.h"
+
+@@ -83,9 +89,12 @@ using namespace std;
+ using namespace XFILE;
+ using namespace MUSIC_INFO;
+ using namespace ADDON;
++using namespace PVR;
+ using namespace INFO;
++using namespace EPG;
+
+-CGUIInfoManager::CGUIInfoManager(void)
++CGUIInfoManager::CGUIInfoManager(void) :
++ Observable()
+ {
+ m_lastSysHeatInfoTime = -SYSHEATUPDATEINTERVAL; // make sure we grab CPU temp on the first pass
+ m_lastMusicBitrateTime = 0;
+@@ -255,6 +264,8 @@ const infomap system_labels[] = {{ "hasnetwork", SYSTEM_ETHERNET_LINK_ACT
+ { "batterylevel", SYSTEM_BATTERY_LEVEL },
+ { "friendlyname", SYSTEM_FRIENDLY_NAME },
+ { "alarmpos", SYSTEM_ALARM_POS },
++ { "isinhibit", SYSTEM_ISINHIBIT },
++ { "hasshutdown", SYSTEM_HAS_SHUTDOWN },
+ { "haspvr", SYSTEM_HAS_PVR }};
+
+ const infomap system_param[] = {{ "hasalarm", SYSTEM_HAS_ALARM },
+@@ -334,7 +345,11 @@ const infomap musicplayer[] = {{ "title", MUSICPLAYER_TITLE },
+ { "hasprevious", MUSICPLAYER_HASPREVIOUS },
+ { "hasnext", MUSICPLAYER_HASNEXT },
+ { "playcount", MUSICPLAYER_PLAYCOUNT },
+- { "lastplayed", MUSICPLAYER_LASTPLAYED }};
++ { "lastplayed", MUSICPLAYER_LASTPLAYED },
++ { "channelname", MUSICPLAYER_CHANNEL_NAME },
++ { "channelnumber", MUSICPLAYER_CHANNEL_NUMBER },
++ { "channelgroup", MUSICPLAYER_CHANNEL_GROUP }
++};
+
+ const infomap videoplayer[] = {{ "title", VIDEOPLAYER_TITLE },
+ { "genre", VIDEOPLAYER_GENRE },
+@@ -376,7 +391,20 @@ const infomap videoplayer[] = {{ "title", VIDEOPLAYER_TITLE },
+ { "lastplayed", VIDEOPLAYER_LASTPLAYED },
+ { "playcount", VIDEOPLAYER_PLAYCOUNT },
+ { "hassubtitles", VIDEOPLAYER_HASSUBTITLES },
+- { "subtitlesenabled", VIDEOPLAYER_SUBTITLESENABLED }};
++ { "subtitlesenabled", VIDEOPLAYER_SUBTITLESENABLED },
++ { "endtime", VIDEOPLAYER_ENDTIME },
++ { "nexttitle", VIDEOPLAYER_NEXT_TITLE },
++ { "nextgenre", VIDEOPLAYER_NEXT_GENRE },
++ { "nextplot", VIDEOPLAYER_NEXT_PLOT },
++ { "nextplotoutline", VIDEOPLAYER_NEXT_PLOT_OUTLINE },
++ { "nextstarttime", VIDEOPLAYER_NEXT_STARTTIME },
++ { "nextendtime", VIDEOPLAYER_NEXT_ENDTIME },
++ { "nextduration", VIDEOPLAYER_NEXT_DURATION },
++ { "channelname", VIDEOPLAYER_CHANNEL_NAME },
++ { "channelnumber", VIDEOPLAYER_CHANNEL_NUMBER },
++ { "channelgroup", VIDEOPLAYER_CHANNEL_GROUP },
++ { "hasepg", VIDEOPLAYER_HAS_EPG },
++ { "parentalrating", VIDEOPLAYER_PARENTAL_RATING }};
+
+ const infomap mediacontainer[] = {{ "hasfiles", CONTAINER_HASFILES },
+ { "hasfolders", CONTAINER_HASFOLDERS },
+@@ -475,7 +503,27 @@ const infomap listitem_labels[]= {{ "thumb", LISTITEM_THUMB },
+ { "originaltitle", LISTITEM_ORIGINALTITLE },
+ { "lastplayed", LISTITEM_LASTPLAYED },
+ { "playcount", LISTITEM_PLAYCOUNT },
+- { "discnumber", LISTITEM_DISC_NUMBER }};
++ { "discnumber", LISTITEM_DISC_NUMBER },
++ { "starttime", LISTITEM_STARTTIME },
++ { "endtime", LISTITEM_ENDTIME },
++ { "startdate", LISTITEM_STARTDATE },
++ { "enddate", LISTITEM_ENDDATE },
++ { "nexttitle", LISTITEM_NEXT_TITLE },
++ { "nextgenre", LISTITEM_NEXT_GENRE },
++ { "nextplot", LISTITEM_NEXT_PLOT },
++ { "nextplotoutline", LISTITEM_NEXT_PLOT_OUTLINE },
++ { "nextstarttime", LISTITEM_NEXT_STARTTIME },
++ { "nextendtime", LISTITEM_NEXT_ENDTIME },
++ { "nextstartdate", LISTITEM_NEXT_STARTDATE },
++ { "nextenddate", LISTITEM_NEXT_ENDDATE },
++ { "channelname", LISTITEM_CHANNEL_NAME },
++ { "channelnumber", LISTITEM_CHANNEL_NUMBER },
++ { "channelgroup", LISTITEM_CHANNEL_GROUP },
++ { "hasepg", LISTITEM_HAS_EPG },
++ { "hastimer", LISTITEM_HASTIMER },
++ { "isrecording", LISTITEM_ISRECORDING },
++ { "isencrypted", LISTITEM_ISENCRYPTED },
++ { "progress", LISTITEM_PROGRESS }};
+
+ const infomap visualisation[] = {{ "locked", VISUALISATION_LOCKED },
+ { "preset", VISUALISATION_PRESET },
+@@ -512,6 +560,51 @@ const infomap playlist[] = {{ "length", PLAYLIST_LENGTH },
+ { "isrepeat", PLAYLIST_ISREPEAT },
+ { "isrepeatone", PLAYLIST_ISREPEATONE }};
+
++const infomap pvr[] = {{ "isrecording", PVR_IS_RECORDING },
++ { "hastimer", PVR_HAS_TIMER },
++ { "hasnonrecordingtimer", PVR_HAS_NONRECORDING_TIMER },
++ { "nowrecordingtitle", PVR_NOW_RECORDING_TITLE },
++ { "nowrecordingdatetime", PVR_NOW_RECORDING_DATETIME },
++ { "nowrecordingchannel", PVR_NOW_RECORDING_CHANNEL },
++ { "nowrecordingchannelicon", PVR_NOW_RECORDING_CHAN_ICO },
++ { "nextrecordingtitle", PVR_NEXT_RECORDING_TITLE },
++ { "nextrecordingdatetime", PVR_NEXT_RECORDING_DATETIME },
++ { "nextrecordingchannel", PVR_NEXT_RECORDING_CHANNEL },
++ { "nextrecordingchannelicon", PVR_NEXT_RECORDING_CHAN_ICO },
++ { "backendname", PVR_BACKEND_NAME },
++ { "backendversion", PVR_BACKEND_VERSION },
++ { "backendhost", PVR_BACKEND_HOST },
++ { "backenddiskspace", PVR_BACKEND_DISKSPACE },
++ { "backendchannels", PVR_BACKEND_CHANNELS },
++ { "backendtimers", PVR_BACKEND_TIMERS },
++ { "backendrecordings", PVR_BACKEND_RECORDINGS },
++ { "backendnumber", PVR_BACKEND_NUMBER },
++ { "hasepg", PVR_HAS_EPG },
++ { "hastxt", PVR_HAS_TXT },
++ { "hasdirector", PVR_HAS_DIRECTOR },
++ { "totaldiscspace", PVR_TOTAL_DISKSPACE },
++ { "nexttimer", PVR_NEXT_TIMER },
++ { "isplayingtv", PVR_IS_PLAYING_TV },
++ { "isplayingradio", PVR_IS_PLAYING_RADIO },
++ { "isplayingrecording", PVR_IS_PLAYING_RECORDING },
++ { "duration", PVR_PLAYING_DURATION },
++ { "time", PVR_PLAYING_TIME },
++ { "progress", PVR_PLAYING_PROGRESS },
++ { "actstreamclient", PVR_ACTUAL_STREAM_CLIENT },
++ { "actstreamdevice", PVR_ACTUAL_STREAM_DEVICE },
++ { "actstreamstatus", PVR_ACTUAL_STREAM_STATUS },
++ { "actstreamsignal", PVR_ACTUAL_STREAM_SIG },
++ { "actstreamsnr", PVR_ACTUAL_STREAM_SNR },
++ { "actstreamber", PVR_ACTUAL_STREAM_BER },
++ { "actstreamunc", PVR_ACTUAL_STREAM_UNC },
++ { "actstreamvideobitrate", PVR_ACTUAL_STREAM_VIDEO_BR },
++ { "actstreamaudiobitrate", PVR_ACTUAL_STREAM_AUDIO_BR },
++ { "actstreamdolbybitrate", PVR_ACTUAL_STREAM_DOLBY_BR },
++ { "actstreamprogrsignal", PVR_ACTUAL_STREAM_SIG_PROGR },
++ { "actstreamprogrsnr", PVR_ACTUAL_STREAM_SNR_PROGR },
++ { "actstreamisencrypted", PVR_ACTUAL_STREAM_ENCRYPTED },
++ { "actstreamencryptionname", PVR_ACTUAL_STREAM_CRYPTION }};
++
+ const infomap slideshow[] = {{ "ispaused", SLIDESHOW_ISPAUSED },
+ { "isactive", SLIDESHOW_ISACTIVE },
+ { "israndom", SLIDESHOW_ISRANDOM }};
+@@ -968,6 +1061,14 @@ int CGUIInfoManager::TranslateSingleString(const CStdString &strCondition)
+ return playlist[i].val;
+ }
+ }
++ else if (cat.name == "pvr")
++ {
++ for (size_t i = 0; i < sizeof(pvr) / sizeof(infomap); i++)
++ {
++ if (prop.name == pvr[i].str)
++ return pvr[i].val;
++ }
++ }
+ }
+ else if (info.size() == 3)
+ {
+@@ -1085,6 +1186,42 @@ CStdString CGUIInfoManager::GetLabel(int info, int contextWindow)
+
+ switch (info)
+ {
++ case PVR_NEXT_RECORDING_CHANNEL:
++ case PVR_NEXT_RECORDING_CHAN_ICO:
++ case PVR_NEXT_RECORDING_DATETIME:
++ case PVR_NEXT_RECORDING_TITLE:
++ case PVR_NOW_RECORDING_CHANNEL:
++ case PVR_NOW_RECORDING_CHAN_ICO:
++ case PVR_NOW_RECORDING_DATETIME:
++ case PVR_NOW_RECORDING_TITLE:
++ case PVR_BACKEND_NAME:
++ case PVR_BACKEND_VERSION:
++ case PVR_BACKEND_HOST:
++ case PVR_BACKEND_DISKSPACE:
++ case PVR_BACKEND_CHANNELS:
++ case PVR_BACKEND_TIMERS:
++ case PVR_BACKEND_RECORDINGS:
++ case PVR_BACKEND_NUMBER:
++ case PVR_TOTAL_DISKSPACE:
++ case PVR_NEXT_TIMER:
++ case PVR_PLAYING_DURATION:
++ case PVR_PLAYING_TIME:
++ case PVR_PLAYING_PROGRESS:
++ case PVR_ACTUAL_STREAM_CLIENT:
++ case PVR_ACTUAL_STREAM_DEVICE:
++ case PVR_ACTUAL_STREAM_STATUS:
++ case PVR_ACTUAL_STREAM_SIG:
++ case PVR_ACTUAL_STREAM_SNR:
++ case PVR_ACTUAL_STREAM_SIG_PROGR:
++ case PVR_ACTUAL_STREAM_SNR_PROGR:
++ case PVR_ACTUAL_STREAM_BER:
++ case PVR_ACTUAL_STREAM_UNC:
++ case PVR_ACTUAL_STREAM_VIDEO_BR:
++ case PVR_ACTUAL_STREAM_AUDIO_BR:
++ case PVR_ACTUAL_STREAM_DOLBY_BR:
++ case PVR_ACTUAL_STREAM_CRYPTION:
++ g_PVRManager.TranslateCharInfo(info, strLabel);
++ break;
+ case WEATHER_CONDITIONS:
+ strLabel = g_weatherManager.GetInfo(WEATHER_LABEL_CURRENT_COND);
+ strLabel = strLabel.Trim();
+@@ -1185,6 +1322,9 @@ CStdString CGUIInfoManager::GetLabel(int info, int contextWindow)
+ case MUSICPLAYER_RATING:
+ case MUSICPLAYER_COMMENT:
+ case MUSICPLAYER_LYRICS:
++ case MUSICPLAYER_CHANNEL_NAME:
++ case MUSICPLAYER_CHANNEL_NUMBER:
++ case MUSICPLAYER_CHANNEL_GROUP:
+ case MUSICPLAYER_PLAYCOUNT:
+ case MUSICPLAYER_LASTPLAYED:
+ strLabel = GetMusicLabel(info);
+@@ -1215,6 +1355,19 @@ CStdString CGUIInfoManager::GetLabel(int info, int contextWindow)
+ case VIDEOPLAYER_WRITER:
+ case VIDEOPLAYER_TAGLINE:
+ case VIDEOPLAYER_TRAILER:
++ case VIDEOPLAYER_STARTTIME:
++ case VIDEOPLAYER_ENDTIME:
++ case VIDEOPLAYER_NEXT_TITLE:
++ case VIDEOPLAYER_NEXT_GENRE:
++ case VIDEOPLAYER_NEXT_PLOT:
++ case VIDEOPLAYER_NEXT_PLOT_OUTLINE:
++ case VIDEOPLAYER_NEXT_STARTTIME:
++ case VIDEOPLAYER_NEXT_ENDTIME:
++ case VIDEOPLAYER_NEXT_DURATION:
++ case VIDEOPLAYER_CHANNEL_NAME:
++ case VIDEOPLAYER_CHANNEL_NUMBER:
++ case VIDEOPLAYER_CHANNEL_GROUP:
++ case VIDEOPLAYER_PARENTAL_RATING:
+ case VIDEOPLAYER_PLAYCOUNT:
+ case VIDEOPLAYER_LASTPLAYED:
+ strLabel = GetVideoLabel(info);
+@@ -1736,6 +1889,11 @@ bool CGUIInfoManager::GetInt(int &value, int info, int contextWindow, const CGUI
+ case SYSTEM_CPU_USAGE:
+ value = g_cpuInfo.getUsedPercentage();
+ return true;
++ case PVR_PLAYING_PROGRESS:
++ case PVR_ACTUAL_STREAM_SIG_PROGR:
++ case PVR_ACTUAL_STREAM_SNR_PROGR:
++ value = g_PVRManager.TranslateIntInfo(info);
++ return true;
+ case SYSTEM_BATTERY_LEVEL:
+ value = g_powerManager.BatteryLevel();
+ return true;
+@@ -1927,6 +2085,10 @@ bool CGUIInfoManager::GetBool(int condition1, int contextWindow, const CGUIListI
+ bReturn = g_Windowing.IsFullScreen();
+ else if (condition == SYSTEM_ISSTANDALONE)
+ bReturn = g_application.IsStandAlone();
++ else if (condition == SYSTEM_ISINHIBIT)
++ bReturn = g_application.IsIdleShutdownInhibited();
++ else if (condition == SYSTEM_HAS_SHUTDOWN)
++ bReturn = (g_guiSettings.GetInt("powermanagement.shutdowntime") > 0);
+ else if (condition == SYSTEM_LOGGEDON)
+ bReturn = !(g_windowManager.GetActiveWindow() == WINDOW_LOGIN_SCREEN);
+ else if (condition == SYSTEM_SHOW_EXIT_BUTTON)
+@@ -1935,6 +2097,9 @@ bool CGUIInfoManager::GetBool(int condition1, int contextWindow, const CGUIListI
+ bReturn = g_settings.UsingLoginScreen();
+ else if (condition == WEATHER_IS_FETCHED)
+ bReturn = g_weatherManager.IsFetched();
++ else if (condition >= PVR_CONDITIONS_START && condition <= PVR_CONDITIONS_END)
++ bReturn = g_PVRManager.TranslateBoolInfo(condition);
++
+ else if (condition == SYSTEM_INTERNET_STATE)
+ {
+ g_sysinfo.GetInfo(condition);
+@@ -1993,7 +2158,8 @@ bool CGUIInfoManager::GetBool(int condition1, int contextWindow, const CGUIListI
+ }
+ }
+ else if (condition == VIDEOPLAYER_HAS_INFO)
+- bReturn = (m_currentFile->HasVideoInfoTag() && !m_currentFile->GetVideoInfoTag()->IsEmpty());
++ bReturn = ((m_currentFile->HasVideoInfoTag() && !m_currentFile->GetVideoInfoTag()->IsEmpty()) ||
++ (m_currentFile->HasPVRChannelInfoTag() && !m_currentFile->GetPVRChannelInfoTag()->IsEmpty()));
+ else if (condition >= CONTAINER_SCROLL_PREVIOUS && condition <= CONTAINER_SCROLL_NEXT)
+ {
+ // no parameters, so we assume it's just requested for a media window. It therefore
+@@ -2191,6 +2357,13 @@ bool CGUIInfoManager::GetBool(int condition1, int contextWindow, const CGUIListI
+ case VISUALISATION_ENABLED:
+ bReturn = !g_guiSettings.GetString("musicplayer.visualisation").IsEmpty();
+ break;
++ case VIDEOPLAYER_HAS_EPG:
++ if (m_currentFile->HasPVRChannelInfoTag())
++ {
++ CEpgInfoTag epgTag;
++ bReturn = m_currentFile->GetPVRChannelInfoTag()->GetEPGNow(epgTag);
++ }
++ break;
+ default: // default, use integer value different from 0 as true
+ {
+ int val;
+@@ -2531,6 +2704,8 @@ bool CGUIInfoManager::GetMultiInfoBool(const GUIInfo &info, int contextWindow, c
+ strContent = "musicvideos";
+ if (m_currentFile->HasVideoInfoTag() && m_currentFile->GetVideoInfoTag()->m_strStatus == "livetv")
+ strContent = "livetv";
++ if (m_currentFile->HasPVRChannelInfoTag())
++ strContent = "livetv";
+ bReturn = m_stringParameters[info.GetData1()].Equals(strContent);
+ }
+ break;
+@@ -3266,6 +3441,31 @@ CStdString CGUIInfoManager::GetMusicTagLabel(int info, const CFileItem *item)
+ return GetItemLabel(item, LISTITEM_COMMENT);
+ case MUSICPLAYER_DURATION:
+ return GetItemLabel(item, LISTITEM_DURATION);
++ case MUSICPLAYER_CHANNEL_NAME:
++ {
++ CPVRChannel* channeltag = m_currentFile->GetPVRChannelInfoTag();
++ if (channeltag)
++ return channeltag->ChannelName();
++ }
++ break;
++ case MUSICPLAYER_CHANNEL_NUMBER:
++ {
++ CPVRChannel* channeltag = m_currentFile->GetPVRChannelInfoTag();
++ if (channeltag)
++ {
++ CStdString strNumber;
++ strNumber.Format("%i", channeltag->ChannelNumber());
++ return strNumber;
++ }
++ }
++ break;
++ case MUSICPLAYER_CHANNEL_GROUP:
++ {
++ CPVRChannel* channeltag = m_currentFile->GetPVRChannelInfoTag();
++ if (channeltag && channeltag->IsRadio())
++ return g_PVRManager.GetPlayingGroup(true)->GroupName();
++ }
++ break;
+ case MUSICPLAYER_PLAYCOUNT:
+ return GetItemLabel(item, LISTITEM_PLAYCOUNT);
+ case MUSICPLAYER_LASTPLAYED:
+@@ -3281,6 +3481,11 @@ CStdString CGUIInfoManager::GetVideoLabel(int item)
+
+ if (item == VIDEOPLAYER_TITLE)
+ {
++ if (m_currentFile->HasPVRChannelInfoTag())
++ {
++ CEpgInfoTag tag;
++ return m_currentFile->GetPVRChannelInfoTag()->GetEPGNow(tag) ? tag.Title() : g_localizeStrings.Get(19055);
++ }
+ if (m_currentFile->HasVideoInfoTag() && !m_currentFile->GetVideoInfoTag()->m_strTitle.IsEmpty())
+ return m_currentFile->GetVideoInfoTag()->m_strTitle;
+ // don't have the title, so use dvdplayer, label, or drop down to title from path
+@@ -3300,6 +3505,73 @@ CStdString CGUIInfoManager::GetVideoLabel(int item)
+ if (g_playlistPlayer.GetCurrentPlaylist() == PLAYLIST_VIDEO)
+ return GetPlaylistLabel(PLAYLIST_POSITION);
+ }
++ else if (m_currentFile->HasPVRChannelInfoTag())
++ {
++ CPVRChannel* tag = m_currentFile->GetPVRChannelInfoTag();
++ CEpgInfoTag epgTag;
++
++ switch (item)
++ {
++ /* Now playing infos */
++ case VIDEOPLAYER_ORIGINALTITLE:
++ return tag->GetEPGNow(epgTag) ? epgTag.Title() : g_localizeStrings.Get(19055);
++ case VIDEOPLAYER_GENRE:
++ return tag->GetEPGNow(epgTag) ? epgTag.Genre() : StringUtils::EmptyString;
++ case VIDEOPLAYER_PLOT:
++ return tag->GetEPGNow(epgTag) ? epgTag.Plot() : StringUtils::EmptyString;
++ case VIDEOPLAYER_PLOT_OUTLINE:
++ return tag->GetEPGNow(epgTag) ? epgTag.PlotOutline() : StringUtils::EmptyString;
++ case VIDEOPLAYER_STARTTIME:
++ return tag->GetEPGNow(epgTag) ? epgTag.StartAsLocalTime().GetAsLocalizedTime("", false) : CDateTime::GetCurrentDateTime().GetAsLocalizedTime("", false);
++ case VIDEOPLAYER_ENDTIME:
++ return tag->GetEPGNow(epgTag) ? epgTag.EndAsLocalTime().GetAsLocalizedTime("", false) : CDateTime::GetCurrentDateTime().GetAsLocalizedTime("", false);
++
++ /* Next playing infos */
++ case VIDEOPLAYER_NEXT_TITLE:
++ return tag->GetEPGNext(epgTag) ? epgTag.Title() : g_localizeStrings.Get(19055);
++ case VIDEOPLAYER_NEXT_GENRE:
++ return tag->GetEPGNext(epgTag) ? epgTag.Genre() : StringUtils::EmptyString;
++ case VIDEOPLAYER_NEXT_PLOT:
++ return tag->GetEPGNext(epgTag) ? epgTag.Plot() : StringUtils::EmptyString;
++ case VIDEOPLAYER_NEXT_PLOT_OUTLINE:
++ return tag->GetEPGNext(epgTag) ? epgTag.PlotOutline() : StringUtils::EmptyString;
++ case VIDEOPLAYER_NEXT_STARTTIME:
++ return tag->GetEPGNext(epgTag) ? epgTag.StartAsLocalTime().GetAsLocalizedTime("", false) : CDateTime::GetCurrentDateTime().GetAsLocalizedTime("", false);
++ case VIDEOPLAYER_NEXT_ENDTIME:
++ return tag->GetEPGNext(epgTag) ? epgTag.EndAsLocalTime().GetAsLocalizedTime("", false) : CDateTime::GetCurrentDateTime().GetAsLocalizedTime("", false);
++ case VIDEOPLAYER_NEXT_DURATION:
++ {
++ CStdString duration;
++ if (tag->GetEPGNext(epgTag) && epgTag.GetDuration() > 0)
++ duration = StringUtils::SecondsToTimeString(epgTag.GetDuration());
++ return duration;
++ }
++
++ case VIDEOPLAYER_PARENTAL_RATING:
++ {
++ CStdString rating;
++ if (tag->GetEPGNow(epgTag) && epgTag.ParentalRating() > 0)
++ rating.Format("%i", epgTag.ParentalRating());
++ return rating;
++ }
++ break;
++
++ /* General channel infos */
++ case VIDEOPLAYER_CHANNEL_NAME:
++ return tag->ChannelName();
++ case VIDEOPLAYER_CHANNEL_NUMBER:
++ {
++ CStdString strNumber;
++ strNumber.Format("%i", tag->ChannelNumber());
++ return strNumber;
++ }
++ case VIDEOPLAYER_CHANNEL_GROUP:
++ {
++ if (tag && !tag->IsRadio())
++ return g_PVRManager.GetPlayingGroup(false)->GroupName();
++ }
++ }
++ }
+ else if (m_currentFile->HasVideoInfoTag())
+ {
+ switch (item)
+@@ -3476,6 +3748,9 @@ void CGUIInfoManager::SetCurrentItem(CFileItem &item)
+ SetCurrentSong(item);
+ else
+ SetCurrentMovie(item);
++
++ SetChanged();
++ NotifyObservers("current-item", true);
+ }
+
+ void CGUIInfoManager::SetCurrentAlbumThumb(const CStdString thumbFileName)
+@@ -3532,11 +3807,15 @@ void CGUIInfoManager::SetCurrentMovie(CFileItem &item)
+ CLog::Log(LOGDEBUG,"CGUIInfoManager::SetCurrentMovie(%s)",item.GetPath().c_str());
+ *m_currentFile = item;
+
+- CVideoDatabase dbs;
+- if (dbs.Open())
++ /* also call GetMovieInfo() when a VideoInfoTag is already present or additional info won't be present in the tag */
++ if (!m_currentFile->HasPVRChannelInfoTag())
+ {
+- dbs.LoadVideoInfo(item.GetPath(), *m_currentFile->GetVideoInfoTag());
+- dbs.Close();
++ CVideoDatabase dbs;
++ if (dbs.Open())
++ {
++ dbs.LoadVideoInfo(item.GetPath(), *m_currentFile->GetVideoInfoTag());
++ dbs.Close();
++ }
+ }
+
+ // Find a thumb for this file.
+@@ -3783,6 +4062,27 @@ bool CGUIInfoManager::GetItemInt(int &value, const CGUIListItem *item, int info)
+
+ switch (info)
+ {
++ case LISTITEM_PROGRESS:
++ {
++ value = 0;
++ if (item->IsFileItem())
++ {
++ const CFileItem *pItem = (const CFileItem *)item;
++ if (pItem && pItem->HasPVRChannelInfoTag())
++ {
++ CEpgInfoTag epgNow;
++ if (pItem->GetPVRChannelInfoTag()->GetEPGNow(epgNow))
++ value = (int) epgNow.ProgressPercentage();
++ }
++ else if (pItem && pItem->HasEPGInfoTag())
++ {
++ value = (int) pItem->GetEPGInfoTag()->ProgressPercentage();
++ }
++ }
++
++ return true;
++ }
++ break;
+ case LISTITEM_PERCENT_PLAYED:
+ if (item->IsFileItem() && ((const CFileItem *)item)->HasVideoInfoTag() && ((const CFileItem *)item)->GetVideoInfoTag()->m_resumePoint.totalTimeInSeconds > 0)
+ value = (int)(100 * ((const CFileItem *)item)->GetVideoInfoTag()->m_resumePoint.timeInSeconds / ((const CFileItem *)item)->GetVideoInfoTag()->m_resumePoint.totalTimeInSeconds);
+@@ -3815,6 +4115,17 @@ CStdString CGUIInfoManager::GetItemLabel(const CFileItem *item, int info)
+ case LISTITEM_LABEL2:
+ return item->GetLabel2();
+ case LISTITEM_TITLE:
++ if (item->HasPVRChannelInfoTag())
++ {
++ CEpgInfoTag epgTag;
++ return item->GetPVRChannelInfoTag()->GetEPGNow(epgTag) ? epgTag.Title() : g_localizeStrings.Get(19055);
++ }
++ if (item->HasPVRRecordingInfoTag())
++ return item->GetPVRRecordingInfoTag()->m_strTitle;
++ if (item->HasEPGInfoTag())
++ return item->GetEPGInfoTag()->Title();
++ if (item->HasPVRTimerInfoTag())
++ return item->GetPVRTimerInfoTag()->m_strTitle;
+ if (item->HasVideoInfoTag())
+ return item->GetVideoInfoTag()->m_strTitle;
+ if (item->HasMusicInfoTag())
+@@ -3901,6 +4212,15 @@ CStdString CGUIInfoManager::GetItemLabel(const CFileItem *item, int info)
+ return item->GetVideoInfoTag()->m_strGenre;
+ if (item->HasMusicInfoTag())
+ return item->GetMusicInfoTag()->GetGenre();
++ if (item->HasPVRChannelInfoTag())
++ {
++ CEpgInfoTag epgTag;
++ return item->GetPVRChannelInfoTag()->GetEPGNow(epgTag) ? epgTag.Genre() : "";
++ }
++ if (item->HasPVRRecordingInfoTag())
++ return item->GetPVRRecordingInfoTag()->m_strGenre;
++ if (item->HasEPGInfoTag())
++ return item->GetEPGInfoTag()->Genre();
+ break;
+ case LISTITEM_FILENAME:
+ case LISTITEM_FILE_EXTENSION:
+@@ -3922,6 +4242,17 @@ CStdString CGUIInfoManager::GetItemLabel(const CFileItem *item, int info)
+ }
+ break;
+ case LISTITEM_DATE:
++ if (item->HasEPGInfoTag())
++ return item->GetEPGInfoTag()->StartAsLocalTime().GetAsLocalizedDateTime(false, false);
++ if (item->HasPVRChannelInfoTag())
++ {
++ CEpgInfoTag epgTag;
++ return item->GetPVRChannelInfoTag()->GetEPGNow(epgTag) ? epgTag.StartAsLocalTime().GetAsLocalizedDateTime(false, false) : CDateTime::GetCurrentDateTime().GetAsLocalizedDateTime(false, false);
++ }
++ if (item->HasPVRRecordingInfoTag())
++ return item->GetPVRRecordingInfoTag()->RecordingTimeAsLocalTime().GetAsLocalizedDateTime(false, false);
++ if (item->HasPVRTimerInfoTag())
++ return item->GetPVRTimerInfoTag()->m_strSummary;
+ if (item->m_dateTime.IsValid())
+ return item->m_dateTime.GetAsLocalizedDate();
+ break;
+@@ -3962,12 +4293,30 @@ CStdString CGUIInfoManager::GetItemLabel(const CFileItem *item, int info)
+ case LISTITEM_DURATION:
+ {
+ CStdString duration;
+- if (item->HasVideoInfoTag())
++ if (item->HasPVRChannelInfoTag())
++ {
++ const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL;
++ CEpgInfoTag tag;
++ if (channel && channel->GetEPGNow(tag))
++ return StringUtils::SecondsToTimeString(tag.GetDuration());
++ return StringUtils::EmptyString;
++ }
++ else if (item->HasPVRRecordingInfoTag())
++ {
++ if (item->GetPVRRecordingInfoTag()->GetDuration() > 0)
++ duration = StringUtils::SecondsToTimeString(item->GetPVRRecordingInfoTag()->GetDuration());
++ }
++ else if (item->HasEPGInfoTag())
++ {
++ if (item->GetEPGInfoTag()->GetDuration() > 0)
++ duration = StringUtils::SecondsToTimeString(item->GetEPGInfoTag()->GetDuration());
++ }
++ else if (item->HasVideoInfoTag())
+ {
+ if (!item->GetVideoInfoTag()->m_strRuntime.IsEmpty())
+ duration = item->GetVideoInfoTag()->m_strRuntime;
+ }
+- if (item->HasMusicInfoTag())
++ else if (item->HasMusicInfoTag())
+ {
+ if (item->GetMusicInfoTag()->GetDuration() > 0)
+ duration = StringUtils::SecondsToTimeString(item->GetMusicInfoTag()->GetDuration());
+@@ -3975,6 +4324,18 @@ CStdString CGUIInfoManager::GetItemLabel(const CFileItem *item, int info)
+ return duration;
+ }
+ case LISTITEM_PLOT:
++ if (item->HasPVRChannelInfoTag())
++ {
++ const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL;
++ CEpgInfoTag tag;
++ if (channel && channel->GetEPGNow(tag))
++ return tag.Plot();
++ return StringUtils::EmptyString;
++ }
++ if (item->HasEPGInfoTag())
++ return item->GetEPGInfoTag()->Plot();
++ if (item->HasPVRRecordingInfoTag())
++ return item->GetPVRRecordingInfoTag()->m_strPlot;
+ if (item->HasVideoInfoTag())
+ {
+ if (!(!item->GetVideoInfoTag()->m_strShowTitle.IsEmpty() && item->GetVideoInfoTag()->m_iSeason == -1)) // dont apply to tvshows
+@@ -3985,6 +4346,18 @@ CStdString CGUIInfoManager::GetItemLabel(const CFileItem *item, int info)
+ }
+ break;
+ case LISTITEM_PLOT_OUTLINE:
++ if (item->HasPVRChannelInfoTag())
++ {
++ const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL;
++ CEpgInfoTag tag;
++ if (channel && channel->GetEPGNow(tag))
++ return tag.PlotOutline();
++ return StringUtils::EmptyString;
++ }
++ if (item->HasEPGInfoTag())
++ return item->GetEPGInfoTag()->PlotOutline();
++ if (item->HasPVRRecordingInfoTag())
++ return item->GetPVRRecordingInfoTag()->m_strPlotOutline;
+ if (item->HasVideoInfoTag())
+ return item->GetVideoInfoTag()->m_strPlotOutline;
+ break;
+@@ -4015,6 +4388,8 @@ CStdString CGUIInfoManager::GetItemLabel(const CFileItem *item, int info)
+ return item->GetVideoInfoTag()->m_strShowTitle;
+ break;
+ case LISTITEM_COMMENT:
++ if (item->HasPVRTimerInfoTag())
++ return item->GetPVRTimerInfoTag()->GetStatus();
+ if (item->HasMusicInfoTag())
+ return item->GetMusicInfoTag()->GetComment();
+ break;
+@@ -4169,6 +4544,173 @@ CStdString CGUIInfoManager::GetItemLabel(const CFileItem *item, int info)
+ if (item->HasVideoInfoTag())
+ return item->GetVideoInfoTag()->m_streamDetails.GetSubtitleLanguage();
+ break;
++ case LISTITEM_STARTTIME:
++ if (item->HasPVRChannelInfoTag())
++ {
++ const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL;
++ CEpgInfoTag tag;
++ if (channel && channel->GetEPGNow(tag))
++ return tag.StartAsLocalTime().GetAsLocalizedTime("", false);
++ return CDateTime::GetCurrentDateTime().GetAsLocalizedTime("", false);
++ }
++ if (item->HasEPGInfoTag())
++ return item->GetEPGInfoTag()->StartAsLocalTime().GetAsLocalizedTime("", false);
++ if (item->HasPVRTimerInfoTag())
++ return item->GetPVRTimerInfoTag()->StartAsLocalTime().GetAsLocalizedTime("", false);
++ if (item->HasPVRRecordingInfoTag())
++ return item->GetPVRRecordingInfoTag()->RecordingTimeAsLocalTime().GetAsLocalizedTime("", false);
++ if (item->m_dateTime.IsValid())
++ return item->m_dateTime.GetAsLocalizedTime("", false);
++ break;
++ case LISTITEM_ENDTIME:
++ if (item->HasPVRChannelInfoTag())
++ {
++ const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL;
++ CEpgInfoTag tag;
++ if (channel && channel->GetEPGNow(tag))
++ return tag.EndAsLocalTime().GetAsLocalizedTime("", false);
++ return CDateTime::GetCurrentDateTime().GetAsLocalizedTime("", false);
++ }
++ if (item->HasEPGInfoTag())
++ return item->GetEPGInfoTag()->EndAsLocalTime().GetAsLocalizedTime("", false);
++ if (item->HasPVRTimerInfoTag())
++ return item->GetPVRTimerInfoTag()->EndAsLocalTime().GetAsLocalizedTime("", false);
++ break;
++ case LISTITEM_STARTDATE:
++ if (item->HasPVRChannelInfoTag())
++ {
++ const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL;
++ CEpgInfoTag tag;
++ if (channel && channel->GetEPGNow(tag))
++ return tag.StartAsLocalTime().GetAsLocalizedDate(true);
++ return CDateTime::GetCurrentDateTime().GetAsLocalizedDate(true);
++ }
++ if (item->HasEPGInfoTag())
++ return item->GetEPGInfoTag()->StartAsLocalTime().GetAsLocalizedDate(true);
++ if (item->HasPVRTimerInfoTag())
++ return item->GetPVRTimerInfoTag()->StartAsLocalTime().GetAsLocalizedDate(true);
++ if (item->HasPVRRecordingInfoTag())
++ return item->GetPVRRecordingInfoTag()->RecordingTimeAsLocalTime().GetAsLocalizedDate(true);
++ if (item->m_dateTime.IsValid())
++ return item->m_dateTime.GetAsLocalizedDate(true);
++ break;
++ case LISTITEM_ENDDATE:
++ if (item->HasPVRChannelInfoTag())
++ {
++ const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL;
++ CEpgInfoTag tag;
++ if (channel && channel->GetEPGNow(tag))
++ return tag.EndAsLocalTime().GetAsLocalizedDate(true);
++ return CDateTime::GetCurrentDateTime().GetAsLocalizedDate(true);
++ }
++ if (item->HasEPGInfoTag())
++ return item->GetEPGInfoTag()->EndAsLocalTime().GetAsLocalizedDate(true);
++ if (item->HasPVRTimerInfoTag())
++ return item->GetPVRTimerInfoTag()->EndAsLocalTime().GetAsLocalizedDate(true);
++ break;
++ case LISTITEM_CHANNEL_NUMBER:
++ {
++ CStdString number;
++ if (item->HasPVRChannelInfoTag())
++ number.Format("%i", item->GetPVRChannelInfoTag()->ChannelNumber());
++ if (item->HasEPGInfoTag() && item->GetEPGInfoTag()->HasPVRChannel())
++ number.Format("%i", item->GetEPGInfoTag()->PVRChannelNumber());
++ if (item->HasPVRTimerInfoTag())
++ number.Format("%i", item->GetPVRTimerInfoTag()->ChannelNumber());
++
++ return number;
++ }
++ break;
++ case LISTITEM_CHANNEL_NAME:
++ if (item->HasPVRChannelInfoTag())
++ return item->GetPVRChannelInfoTag()->ChannelName();
++ if (item->HasEPGInfoTag() && item->GetEPGInfoTag()->HasPVRChannel())
++ return item->GetEPGInfoTag()->PVRChannelName();
++ if (item->HasPVRRecordingInfoTag())
++ return item->GetPVRRecordingInfoTag()->m_strChannelName;
++ if (item->HasPVRTimerInfoTag())
++ return item->GetPVRTimerInfoTag()->ChannelName();
++ break;
++ case LISTITEM_NEXT_STARTTIME:
++ {
++ const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL;
++ CEpgInfoTag tag;
++ if (channel && channel->GetEPGNext(tag))
++ return tag.StartAsLocalTime().GetAsLocalizedTime("", false);
++ }
++ return CDateTime::GetCurrentDateTime().GetAsLocalizedTime("", false);
++ case LISTITEM_NEXT_ENDTIME:
++ {
++ const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL;
++ CEpgInfoTag tag;
++ if (channel && channel->GetEPGNext(tag))
++ return tag.EndAsLocalTime().GetAsLocalizedTime("", false);
++ }
++ return CDateTime::GetCurrentDateTime().GetAsLocalizedTime("", false);
++ case LISTITEM_NEXT_STARTDATE:
++ {
++ const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL;
++ CEpgInfoTag tag;
++ if (channel && channel->GetEPGNext(tag))
++ return tag.StartAsLocalTime().GetAsLocalizedDate(true);
++ }
++ return CDateTime::GetCurrentDateTime().GetAsLocalizedDate(true);
++ case LISTITEM_NEXT_ENDDATE:
++ {
++ const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL;
++ CEpgInfoTag tag;
++ if (channel && channel->GetEPGNext(tag))
++ return tag.EndAsLocalTime().GetAsLocalizedDate(true);
++ }
++ return CDateTime::GetCurrentDateTime().GetAsLocalizedDate(true);
++ case LISTITEM_NEXT_PLOT:
++ {
++ const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL;
++ CEpgInfoTag tag;
++ if (channel && channel->GetEPGNext(tag))
++ return tag.Plot();
++ }
++ return StringUtils::EmptyString;
++ case LISTITEM_NEXT_PLOT_OUTLINE:
++ {
++ const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL;
++ CEpgInfoTag tag;
++ if (channel && channel->GetEPGNext(tag))
++ return tag.PlotOutline();
++ }
++ return StringUtils::EmptyString;
++ case LISTITEM_NEXT_DURATION:
++ {
++ const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL;
++ CEpgInfoTag tag;
++ if (channel && channel->GetEPGNext(tag))
++ return StringUtils::SecondsToTimeString(tag.GetDuration());
++ }
++ return StringUtils::EmptyString;
++ case LISTITEM_NEXT_GENRE:
++ {
++ const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL;
++ CEpgInfoTag tag;
++ if (channel && channel->GetEPGNext(tag))
++ return tag.Genre();
++ }
++ return StringUtils::EmptyString;
++ case LISTITEM_NEXT_TITLE:
++ {
++ const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL;
++ CEpgInfoTag tag;
++ if (channel && channel->GetEPGNext(tag))
++ return tag.Title();
++ }
++ return StringUtils::EmptyString;
++ case LISTITEM_PARENTALRATING:
++ {
++ CStdString rating;
++ if (item->HasEPGInfoTag() && item->GetEPGInfoTag()->ParentalRating() > 0)
++ rating.Format("%i", item->GetEPGInfoTag()->ParentalRating());
++ return rating;
++ }
++ break;
+ case LISTITEM_PERCENT_PLAYED:
+ {
+ int val;
+@@ -4245,8 +4787,68 @@ bool CGUIInfoManager::GetItemBool(const CGUIListItem *item, int condition) const
+ return item->IsSelected();
+ else if (condition == LISTITEM_IS_FOLDER)
+ return item->m_bIsFolder;
+- else if (condition == LISTITEM_IS_RESUMABLE)
+- return (item->IsFileItem() && ((const CFileItem *)item)->HasVideoInfoTag() && ((const CFileItem *)item)->GetVideoInfoTag()->m_resumePoint.totalTimeInSeconds > 0);
++
++ if (item->IsFileItem())
++ {
++ const CFileItem *pItem = (const CFileItem *)item;
++ if (condition == LISTITEM_IS_RESUMABLE)
++ return (pItem->HasVideoInfoTag() && pItem->GetVideoInfoTag()->m_resumePoint.totalTimeInSeconds > 0);
++ else if (condition == LISTITEM_ISRECORDING)
++ {
++ if (!g_PVRManager.IsStarted())
++ return false;
++
++ if (pItem->HasPVRChannelInfoTag())
++ {
++ return pItem->GetPVRChannelInfoTag()->IsRecording();
++ }
++ else if (pItem->HasEPGInfoTag())
++ {
++ CPVRTimerInfoTag *timer = g_PVRTimers->GetMatch(pItem);
++ if (timer)
++ return timer->IsRecording();
++ }
++ else if (pItem->HasPVRTimerInfoTag())
++ {
++ const CPVRTimerInfoTag *timer = pItem->GetPVRTimerInfoTag();
++ if (timer)
++ return timer->IsRecording();
++ }
++ }
++ else if (condition == LISTITEM_HASTIMER)
++ {
++ if (pItem->HasEPGInfoTag())
++ {
++ CPVRTimerInfoTag *timer = g_PVRTimers->GetMatch(pItem);
++ if (timer)
++ return timer->IsActive();
++ }
++ }
++ else if (condition == LISTITEM_HAS_EPG)
++ {
++ if (pItem->HasPVRChannelInfoTag())
++ {
++ CEpgInfoTag epgTag;
++ return pItem->GetPVRChannelInfoTag()->GetEPGNow(epgTag);
++ }
++ else
++ {
++ return pItem->HasEPGInfoTag();
++ }
++ }
++ else if (condition == LISTITEM_ISENCRYPTED)
++ {
++ if (pItem->HasPVRChannelInfoTag())
++ {
++ return pItem->GetPVRChannelInfoTag()->IsEncrypted();
++ }
++ else if (pItem->HasEPGInfoTag() && pItem->GetEPGInfoTag()->HasPVRChannel())
++ {
++ return pItem->GetEPGInfoTag()->ChannelTag()->IsEncrypted();
++ }
++ }
++ }
++
+ return false;
+ }
+
+diff --git a/xbmc/GUIInfoManager.h b/xbmc/GUIInfoManager.h
+index eb4501e..6db2df6 100644
+--- a/xbmc/GUIInfoManager.h
++++ b/xbmc/GUIInfoManager.h
+@@ -32,6 +32,7 @@
+ #include "guilib/IMsgTargetCallback.h"
+ #include "inttypes.h"
+ #include "XBDateTime.h"
++#include "utils/Observer.h"
+ #include "interfaces/info/SkinVariable.h"
+
+ #include <list>
+@@ -143,7 +144,7 @@ namespace INFO
+ #define SYSTEM_HASLOCKS 140
+ #define SYSTEM_ISMASTER 141
+ #define SYSTEM_TRAYOPEN 142
+-#define SYSTEM_SHOW_EXIT_BUTTON 143
++#define SYSTEM_SHOW_EXIT_BUTTON 143
+ #define SYSTEM_ALARM_POS 144
+ #define SYSTEM_LOGGEDON 145
+ #define SYSTEM_PROFILENAME 146
+@@ -178,7 +179,9 @@ namespace INFO
+ #define SYSTEM_PROFILECOUNT 181
+ #define SYSTEM_ISFULLSCREEN 182
+ #define SYSTEM_ISSTANDALONE 183
+-#define SYSTEM_HAS_PVR 184
++#define SYSTEM_ISINHIBIT 184
++#define SYSTEM_HAS_SHUTDOWN 185
++#define SYSTEM_HAS_PVR 186
+
+ #define NETWORK_IP_ADDRESS 190
+ #define NETWORK_MAC_ADDRESS 191
+@@ -216,6 +219,9 @@ namespace INFO
+ #define MUSICPLAYER_ALBUM_ARTIST 226
+ #define MUSICPLAYER_PLAYCOUNT 227
+ #define MUSICPLAYER_LASTPLAYED 228
++#define MUSICPLAYER_CHANNEL_NAME 229
++#define MUSICPLAYER_CHANNEL_NUMBER 230
++#define MUSICPLAYER_CHANNEL_GROUP 231
+
+ #define VIDEOPLAYER_TITLE 250
+ #define VIDEOPLAYER_GENRE 251
+@@ -261,14 +267,29 @@ namespace INFO
+ #define VIDEOPLAYER_PLAYCOUNT 293
+ #define VIDEOPLAYER_LASTPLAYED 294
+
+-#define AUDIOSCROBBLER_ENABLED 300
+-#define AUDIOSCROBBLER_CONN_STATE 301
+-#define AUDIOSCROBBLER_SUBMIT_INT 302
+-#define AUDIOSCROBBLER_FILES_CACHED 303
+-#define AUDIOSCROBBLER_SUBMIT_STATE 304
+-#define LASTFM_RADIOPLAYING 305
+-#define LASTFM_CANLOVE 306
+-#define LASTFM_CANBAN 307
++#define VIDEOPLAYER_STARTTIME 295
++#define VIDEOPLAYER_ENDTIME 296
++#define VIDEOPLAYER_NEXT_TITLE 297
++#define VIDEOPLAYER_NEXT_GENRE 298
++#define VIDEOPLAYER_NEXT_PLOT 299
++#define VIDEOPLAYER_NEXT_PLOT_OUTLINE 300
++#define VIDEOPLAYER_NEXT_STARTTIME 301
++#define VIDEOPLAYER_NEXT_ENDTIME 302
++#define VIDEOPLAYER_NEXT_DURATION 303
++#define VIDEOPLAYER_CHANNEL_NAME 304
++#define VIDEOPLAYER_CHANNEL_NUMBER 305
++#define VIDEOPLAYER_CHANNEL_GROUP 306
++#define VIDEOPLAYER_PARENTAL_RATING 307
++#define VIDEOPLAYER_HAS_EPG 308
++
++#define AUDIOSCROBBLER_ENABLED 325
++#define AUDIOSCROBBLER_CONN_STATE 326
++#define AUDIOSCROBBLER_SUBMIT_INT 327
++#define AUDIOSCROBBLER_FILES_CACHED 328
++#define AUDIOSCROBBLER_SUBMIT_STATE 329
++#define LASTFM_RADIOPLAYING 330
++#define LASTFM_CANLOVE 331
++#define LASTFM_CANBAN 332
+
+ #define CONTAINER_SCROLL_PREVIOUS 345 // NOTE: These 5 must be kept in this consecutive order
+ #define CONTAINER_MOVE_PREVIOUS 346
+@@ -409,6 +430,56 @@ namespace INFO
+ #define FANART_COLOR3 1002
+ #define FANART_IMAGE 1003
+
++#define PVR_CONDITIONS_START 1100
++#define PVR_IS_RECORDING (PVR_CONDITIONS_START)
++#define PVR_HAS_TIMER (PVR_CONDITIONS_START + 1)
++#define PVR_HAS_NONRECORDING_TIMER (PVR_CONDITIONS_START + 2)
++#define PVR_HAS_EPG (PVR_CONDITIONS_START + 3)
++#define PVR_HAS_TXT (PVR_CONDITIONS_START + 4)
++#define PVR_HAS_DIRECTOR (PVR_CONDITIONS_START + 5)
++#define PVR_IS_PLAYING_TV (PVR_CONDITIONS_START + 6)
++#define PVR_IS_PLAYING_RADIO (PVR_CONDITIONS_START + 7)
++#define PVR_IS_PLAYING_RECORDING (PVR_CONDITIONS_START + 8)
++#define PVR_ACTUAL_STREAM_ENCRYPTED (PVR_CONDITIONS_START + 9)
++#define PVR_CONDITIONS_END PVR_ACTUAL_STREAM_ENCRYPTED
++
++#define PVR_STRINGS_START 1200
++#define PVR_NEXT_RECORDING_CHANNEL (PVR_STRINGS_START)
++#define PVR_NEXT_RECORDING_CHAN_ICO (PVR_STRINGS_START + 1)
++#define PVR_NEXT_RECORDING_DATETIME (PVR_STRINGS_START + 2)
++#define PVR_NEXT_RECORDING_TITLE (PVR_STRINGS_START + 3)
++#define PVR_NOW_RECORDING_CHANNEL (PVR_STRINGS_START + 4)
++#define PVR_NOW_RECORDING_CHAN_ICO (PVR_STRINGS_START + 5)
++#define PVR_NOW_RECORDING_DATETIME (PVR_STRINGS_START + 6)
++#define PVR_NOW_RECORDING_TITLE (PVR_STRINGS_START + 7)
++#define PVR_BACKEND_NAME (PVR_STRINGS_START + 8)
++#define PVR_BACKEND_VERSION (PVR_STRINGS_START + 9)
++#define PVR_BACKEND_HOST (PVR_STRINGS_START + 10)
++#define PVR_BACKEND_DISKSPACE (PVR_STRINGS_START + 11)
++#define PVR_BACKEND_CHANNELS (PVR_STRINGS_START + 12)
++#define PVR_BACKEND_TIMERS (PVR_STRINGS_START + 13)
++#define PVR_BACKEND_RECORDINGS (PVR_STRINGS_START + 14)
++#define PVR_BACKEND_NUMBER (PVR_STRINGS_START + 15)
++#define PVR_TOTAL_DISKSPACE (PVR_STRINGS_START + 16)
++#define PVR_NEXT_TIMER (PVR_STRINGS_START + 17)
++#define PVR_PLAYING_DURATION (PVR_STRINGS_START + 18)
++#define PVR_PLAYING_TIME (PVR_STRINGS_START + 19)
++#define PVR_PLAYING_PROGRESS (PVR_STRINGS_START + 20)
++#define PVR_ACTUAL_STREAM_CLIENT (PVR_STRINGS_START + 21)
++#define PVR_ACTUAL_STREAM_DEVICE (PVR_STRINGS_START + 22)
++#define PVR_ACTUAL_STREAM_STATUS (PVR_STRINGS_START + 23)
++#define PVR_ACTUAL_STREAM_SIG (PVR_STRINGS_START + 24)
++#define PVR_ACTUAL_STREAM_SNR (PVR_STRINGS_START + 25)
++#define PVR_ACTUAL_STREAM_SIG_PROGR (PVR_STRINGS_START + 26)
++#define PVR_ACTUAL_STREAM_SNR_PROGR (PVR_STRINGS_START + 27)
++#define PVR_ACTUAL_STREAM_BER (PVR_STRINGS_START + 28)
++#define PVR_ACTUAL_STREAM_UNC (PVR_STRINGS_START + 29)
++#define PVR_ACTUAL_STREAM_VIDEO_BR (PVR_STRINGS_START + 30)
++#define PVR_ACTUAL_STREAM_AUDIO_BR (PVR_STRINGS_START + 31)
++#define PVR_ACTUAL_STREAM_DOLBY_BR (PVR_STRINGS_START + 32)
++#define PVR_ACTUAL_STREAM_CRYPTION (PVR_STRINGS_START + 33)
++#define PVR_STRINGS_END PVR_ACTUAL_STREAM_CRYPTION
++
+ #define WINDOW_PROPERTY 9993
+ #define WINDOW_IS_TOPMOST 9994
+ #define WINDOW_IS_VISIBLE 9995
+@@ -488,9 +559,31 @@ namespace INFO
+ #define LISTITEM_LASTPLAYED (LISTITEM_START + 57)
+ #define LISTITEM_FOLDERPATH (LISTITEM_START + 58)
+ #define LISTITEM_DISC_NUMBER (LISTITEM_START + 59)
+-#define LISTITEM_FILE_EXTENSION (LISTITEM_START + 60)
+-#define LISTITEM_IS_RESUMABLE (LISTITEM_START + 61)
+-#define LISTITEM_PERCENT_PLAYED (LISTITEM_START + 62)
++#define LISTITEM_STARTTIME (LISTITEM_START + 60)
++#define LISTITEM_ENDTIME (LISTITEM_START + 61)
++#define LISTITEM_STARTDATE (LISTITEM_START + 62)
++#define LISTITEM_ENDDATE (LISTITEM_START + 63)
++#define LISTITEM_NEXT_TITLE (LISTITEM_START + 64)
++#define LISTITEM_NEXT_GENRE (LISTITEM_START + 65)
++#define LISTITEM_NEXT_PLOT (LISTITEM_START + 66)
++#define LISTITEM_NEXT_PLOT_OUTLINE (LISTITEM_START + 67)
++#define LISTITEM_NEXT_STARTTIME (LISTITEM_START + 68)
++#define LISTITEM_NEXT_ENDTIME (LISTITEM_START + 69)
++#define LISTITEM_NEXT_STARTDATE (LISTITEM_START + 70)
++#define LISTITEM_NEXT_ENDDATE (LISTITEM_START + 71)
++#define LISTITEM_NEXT_DURATION (LISTITEM_START + 72)
++#define LISTITEM_CHANNEL_NAME (LISTITEM_START + 73)
++#define LISTITEM_CHANNEL_NUMBER (LISTITEM_START + 74)
++#define LISTITEM_CHANNEL_GROUP (LISTITEM_START + 75)
++#define LISTITEM_HASTIMER (LISTITEM_START + 76)
++#define LISTITEM_ISRECORDING (LISTITEM_START + 77)
++#define LISTITEM_ISENCRYPTED (LISTITEM_START + 78)
++#define LISTITEM_PARENTALRATING (LISTITEM_START + 79)
++#define LISTITEM_PROGRESS (LISTITEM_START + 80)
++#define LISTITEM_FILE_EXTENSION (LISTITEM_START + 81)
++#define LISTITEM_IS_RESUMABLE (LISTITEM_START + 82)
++#define LISTITEM_PERCENT_PLAYED (LISTITEM_START + 83)
++#define LISTITEM_HAS_EPG (LISTITEM_START + 84)
+
+ #define LISTITEM_PROPERTY_START (LISTITEM_START + 200)
+ #define LISTITEM_PROPERTY_END (LISTITEM_PROPERTY_START + 1000)
+@@ -547,7 +640,7 @@ private:
+ \ingroup strings
+ \brief
+ */
+-class CGUIInfoManager : public IMsgTargetCallback
++class CGUIInfoManager : public IMsgTargetCallback, public Observable
+ {
+ public:
+ CGUIInfoManager(void);
+diff --git a/xbmc/GUIViewControl.cpp b/xbmc/GUIViewControl.cpp
+index ce0b945..0f043fc 100644
+--- a/xbmc/GUIViewControl.cpp
++++ b/xbmc/GUIViewControl.cpp
+@@ -124,8 +124,9 @@ void CGUIViewControl::SetCurrentView(int viewMode)
+ g_windowManager.SendMessage(msg);
+ }
+
+- // Update our view control
+- UpdateViewAsControl(((CGUIBaseContainer *)pNewView)->GetLabel());
++ // Update our view control only if we are not in the TV Window
++ if (m_parentWindow != WINDOW_PVR)
++ UpdateViewAsControl(((CGUIBaseContainer *)pNewView)->GetLabel());
+ }
+
+ void CGUIViewControl::SetItems(CFileItemList &items)
+diff --git a/xbmc/GUIViewState.cpp b/xbmc/GUIViewState.cpp
+index 2c5f7c7..3a1dc98 100644
+--- a/xbmc/GUIViewState.cpp
++++ b/xbmc/GUIViewState.cpp
+@@ -20,6 +20,7 @@
+ */
+
+ #include "GUIViewState.h"
++#include "pvr/windows/GUIViewStatePVR.h"
+ #include "addons/GUIViewStateAddonBrowser.h"
+ #include "music/GUIViewStateMusic.h"
+ #include "video/GUIViewStateVideo.h"
+@@ -47,6 +48,7 @@
+
+ using namespace std;
+ using namespace ADDON;
++using namespace PVR;
+
+ CStdString CGUIViewState::m_strPlaylistDirectory;
+ VECSOURCES CGUIViewState::m_sources;
+@@ -119,6 +121,9 @@ CGUIViewState* CGUIViewState::GetViewState(int windowId, const CFileItemList& it
+ if (windowId==WINDOW_VIDEO_PLAYLIST)
+ return new CGUIViewStateWindowVideoPlaylist(items);
+
++ if (windowId==WINDOW_PVR)
++ return new CGUIViewStatePVR(items);
++
+ if (windowId==WINDOW_PICTURES)
+ return new CGUIViewStateWindowPictures(items);
+
+diff --git a/xbmc/SortFileItem.cpp b/xbmc/SortFileItem.cpp
+index e69ca91..8268022 100644
+--- a/xbmc/SortFileItem.cpp
++++ b/xbmc/SortFileItem.cpp
+@@ -20,6 +20,10 @@
+ */
+
+ #include "SortFileItem.h"
++#include "video/VideoInfoTag.h"
++#include "pvr/channels/PVRChannel.h"
++#include "epg/Epg.h"
++#include "pvr/timers/PVRTimerInfoTag.h"
+ #include "settings/AdvancedSettings.h"
+ #include "utils/StringUtils.h"
+ #include "music/tags/MusicInfoTag.h"
+@@ -28,6 +32,8 @@
+ #include "utils/log.h"
+ #include "video/VideoInfoTag.h"
+
++using namespace PVR;
++
+ #define RETURN_IF_NULL(x,y) if ((x) == NULL) { CLog::Log(LOGWARNING, "%s, sort item is null", __FUNCTION__); return y; }
+
+ CStdString SSortFileItem::RemoveArticles(const CStdString &label)
+@@ -483,6 +489,18 @@ void SSortFileItem::ByProductionCode(CFileItemPtr &item)
+ item->SetSortLabel(item->GetVideoInfoTag()->m_strProductionCode);
+ }
+
++void SSortFileItem::ByChannel(CFileItemPtr &item)
++{
++ if (!item) return;
++
++ if (item->IsEPG() || item->IsPVRChannel())
++ {
++ CPVRChannel *channel = item->GetPVRChannelInfoTag();
++ if (channel)
++ item->SetSortLabel(channel->ChannelName());
++ }
++}
++
+ void SSortFileItem::ByBitrate(CFileItemPtr &item)
+ {
+ if (!item) return;
+diff --git a/xbmc/SortFileItem.h b/xbmc/SortFileItem.h
+index 9c64c57..3e088d1 100644
+--- a/xbmc/SortFileItem.h
++++ b/xbmc/SortFileItem.h
+@@ -57,6 +57,7 @@ struct SSortFileItem
+ static void BySongTrackNum(CFileItemPtr &item);
+ static void BySongDuration(CFileItemPtr &item);
+ static void BySongRating(CFileItemPtr &item);
++ static void ByChannel(CFileItemPtr &item);
+
+ static void ByProgramCount(CFileItemPtr &item);
+
+@@ -120,6 +121,7 @@ typedef enum {
+ SORT_METHOD_PLAYCOUNT,
+ SORT_METHOD_LISTENERS,
+ SORT_METHOD_UNSORTED,
++ SORT_METHOD_CHANNEL,
+ SORT_METHOD_BITRATE,
+ SORT_METHOD_MAX
+ } SORT_METHOD;
+diff --git a/xbmc/URL.cpp b/xbmc/URL.cpp
+index 05781f4..951912f 100644
+--- a/xbmc/URL.cpp
++++ b/xbmc/URL.cpp
+@@ -157,6 +157,7 @@ void CURL::Parse(const CStdString& strURL1)
+ int iEnd = strURL.length();
+ const char* sep = NULL;
+
++ //TODO fix all Addon paths
+ CStdString strProtocol2 = GetTranslatedProtocol();
+ if(m_strProtocol.Equals("rss") ||
+ m_strProtocol.Equals("rar") ||
+@@ -166,6 +167,7 @@ void CURL::Parse(const CStdString& strURL1)
+ if(strProtocol2.Equals("http")
+ || strProtocol2.Equals("https")
+ || strProtocol2.Equals("plugin")
++ || m_strProtocol.Equals("addon")
+ || strProtocol2.Equals("hdhomerun")
+ || strProtocol2.Equals("rtsp")
+ || strProtocol2.Equals("zip"))
+@@ -290,6 +292,7 @@ void CURL::Parse(const CStdString& strURL1)
+ || m_strProtocol.CompareNoCase("videodb") == 0
+ || m_strProtocol.CompareNoCase("sources") == 0
+ || m_strProtocol.CompareNoCase("lastfm") == 0
++ || m_strProtocol.CompareNoCase("pvr") == 0
+ || m_strProtocol.Left(3).CompareNoCase("mem") == 0)
+ {
+ if (m_strHostName != "" && m_strFileName != "")
+diff --git a/xbmc/Util.cpp b/xbmc/Util.cpp
+index 7e6489b..a43ac2a 100644
+--- a/xbmc/Util.cpp
++++ b/xbmc/Util.cpp
+@@ -43,6 +43,7 @@
+ #include "Util.h"
+ #include "addons/Addon.h"
+ #include "storage/IoSupport.h"
++#include "filesystem/PVRDirectory.h"
+ #include "filesystem/StackDirectory.h"
+ #include "filesystem/MultiPathDirectory.h"
+ #include "filesystem/DirectoryCache.h"
+@@ -562,6 +563,39 @@ void CUtil::GetHomePath(CStdString& strPath, const CStdString& strTarget)
+ #endif
+ }
+
++bool CUtil::IsPVR(const CStdString& strFile)
++{
++ return strFile.Left(4).Equals("pvr:");
++}
++
++bool CUtil::IsHTSP(const CStdString& strFile)
++{
++ return strFile.Left(5).Equals("htsp:");
++}
++
++bool CUtil::IsLiveTV(const CStdString& strFile)
++{
++ if (strFile.Left(14).Equals("pvr://channels"))
++ return true;
++
++ if(URIUtils::IsTuxBox(strFile)
++ || URIUtils::IsVTP(strFile)
++ || URIUtils::IsHDHomeRun(strFile)
++ || URIUtils::IsHTSP(strFile)
++ || strFile.Left(4).Equals("sap:"))
++ return true;
++
++ if (URIUtils::IsMythTV(strFile) && CMythDirectory::IsLiveTV(strFile))
++ return true;
++
++ return false;
++}
++
++bool CUtil::IsTVRecording(const CStdString& strFile)
++{
++ return strFile.Left(15).Equals("pvr://recording");
++}
++
+ bool CUtil::IsPicture(const CStdString& strFile)
+ {
+ CStdString extension = URIUtils::GetExtension(strFile);
+@@ -1896,6 +1930,8 @@ bool CUtil::SupportsFileOperations(const CStdString& strPath)
+ return true;
+ if (URIUtils::IsSmb(strPath))
+ return true;
++ if (CUtil::IsTVRecording(strPath))
++ return CPVRDirectory::SupportsFileOperations(strPath);
+ if (URIUtils::IsNfs(strPath))
+ return true;
+ if (URIUtils::IsAfp(strPath))
+diff --git a/xbmc/Util.h b/xbmc/Util.h
+index 96de5d1..ef94385 100644
+--- a/xbmc/Util.h
++++ b/xbmc/Util.h
+@@ -65,6 +65,10 @@ public:
+ static void GetQualifiedFilename(const CStdString &strBasePath, CStdString &strFilename);
+ static void RunShortcut(const char* szPath);
+ static void GetHomePath(CStdString& strPath, const CStdString& strTarget = "XBMC_HOME");
++ static bool IsPVR(const CStdString& strFile);
++ static bool IsHTSP(const CStdString& strFile);
++ static bool IsLiveTV(const CStdString& strFile);
++ static bool IsTVRecording(const CStdString& strFile);
+ static bool ExcludeFileOrFolder(const CStdString& strFileOrFolder, const CStdStringArray& regexps);
+ static void GetFileAndProtocol(const CStdString& strURL, CStdString& strDir);
+ static int GetDVDIfoTitle(const CStdString& strPathFile);
+diff --git a/xbmc/XBDateTime.cpp b/xbmc/XBDateTime.cpp
+index 5e97385..47d59e9 100644
+--- a/xbmc/XBDateTime.cpp
++++ b/xbmc/XBDateTime.cpp
+@@ -862,6 +862,45 @@ CStdString CDateTime::GetAsDBDateTime() const
+ return date;
+ }
+
++CStdString CDateTime::GetAsSaveString() const
++{
++ SYSTEMTIME st;
++ GetAsSystemTime(st);
++
++ CStdString date;
++ date.Format("%04i%02i%02i_%02i%02i%02i", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
++
++ return date;
++}
++
++void CDateTime::SetFromUTCDateTime(const CDateTime &dateTime)
++{
++ TIME_ZONE_INFORMATION tz;
++ CDateTime tmp(dateTime);
++
++ switch(GetTimeZoneInformation(&tz))
++ {
++ case TIME_ZONE_ID_DAYLIGHT:
++ tmp -= CDateTimeSpan(0, 0, tz.Bias + tz.DaylightBias, 0);
++ break;
++ case TIME_ZONE_ID_STANDARD:
++ tmp -= CDateTimeSpan(0, 0, tz.Bias + tz.StandardBias, 0);
++ break;
++ case TIME_ZONE_ID_UNKNOWN:
++ tmp -= CDateTimeSpan(0, 0, tz.Bias, 0);
++ break;
++ }
++
++ m_time = tmp.m_time;
++ m_state = tmp.m_state;
++}
++
++void CDateTime::SetFromUTCDateTime(const time_t &dateTime)
++{
++ CDateTime tmp(dateTime);
++ SetFromUTCDateTime(tmp);
++}
++
+ void CDateTime::SetFromW3CDate(const CStdString &dateTime)
+ {
+ CStdString date, time, zone;
+diff --git a/xbmc/XBDateTime.h b/xbmc/XBDateTime.h
+index d4a36fe..c161616 100644
+--- a/xbmc/XBDateTime.h
++++ b/xbmc/XBDateTime.h
+@@ -169,6 +169,8 @@ public:
+ void SetFromDBDate(const CStdString &date);
+ void SetFromDBTime(const CStdString &time);
+ void SetFromW3CDate(const CStdString &date);
++ void SetFromUTCDateTime(const CDateTime &dateTime);
++ void SetFromUTCDateTime(const time_t &dateTime);
+
+ /*! \brief set from a database datetime format YYYY-MM-DD HH:MM:SS
+ \sa GetAsDBDateTime()
+@@ -181,6 +183,7 @@ public:
+ void GetAsTimeStamp(FILETIME& time) const;
+
+ CDateTime GetAsUTCDateTime() const;
++ CStdString GetAsSaveString() const;
+ CStdString GetAsDBDateTime() const;
+ CStdString GetAsDBDate() const;
+ CStdString GetAsLocalizedDate(bool longDate=false, bool withShortNames=true) const;
+diff --git a/xbmc/addons/Addon.cpp b/xbmc/addons/Addon.cpp
+index 98e7f57..9ce8b9b 100644
+--- a/xbmc/addons/Addon.cpp
++++ b/xbmc/addons/Addon.cpp
+@@ -75,7 +75,7 @@ static const TypeMapping types[] =
+ {"xbmc.gui.skin", ADDON_SKIN, 166, "DefaultAddonSkin.png" },
+ {"xbmc.gui.webinterface", ADDON_WEB_INTERFACE, 199, "DefaultAddonWebSkin.png" },
+ {"xbmc.addon.repository", ADDON_REPOSITORY, 24011, "DefaultAddonRepository.png" },
+- {"pvrclient", ADDON_PVRDLL, 0, "" },
++ {"xbmc.pvrclient", ADDON_PVRDLL, 24019, "" },
+ {"xbmc.addon.video", ADDON_VIDEO, 1037, "DefaultAddonVideo.png" },
+ {"xbmc.addon.audio", ADDON_AUDIO, 1038, "DefaultAddonMusic.png" },
+ {"xbmc.addon.image", ADDON_IMAGE, 1039, "DefaultAddonPicture.png" },
+@@ -281,6 +281,9 @@ void CAddon::BuildLibName(const cp_extension_t *extension)
+ case ADDON_VIZ:
+ ext = ADDON_VIS_EXT;
+ break;
++ case ADDON_PVRDLL:
++ ext = ADDON_PVRDLL_EXT;
++ break;
+ case ADDON_SCRIPT:
+ case ADDON_SCRIPT_LIBRARY:
+ case ADDON_SCRIPT_LYRICS:
+@@ -316,6 +319,7 @@ void CAddon::BuildLibName(const cp_extension_t *extension)
+ case ADDON_SCRAPER_MUSICVIDEOS:
+ case ADDON_SCRAPER_TVSHOWS:
+ case ADDON_SCRAPER_LIBRARY:
++ case ADDON_PVRDLL:
+ case ADDON_PLUGIN:
+ case ADDON_SERVICE:
+ {
+diff --git a/xbmc/addons/Addon.h b/xbmc/addons/Addon.h
+index 7dc63b9..704d54e 100644
+--- a/xbmc/addons/Addon.h
++++ b/xbmc/addons/Addon.h
+@@ -29,6 +29,7 @@
+
+ class CURL;
+ class TiXmlElement;
++class CAddonCallbacksAddon;
+
+ typedef struct cp_plugin_info_t cp_plugin_info_t;
+ typedef struct cp_extension_t cp_extension_t;
+@@ -171,6 +172,8 @@ public:
+ virtual bool ReloadSettings();
+
+ protected:
++ friend class CAddonCallbacksAddon;
++
+ CAddon(const CAddon&); // protected as all copying is handled by Clone()
+ CAddon(const CAddon&, const AddonPtr&);
+ const AddonPtr Parent() const { return m_parent; }
+@@ -209,7 +212,7 @@ protected:
+ bool m_userSettingsLoaded;
+
+ private:
+- friend class AddonMgr;
++ friend class CAddonMgr;
+ AddonProps m_props;
+ const AddonPtr m_parent;
+ CStdString m_userSettingsPath;
+diff --git a/xbmc/addons/AddonCallbacks.cpp b/xbmc/addons/AddonCallbacks.cpp
+new file mode 100644
+index 0000000..50bde6e
+--- /dev/null
++++ b/xbmc/addons/AddonCallbacks.cpp
+@@ -0,0 +1,142 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "Addon.h"
++#include "AddonCallbacks.h"
++#include "AddonCallbacksAddon.h"
++#include "AddonCallbacksGUI.h"
++#include "AddonCallbacksPVR.h"
++#include "filesystem/SpecialProtocol.h"
++#include "utils/log.h"
++
++namespace ADDON
++{
++
++CAddonCallbacks::CAddonCallbacks(CAddon* addon)
++{
++ m_addon = addon;
++ m_callbacks = new AddonCB;
++ m_helperAddon = NULL;
++ m_helperGUI = NULL;
++ m_helperPVR = NULL;
++
++ m_callbacks->libBasePath = strdup(_P("special://xbmcbin/addons"));
++ m_callbacks->addonData = this;
++ m_callbacks->AddOnLib_RegisterMe = CAddonCallbacks::AddOnLib_RegisterMe;
++ m_callbacks->AddOnLib_UnRegisterMe = CAddonCallbacks::AddOnLib_UnRegisterMe;
++ m_callbacks->GUILib_RegisterMe = CAddonCallbacks::GUILib_RegisterMe;
++ m_callbacks->GUILib_UnRegisterMe = CAddonCallbacks::GUILib_UnRegisterMe;
++ m_callbacks->PVRLib_RegisterMe = CAddonCallbacks::PVRLib_RegisterMe;
++ m_callbacks->PVRLib_UnRegisterMe = CAddonCallbacks::PVRLib_UnRegisterMe;
++}
++
++CAddonCallbacks::~CAddonCallbacks()
++{
++ delete m_helperAddon;
++ m_helperAddon = NULL;
++ delete m_helperGUI;
++ m_helperGUI = NULL;
++ delete m_helperPVR;
++ m_helperPVR = NULL;
++ delete m_callbacks->libBasePath;
++ delete m_callbacks;
++ m_callbacks = NULL;
++}
++
++CB_AddOnLib* CAddonCallbacks::AddOnLib_RegisterMe(void *addonData)
++{
++ CAddonCallbacks* addon = (CAddonCallbacks*) addonData;
++ if (addon == NULL)
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacks - %s - called with a null pointer", __FUNCTION__);
++ return NULL;
++ }
++
++ addon->m_helperAddon = new CAddonCallbacksAddon(addon->m_addon);
++ return addon->m_helperAddon->GetCallbacks();
++}
++
++void CAddonCallbacks::AddOnLib_UnRegisterMe(void *addonData, CB_AddOnLib *cbTable)
++{
++ CAddonCallbacks* addon = (CAddonCallbacks*) addonData;
++ if (addon == NULL)
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacks - %s - called with a null pointer", __FUNCTION__);
++ return;
++ }
++
++ delete addon->m_helperAddon;
++ addon->m_helperAddon = NULL;
++}
++
++CB_GUILib* CAddonCallbacks::GUILib_RegisterMe(void *addonData)
++{
++ CAddonCallbacks* addon = (CAddonCallbacks*) addonData;
++ if (addon == NULL)
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacks - %s - called with a null pointer", __FUNCTION__);
++ return NULL;
++ }
++
++ addon->m_helperGUI = new CAddonCallbacksGUI(addon->m_addon);
++ return addon->m_helperGUI->GetCallbacks();
++}
++
++void CAddonCallbacks::GUILib_UnRegisterMe(void *addonData, CB_GUILib *cbTable)
++{
++ CAddonCallbacks* addon = (CAddonCallbacks*) addonData;
++ if (addon == NULL)
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacks - %s - called with a null pointer", __FUNCTION__);
++ return;
++ }
++
++ delete addon->m_helperGUI;
++ addon->m_helperGUI = NULL;
++}
++
++CB_PVRLib* CAddonCallbacks::PVRLib_RegisterMe(void *addonData)
++{
++ CAddonCallbacks* addon = (CAddonCallbacks*) addonData;
++ if (addon == NULL)
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacks - %s - called with a null pointer", __FUNCTION__);
++ return NULL;
++ }
++
++ addon->m_helperPVR = new CAddonCallbacksPVR(addon->m_addon);
++ return addon->m_helperPVR->GetCallbacks();
++}
++
++void CAddonCallbacks::PVRLib_UnRegisterMe(void *addonData, CB_PVRLib *cbTable)
++{
++ CAddonCallbacks* addon = (CAddonCallbacks*) addonData;
++ if (addon == NULL)
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacks - %s - called with a null pointer", __FUNCTION__);
++ return;
++ }
++
++ delete addon->m_helperPVR;
++ addon->m_helperPVR = NULL;
++}
++
++}; /* namespace ADDON */
+diff --git a/xbmc/addons/AddonCallbacks.h b/xbmc/addons/AddonCallbacks.h
+new file mode 100644
+index 0000000..89d2518
+--- /dev/null
++++ b/xbmc/addons/AddonCallbacks.h
+@@ -0,0 +1,292 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.h"
++#include "addons/include/xbmc_pvr_types.h"
++#include "../../addons/library.xbmc.addon/libXBMC_addon.h"
++#include "../../addons/library.xbmc.gui/libXBMC_gui.h"
++
++typedef void (*AddOnLogCallback)(void *addonData, const ADDON::addon_log_t loglevel, const char *msg);
++typedef void (*AddOnQueueNotification)(void *addonData, const ADDON::queue_msg_t type, const char *msg);
++typedef bool (*AddOnGetSetting)(void *addonData, const char *settingName, void *settingValue);
++typedef char* (*AddOnUnknownToUTF8)(const char *sourceDest);
++typedef const char* (*AddOnGetLocalizedString)(const void* addonData, long dwCode);
++typedef const char* (*AddOnGetDVDMenuLanguage)(const void* addonData);
++typedef const char* (*AddOnGetLocalizedDate)(const void* addonData,time_t time, bool bLongDate, bool bWithShortNames);
++typedef const char* (*AddOnGetLocalizedTime)(const void* addonData,time_t time, bool bWithSeconds);
++
++typedef struct CB_AddOn
++{
++ AddOnLogCallback Log;
++ AddOnQueueNotification QueueNotification;
++ AddOnGetSetting GetSetting;
++ AddOnUnknownToUTF8 UnknownToUTF8;
++ AddOnGetLocalizedString GetLocalizedString;
++ AddOnGetDVDMenuLanguage GetDVDMenuLanguage;
++ AddOnGetLocalizedDate GetLocalizedDate;
++ AddOnGetLocalizedTime GetLocalizedTime;
++} CB_AddOnLib;
++
++typedef void (*GUILock)();
++typedef void (*GUIUnlock)();
++typedef int (*GUIGetScreenHeight)();
++typedef int (*GUIGetScreenWidth)();
++typedef int (*GUIGetVideoResolution)();
++typedef int (*GUIDialog_ShowYesNo)(const char* heading, const char* line0, const char* line1, const char* line2, int* bCanceled, const char* noLabel, const char* yesLabel);
++typedef GUIHANDLE (*GUIWindow_New)(void *addonData, const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog);
++typedef void (*GUIWindow_Delete)(void *addonData, GUIHANDLE handle);
++typedef void (*GUIWindow_SetCallbacks)(void *addonData, GUIHANDLE handle, GUIHANDLE clienthandle, bool (*)(GUIHANDLE handle), bool (*)(GUIHANDLE handle, int), bool (*)(GUIHANDLE handle, int), bool (*)(GUIHANDLE handle, int),bool (*)(GUIHANDLE ,int ,int , unsigned int));
++typedef bool (*GUIWindow_Show)(void *addonData, GUIHANDLE handle);
++typedef bool (*GUIWindow_Close)(void *addonData, GUIHANDLE handle);
++typedef bool (*GUIWindow_DoModal)(void *addonData, GUIHANDLE handle);
++typedef bool (*GUIWindow_SetFocusId)(void *addonData, GUIHANDLE handle, int iControlId);
++typedef int (*GUIWindow_GetFocusId)(void *addonData, GUIHANDLE handle);
++typedef bool (*GUIWindow_SetCoordinateResolution)(void *addonData, GUIHANDLE handle, int res);
++typedef void (*GUIWindow_SetProperty)(void *addonData, GUIHANDLE handle, const char *key, const char *value);
++typedef void (*GUIWindow_SetPropertyInt)(void *addonData, GUIHANDLE handle, const char *key, int value);
++typedef void (*GUIWindow_SetPropertyBool)(void *addonData, GUIHANDLE handle, const char *key, bool value);
++typedef void (*GUIWindow_SetPropertyDouble)(void *addonData, GUIHANDLE handle, const char *key, double value);
++typedef const char* (*GUIWindow_GetProperty)(void *addonData, GUIHANDLE handle, const char *key);
++typedef int (*GUIWindow_GetPropertyInt)(void *addonData, GUIHANDLE handle, const char *key);
++typedef bool (*GUIWindow_GetPropertyBool)(void *addonData, GUIHANDLE handle, const char *key);
++typedef double (*GUIWindow_GetPropertyDouble)(void *addonData, GUIHANDLE handle, const char *key);
++typedef void (*GUIWindow_ClearProperties)(void *addonData, GUIHANDLE handle);
++typedef int (*GUIWindow_GetListSize)(void *addonData, GUIHANDLE handle);
++typedef void (*GUIWindow_ClearList)(void *addonData, GUIHANDLE handle);
++typedef GUIHANDLE (*GUIWindow_AddItem)(void *addonData, GUIHANDLE handle, GUIHANDLE item, int itemPosition);
++typedef GUIHANDLE (*GUIWindow_AddStringItem)(void *addonData, GUIHANDLE handle, const char *itemName, int itemPosition);
++typedef void (*GUIWindow_RemoveItem)(void *addonData, GUIHANDLE handle, int itemPosition);
++typedef GUIHANDLE (*GUIWindow_GetListItem)(void *addonData, GUIHANDLE handle, int listPos);
++typedef void (*GUIWindow_AddContextMenuButton)(void *addonData, GUIHANDLE handle,int controlId,unsigned int contextButtonId,const char* label);
++typedef void (*GUIWindow_SetCurrentListPosition)(void *addonData, GUIHANDLE handle, int listPos);
++typedef int (*GUIWindow_GetCurrentListPosition)(void *addonData, GUIHANDLE handle);
++typedef GUIHANDLE (*GUIWindow_GetControl_Spin)(void *addonData, GUIHANDLE handle, int controlId);
++typedef GUIHANDLE (*GUIWindow_GetControl_ListContainer)(void *addonData, GUIHANDLE handle, int controlId,GUIHANDLE *listItems);
++typedef void (*GUIWindow_ReleaseControl_ListContainer)(GUIHANDLE listItems);
++typedef GUIHANDLE (*GUIWindow_GetControl_Button)(void *addonData, GUIHANDLE handle, int controlId);
++typedef GUIHANDLE (*GUIWindow_GetControl_RadioButton)(void *addonData, GUIHANDLE handle, int controlId);
++typedef GUIHANDLE (*GUIWindow_GetControl_Edit)(void *addonData, GUIHANDLE handle, int controlId);
++typedef GUIHANDLE (*GUIWindow_GetControl_Progress)(void *addonData, GUIHANDLE handle, int controlId);
++typedef void (*GUIWindow_SetControlLabel)(void *addonData, GUIHANDLE handle, int controlId, const char *label);
++typedef void (*GUIControl_Spin_SetVisible)(void *addonData, GUIHANDLE spinhandle, bool yesNo);
++typedef void (*GUIControl_Spin_SetText)(void *addonData, GUIHANDLE spinhandle, const char *label);
++typedef void (*GUIControl_Spin_Clear)(void *addonData, GUIHANDLE spinhandle);
++typedef void (*GUIControl_Spin_AddLabel)(void *addonData, GUIHANDLE spinhandle, const char *label, int iValue);
++typedef int (*GUIControl_Spin_GetValue)(void *addonData, GUIHANDLE spinhandle);
++typedef void (*GUIControl_Spin_SetValue)(void *addonData, GUIHANDLE spinhandle, int iValue);
++typedef void (*GUIControl_ListContainer_SetVisible)(void *addonData, GUIHANDLE handle, bool yesNo);
++typedef void (*GUIControl_ListContainer_AddItems)(void *addonData, GUIHANDLE handle,GUIHANDLE listItems,GUIHANDLE items[],int size);
++typedef GUIHANDLE (*GUIControl_ListContainer_GetItem)(void *addonData, GUIHANDLE handle,GUIHANDLE listItems,int index);
++typedef int (*GUIControl_ListContainer_GetSelected)(void *addonData, GUIHANDLE handle);
++typedef void (*GUIControl_ListContainer_Reset)(void *addonData, GUIHANDLE handle,GUIHANDLE listItems);
++typedef void (*GUIControl_RadioButton_SetVisible)(void *addonData, GUIHANDLE handle, bool yesNo);
++typedef void (*GUIControl_RadioButton_SetText)(void *addonData, GUIHANDLE handle, const char *label);
++typedef void (*GUIControl_RadioButton_SetSelected)(void *addonData, GUIHANDLE handle, bool yesNo);
++typedef bool (*GUIControl_RadioButton_IsSelected)(void *addonData, GUIHANDLE handle);
++typedef void (*GUIControl_Progress_SetPercentage)(void *addonData, GUIHANDLE handle, float fPercent);
++typedef float (*GUIControl_Progress_GetPercentage)(void *addonData, GUIHANDLE handle);
++typedef void (*GUIControl_Progress_SetInfo)(void *addonData, GUIHANDLE handle, int iInfo);
++typedef int (*GUIControl_Progress_GetInfo)(void *addonData, GUIHANDLE handle);
++typedef const char* (*GUIControl_Progress_GetDescription)(void *addonData, GUIHANDLE handle);
++typedef GUIHANDLE (*GUIListItem_Create)(void *addonData, const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path);
++typedef void (*GUIListItem_Destroy)(void *addonData, GUIHANDLE handle);
++typedef const char* (*GUIListItem_GetLabel)(void *addonData, GUIHANDLE handle);
++typedef void (*GUIListItem_SetLabel)(void *addonData, GUIHANDLE handle, const char *label);
++typedef const char* (*GUIListItem_GetLabel2)(void *addonData, GUIHANDLE handle);
++typedef void (*GUIListItem_SetLabel2)(void *addonData, GUIHANDLE handle, const char *label);
++typedef void (*GUIListItem_SetIconImage)(void *addonData, GUIHANDLE handle, const char *image);
++typedef void (*GUIListItem_SetThumbnailImage)(void *addonData, GUIHANDLE handle, const char *image);
++typedef void (*GUIListItem_SetInfo)(void *addonData, GUIHANDLE handle, const char *info);
++typedef void (*GUIListItem_SetProperty)(void *addonData, GUIHANDLE handle, const char *key, const char *value);
++typedef const char* (*GUIListItem_GetProperty)(void *addonData, GUIHANDLE handle, const char *key);
++typedef void (*GUIListItem_SetPath)(void *addonData, GUIHANDLE handle, const char *path);
++
++typedef struct CB_GUILib
++{
++ GUILock Lock;
++ GUIUnlock Unlock;
++ GUIGetScreenHeight GetScreenHeight;
++ GUIGetScreenWidth GetScreenWidth;
++ GUIGetVideoResolution GetVideoResolution;
++ GUIDialog_ShowYesNo Dialog_ShowYesNo;
++ GUIWindow_New Window_New;
++ GUIWindow_Delete Window_Delete;
++ GUIWindow_SetCallbacks Window_SetCallbacks;
++ GUIWindow_Show Window_Show;
++ GUIWindow_Close Window_Close;
++ GUIWindow_DoModal Window_DoModal;
++ GUIWindow_SetFocusId Window_SetFocusId;
++ GUIWindow_GetFocusId Window_GetFocusId;
++ GUIWindow_SetCoordinateResolution Window_SetCoordinateResolution;
++ GUIWindow_SetProperty Window_SetProperty;
++ GUIWindow_SetPropertyInt Window_SetPropertyInt;
++ GUIWindow_SetPropertyBool Window_SetPropertyBool;
++ GUIWindow_SetPropertyDouble Window_SetPropertyDouble;
++ GUIWindow_GetProperty Window_GetProperty;
++ GUIWindow_GetPropertyInt Window_GetPropertyInt;
++ GUIWindow_GetPropertyBool Window_GetPropertyBool;
++ GUIWindow_GetPropertyDouble Window_GetPropertyDouble;
++ GUIWindow_ClearProperties Window_ClearProperties;
++ GUIWindow_GetListSize Window_GetListSize;
++ GUIWindow_ClearList Window_ClearList;
++ GUIWindow_AddItem Window_AddItem;
++ GUIWindow_AddStringItem Window_AddStringItem;
++ GUIWindow_RemoveItem Window_RemoveItem;
++ GUIWindow_GetListItem Window_GetListItem;
++ GUIWindow_AddContextMenuButton Window_AddContextMenuButton;
++ GUIWindow_SetCurrentListPosition Window_SetCurrentListPosition;
++ GUIWindow_GetCurrentListPosition Window_GetCurrentListPosition;
++ GUIWindow_GetControl_Spin Window_GetControl_Spin;
++ GUIWindow_GetControl_ListContainer Window_GetControl_ListContainer;
++ GUIWindow_ReleaseControl_ListContainer Window_ReleaseControl_ListContainer;
++ GUIWindow_GetControl_Button Window_GetControl_Button;
++ GUIWindow_GetControl_RadioButton Window_GetControl_RadioButton;
++ GUIWindow_GetControl_Edit Window_GetControl_Edit;
++ GUIWindow_GetControl_Progress Window_GetControl_Progress;
++ GUIWindow_SetControlLabel Window_SetControlLabel;
++ GUIControl_Spin_SetVisible Control_Spin_SetVisible;
++ GUIControl_Spin_SetText Control_Spin_SetText;
++ GUIControl_Spin_Clear Control_Spin_Clear;
++ GUIControl_Spin_AddLabel Control_Spin_AddLabel;
++ GUIControl_Spin_GetValue Control_Spin_GetValue;
++ GUIControl_Spin_SetValue Control_Spin_SetValue;
++ GUIControl_ListContainer_SetVisible Control_ListContainer_SetVisible;
++ GUIControl_ListContainer_AddItems Control_ListContainer_AddItems;
++ GUIControl_ListContainer_GetItem Control_ListContainer_GetItem;
++ GUIControl_ListContainer_GetSelected Control_ListContainer_GetSelected;
++ GUIControl_ListContainer_Reset Control_ListContainer_Reset;
++ GUIControl_RadioButton_SetVisible Control_RadioButton_SetVisible;
++ GUIControl_RadioButton_SetText Control_RadioButton_SetText;
++ GUIControl_RadioButton_SetSelected Control_RadioButton_SetSelected;
++ GUIControl_RadioButton_IsSelected Control_RadioButton_IsSelected;
++ GUIControl_Progress_SetPercentage Control_Progress_SetPercentage;
++ GUIControl_Progress_GetPercentage Control_Progress_GetPercentage;
++ GUIControl_Progress_SetInfo Control_Progress_SetInfo;
++ GUIControl_Progress_GetInfo Control_Progress_GetInfo;
++ GUIControl_Progress_GetDescription Control_Progress_GetDescription;
++ GUIListItem_Create ListItem_Create;
++ GUIListItem_Destroy ListItem_Destroy;
++ GUIListItem_GetLabel ListItem_GetLabel;
++ GUIListItem_SetLabel ListItem_SetLabel;
++ GUIListItem_GetLabel2 ListItem_GetLabel2;
++ GUIListItem_SetLabel2 ListItem_SetLabel2;
++ GUIListItem_SetIconImage ListItem_SetIconImage;
++ GUIListItem_SetThumbnailImage ListItem_SetThumbnailImage;
++ GUIListItem_SetInfo ListItem_SetInfo;
++ GUIListItem_SetProperty ListItem_SetProperty;
++ GUIListItem_GetProperty ListItem_GetProperty;
++ GUIListItem_SetPath ListItem_SetPath;
++
++} CB_GUILib;
++
++typedef void (*PVRTransferEpgEntry)(void *userData, const PVR_HANDLE handle, const EPG_TAG *epgentry);
++typedef void (*PVRTransferChannelEntry)(void *userData, const PVR_HANDLE handle, const PVR_CHANNEL *chan);
++typedef void (*PVRTransferTimerEntry)(void *userData, const PVR_HANDLE handle, const PVR_TIMER *timer);
++typedef void (*PVRTransferRecordingEntry)(void *userData, const PVR_HANDLE handle, const PVR_RECORDING *recording);
++typedef void (*PVRAddMenuHook)(void *addonData, PVR_MENUHOOK *hook);
++typedef void (*PVRRecording)(void *addonData, const char *Name, const char *FileName, bool On);
++typedef void (*PVRTriggerChannelUpdate)(void *addonData);
++typedef void (*PVRTriggerTimerUpdate)(void *addonData);
++typedef void (*PVRTriggerRecordingUpdate)(void *addonData);
++typedef void (*PVRTriggerChannelGroupsUpdate)(void *addonData);
++
++typedef void (*PVRTransferChannelGroup)(void *addonData, const PVR_HANDLE handle, const PVR_CHANNEL_GROUP *group);
++typedef void (*PVRTransferChannelGroupMember)(void *addonData, const PVR_HANDLE handle, const PVR_CHANNEL_GROUP_MEMBER *member);
++
++typedef void (*PVRFreeDemuxPacket)(void *addonData, DemuxPacket* pPacket);
++typedef DemuxPacket* (*PVRAllocateDemuxPacket)(void *addonData, int iDataSize);
++
++typedef struct CB_PVRLib
++{
++ PVRTransferEpgEntry TransferEpgEntry;
++ PVRTransferChannelEntry TransferChannelEntry;
++ PVRTransferTimerEntry TransferTimerEntry;
++ PVRTransferRecordingEntry TransferRecordingEntry;
++ PVRAddMenuHook AddMenuHook;
++ PVRRecording Recording;
++ PVRTriggerChannelUpdate TriggerChannelUpdate;
++ PVRTriggerTimerUpdate TriggerTimerUpdate;
++ PVRTriggerRecordingUpdate TriggerRecordingUpdate;
++ PVRTriggerChannelGroupsUpdate TriggerChannelGroupsUpdate;
++ PVRFreeDemuxPacket FreeDemuxPacket;
++ PVRAllocateDemuxPacket AllocateDemuxPacket;
++ PVRTransferChannelGroup TransferChannelGroup;
++ PVRTransferChannelGroupMember TransferChannelGroupMember;
++
++} CB_PVRLib;
++
++
++typedef CB_AddOnLib* (*XBMCAddOnLib_RegisterMe)(void *addonData);
++typedef void (*XBMCAddOnLib_UnRegisterMe)(void *addonData, CB_AddOnLib *cbTable);
++typedef CB_GUILib* (*XBMCGUILib_RegisterMe)(void *addonData);
++typedef void (*XBMCGUILib_UnRegisterMe)(void *addonData, CB_GUILib *cbTable);
++typedef CB_PVRLib* (*XBMCPVRLib_RegisterMe)(void *addonData);
++typedef void (*XBMCPVRLib_UnRegisterMe)(void *addonData, CB_PVRLib *cbTable);
++
++typedef struct AddonCB
++{
++ const char *libBasePath; ///> Never, never change this!!!
++ void *addonData;
++ XBMCAddOnLib_RegisterMe AddOnLib_RegisterMe;
++ XBMCAddOnLib_UnRegisterMe AddOnLib_UnRegisterMe;
++ XBMCGUILib_RegisterMe GUILib_RegisterMe;
++ XBMCGUILib_UnRegisterMe GUILib_UnRegisterMe;
++ XBMCPVRLib_RegisterMe PVRLib_RegisterMe;
++ XBMCPVRLib_UnRegisterMe PVRLib_UnRegisterMe;
++} AddonCB;
++
++
++namespace ADDON
++{
++
++class CAddon;
++class CAddonCallbacksAddon;
++class CAddonCallbacksGUI;
++class CAddonCallbacksPVR;
++
++class CAddonCallbacks
++{
++public:
++ CAddonCallbacks(CAddon* addon);
++ ~CAddonCallbacks();
++ AddonCB *GetCallbacks() { return m_callbacks; }
++
++ static CB_AddOnLib* AddOnLib_RegisterMe(void *addonData);
++ static void AddOnLib_UnRegisterMe(void *addonData, CB_AddOnLib *cbTable);
++ static CB_GUILib* GUILib_RegisterMe(void *addonData);
++ static void GUILib_UnRegisterMe(void *addonData, CB_GUILib *cbTable);
++ static CB_PVRLib* PVRLib_RegisterMe(void *addonData);
++ static void PVRLib_UnRegisterMe(void *addonData, CB_PVRLib *cbTable);
++
++ CAddonCallbacksAddon *GetHelperAddon() { return m_helperAddon; }
++ CAddonCallbacksGUI *GetHelperGUI() { return m_helperGUI; }
++ CAddonCallbacksPVR *GetHelperPVR() { return m_helperPVR; }
++
++private:
++ AddonCB *m_callbacks;
++ CAddon *m_addon;
++ CAddonCallbacksAddon *m_helperAddon;
++ CAddonCallbacksGUI *m_helperGUI;
++ CAddonCallbacksPVR *m_helperPVR;
++};
++
++}; /* namespace ADDON */
+diff --git a/xbmc/addons/AddonCallbacksAddon.cpp b/xbmc/addons/AddonCallbacksAddon.cpp
+new file mode 100644
+index 0000000..7d290b4
+--- /dev/null
++++ b/xbmc/addons/AddonCallbacksAddon.cpp
+@@ -0,0 +1,359 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "Application.h"
++#include "Addon.h"
++#include "AddonCallbacksAddon.h"
++#include "utils/log.h"
++#include "LangInfo.h"
++#include "dialogs/GUIDialogKaiToast.h"
++#include "XBDateTime.h"
++
++namespace ADDON
++{
++
++CAddonCallbacksAddon::CAddonCallbacksAddon(CAddon* addon)
++{
++ m_addon = addon;
++ m_callbacks = new CB_AddOnLib;
++
++ /* write XBMC addon-on specific add-on function addresses to the callback table */
++ m_callbacks->Log = AddOnLog;
++ m_callbacks->QueueNotification = QueueNotification;
++ m_callbacks->GetSetting = GetAddonSetting;
++ m_callbacks->UnknownToUTF8 = UnknownToUTF8;
++ m_callbacks->GetLocalizedString = GetLocalizedString;
++ m_callbacks->GetDVDMenuLanguage = GetDVDMenuLanguage;
++ m_callbacks->GetLocalizedDate = GetLocalizedDate;
++ m_callbacks->GetLocalizedTime = GetLocalizedTime;
++}
++
++CAddonCallbacksAddon::~CAddonCallbacksAddon()
++{
++ /* delete the callback table */
++ delete m_callbacks;
++}
++
++void CAddonCallbacksAddon::AddOnLog(void *addonData, const addon_log_t addonLogLevel, const char *strMessage)
++{
++ CAddonCallbacks* addon = (CAddonCallbacks*) addonData;
++ if (addon == NULL || strMessage == NULL)
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - called with a null pointer", __FUNCTION__);
++ return;
++ }
++
++ CAddonCallbacksAddon* addonHelper = addon->GetHelperAddon();
++
++ try
++ {
++ int xbmcLogLevel = LOGNONE;
++ switch (addonLogLevel)
++ {
++ case LOG_ERROR:
++ xbmcLogLevel = LOGERROR;
++ break;
++ case LOG_INFO:
++ xbmcLogLevel = LOGINFO;
++ break;
++ case LOG_NOTICE:
++ xbmcLogLevel = LOGNOTICE;
++ break;
++ case LOG_DEBUG:
++ default:
++ xbmcLogLevel = LOGDEBUG;
++ break;
++ }
++
++ CStdString strXbmcMessage;
++ strXbmcMessage.Format("AddOnLog: %s: %s", addonHelper->m_addon->Name().c_str(), strMessage);
++ CLog::Log(xbmcLogLevel, "%s", strXbmcMessage.c_str());
++ }
++ catch (std::exception &e)
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - exception '%s' caught in call in add-on '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), addonHelper->m_addon->Name().c_str(), addonHelper->m_addon->Author().c_str());
++ }
++}
++
++void CAddonCallbacksAddon::QueueNotification(void *addonData, const queue_msg_t type, const char *strMessage)
++{
++ CAddonCallbacks* addon = (CAddonCallbacks*) addonData;
++ if (addon == NULL || strMessage == NULL)
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - called with a null pointer", __FUNCTION__);
++ return;
++ }
++
++ CAddonCallbacksAddon* addonHelper = addon->GetHelperAddon();
++
++ try
++ {
++ switch (type)
++ {
++ case QUEUE_WARNING:
++ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Warning, addonHelper->m_addon->Name(), strMessage, 3000, true);
++ CLog::Log(LOGDEBUG, "CAddonCallbacksAddon - %s - %s - Warning Message: '%s'", __FUNCTION__, addonHelper->m_addon->Name().c_str(), strMessage);
++ break;
++
++ case QUEUE_ERROR:
++ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, addonHelper->m_addon->Name(), strMessage, 3000, true);
++ CLog::Log(LOGDEBUG, "CAddonCallbacksAddon - %s - %s - Error Message : '%s'", __FUNCTION__, addonHelper->m_addon->Name().c_str(), strMessage);
++ break;
++
++ case QUEUE_INFO:
++ default:
++ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, addonHelper->m_addon->Name(), strMessage, 3000, false);
++ CLog::Log(LOGDEBUG, "CAddonCallbacksAddon - %s - %s - Info Message : '%s'", __FUNCTION__, addonHelper->m_addon->Name().c_str(), strMessage);
++ break;
++ }
++ }
++ catch (std::exception &e)
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - exception '%s' caught in call in add-on '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), addonHelper->m_addon->Name().c_str(), addonHelper->m_addon->Author().c_str());
++ }
++}
++
++/*bool CAddonCallbacksAddon::UpdateAddonSetting(void *addonData, const char *strSettingName, void *settingValue)
++{
++ CAddonCallbacks* addon = (CAddonCallbacks*) addonData;
++ if (addon == NULL || strSettingName == NULL || settingValue == NULL)
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - called with a null pointer", __FUNCTION__);
++ return false;
++ }
++
++ CAddonCallbacksAddon* addonHelper = addon->GetHelperAddon();
++
++ try
++ {
++ CLog::Log(LOGDEBUG, "CAddonCallbacksAddon - %s - add-on '%s' requests update of setting '%s'", __FUNCTION__, addonHelper->m_addon->Name().c_str(), strSettingName);
++
++ if (!addonHelper->m_addon->ReloadSettings())
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - could't get settings for add-on '%s'", __FUNCTION__, addonHelper->m_addon->Name().c_str());
++ return false;
++ }
++
++ const TiXmlElement *category = addonHelper->m_addon->GetSettingsXML()->FirstChildElement("category");
++ if (!category) // add a default one...
++ category = addonHelper->m_addon->GetSettingsXML();
++
++ while (category)
++ {
++ const TiXmlElement *setting = category->FirstChildElement("setting");
++ while (setting)
++ {
++ const char *id = setting->Attribute("id");
++ const char *type = setting->Attribute("type");
++ CStdString value;
++ if (strcmpi(id, strSettingName) == 0 && type)
++ {
++ if (strcmpi(type, "text") == 0 || strcmpi(type, "ipaddress") == 0 ||
++ strcmpi(type, "folder") == 0 || strcmpi(type, "action") == 0 ||
++ strcmpi(type, "music") == 0 || strcmpi(type, "pictures") == 0 ||
++ strcmpi(type, "folder") == 0 || strcmpi(type, "programs") == 0 ||
++ strcmpi(type, "files") == 0 || strcmpi(type, "fileenum") == 0)
++ {
++ value = (char*) settingValue;
++ addonHelper->m_addon->UpdateSetting(id).c_str(), value);
++ return true;
++ }
++ else if (strcmpi(type, "number") == 0 || strcmpi(type, "enum") == 0 ||
++ strcmpi(type, "labelenum") == 0)
++ {
++ value.Format("%i",*(int*) settingValue);
++ addonHelper->m_addon->UpdateSetting(id).c_str(), value);
++ return true;
++ }
++ else if (strcmpi(type, "bool") == 0)
++ {
++ value = *(bool*) settingValue?"true" : "false";
++ addonHelper->m_addon->UpdateSetting(id).c_str(), value);
++ return true;
++ }
++ }
++ setting = setting->NextSiblingElement("setting");
++ }
++ category = category->NextSiblingElement("category");
++ }
++ CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - can't find setting '%s' in '%s'", __FUNCTION__, strSettingName, addonHelper->m_addon->Name().c_str());
++ }
++ catch (std::exception &e)
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - exception '%s' caught in call in add-on '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), addonHelper->m_addon->Name().c_str(), addonHelper->m_addon->Author().c_str());
++ }
++
++ return false;
++}*/
++
++
++bool CAddonCallbacksAddon::GetAddonSetting(void *addonData, const char *strSettingName, void *settingValue)
++{
++ CAddonCallbacks* addon = (CAddonCallbacks*) addonData;
++ if (addon == NULL || strSettingName == NULL || settingValue == NULL)
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - called with a null pointer", __FUNCTION__);
++ return false;
++ }
++
++ CAddonCallbacksAddon* addonHelper = addon->GetHelperAddon();
++
++ try
++ {
++ CLog::Log(LOGDEBUG, "CAddonCallbacksAddon - %s - add-on '%s' requests setting '%s'", __FUNCTION__, addonHelper->m_addon->Name().c_str(), strSettingName);
++
++ if (!addonHelper->m_addon->ReloadSettings())
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - could't get settings for add-on '%s'", __FUNCTION__, addonHelper->m_addon->Name().c_str());
++ return false;
++ }
++
++ const TiXmlElement *category = addonHelper->m_addon->GetSettingsXML()->FirstChildElement("category");
++ if (!category) // add a default one...
++ category = addonHelper->m_addon->GetSettingsXML();
++
++ while (category)
++ {
++ const TiXmlElement *setting = category->FirstChildElement("setting");
++ while (setting)
++ {
++ const char *id = setting->Attribute("id");
++ const char *type = setting->Attribute("type");
++
++ if (strcmpi(id, strSettingName) == 0 && type)
++ {
++ if (strcmpi(type, "text") == 0 || strcmpi(type, "ipaddress") == 0 ||
++ strcmpi(type, "folder") == 0 || strcmpi(type, "action") == 0 ||
++ strcmpi(type, "music") == 0 || strcmpi(type, "pictures") == 0 ||
++ strcmpi(type, "folder") == 0 || strcmpi(type, "programs") == 0 ||
++ strcmpi(type, "files") == 0 || strcmpi(type, "fileenum") == 0)
++ {
++ strcpy((char*) settingValue, addonHelper->m_addon->GetSetting(id).c_str());
++ return true;
++ }
++ else if (strcmpi(type, "number") == 0 || strcmpi(type, "enum") == 0 ||
++ strcmpi(type, "labelenum") == 0)
++ {
++ *(int*) settingValue = (int) atoi(addonHelper->m_addon->GetSetting(id));
++ return true;
++ }
++ else if (strcmpi(type, "bool") == 0)
++ {
++ *(bool*) settingValue = (bool) (addonHelper->m_addon->GetSetting(id) == "true" ? true : false);
++ return true;
++ }
++ }
++ setting = setting->NextSiblingElement("setting");
++ }
++ category = category->NextSiblingElement("category");
++ }
++ CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - can't find setting '%s' in '%s'", __FUNCTION__, strSettingName, addonHelper->m_addon->Name().c_str());
++ }
++ catch (std::exception &e)
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - exception '%s' caught in call in add-on '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), addonHelper->m_addon->Name().c_str(), addonHelper->m_addon->Author().c_str());
++ }
++
++ return false;
++}
++
++char* CAddonCallbacksAddon::UnknownToUTF8(const char *strSource)
++{
++ CStdString string;
++ if (strSource != NULL)
++ g_charsetConverter.unknownToUTF8(strSource, string);
++ else
++ string = "";
++ char *buffer = (char*) malloc (string.length()+1);
++ strcpy(buffer, string.c_str());
++ return buffer;
++}
++
++const char* CAddonCallbacksAddon::GetLocalizedString(const void* addonData, long dwCode)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || g_application.m_bStop)
++ return NULL;
++
++ CAddonCallbacksAddon* addonHelper = helper->GetHelperAddon();
++
++ CStdString string;
++ if (dwCode >= 30000 && dwCode <= 30999)
++ string = addonHelper->m_addon->GetString(dwCode).c_str();
++ else if (dwCode >= 32000 && dwCode <= 32999)
++ string = addonHelper->m_addon->GetString(dwCode).c_str();
++ else
++ string = g_localizeStrings.Get(dwCode).c_str();
++
++ char *buffer = (char*) malloc (string.length()+1);
++ strcpy(buffer, string.c_str());
++ return buffer;
++}
++
++const char* CAddonCallbacksAddon::GetDVDMenuLanguage(const void* addonData)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper)
++ return NULL;
++
++ CStdString string = g_langInfo.GetDVDMenuLanguage();
++
++ char *buffer = (char*) malloc (string.length()+1);
++ strcpy(buffer, string.c_str());
++ return buffer;
++}
++
++const char* CAddonCallbacksAddon::GetLocalizedDate(const void* addonData,time_t time, bool bLongDate, bool bWithShortNames)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper)
++ return NULL;
++
++ CDateTime date(time);
++
++ CStdString string = date.GetAsLocalizedDate(bLongDate,bWithShortNames);
++
++ char *buffer = (char*) malloc (string.length()+1);
++ strcpy(buffer, string.c_str());
++ return buffer;
++}
++
++const char* CAddonCallbacksAddon::GetLocalizedTime(const void* addonData,time_t time, bool bWithSeconds)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper)
++ return NULL;
++
++ CDateTime date(time);
++
++ CStdString string = date.GetAsLocalizedTime("",bWithSeconds);
++
++ char *buffer = (char*) malloc (string.length()+1);
++ strcpy(buffer, string.c_str());
++ return buffer;
++}
++
++
++}; /* namespace ADDON */
+diff --git a/xbmc/addons/AddonCallbacksAddon.h b/xbmc/addons/AddonCallbacksAddon.h
+new file mode 100644
+index 0000000..143e669
+--- /dev/null
++++ b/xbmc/addons/AddonCallbacksAddon.h
+@@ -0,0 +1,109 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "AddonCallbacks.h"
++
++namespace ADDON
++{
++
++class CAddonCallbacksAddon
++{
++public:
++ CAddonCallbacksAddon(CAddon* addon);
++ ~CAddonCallbacksAddon();
++
++ /*!
++ * @return The callback table.
++ */
++ CB_AddOnLib *GetCallbacks() { return m_callbacks; }
++
++ /*!
++ * @brief Add a message to XBMC's log.
++ * @param addonData A pointer to the add-on.
++ * @param addonLogLevel The log level of the message.
++ * @param strMessage The message itself.
++ */
++ static void AddOnLog(void *addonData, const addon_log_t addonLogLevel, const char *strMessage);
++
++ /*!
++ * @brief Queue a notification in the GUI.
++ * @param addonData A pointer to the add-on.
++ * @param type The message type.
++ * @param strMessage The message to display.
++ */
++ static void QueueNotification(void *addonData, const queue_msg_t type, const char *strMessage);
++
++ /*!
++ * @brief Get a settings value for this add-on.
++ * @param addonData A pointer to the add-on.
++ * @param settingName The name of the setting to get.
++ * @param settingValue The value.
++ * @return True if the settings was fetched successfully, false otherwise.
++ */
++ static bool GetAddonSetting(void *addonData, const char *strSettingName, void *settingValue);
++
++ /*!
++ * @brief Translate a string with an unknown encoding to UTF8.
++ * @param sourceDest The source string.
++ * @return The converted string.
++ */
++ static char *UnknownToUTF8(const char *strSource);
++
++ /*!
++ * @brief Get a localised message.
++ * @param addonData A pointer to the add-on.
++ * @param dwCode The code of the message to get.
++ * @return The message.
++ */
++ static const char *GetLocalizedString(const void* addonData, long dwCode);
++
++ /*!
++ * @brief Get the DVD menu language.
++ * @param addonData A pointer to the add-on.
++ * @return The language.
++ */
++ static const char *GetDVDMenuLanguage(const void* addonData);
++
++ /*!
++ * @brief Get a localised date
++ * @param addonData A pointer to the add-on.
++ * @param time The time_t to convert
++ * @param bLongDate Use long date format
++ * @param bWithShortNames Abbreviation month and weekday
++ * @return The localised date
++ */
++ static const char *GetLocalizedDate(const void* addonData,time_t time, bool bLongDate, bool bWithShortNames);
++
++ /*!
++ * @brief Get the localised time
++ * @param addonData A pointer to the add-on.
++ * @param time The time_t to convert
++ * @param bWithSeconds Include seconds
++ * @return The localised time
++ */
++ static const char *GetLocalizedTime(const void* addonData,time_t time, bool bWithSeconds);
++private:
++ CB_AddOnLib *m_callbacks; /*!< callback addresses */
++ CAddon *m_addon; /*!< the add-on */
++};
++
++}; /* namespace ADDON */
+diff --git a/xbmc/addons/AddonCallbacksGUI.cpp b/xbmc/addons/AddonCallbacksGUI.cpp
+new file mode 100644
+index 0000000..4a2665b
+--- /dev/null
++++ b/xbmc/addons/AddonCallbacksGUI.cpp
+@@ -0,0 +1,1762 @@
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "Application.h"
++#include "Addon.h"
++#include "AddonCallbacksGUI.h"
++#include "utils/log.h"
++#include "Skin.h"
++#include "FileItem.h"
++#include "filesystem/File.h"
++#include "utils/URIUtils.h"
++#include "utils/TimeUtils.h"
++#include "guilib/GUIWindowManager.h"
++#include "guilib/TextureManager.h"
++#include "settings/GUISettings.h"
++#include "guilib/GUISpinControlEx.h"
++#include "guilib/GUIRadioButtonControl.h"
++#include "guilib/GUISettingsSliderControl.h"
++#include "guilib/GUIEditControl.h"
++#include "guilib/GUIProgressControl.h"
++#include "guilib/GUIListContainer.h"
++#include "guilib/GUIWrappingListContainer.h"
++#include "guilib/GUIPanelContainer.h"
++#include "guilib/GUIFixedListContainer.h"
++#include "epg/GUIEPGGridContainer.h"
++#include "dialogs/GUIDialogYesNo.h"
++#include "GUIInfoManager.h"
++
++#define CONTROL_BTNVIEWASICONS 2
++#define CONTROL_BTNSORTBY 3
++#define CONTROL_BTNSORTASC 4
++#define CONTROL_LABELFILES 12
++
++using namespace std;
++
++namespace ADDON
++{
++
++static int iXBMCGUILockRef = 0;
++
++CAddonCallbacksGUI::CAddonCallbacksGUI(CAddon* addon)
++{
++ m_addon = addon;
++ m_callbacks = new CB_GUILib;
++
++ /* GUI Helper functions */
++ m_callbacks->Lock = CAddonCallbacksGUI::Lock;
++ m_callbacks->Unlock = CAddonCallbacksGUI::Unlock;
++ m_callbacks->GetScreenHeight = CAddonCallbacksGUI::GetScreenHeight;
++ m_callbacks->GetScreenWidth = CAddonCallbacksGUI::GetScreenWidth;
++ m_callbacks->GetVideoResolution = CAddonCallbacksGUI::GetVideoResolution;
++ m_callbacks->Dialog_ShowYesNo = CAddonCallbacksGUI::Dialog_ShowYesNo;
++ m_callbacks->Window_New = CAddonCallbacksGUI::Window_New;
++ m_callbacks->Window_Delete = CAddonCallbacksGUI::Window_Delete;
++ m_callbacks->Window_SetCallbacks = CAddonCallbacksGUI::Window_SetCallbacks;
++ m_callbacks->Window_Show = CAddonCallbacksGUI::Window_Show;
++ m_callbacks->Window_Close = CAddonCallbacksGUI::Window_Close;
++ m_callbacks->Window_DoModal = CAddonCallbacksGUI::Window_DoModal;
++ m_callbacks->Window_SetFocusId = CAddonCallbacksGUI::Window_SetFocusId;
++ m_callbacks->Window_GetFocusId = CAddonCallbacksGUI::Window_GetFocusId;
++ m_callbacks->Window_SetCoordinateResolution = CAddonCallbacksGUI::Window_SetCoordinateResolution;
++ m_callbacks->Window_SetProperty = CAddonCallbacksGUI::Window_SetProperty;
++ m_callbacks->Window_SetPropertyInt = CAddonCallbacksGUI::Window_SetPropertyInt;
++ m_callbacks->Window_SetPropertyBool = CAddonCallbacksGUI::Window_SetPropertyBool;
++ m_callbacks->Window_SetPropertyDouble = CAddonCallbacksGUI::Window_SetPropertyDouble;
++ m_callbacks->Window_GetProperty = CAddonCallbacksGUI::Window_GetProperty;
++ m_callbacks->Window_GetPropertyInt = CAddonCallbacksGUI::Window_GetPropertyInt;
++ m_callbacks->Window_GetPropertyBool = CAddonCallbacksGUI::Window_GetPropertyBool;
++ m_callbacks->Window_GetPropertyDouble = CAddonCallbacksGUI::Window_GetPropertyDouble;
++ m_callbacks->Window_ClearProperties = CAddonCallbacksGUI::Window_ClearProperties;
++
++ m_callbacks->Window_GetListSize = CAddonCallbacksGUI::Window_GetListSize;
++ m_callbacks->Window_ClearList = CAddonCallbacksGUI::Window_ClearList;
++ m_callbacks->Window_AddItem = CAddonCallbacksGUI::Window_AddItem;
++ m_callbacks->Window_AddStringItem = CAddonCallbacksGUI::Window_AddStringItem;
++ m_callbacks->Window_RemoveItem = CAddonCallbacksGUI::Window_RemoveItem;
++ m_callbacks->Window_GetListItem = CAddonCallbacksGUI::Window_GetListItem;
++ m_callbacks->Window_SetCurrentListPosition = CAddonCallbacksGUI::Window_SetCurrentListPosition;
++ m_callbacks->Window_GetCurrentListPosition = CAddonCallbacksGUI::Window_GetCurrentListPosition;
++
++ m_callbacks->Window_AddContextMenuButton = CAddonCallbacksGUI::Window_AddContextMenuButton;
++
++ m_callbacks->Window_GetControl_Spin = CAddonCallbacksGUI::Window_GetControl_Spin;
++ m_callbacks->Window_GetControl_ListContainer= CAddonCallbacksGUI::Window_GetControl_ListContainer;
++ m_callbacks->Window_ReleaseControl_ListContainer= CAddonCallbacksGUI::Window_ReleaseControl_ListContainer;
++ m_callbacks->Window_GetControl_Button = CAddonCallbacksGUI::Window_GetControl_Button;
++ m_callbacks->Window_GetControl_RadioButton = CAddonCallbacksGUI::Window_GetControl_RadioButton;
++ m_callbacks->Window_GetControl_Edit = CAddonCallbacksGUI::Window_GetControl_Edit;
++ m_callbacks->Window_GetControl_Progress = CAddonCallbacksGUI::Window_GetControl_Progress;
++
++ m_callbacks->Window_SetControlLabel = CAddonCallbacksGUI::Window_SetControlLabel;
++
++ m_callbacks->Control_Spin_SetVisible = CAddonCallbacksGUI::Control_Spin_SetVisible;
++ m_callbacks->Control_Spin_SetText = CAddonCallbacksGUI::Control_Spin_SetText;
++ m_callbacks->Control_Spin_Clear = CAddonCallbacksGUI::Control_Spin_Clear;
++ m_callbacks->Control_Spin_AddLabel = CAddonCallbacksGUI::Control_Spin_AddLabel;
++ m_callbacks->Control_Spin_GetValue = CAddonCallbacksGUI::Control_Spin_GetValue;
++ m_callbacks->Control_Spin_SetValue = CAddonCallbacksGUI::Control_Spin_SetValue;
++
++ m_callbacks->Control_ListContainer_AddItems = CAddonCallbacksGUI::Control_ListContainer_AddItems;
++ m_callbacks->Control_ListContainer_GetItem = CAddonCallbacksGUI::Control_ListContainer_GetItem;
++ m_callbacks->Control_ListContainer_GetSelected = CAddonCallbacksGUI::Control_ListContainer_GetSelected;
++ m_callbacks->Control_ListContainer_Reset = CAddonCallbacksGUI::Control_ListContainer_Reset;
++ m_callbacks->Control_ListContainer_SetVisible=CAddonCallbacksGUI::Control_ListContainer_SetVisible;
++
++ m_callbacks->Control_RadioButton_SetVisible = CAddonCallbacksGUI::Control_RadioButton_SetVisible;
++ m_callbacks->Control_RadioButton_SetText = CAddonCallbacksGUI::Control_RadioButton_SetText;
++ m_callbacks->Control_RadioButton_SetSelected= CAddonCallbacksGUI::Control_RadioButton_SetSelected;
++ m_callbacks->Control_RadioButton_IsSelected = CAddonCallbacksGUI::Control_RadioButton_IsSelected;
++
++ m_callbacks->Control_Progress_SetPercentage = CAddonCallbacksGUI::Control_Progress_SetPercentage;
++ m_callbacks->Control_Progress_GetPercentage = CAddonCallbacksGUI::Control_Progress_GetPercentage;
++ m_callbacks->Control_Progress_SetInfo = CAddonCallbacksGUI::Control_Progress_SetInfo;
++ m_callbacks->Control_Progress_GetInfo = CAddonCallbacksGUI::Control_Progress_GetInfo;
++ m_callbacks->Control_Progress_GetDescription= CAddonCallbacksGUI::Control_Progress_GetDescription;
++
++ m_callbacks->ListItem_Create = CAddonCallbacksGUI::ListItem_Create;
++ m_callbacks->ListItem_Destroy = CAddonCallbacksGUI::ListItem_Destroy;
++ m_callbacks->ListItem_GetLabel = CAddonCallbacksGUI::ListItem_GetLabel;
++ m_callbacks->ListItem_SetLabel = CAddonCallbacksGUI::ListItem_SetLabel;
++ m_callbacks->ListItem_GetLabel2 = CAddonCallbacksGUI::ListItem_GetLabel2;
++ m_callbacks->ListItem_SetLabel2 = CAddonCallbacksGUI::ListItem_SetLabel2;
++ m_callbacks->ListItem_SetIconImage = CAddonCallbacksGUI::ListItem_SetIconImage;
++ m_callbacks->ListItem_SetThumbnailImage = CAddonCallbacksGUI::ListItem_SetThumbnailImage;
++ m_callbacks->ListItem_SetInfo = CAddonCallbacksGUI::ListItem_SetInfo;
++ m_callbacks->ListItem_SetProperty = CAddonCallbacksGUI::ListItem_SetProperty;
++ m_callbacks->ListItem_GetProperty = CAddonCallbacksGUI::ListItem_GetProperty;
++ m_callbacks->ListItem_SetPath = CAddonCallbacksGUI::ListItem_SetPath;
++}
++
++CAddonCallbacksGUI::~CAddonCallbacksGUI()
++{
++ delete m_callbacks;
++}
++
++void CAddonCallbacksGUI::Lock()
++{
++ if (iXBMCGUILockRef == 0) g_graphicsContext.Lock();
++ iXBMCGUILockRef++;
++}
++
++void CAddonCallbacksGUI::Unlock()
++{
++ if (iXBMCGUILockRef > 0)
++ {
++ iXBMCGUILockRef--;
++ if (iXBMCGUILockRef == 0) g_graphicsContext.Unlock();
++ }
++}
++
++int CAddonCallbacksGUI::GetScreenHeight()
++{
++ return g_graphicsContext.GetHeight();
++}
++
++int CAddonCallbacksGUI::GetScreenWidth()
++{
++ return g_graphicsContext.GetWidth();
++}
++
++int CAddonCallbacksGUI::GetVideoResolution()
++{
++ return (int)g_graphicsContext.GetVideoResolution();
++}
++
++int CAddonCallbacksGUI::Dialog_ShowYesNo(const char* heading, const char* line0, const char* line1, const char* line2, int* bCanceled, const char* noLabel, const char* yesLabel)
++{
++ const CStdString strHeading = heading? heading : "";
++ const CStdString strLine0 = line0? line0 : "";
++ const CStdString strLine1 = line1? line1 : "";
++ const CStdString strLine2 = line2? line2 : "";
++ bool boolCanceled=false;
++ const CStdString strNoLabel = noLabel? noLabel : "";
++ const CStdString strYesLabel = yesLabel? yesLabel : "";
++ bool retval = CGUIDialogYesNo::ShowAndGetInput(strHeading, strLine0, strLine1, strLine2, boolCanceled, strNoLabel, strYesLabel);
++ if(bCanceled)
++ *bCanceled = boolCanceled? 1 : 0;
++ return static_cast<int>(retval);
++}
++
++GUIHANDLE CAddonCallbacksGUI::Window_New(void *addonData, const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper)
++ return NULL;
++
++ CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI();
++
++ RESOLUTION_INFO res;
++ CStdString strSkinPath;
++ if (!forceFallback)
++ {
++ /* Check to see if the XML file exists in current skin. If not use
++ fallback path to find a skin for the addon */
++ strSkinPath = g_SkinInfo->GetSkinPath(xmlFilename, &res);
++
++ if (!XFILE::CFile::Exists(strSkinPath))
++ {
++ /* Check for the matching folder for the skin in the fallback skins folder */
++ CStdString basePath;
++ URIUtils::AddFileToFolder(guiHelper->m_addon->Path(), "resources", basePath);
++ URIUtils::AddFileToFolder(basePath, "skins", basePath);
++ URIUtils::AddFileToFolder(basePath, URIUtils::GetFileName(g_SkinInfo->Path()), basePath);
++ strSkinPath = g_SkinInfo->GetSkinPath(xmlFilename, &res, basePath);
++ if (!XFILE::CFile::Exists(strSkinPath))
++ {
++ /* Finally fallback to the DefaultSkin as it didn't exist in either the
++ XBMC Skin folder or the fallback skin folder */
++ forceFallback = true;
++ }
++ }
++ }
++
++ if (forceFallback)
++ {
++ //FIXME make this static method of current skin?
++ CStdString str("none");
++ AddonProps props(str, ADDON_SKIN, str, str);
++ CSkinInfo skinInfo(props);
++ CStdString basePath;
++ URIUtils::AddFileToFolder(guiHelper->m_addon->Path(), "resources", basePath);
++ URIUtils::AddFileToFolder(basePath, "skins", basePath);
++ URIUtils::AddFileToFolder(basePath, defaultSkin, basePath);
++
++ skinInfo.Start(basePath);
++ strSkinPath = skinInfo.GetSkinPath(xmlFilename, &res, basePath);
++
++ if (!XFILE::CFile::Exists(strSkinPath))
++ {
++ CLog::Log(LOGERROR, "Window_New: %s/%s - XML File '%s' for Window is missing, contact Developer '%s' of this AddOn", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str(), strSkinPath.c_str(), guiHelper->m_addon->Author().c_str());
++ return NULL;
++ }
++ }
++ // window id's 14000 - 14100 are reserved for addons
++ // get first window id that is not in use
++ int id = WINDOW_ADDON_START;
++ // if window 14099 is in use it means addon can't create more windows
++ Lock();
++ if (g_windowManager.GetWindow(WINDOW_ADDON_END))
++ {
++ Unlock();
++ CLog::Log(LOGERROR, "Window_New: %s/%s - maximum number of windows reached", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
++ return NULL;
++ }
++ while(id < WINDOW_ADDON_END && g_windowManager.GetWindow(id) != NULL) id++;
++ Unlock();
++
++ CGUIWindow *window;
++ if (!asDialog)
++ window = new CGUIAddonWindow(id, strSkinPath, guiHelper->m_addon);
++ else
++ window = new CGUIAddonWindowDialog(id, strSkinPath, guiHelper->m_addon);
++
++ Lock();
++ g_windowManager.Add(window);
++ Unlock();
++
++ window->SetCoordsRes(res);
++
++ return window;
++}
++
++void CAddonCallbacksGUI::Window_Delete(void *addonData, GUIHANDLE handle)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper)
++ return;
++
++ CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI();
++
++ if (!handle)
++ {
++ CLog::Log(LOGERROR, "Window_Show: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
++ return;
++ }
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++ CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
++ if (!pWindow)
++ return;
++
++ Lock();
++ // first change to an existing window
++ if (g_windowManager.GetActiveWindow() == pAddonWindow->m_iWindowId && !g_application.m_bStop)
++ {
++ if(g_windowManager.GetWindow(pAddonWindow->m_iOldWindowId))
++ g_windowManager.ActivateWindow(pAddonWindow->m_iOldWindowId);
++ else // old window does not exist anymore, switch to home
++ g_windowManager.ActivateWindow(WINDOW_HOME);
++ }
++ // Free any window properties
++ pAddonWindow->ClearProperties();
++ // free the window's resources and unload it (free all guicontrols)
++ pAddonWindow->FreeResources(true);
++
++ g_windowManager.Remove(pAddonWindow->GetID());
++ delete pAddonWindow;
++ Unlock();
++}
++
++void CAddonCallbacksGUI::Window_SetCallbacks(void *addonData, GUIHANDLE handle, GUIHANDLE clienthandle, bool (*initCB)(GUIHANDLE), bool (*clickCB)(GUIHANDLE, int), bool (*focusCB)(GUIHANDLE, int), bool (*onActionCB)(GUIHANDLE handle, int),bool (*onContextMenuCB)(GUIHANDLE ,int ,int , unsigned int))
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return;
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++
++ Lock();
++ pAddonWindow->m_clientHandle = clienthandle;
++ pAddonWindow->CBOnInit = initCB;
++ pAddonWindow->CBOnClick = clickCB;
++ pAddonWindow->CBOnFocus = focusCB;
++ pAddonWindow->CBOnAction = onActionCB;
++ pAddonWindow->CBOnContextMenu = onContextMenuCB;
++ Unlock();
++}
++
++bool CAddonCallbacksGUI::Window_Show(void *addonData, GUIHANDLE handle)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper)
++ return false;
++
++ CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI();
++
++ if (!handle)
++ {
++ CLog::Log(LOGERROR, "Window_Show: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
++ return false;
++ }
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++ CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
++ if (!pWindow)
++ return false;
++
++ if (pAddonWindow->m_iOldWindowId != pAddonWindow->m_iWindowId && pAddonWindow->m_iWindowId != g_windowManager.GetActiveWindow())
++ pAddonWindow->m_iOldWindowId = g_windowManager.GetActiveWindow();
++
++ Lock();
++ if (pAddonWindow->IsDialog())
++ ((CGUIAddonWindowDialog*)pAddonWindow)->Show();
++ else
++ g_windowManager.ActivateWindow(pAddonWindow->m_iWindowId);
++ Unlock();
++
++ return true;
++}
++
++bool CAddonCallbacksGUI::Window_Close(void *addonData, GUIHANDLE handle)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper)
++ return false;
++
++ CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI();
++
++ if (!handle)
++ {
++ CLog::Log(LOGERROR, "Window_Close: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
++ return false;
++ }
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++ CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
++ if (!pWindow)
++ return false;
++
++ pAddonWindow->m_bModal = false;
++ if (pAddonWindow->IsDialog())
++ ((CGUIAddonWindowDialog*)pAddonWindow)->PulseActionEvent();
++ else
++ ((CGUIAddonWindow*)pAddonWindow)->PulseActionEvent();
++
++ Lock();
++ // if it's a dialog, we have to close it a bit different
++ if (pAddonWindow->IsDialog())
++ ((CGUIAddonWindowDialog*)pAddonWindow)->Show(false);
++ else
++ g_windowManager.ActivateWindow(pAddonWindow->m_iOldWindowId);
++ pAddonWindow->m_iOldWindowId = 0;
++
++ Unlock();
++
++ return true;
++}
++
++bool CAddonCallbacksGUI::Window_DoModal(void *addonData, GUIHANDLE handle)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper)
++ return false;
++
++ CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI();
++
++ if (!handle)
++ {
++ CLog::Log(LOGERROR, "Window_DoModal: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
++ return false;
++ }
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++ CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
++ if (!pWindow)
++ return false;
++
++ pAddonWindow->m_bModal = true;
++
++ if (pAddonWindow->m_iWindowId != g_windowManager.GetActiveWindow())
++ Window_Show(addonData, handle);
++
++ return true;
++}
++
++bool CAddonCallbacksGUI::Window_SetFocusId(void *addonData, GUIHANDLE handle, int iControlId)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper)
++ return false;
++
++ CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI();
++
++ if (!handle)
++ {
++ CLog::Log(LOGERROR, "Window_SetFocusId: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
++ return false;
++ }
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++ CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
++ if (!pWindow)
++ return false;
++
++ if(!pWindow->GetControl(iControlId))
++ {
++ CLog::Log(LOGERROR, "Window_SetFocusId: %s/%s - Control does not exist in window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
++ return false;
++ }
++
++ Lock();
++ CGUIMessage msg = CGUIMessage(GUI_MSG_SETFOCUS, pAddonWindow->m_iWindowId, iControlId);
++ pWindow->OnMessage(msg);
++ Unlock();
++
++ return true;
++}
++
++int CAddonCallbacksGUI::Window_GetFocusId(void *addonData, GUIHANDLE handle)
++{
++ int iControlId = -1;
++
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper)
++ return iControlId;
++
++ CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI();
++
++ if (!handle)
++ {
++ CLog::Log(LOGERROR, "Window_GetFocusId: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
++ return iControlId;
++ }
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++ CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
++ if (!pWindow)
++ return iControlId;
++
++ Lock();
++ iControlId = pWindow->GetFocusedControlID();
++ Unlock();
++
++ if (iControlId == -1)
++ {
++ CLog::Log(LOGERROR, "Window_GetFocusId: %s/%s - No control in this window has focus", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
++ return iControlId;
++ }
++
++ return iControlId;
++}
++
++bool CAddonCallbacksGUI::Window_SetCoordinateResolution(void *addonData, GUIHANDLE handle, int res)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper)
++ return false;
++
++ CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI();
++
++ if (!handle)
++ {
++ CLog::Log(LOGERROR, "SetCoordinateResolution: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
++ return false;
++ }
++
++ if (res < RES_HDTV_1080i || res > RES_AUTORES)
++ {
++ CLog::Log(LOGERROR, "SetCoordinateResolution: %s/%s - Invalid resolution", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
++ return false;
++ }
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++ CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
++ if (!pWindow)
++ return false;
++
++ pWindow->SetCoordsRes((RESOLUTION)res);
++
++ return true;
++}
++
++void CAddonCallbacksGUI::Window_SetProperty(void *addonData, GUIHANDLE handle, const char *key, const char *value)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper)
++ return;
++
++ CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI();
++
++ if (!handle)
++ {
++ CLog::Log(LOGERROR, "Window_SetProperty: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
++ return;
++ }
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++ CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
++ if (!pWindow)
++ return;
++
++ CStdString lowerKey = key;
++
++ Lock();
++ pWindow->SetProperty(lowerKey.ToLower(), value);
++ Unlock();
++}
++
++void CAddonCallbacksGUI::Window_SetPropertyInt(void *addonData, GUIHANDLE handle, const char *key, int value)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper)
++ return;
++
++ CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI();
++
++ if (!handle)
++ {
++ CLog::Log(LOGERROR, "Window_SetPropertyInt: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
++ return;
++ }
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++ CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
++ if (!pWindow)
++ return;
++
++ CStdString lowerKey = key;
++
++ Lock();
++ pWindow->SetProperty(lowerKey.ToLower(), value);
++ Unlock();
++}
++
++void CAddonCallbacksGUI::Window_SetPropertyBool(void *addonData, GUIHANDLE handle, const char *key, bool value)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper)
++ return;
++
++ CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI();
++
++ if (!handle)
++ {
++ CLog::Log(LOGERROR, "Window_SetPropertyBool: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
++ return;
++ }
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++ CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
++ if (!pWindow)
++ return;
++
++ CStdString lowerKey = key;
++
++ Lock();
++ pWindow->SetProperty(lowerKey.ToLower(), value);
++ Unlock();
++}
++
++void CAddonCallbacksGUI::Window_SetPropertyDouble(void *addonData, GUIHANDLE handle, const char *key, double value)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper)
++ return;
++
++ CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI();
++
++ if (!handle)
++ {
++ CLog::Log(LOGERROR, "Window_SetPropertyDouble: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
++ return;
++ }
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++ CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
++ if (!pWindow)
++ return;
++
++ CStdString lowerKey = key;
++
++ Lock();
++ pWindow->SetProperty(lowerKey.ToLower(), value);
++ Unlock();
++}
++
++const char* CAddonCallbacksGUI::Window_GetProperty(void *addonData, GUIHANDLE handle, const char *key)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper)
++ return NULL;
++
++ CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI();
++
++ if (!handle)
++ {
++ CLog::Log(LOGERROR, "Window_GetProperty: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
++ return NULL;
++ }
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++ CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
++ if (!pWindow)
++ return NULL;
++
++ Lock();
++ CStdString lowerKey = key;
++ string value = pWindow->GetProperty(lowerKey.ToLower()).asString();
++ Unlock();
++
++ return value.c_str();
++}
++
++int CAddonCallbacksGUI::Window_GetPropertyInt(void *addonData, GUIHANDLE handle, const char *key)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper)
++ return -1;
++
++ CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI();
++
++ if (!handle)
++ {
++ CLog::Log(LOGERROR, "Window_GetPropertyInt: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
++ return -1;
++ }
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++ CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
++ if (!pWindow)
++ return -1;
++
++ Lock();
++ CStdString lowerKey = key;
++ int value = pWindow->GetProperty(lowerKey.ToLower()).asInteger();
++ Unlock();
++
++ return value;
++}
++
++bool CAddonCallbacksGUI::Window_GetPropertyBool(void *addonData, GUIHANDLE handle, const char *key)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper)
++ return false;
++
++ CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI();
++
++ if (!handle)
++ {
++ CLog::Log(LOGERROR, "Window_GetPropertyBool: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
++ return false;
++ }
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++ CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
++ if (!pWindow)
++ return false;
++
++ Lock();
++ CStdString lowerKey = key;
++ bool value = pWindow->GetProperty(lowerKey.ToLower()).asBoolean();
++ Unlock();
++
++ return value;
++}
++
++double CAddonCallbacksGUI::Window_GetPropertyDouble(void *addonData, GUIHANDLE handle, const char *key)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper)
++ return 0.0;
++
++ CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI();
++
++ if (!handle)
++ {
++ CLog::Log(LOGERROR, "Window_GetPropertyDouble: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
++ return 0.0;
++ }
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++ CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
++ if (!pWindow)
++ return 0.0;
++
++ Lock();
++ CStdString lowerKey = key;
++ double value = pWindow->GetProperty(lowerKey.ToLower()).asDouble();
++ Unlock();
++
++ return value;
++}
++
++void CAddonCallbacksGUI::Window_ClearProperties(void *addonData, GUIHANDLE handle)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper)
++ return;
++
++ CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI();
++
++ if (!handle)
++ {
++ CLog::Log(LOGERROR, "Window_ClearProperties: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
++ return;
++ }
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++ CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
++ if (!pWindow)
++ return;
++
++ Lock();
++ pWindow->ClearProperties();
++ Unlock();
++}
++
++int CAddonCallbacksGUI::Window_GetListSize(void *addonData, GUIHANDLE handle)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return -1;
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++
++ Lock();
++ int listSize = pAddonWindow->GetListSize();
++ Unlock();
++
++ return listSize;
++}
++
++void CAddonCallbacksGUI::Window_ClearList(void *addonData, GUIHANDLE handle)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return;
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++
++ Lock();
++ pAddonWindow->ClearList();
++ Unlock();
++
++ return;
++}
++
++GUIHANDLE CAddonCallbacksGUI::Window_AddItem(void *addonData, GUIHANDLE handle, GUIHANDLE item, int itemPosition)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle || !item)
++ return NULL;
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++ CFileItemPtr pItem((CFileItem*)item);
++ Lock();
++ pAddonWindow->AddItem(pItem, itemPosition);
++ Unlock();
++
++ return item;
++}
++
++GUIHANDLE CAddonCallbacksGUI::Window_AddStringItem(void *addonData, GUIHANDLE handle, const char *itemName, int itemPosition)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle || !itemName)
++ return NULL;
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++ CFileItemPtr item(new CFileItem(itemName));
++ Lock();
++ pAddonWindow->AddItem(item, itemPosition);
++ Unlock();
++
++ return item.get();
++}
++
++void CAddonCallbacksGUI::Window_RemoveItem(void *addonData, GUIHANDLE handle, int itemPosition)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return;
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++
++ Lock();
++ pAddonWindow->RemoveItem(itemPosition);
++ Unlock();
++
++ return;
++}
++
++GUIHANDLE CAddonCallbacksGUI::Window_GetListItem(void *addonData, GUIHANDLE handle, int listPos)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return NULL;
++
++ CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI();
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++
++ Lock();
++ CFileItemPtr fi = pAddonWindow->GetListItem(listPos);
++ if (fi == NULL)
++ {
++ Unlock();
++ CLog::Log(LOGERROR, "Window_GetListItem: %s/%s - Index out of range", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
++ return NULL;
++ }
++ Unlock();
++
++ return fi.get();
++}
++
++void CAddonCallbacksGUI::Window_SetCurrentListPosition(void *addonData, GUIHANDLE handle, int listPos)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return;
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++
++ Lock();
++ pAddonWindow->SetCurrentListPosition(listPos);
++ Unlock();
++
++ return;
++}
++
++int CAddonCallbacksGUI::Window_GetCurrentListPosition(void *addonData, GUIHANDLE handle)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return -1;
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++
++ Lock();
++ int listPos = pAddonWindow->GetCurrentListPosition();
++ Unlock();
++
++ return listPos;
++}
++
++void CAddonCallbacksGUI::Window_AddContextMenuButton(void *addonData, GUIHANDLE handle,int controlId,unsigned int contextButtonId,const char* label)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return;
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++
++ Lock();
++ pAddonWindow->AddContextMenuButton(controlId,contextButtonId,label);
++ Unlock();
++}
++
++GUIHANDLE CAddonCallbacksGUI::Window_GetControl_Spin(void *addonData, GUIHANDLE handle, int controlId)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return NULL;
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++ CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId);
++ if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_SPINEX)
++ return NULL;
++
++ return pGUIControl;
++}
++
++GUIHANDLE CAddonCallbacksGUI::Window_GetControl_ListContainer(void *addonData, GUIHANDLE handle, int controlId,GUIHANDLE *listItems)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return NULL;
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++ CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId);
++ if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTAINER_LIST)
++ return NULL;
++
++ *listItems = (GUIHANDLE) new CFileItemList();
++ return pGUIControl;
++}
++
++void CAddonCallbacksGUI::Window_ReleaseControl_ListContainer(GUIHANDLE listItems)
++{
++ CFileItemList* pListItems = (CFileItemList*) listItems;
++ if(pListItems)
++ delete pListItems;
++}
++
++GUIHANDLE CAddonCallbacksGUI::Window_GetControl_Button(void *addonData, GUIHANDLE handle, int controlId)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return NULL;
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++ CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId);
++ if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_BUTTON)
++ return NULL;
++
++ return pGUIControl;
++}
++
++GUIHANDLE CAddonCallbacksGUI::Window_GetControl_RadioButton(void *addonData, GUIHANDLE handle, int controlId)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return NULL;
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++ CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId);
++ if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_RADIO)
++ return NULL;
++
++ return pGUIControl;
++}
++
++GUIHANDLE CAddonCallbacksGUI::Window_GetControl_Edit(void *addonData, GUIHANDLE handle, int controlId)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return NULL;
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++ CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId);
++ if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_EDIT)
++ return NULL;
++
++ return pGUIControl;
++}
++
++GUIHANDLE CAddonCallbacksGUI::Window_GetControl_Progress(void *addonData, GUIHANDLE handle, int controlId)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return NULL;
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++ CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId);
++ if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_PROGRESS)
++ return NULL;
++
++ return pGUIControl;
++}
++
++void CAddonCallbacksGUI::Window_SetControlLabel(void *addonData, GUIHANDLE handle, int controlId, const char *label)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return;
++
++ CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
++
++ CGUIMessage msg(GUI_MSG_LABEL_SET, pAddonWindow->m_iWindowId, controlId);
++ msg.SetLabel(label);
++ pAddonWindow->OnMessage(msg);
++}
++
++void CAddonCallbacksGUI::Control_Spin_SetVisible(void *addonData, GUIHANDLE spinhandle, bool yesNo)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !spinhandle)
++ return;
++
++ CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle;
++ pSpin->SetVisible(yesNo);
++}
++
++void CAddonCallbacksGUI::Control_Spin_SetText(void *addonData, GUIHANDLE spinhandle, const char *label)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !spinhandle)
++ return;
++
++ CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle;
++ pSpin->SetText(label);
++}
++
++void CAddonCallbacksGUI::Control_Spin_Clear(void *addonData, GUIHANDLE spinhandle)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !spinhandle)
++ return;
++
++ CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle;
++ pSpin->Clear();
++}
++
++void CAddonCallbacksGUI::Control_Spin_AddLabel(void *addonData, GUIHANDLE spinhandle, const char *label, int iValue)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !spinhandle)
++ return;
++
++ CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle;
++ pSpin->AddLabel(label, iValue);
++}
++
++int CAddonCallbacksGUI::Control_Spin_GetValue(void *addonData, GUIHANDLE spinhandle)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !spinhandle)
++ return -1;
++
++ CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle;
++ return pSpin->GetValue();
++}
++
++void CAddonCallbacksGUI::Control_Spin_SetValue(void *addonData, GUIHANDLE spinhandle, int iValue)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !spinhandle)
++ return;
++
++ CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle;
++ pSpin->SetValue(iValue);
++}
++
++
++void CAddonCallbacksGUI::Control_ListContainer_SetVisible(void *addonData, GUIHANDLE handle, bool yesNo)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return;
++
++ CGUIListContainer *pListContainer = (CGUIListContainer*) handle;
++ pListContainer->SetVisible(yesNo);
++}
++
++
++void CAddonCallbacksGUI::Control_ListContainer_AddItems(void *addonData, GUIHANDLE handle,GUIHANDLE listItems,GUIHANDLE items[],int size)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle || !items)
++ return;
++
++ CFileItem **pItems = (CFileItem**) items;
++ CGUIListContainer *pListContainer = (CGUIListContainer*) handle;
++ CFileItemList *itemlist = (CFileItemList*) listItems;
++ for(int i = 0;i < size; i++)
++ itemlist->Add(CFileItemPtr(pItems[i]));
++ CGUIMessage msg(GUI_MSG_LABEL_BIND, pListContainer->GetParentID(), pListContainer->GetID(), 0, 0, itemlist);
++ msg.SetPointer(itemlist);
++ g_windowManager.SendThreadMessage(msg, pListContainer->GetParentID());
++}
++
++GUIHANDLE CAddonCallbacksGUI::Control_ListContainer_GetItem(void *addonData, GUIHANDLE handle,GUIHANDLE listItems,int index)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return NULL;
++ CFileItem* pFileItem = NULL;
++
++ CGUIListContainer *pListContainer = (CGUIListContainer*)handle;
++
++ if(pListContainer->GetListItem(index,INFOFLAG_LISTITEM_POSITION)->IsFileItem())
++ pFileItem = (CFileItem*)pListContainer->GetListItem(index,INFOFLAG_LISTITEM_POSITION).get();
++ return pFileItem;
++}
++
++int CAddonCallbacksGUI::Control_ListContainer_GetSelected(void *addonData, GUIHANDLE handle)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return -1;
++
++ CGUIListContainer *pListContainer = (CGUIListContainer*)handle;
++
++ return pListContainer->GetSelectedItem();
++}
++
++void CAddonCallbacksGUI::Control_ListContainer_Reset(void *addonData, GUIHANDLE handle,GUIHANDLE listItems)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return;
++ CFileItemList *itemlist = (CFileItemList*) listItems;
++ CGUIListContainer *pListContainer = (CGUIListContainer*)handle;
++ CGUIMessage msg(GUI_MSG_LABEL_RESET, pListContainer->GetParentID(), pListContainer->GetID());
++ g_windowManager.SendThreadMessage(msg, pListContainer->GetParentID());
++ itemlist->Clear();
++}
++
++void CAddonCallbacksGUI::Control_RadioButton_SetVisible(void *addonData, GUIHANDLE handle, bool yesNo)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return;
++
++ CGUIRadioButtonControl *pRadioButton = (CGUIRadioButtonControl*)handle;
++ pRadioButton->SetVisible(yesNo);
++}
++
++void CAddonCallbacksGUI::Control_RadioButton_SetText(void *addonData, GUIHANDLE handle, const char *label)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return;
++
++ CGUIRadioButtonControl *pRadioButton = (CGUIRadioButtonControl*)handle;
++ pRadioButton->SetLabel(label);
++}
++
++void CAddonCallbacksGUI::Control_RadioButton_SetSelected(void *addonData, GUIHANDLE handle, bool yesNo)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return;
++
++ CGUIRadioButtonControl *pRadioButton = (CGUIRadioButtonControl*)handle;
++ pRadioButton->SetSelected(yesNo);
++}
++
++bool CAddonCallbacksGUI::Control_RadioButton_IsSelected(void *addonData, GUIHANDLE handle)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return false;
++
++ CGUIRadioButtonControl *pRadioButton = (CGUIRadioButtonControl*)handle;
++ return pRadioButton->IsSelected();
++}
++
++void CAddonCallbacksGUI::Control_Progress_SetPercentage(void *addonData, GUIHANDLE handle, float fPercent)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return;
++
++ CGUIProgressControl *pControl = (CGUIProgressControl*)handle;
++ pControl->SetPercentage(fPercent);
++}
++
++float CAddonCallbacksGUI::Control_Progress_GetPercentage(void *addonData, GUIHANDLE handle)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return 0.0;
++
++ CGUIProgressControl *pControl = (CGUIProgressControl*)handle;
++ return pControl->GetPercentage();
++}
++
++void CAddonCallbacksGUI::Control_Progress_SetInfo(void *addonData, GUIHANDLE handle, int iInfo)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return;
++
++ CGUIProgressControl *pControl = (CGUIProgressControl*)handle;
++ pControl->SetInfo(iInfo);
++}
++
++int CAddonCallbacksGUI::Control_Progress_GetInfo(void *addonData, GUIHANDLE handle)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return -1;
++
++ CGUIProgressControl *pControl = (CGUIProgressControl*)handle;
++ return pControl->GetInfo();
++}
++
++const char* CAddonCallbacksGUI::Control_Progress_GetDescription(void *addonData, GUIHANDLE handle)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return NULL;
++
++ CGUIProgressControl *pControl = (CGUIProgressControl*)handle;
++ CStdString string = pControl->GetDescription();
++
++ char *buffer = (char*) malloc (string.length()+1);
++ strcpy(buffer, string.c_str());
++ return buffer;
++}
++
++GUIHANDLE CAddonCallbacksGUI::ListItem_Create(void *addonData, const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper)
++ return NULL;
++
++ // create CFileItem
++ CFileItem *pItem = new CFileItem();
++ if (!pItem)
++ return NULL;
++
++ if (label)
++ pItem->SetLabel(label);
++ if (label2)
++ pItem->SetLabel2(label2);
++ if (iconImage)
++ pItem->SetIconImage(iconImage);
++ if (thumbnailImage)
++ pItem->SetThumbnailImage(thumbnailImage);
++ if (path)
++ pItem->SetPath(path);
++
++ return pItem;
++}
++
++void CAddonCallbacksGUI::ListItem_Destroy(void *addonData, GUIHANDLE handle)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return;
++ CFileItem *pItem = (CFileItem*) handle;
++ delete pItem;
++}
++
++const char* CAddonCallbacksGUI::ListItem_GetLabel(void *addonData, GUIHANDLE handle)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return NULL;
++
++ CStdString string = ((CFileItem*)handle)->GetLabel();
++ char *buffer = (char*) malloc (string.length()+1);
++ strcpy(buffer, string.c_str());
++ return buffer;
++}
++
++void CAddonCallbacksGUI::ListItem_SetLabel(void *addonData, GUIHANDLE handle, const char *label)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return;
++
++ ((CFileItem*)handle)->SetLabel(label);
++}
++
++const char* CAddonCallbacksGUI::ListItem_GetLabel2(void *addonData, GUIHANDLE handle)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return NULL;
++
++ CStdString string = ((CFileItem*)handle)->GetLabel2();
++
++ char *buffer = (char*) malloc (string.length()+1);
++ strcpy(buffer, string.c_str());
++ return buffer;
++}
++
++void CAddonCallbacksGUI::ListItem_SetLabel2(void *addonData, GUIHANDLE handle, const char *label)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return;
++
++ ((CFileItem*)handle)->SetLabel2(label);
++}
++
++void CAddonCallbacksGUI::ListItem_SetIconImage(void *addonData, GUIHANDLE handle, const char *image)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return;
++
++ ((CFileItem*)handle)->SetIconImage(image);
++}
++
++void CAddonCallbacksGUI::ListItem_SetThumbnailImage(void *addonData, GUIHANDLE handle, const char *image)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return;
++
++ ((CFileItem*)handle)->SetThumbnailImage(image);
++}
++
++void CAddonCallbacksGUI::ListItem_SetInfo(void *addonData, GUIHANDLE handle, const char *info)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return;
++
++}
++
++void CAddonCallbacksGUI::ListItem_SetProperty(void *addonData, GUIHANDLE handle, const char *key, const char *value)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return;
++
++ ((CFileItem*)handle)->SetProperty(key, value);
++}
++
++const char* CAddonCallbacksGUI::ListItem_GetProperty(void *addonData, GUIHANDLE handle, const char *key)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return NULL;
++
++ string string = ((CFileItem*)handle)->GetProperty(key).asString();
++ char *buffer = (char*) malloc (string.length()+1);
++ strcpy(buffer, string.c_str());
++ return buffer;
++}
++
++void CAddonCallbacksGUI::ListItem_SetPath(void *addonData, GUIHANDLE handle, const char *path)
++{
++ CAddonCallbacks* helper = (CAddonCallbacks*) addonData;
++ if (!helper || !handle)
++ return;
++
++ ((CFileItem*)handle)->SetPath(path);
++}
++
++
++
++
++
++
++
++CGUIAddonWindow::CGUIAddonWindow(int id, CStdString strXML, CAddon* addon)
++ : CGUIMediaWindow(id, strXML)
++ , m_iWindowId(id)
++ , m_iOldWindowId(0)
++ , m_bModal(false)
++ , m_bIsDialog(false)
++ , m_actionEvent(true)
++ , m_addon(addon)
++{
++ m_loadOnDemand = false;
++ CBOnInit = NULL;
++ CBOnFocus = NULL;
++ CBOnClick = NULL;
++ CBOnAction = NULL;
++ CBOnContextMenu = NULL;
++}
++
++CGUIAddonWindow::~CGUIAddonWindow(void)
++{
++}
++
++bool CGUIAddonWindow::OnAction(const CAction &action)
++{
++ // do the base class window first, and the call to python after this
++ bool ret = CGUIWindow::OnAction(action); // we don't currently want the mediawindow actions here
++ if (CBOnAction)
++ {
++ CBOnAction(m_clientHandle, action.GetID());
++ }
++ return ret;
++}
++
++bool CGUIAddonWindow::OnMessage(CGUIMessage& message)
++{
++ // TODO: We shouldn't be dropping down to CGUIWindow in any of this ideally.
++ // We have to make up our minds about what python should be doing and
++ // what this side of things should be doing
++ switch (message.GetMessage())
++ {
++ case GUI_MSG_WINDOW_DEINIT:
++ {
++ return CGUIMediaWindow::OnMessage(message);
++ }
++ break;
++
++ case GUI_MSG_WINDOW_INIT:
++ {
++ CGUIMediaWindow::OnMessage(message);
++ if (CBOnInit)
++ CBOnInit(m_clientHandle);
++
++ return true;
++ }
++ break;
++
++ case GUI_MSG_SETFOCUS:
++ {
++ if (m_viewControl.HasControl(message.GetControlId()) && m_viewControl.GetCurrentControl() != (int)message.GetControlId())
++ {
++ m_viewControl.SetFocused();
++ return true;
++ }
++ // check if our focused control is one of our category buttons
++ int iControl = message.GetControlId();
++ if (CBOnFocus)
++ {
++ CBOnFocus(m_clientHandle, iControl);
++ }
++ }
++ break;
++ case GUI_MSG_CLICKED:
++ {
++ int iControl=message.GetSenderId();
++ // Handle Sort/View internally. Scripters shouldn't use ID 2, 3 or 4.
++ if (iControl == CONTROL_BTNSORTASC) // sort asc
++ {
++ CLog::Log(LOGINFO, "WindowXML: Internal asc/dsc button not implemented");
++ /*if (m_guiState.get())
++ m_guiState->SetNextSortOrder();
++ UpdateFileList();*/
++ return true;
++ }
++ else if (iControl == CONTROL_BTNSORTBY) // sort by
++ {
++ CLog::Log(LOGINFO, "WindowXML: Internal sort button not implemented");
++ /*if (m_guiState.get())
++ m_guiState->SetNextSortMethod();
++ UpdateFileList();*/
++ return true;
++ }
++
++ if (CBOnClick && iControl && iControl != (int)this->GetID())
++ {
++ CGUIControl* controlClicked = (CGUIControl*)this->GetControl(iControl);
++
++ // The old python way used to check list AND SELECITEM method or if its a button, checkmark.
++ // Its done this way for now to allow other controls without a python version like togglebutton to still raise a onAction event
++ if (controlClicked) // Will get problems if we the id is not on the window and we try to do GetControlType on it. So check to make sure it exists
++ {
++ if ((controlClicked->IsContainer() && (message.GetParam1() == ACTION_SELECT_ITEM ||
++ message.GetParam1() == ACTION_MOUSE_LEFT_CLICK)) ||
++ !controlClicked->IsContainer())
++ {
++ CBOnClick(m_clientHandle, iControl);
++ }
++ else if (controlClicked->IsContainer() && message.GetParam1() == ACTION_MOUSE_RIGHT_CLICK && m_mContextButtons.count(iControl))
++ {
++ iCurrentContextMenuControl = iControl;
++ int iItemSelected = SelectContainerItem( controlClicked, message.GetParam2(),true);
++ if(GetSelectedContainerItem(controlClicked, message.GetParam2())->HasProperty("ContextMenuOverride"))
++ iCurrentContextMenuControl = atoi(GetSelectedContainerItem(controlClicked, message.GetParam2())->GetProperty("ContextMenuOverride").asString("-1").c_str());
++ bool retval = CGUIMediaWindow::OnPopupMenu(iItemSelected);
++ SelectContainerItem( controlClicked, message.GetParam2(),false);
++ return retval;
++// PyXBMCAction* inf = new PyXBMCAction;
++// inf->pObject = Action_FromAction(CAction(ACTION_CONTEXT_MENU));
++// inf->pCallbackWindow = pCallbackWindow;
++//
++// // aquire lock?
++// PyXBMC_AddPendingCall(Py_XBMC_Event_OnAction, inf);
++// PulseActionEvent();
++ }
++ return true;
++ }
++ }
++ }
++ break;
++ }
++
++ return CGUIMediaWindow::OnMessage(message);
++}
++
++int CGUIAddonWindow::SelectContainerItem(CGUIControl* control, int offset,bool select)
++{
++ int iItemSelected = -1;
++ switch ( control->GetControlType() )
++ {
++ case CGUIControl::GUICONTAINER_LIST:
++ iItemSelected = ((CGUIListContainer*)control)->GetSelectedItem();
++ ((CGUIListContainer*)control)->GetListItem(offset)->Select(select);
++ break;
++ case CGUIControl::GUICONTAINER_EPGGRID:
++ iItemSelected = ((EPG::CGUIEPGGridContainer*)control)->GetSelectedItem();
++ ((EPG::CGUIEPGGridContainer*)control)->GetListItem(offset)->Select(select);
++ break;
++ case CGUIControl::GUICONTAINER_FIXEDLIST:
++ iItemSelected = ((CGUIFixedListContainer*)control)->GetSelectedItem();
++ ((CGUIFixedListContainer*)control)->GetListItem(offset)->Select(select);
++ break;
++ case CGUIControl::GUICONTAINER_PANEL:
++ iItemSelected = ((CGUIPanelContainer*)control)->GetSelectedItem();
++ ((CGUIPanelContainer*)control)->GetListItem(offset)->Select(select);
++ break;
++ case CGUIControl::GUICONTAINER_WRAPLIST:
++ iItemSelected = ((CGUIWrappingListContainer*)control)->GetSelectedItem();
++ ((CGUIWrappingListContainer*)control)->GetListItem(offset)->Select(select);
++ break;
++ }
++ return iItemSelected;
++}
++
++CGUIListItemPtr CGUIAddonWindow::GetSelectedContainerItem(CGUIControl* control,int offset)
++{
++ switch ( control->GetControlType() )
++ {
++ case CGUIControl::GUICONTAINER_LIST:
++ return ((CGUIListContainer*)control)->GetListItem(offset);
++ break;
++ case CGUIControl::GUICONTAINER_EPGGRID:
++ return ((EPG::CGUIEPGGridContainer*)control)->GetListItem(offset);
++ break;
++ case CGUIControl::GUICONTAINER_FIXEDLIST:
++ return ((CGUIFixedListContainer*)control)->GetListItem(offset);
++ break;
++ case CGUIControl::GUICONTAINER_PANEL:
++ return ((CGUIPanelContainer*)control)->GetListItem(offset);
++ break;
++ case CGUIControl::GUICONTAINER_WRAPLIST:
++ return ((CGUIWrappingListContainer*)control)->GetListItem(offset);
++ break;
++ }
++ return CGUIListItemPtr();
++}
++
++
++void CGUIAddonWindow::AllocResources(bool forceLoad /*= FALSE */)
++{
++ CStdString tmpDir;
++ URIUtils::GetDirectory(GetProperty("xmlfile").asString(), tmpDir);
++ CStdString fallbackMediaPath;
++ URIUtils::GetParentPath(tmpDir, fallbackMediaPath);
++ URIUtils::RemoveSlashAtEnd(fallbackMediaPath);
++ m_mediaDir = fallbackMediaPath;
++
++ //CLog::Log(LOGDEBUG, "CGUIPythonWindowXML::AllocResources called: %s", fallbackMediaPath.c_str());
++ g_TextureManager.AddTexturePath(m_mediaDir);
++ CGUIMediaWindow::AllocResources(forceLoad);
++ g_TextureManager.RemoveTexturePath(m_mediaDir);
++}
++
++void CGUIAddonWindow::FreeResources(bool forceUnLoad /*= FALSE */)
++{
++ // Unload temporary language strings
++ ClearAddonStrings();
++
++ CGUIMediaWindow::FreeResources(forceUnLoad);
++}
++
++void CGUIAddonWindow::Render()
++{
++ g_TextureManager.AddTexturePath(m_mediaDir);
++ CGUIMediaWindow::Render();
++ g_TextureManager.RemoveTexturePath(m_mediaDir);
++}
++
++void CGUIAddonWindow::Update()
++{
++}
++
++void CGUIAddonWindow::AddItem(CFileItemPtr fileItem, int itemPosition)
++{
++ if (itemPosition == -1 || itemPosition > m_vecItems->Size())
++ {
++ m_vecItems->Add(fileItem);
++ }
++ else if (itemPosition < -1 && !(itemPosition-1 < m_vecItems->Size()))
++ {
++ m_vecItems->AddFront(fileItem,0);
++ }
++ else
++ {
++ m_vecItems->AddFront(fileItem,itemPosition);
++ }
++ m_viewControl.SetItems(*m_vecItems);
++ UpdateButtons();
++}
++
++void CGUIAddonWindow::RemoveItem(int itemPosition)
++{
++ m_vecItems->Remove(itemPosition);
++ m_viewControl.SetItems(*m_vecItems);
++ UpdateButtons();
++}
++
++int CGUIAddonWindow::GetCurrentListPosition()
++{
++ return m_viewControl.GetSelectedItem();
++}
++
++void CGUIAddonWindow::SetCurrentListPosition(int item)
++{
++ m_viewControl.SetSelectedItem(item);
++}
++
++int CGUIAddonWindow::GetListSize()
++{
++ return m_vecItems->Size();
++}
++
++CFileItemPtr CGUIAddonWindow::GetListItem(int position)
++{
++ if (position < 0 || position >= m_vecItems->Size()) return CFileItemPtr();
++ return m_vecItems->Get(position);
++}
++
++void CGUIAddonWindow::ClearList()
++{
++ ClearFileItems();
++
++ m_viewControl.SetItems(*m_vecItems);
++ UpdateButtons();
++}
++
++void CGUIAddonWindow::AddContextMenuButton(int controlId,unsigned int contextButtonId,const char* label)
++{
++ if( !m_mContextButtons.count(controlId))
++ m_mContextButtons[controlId] = CContextButtons();
++ m_mContextButtons.at(controlId).Add(contextButtonId,label);
++}
++
++void CGUIAddonWindow::GetContextButtons(int itemNumber, CContextButtons &buttons)
++{
++ if(m_mContextButtons.count(iCurrentContextMenuControl))
++ buttons = m_mContextButtons.at(iCurrentContextMenuControl);
++ // maybe on day we can make an easy way to do this context menu
++ // with out this method overriding the MediaWindow version, it will display 'Add to Favorites'
++}
++
++bool CGUIAddonWindow::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
++{
++ if(CBOnContextMenu)
++ return CBOnContextMenu(m_clientHandle,iCurrentContextMenuControl,itemNumber,button);
++ return false;
++}
++
++void CGUIAddonWindow::WaitForActionEvent(unsigned int timeout)
++{
++ m_actionEvent.WaitMSec(timeout);
++ m_actionEvent.Reset();
++}
++
++void CGUIAddonWindow::PulseActionEvent()
++{
++ m_actionEvent.Set();
++}
++
++void CGUIAddonWindow::ClearAddonStrings()
++{
++ // Unload temporary language strings
++ g_localizeStrings.ClearBlock(m_addon->Path());
++}
++
++bool CGUIAddonWindow::OnClick(int iItem)
++{
++ // Hook Over calling CGUIMediaWindow::OnClick(iItem) results in it trying to PLAY the file item
++ // which if its not media is BAD and 99 out of 100 times undesireable.
++ return false;
++}
++
++// SetupShares();
++/*
++ CGUIMediaWindow::OnWindowLoaded() calls SetupShares() so override it
++and just call UpdateButtons();
++*/
++void CGUIAddonWindow::SetupShares()
++{
++ UpdateButtons();
++}
++
++
++CGUIAddonWindowDialog::CGUIAddonWindowDialog(int id, CStdString strXML, CAddon* addon)
++: CGUIAddonWindow(id,strXML,addon)
++{
++ m_bRunning = false;
++ m_loadOnDemand = false;
++ m_bIsDialog = true;
++}
++
++CGUIAddonWindowDialog::~CGUIAddonWindowDialog(void)
++{
++}
++
++bool CGUIAddonWindowDialog::OnMessage(CGUIMessage &message)
++{
++ if (message.GetMessage() == GUI_MSG_WINDOW_DEINIT)
++ {
++ CGUIWindow *pWindow = g_windowManager.GetWindow(g_windowManager.GetActiveWindow());
++ if (pWindow)
++ g_windowManager.ShowOverlay(pWindow->GetOverlayState());
++ return CGUIWindow::OnMessage(message);
++ }
++ return CGUIAddonWindow::OnMessage(message);
++}
++
++void CGUIAddonWindowDialog::Show(bool show /* = true */)
++{
++ unsigned int iCount = g_graphicsContext.exit();
++ ThreadMessage tMsg = {TMSG_GUI_ADDON_DIALOG, 1, show ? 1 : 0};
++ tMsg.lpVoid = this;
++ g_application.getApplicationMessenger().SendMessage(tMsg, true);
++ g_graphicsContext.restore(iCount);
++}
++
++void CGUIAddonWindowDialog::Show_Internal(bool show /* = true */)
++{
++ if (show)
++ {
++ m_bModal = true;
++ m_bRunning = true;
++ g_windowManager.RouteToWindow(this);
++
++ // active this window...
++ CGUIMessage msg(GUI_MSG_WINDOW_INIT, 0, 0, WINDOW_INVALID, m_iWindowId);
++ OnMessage(msg);
++
++ while (m_bRunning && !g_application.m_bStop)
++ {
++ g_windowManager.Process(CTimeUtils::GetFrameTime());
++ g_windowManager.ProcessRenderLoop();
++ }
++ }
++ else // hide
++ {
++ m_bRunning = false;
++
++ CGUIMessage msg(GUI_MSG_WINDOW_DEINIT,0,0);
++ OnMessage(msg);
++
++ g_windowManager.RemoveDialog(GetID());
++ }
++}
++
++}; /* namespace ADDON */
+diff --git a/xbmc/addons/AddonCallbacksGUI.h b/xbmc/addons/AddonCallbacksGUI.h
+new file mode 100644
+index 0000000..f89cad5
+--- /dev/null
++++ b/xbmc/addons/AddonCallbacksGUI.h
+@@ -0,0 +1,225 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++
++#include "AddonCallbacks.h"
++#include "windows/GUIMediaWindow.h"
++#include "settings/GUIDialogSettings.h"
++#include "threads/Event.h"
++
++class CGUISpinControlEx;
++class CGUIButtonControl;
++class CGUIRadioButtonControl;
++class CGUISettingsSliderControl;
++class CGUIEditControl;
++
++namespace ADDON
++{
++
++class CAddonCallbacksGUI
++{
++public:
++ CAddonCallbacksGUI(CAddon* addon);
++ ~CAddonCallbacksGUI();
++
++ /**! \name General Functions */
++ CB_GUILib *GetCallbacks() { return m_callbacks; }
++
++ static void Lock();
++ static void Unlock();
++ static int GetScreenHeight();
++ static int GetScreenWidth();
++ static int GetVideoResolution();
++
++ static int Dialog_ShowYesNo(const char* heading, const char* line0, const char* line1, const char* line2, int* bCanceled, const char* noLabel, const char* yesLabel);
++
++ static GUIHANDLE Window_New(void *addonData, const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog);
++ static void Window_Delete(void *addonData, GUIHANDLE handle);
++ static void Window_SetCallbacks(void *addonData, GUIHANDLE handle, GUIHANDLE clienthandle, bool (*initCB)(GUIHANDLE), bool (*clickCB)(GUIHANDLE, int), bool (*focusCB)(GUIHANDLE, int), bool (*onActionCB)(GUIHANDLE handle, int),bool (*onContextMenuCB)(GUIHANDLE ,int ,int , unsigned int));
++ static bool Window_Show(void *addonData, GUIHANDLE handle);
++ static bool Window_Close(void *addonData, GUIHANDLE handle);
++ static bool Window_DoModal(void *addonData, GUIHANDLE handle);
++ static bool Window_SetFocusId(void *addonData, GUIHANDLE handle, int iControlId);
++ static int Window_GetFocusId(void *addonData, GUIHANDLE handle);
++ static bool Window_SetCoordinateResolution(void *addonData, GUIHANDLE handle, int res);
++ static void Window_SetProperty(void *addonData, GUIHANDLE handle, const char *key, const char *value);
++ static void Window_SetPropertyInt(void *addonData, GUIHANDLE handle, const char *key, int value);
++ static void Window_SetPropertyBool(void *addonData, GUIHANDLE handle, const char *key, bool value);
++ static void Window_SetPropertyDouble(void *addonData, GUIHANDLE handle, const char *key, double value);
++ static const char * Window_GetProperty(void *addonData, GUIHANDLE handle, const char *key);
++ static int Window_GetPropertyInt(void *addonData, GUIHANDLE handle, const char *key);
++ static bool Window_GetPropertyBool(void *addonData, GUIHANDLE handle, const char *key);
++ static double Window_GetPropertyDouble(void *addonData, GUIHANDLE handle, const char *key);
++ static void Window_ClearProperties(void *addonData, GUIHANDLE handle);
++ static int Window_GetListSize(void *addonData, GUIHANDLE handle);
++ static void Window_ClearList(void *addonData, GUIHANDLE handle);
++ static GUIHANDLE Window_AddItem(void *addonData, GUIHANDLE handle, GUIHANDLE item, int itemPosition);
++ static GUIHANDLE Window_AddStringItem(void *addonData, GUIHANDLE handle, const char *itemName, int itemPosition);
++ static void Window_RemoveItem(void *addonData, GUIHANDLE handle, int itemPosition);
++ static GUIHANDLE Window_GetListItem(void *addonData, GUIHANDLE handle, int listPos);
++ static void Window_SetCurrentListPosition(void *addonData, GUIHANDLE handle, int listPos);
++ static int Window_GetCurrentListPosition(void *addonData, GUIHANDLE handle);
++ static void Window_AddContextMenuButton(void *addonData, GUIHANDLE handle,int controlId,unsigned int contextButtonId,const char* label);
++ static GUIHANDLE Window_GetControl_Spin(void *addonData, GUIHANDLE handle, int controlId);
++ static GUIHANDLE Window_GetControl_ListContainer(void *addonData, GUIHANDLE handle, int controlId,GUIHANDLE *listItems);
++ static void Window_ReleaseControl_ListContainer(GUIHANDLE listItems);
++ static GUIHANDLE Window_GetControl_Button(void *addonData, GUIHANDLE handle, int controlId);
++ static GUIHANDLE Window_GetControl_RadioButton(void *addonData, GUIHANDLE handle, int controlId);
++ static GUIHANDLE Window_GetControl_Edit(void *addonData, GUIHANDLE handle, int controlId);
++ static GUIHANDLE Window_GetControl_Progress(void *addonData, GUIHANDLE handle, int controlId);
++ static void Window_SetControlLabel(void *addonData, GUIHANDLE handle, int controlId, const char *label);
++ static void Control_Spin_SetVisible(void *addonData, GUIHANDLE spinhandle, bool yesNo);
++ static void Control_Spin_SetText(void *addonData, GUIHANDLE spinhandle, const char *label);
++ static void Control_Spin_Clear(void *addonData, GUIHANDLE spinhandle);
++ static void Control_Spin_AddLabel(void *addonData, GUIHANDLE spinhandle, const char *label, int iValue);
++ static int Control_Spin_GetValue(void *addonData, GUIHANDLE spinhandle);
++ static void Control_Spin_SetValue(void *addonData, GUIHANDLE spinhandle, int iValue);
++ static void Control_ListContainer_SetVisible(void *addonData, GUIHANDLE handle, bool yesNo);
++ static void Control_ListContainer_AddItems(void *addonData, GUIHANDLE handle,GUIHANDLE listItems,GUIHANDLE items[],int size);
++ static GUIHANDLE Control_ListContainer_GetItem(void *addonData, GUIHANDLE handle,GUIHANDLE listItems,int index);
++ static int Control_ListContainer_GetSelected(void *addonData, GUIHANDLE handle);
++ static void Control_ListContainer_Reset(void *addonData, GUIHANDLE handle,GUIHANDLE listItems);
++ static void Control_RadioButton_SetVisible(void *addonData, GUIHANDLE handle, bool yesNo);
++ static void Control_RadioButton_SetText(void *addonData, GUIHANDLE handle, const char *label);
++ static void Control_RadioButton_SetSelected(void *addonData, GUIHANDLE handle, bool yesNo);
++ static bool Control_RadioButton_IsSelected(void *addonData, GUIHANDLE handle);
++ static void Control_Progress_SetPercentage(void *addonData, GUIHANDLE handle, float fPercent);
++ static float Control_Progress_GetPercentage(void *addonData, GUIHANDLE handle);
++ static void Control_Progress_SetInfo(void *addonData, GUIHANDLE handle, int iInfo);
++ static int Control_Progress_GetInfo(void *addonData, GUIHANDLE handle);
++ static const char * Control_Progress_GetDescription(void *addonData, GUIHANDLE handle);
++ static GUIHANDLE ListItem_Create(void *addonData, const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path);
++ static void ListItem_Destroy(void *addonData, GUIHANDLE handle);
++ static const char * ListItem_GetLabel(void *addonData, GUIHANDLE handle);
++ static void ListItem_SetLabel(void *addonData, GUIHANDLE handle, const char *label);
++ static const char * ListItem_GetLabel2(void *addonData, GUIHANDLE handle);
++ static void ListItem_SetLabel2(void *addonData, GUIHANDLE handle, const char *label);
++ static void ListItem_SetIconImage(void *addonData, GUIHANDLE handle, const char *image);
++ static void ListItem_SetThumbnailImage(void *addonData, GUIHANDLE handle, const char *image);
++ static void ListItem_SetInfo(void *addonData, GUIHANDLE handle, const char *info);
++ static void ListItem_SetProperty(void *addonData, GUIHANDLE handle, const char *key, const char *value);
++ static const char * ListItem_GetProperty(void *addonData, GUIHANDLE handle, const char *key);
++ static void ListItem_SetPath(void *addonData, GUIHANDLE handle, const char *path);
++
++private:
++ CB_GUILib *m_callbacks;
++ CAddon *m_addon;
++};
++
++class CGUIAddonWindow : public CGUIMediaWindow
++{
++friend class CAddonCallbacksGUI;
++
++public:
++ CGUIAddonWindow(int id, CStdString strXML, CAddon* addon);
++ virtual ~CGUIAddonWindow(void);
++
++ virtual bool OnMessage(CGUIMessage& message);
++ virtual bool OnAction(const CAction &action);
++ virtual void AllocResources(bool forceLoad = false);
++ virtual void FreeResources(bool forceUnLoad = false);
++ virtual void Render();
++ void WaitForActionEvent(unsigned int timeout);
++ void PulseActionEvent();
++ void AddItem(CFileItemPtr fileItem, int itemPosition);
++ void RemoveItem(int itemPosition);
++ void ClearList();
++ CFileItemPtr GetListItem(int position);
++ int GetListSize();
++ int GetCurrentListPosition();
++ void SetCurrentListPosition(int item);
++ virtual bool OnClick(int iItem);
++ void AddContextMenuButton(int controlId,unsigned int contextButtonId,const char* label);
++
++protected:
++ virtual void Update();
++ virtual void GetContextButtons(int itemNumber, CContextButtons &buttons);
++ virtual bool OnContextButton(int itemNumber, CONTEXT_BUTTON button);
++ void ClearAddonStrings();
++ void SetupShares();
++
++ bool (*CBOnInit)(GUIHANDLE cbhdl);
++ bool (*CBOnFocus)(GUIHANDLE cbhdl, int controlId);
++ bool (*CBOnClick)(GUIHANDLE cbhdl, int controlId);
++ bool (*CBOnAction)(GUIHANDLE cbhdl, int);
++ bool (*CBOnContextMenu)(GUIHANDLE chdl,int controlId,int itemNumber, unsigned int contextButtonId);
++
++ GUIHANDLE m_clientHandle;
++ const int m_iWindowId;
++ int m_iOldWindowId;
++ bool m_bModal;
++ bool m_bIsDialog;
++
++ std::map<int,CContextButtons> m_mContextButtons;
++ int iCurrentContextMenuControl;
++
++private:
++ CEvent m_actionEvent;
++ CAddon *m_addon;
++ CStdString m_mediaDir;
++ int SelectContainerItem(CGUIControl* control, int offset,bool select);
++ CGUIListItemPtr GetSelectedContainerItem(CGUIControl* control,int offset);
++};
++
++class CGUIAddonWindowDialog : public CGUIAddonWindow
++{
++public:
++ CGUIAddonWindowDialog(int id, CStdString strXML, CAddon* addon);
++ virtual ~CGUIAddonWindowDialog(void);
++
++ void Show(bool show = true);
++ virtual bool OnMessage(CGUIMessage &message);
++ virtual bool IsDialogRunning() const { return m_bRunning; }
++ virtual bool IsDialog() const { return true;};
++ virtual bool IsModalDialog() const { return true; };
++ virtual bool IsMediaWindow() const { return false; };
++
++ void Show_Internal(bool show = true);
++
++private:
++ bool m_bRunning;
++};
++
++// TODO:
++//class CGUIAddonDialogSettings : public CGUIDialogSettings
++//{
++//friend class CAddonCallbacksGUI;
++//
++//public:
++// CGUIAddonDialogSettings(int id, const char *xmlFile);
++// virtual ~CGUIDialogSettings(void){}
++// virtual bool OnMessage(CGUIMessage &message);/*??*/
++//
++// virtual void OnSliderChange(void *data, CGUISliderControl *slider);/*??*/
++//protected:
++// virtual void OnOkay() {};/*callback*/
++// virtual void OnCancel() {};/*callback*/
++// virtual bool OnBack(int actionID);/*??*/
++// virtual void OnInitWindow();/*??*/
++// virtual void SetupPage();/*??*/
++// virtual void CreateSettings() {};/*callback*/
++// virtual void OnSettingChanged(SettingInfo &setting) {};/*callback*/
++// SettingInfo GetSetting(int n);
++// };
++//
++};
++ /* namespace ADDON */
+diff --git a/xbmc/addons/AddonCallbacksPVR.cpp b/xbmc/addons/AddonCallbacksPVR.cpp
+new file mode 100644
+index 0000000..6f72add
+--- /dev/null
++++ b/xbmc/addons/AddonCallbacksPVR.cpp
+@@ -0,0 +1,311 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "Application.h"
++#include "AddonCallbacksPVR.h"
++#include "settings/AdvancedSettings.h"
++#include "utils/log.h"
++#include "dialogs/GUIDialogKaiToast.h"
++
++#include "epg/Epg.h"
++#include "pvr/PVRManager.h"
++#include "pvr/channels/PVRChannelGroupsContainer.h"
++#include "pvr/channels/PVRChannelGroupInternal.h"
++#include "pvr/addons/PVRClient.h"
++#include "pvr/recordings/PVRRecordings.h"
++#include "pvr/timers/PVRTimers.h"
++#include "pvr/timers/PVRTimerInfoTag.h"
++
++using namespace PVR;
++using namespace EPG;
++
++namespace ADDON
++{
++
++CAddonCallbacksPVR::CAddonCallbacksPVR(CAddon* addon)
++{
++ m_addon = addon;
++ m_callbacks = new CB_PVRLib;
++
++ /* write XBMC PVR specific add-on function addresses to callback table */
++ m_callbacks->TransferEpgEntry = PVRTransferEpgEntry;
++ m_callbacks->TransferChannelEntry = PVRTransferChannelEntry;
++ m_callbacks->TransferTimerEntry = PVRTransferTimerEntry;
++ m_callbacks->TransferRecordingEntry = PVRTransferRecordingEntry;
++ m_callbacks->AddMenuHook = PVRAddMenuHook;
++ m_callbacks->Recording = PVRRecording;
++ m_callbacks->TriggerChannelUpdate = PVRTriggerChannelUpdate;
++ m_callbacks->TriggerChannelGroupsUpdate = PVRTriggerChannelGroupsUpdate;
++ m_callbacks->TriggerTimerUpdate = PVRTriggerTimerUpdate;
++ m_callbacks->TriggerRecordingUpdate = PVRTriggerRecordingUpdate;
++ m_callbacks->FreeDemuxPacket = PVRFreeDemuxPacket;
++ m_callbacks->AllocateDemuxPacket = PVRAllocateDemuxPacket;
++ m_callbacks->TransferChannelGroup = PVRTransferChannelGroup;
++ m_callbacks->TransferChannelGroupMember = PVRTransferChannelGroupMember;
++}
++
++CAddonCallbacksPVR::~CAddonCallbacksPVR()
++{
++ /* delete the callback table */
++ delete m_callbacks;
++}
++
++void CAddonCallbacksPVR::PVRTransferChannelGroup(void *addonData, const PVR_HANDLE handle, const PVR_CHANNEL_GROUP *group)
++{
++ CAddonCallbacks* addon = (CAddonCallbacks*) addonData;
++ if (addon == NULL || handle == NULL || group == NULL || handle->dataAddress == NULL)
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacksPVR - %s - called with a null pointer", __FUNCTION__);
++ return;
++ }
++
++ if (strlen(group->strGroupName) == 0)
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacksPVR - %s - empty group name", __FUNCTION__);
++ return;
++ }
++
++ CPVRChannelGroups *xbmcGroups = (CPVRChannelGroups *) handle->dataAddress;
++ CPVRChannelGroup xbmcGroup(*group);
++
++ /* transfer this entry to the groups container */
++ xbmcGroups->UpdateFromClient(xbmcGroup);
++}
++
++void CAddonCallbacksPVR::PVRTransferChannelGroupMember(void *addonData, const PVR_HANDLE handle, const PVR_CHANNEL_GROUP_MEMBER *member)
++{
++ CAddonCallbacks* addon = (CAddonCallbacks*) addonData;
++ if (addon == NULL || handle == NULL || member == NULL || handle->dataAddress == NULL)
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacksPVR - %s - called with a null pointer", __FUNCTION__);
++ return;
++ }
++
++ CPVRClient* client = (CPVRClient*) handle->callerAddress;
++ CPVRChannelGroup *group = (CPVRChannelGroup *) handle->dataAddress;
++ CPVRChannel *channel = (CPVRChannel *) g_PVRChannelGroups->GetByUniqueID(member->iChannelUniqueId, client->GetClientID());
++ if (!group || !channel)
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacksPVR - %s - cannot find group '%s' or channel '%d'", __FUNCTION__, member->strGroupName, member->iChannelUniqueId);
++ }
++ else if (group->IsRadio() == channel->IsRadio())
++ {
++ /* transfer this entry to the group */
++ group->AddToGroup(*channel, member->iChannelNumber, false);
++ }
++}
++
++void CAddonCallbacksPVR::PVRTransferEpgEntry(void *addonData, const PVR_HANDLE handle, const EPG_TAG *epgentry)
++{
++ CAddonCallbacks* addon = (CAddonCallbacks*) addonData;
++ if (addon == NULL || handle == NULL || epgentry == NULL)
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacksPVR - %s - called with a null pointer", __FUNCTION__);
++ return;
++ }
++
++ CEpg *xbmcEpg = (CEpg*) handle->dataAddress;
++
++ EPG_TAG *epgentry2 = (EPG_TAG*) epgentry;
++ bool bUpdateDatabase = handle->dataIdentifier == 1;
++
++ /* transfer this entry to the epg */
++ xbmcEpg->UpdateEntry(epgentry2, bUpdateDatabase);
++}
++
++void CAddonCallbacksPVR::PVRTransferChannelEntry(void *addonData, const PVR_HANDLE handle, const PVR_CHANNEL *channel)
++{
++ CAddonCallbacks* addon = (CAddonCallbacks*) addonData;
++ if (addon == NULL || handle == NULL || channel == NULL)
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacksPVR - %s - called with a null pointer", __FUNCTION__);
++ return;
++ }
++
++ CPVRClient* client = (CPVRClient*) handle->callerAddress;
++ CPVRChannelGroupInternal *xbmcChannels = (CPVRChannelGroupInternal*) handle->dataAddress;
++
++ CPVRChannel channelTag(*channel, client->GetClientID());
++
++ /* transfer this entry to the internal channels group */
++ xbmcChannels->UpdateFromClient(channelTag);
++}
++
++void CAddonCallbacksPVR::PVRTransferRecordingEntry(void *addonData, const PVR_HANDLE handle, const PVR_RECORDING *recording)
++{
++ CAddonCallbacks* addon = (CAddonCallbacks*) addonData;
++ if (addon == NULL || handle == NULL || recording == NULL)
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacksPVR - %s - called with a null pointer", __FUNCTION__);
++ return;
++ }
++
++ CPVRClient* client = (CPVRClient*) handle->callerAddress;
++ CPVRRecordings *xbmcRecordings = (CPVRRecordings*) handle->dataAddress;
++
++ CPVRRecording tag(*recording, client->GetClientID());
++
++ /* transfer this entry to the recordings container */
++ xbmcRecordings->UpdateFromClient(tag);
++}
++
++void CAddonCallbacksPVR::PVRTransferTimerEntry(void *addonData, const PVR_HANDLE handle, const PVR_TIMER *timer)
++{
++ CAddonCallbacks* addon = (CAddonCallbacks*) addonData;
++ if (addon == NULL || handle == NULL || timer == NULL)
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacksPVR - %s - called with a null pointer", __FUNCTION__);
++ return;
++ }
++
++ CPVRTimers *xbmcTimers = (CPVRTimers*) handle->dataAddress;
++ CPVRClient* client = (CPVRClient*) handle->callerAddress;
++ CPVRChannel *channel = (CPVRChannel *) g_PVRChannelGroups->GetByUniqueID(timer->iClientChannelUid, client->GetClientID());
++
++ if (channel == NULL)
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacksPVR - %s - cannot find channel %d on client %d",
++ __FUNCTION__, timer->iClientChannelUid, client->GetClientID());
++ return;
++ }
++
++ CPVRTimerInfoTag tag(*timer, channel, client->GetClientID());
++
++ /* transfer this entry to the timers container */
++ xbmcTimers->UpdateFromClient(tag);
++}
++
++void CAddonCallbacksPVR::PVRAddMenuHook(void *addonData, PVR_MENUHOOK *hook)
++{
++ CAddonCallbacks* addon = (CAddonCallbacks*) addonData;
++ if (addon == NULL || hook == NULL)
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacksPVR - %s - called with a null pointer", __FUNCTION__);
++ return;
++ }
++
++ CAddonCallbacksPVR* addonHelper = addon->GetHelperPVR();
++ CPVRClient* client = (CPVRClient*) addonHelper->m_addon;
++ PVR_MENUHOOKS *hooks = client->GetMenuHooks();
++
++ PVR_MENUHOOK hookInt;
++ hookInt.iHookId = hook->iHookId;
++ hookInt.iLocalizedStringId = hook->iLocalizedStringId;
++
++ /* add this new hook */
++ hooks->push_back(hookInt);
++}
++
++void CAddonCallbacksPVR::PVRRecording(void *addonData, const char *strName, const char *strFileName, bool bOnOff)
++{
++ CAddonCallbacks* addon = (CAddonCallbacks*) addonData;
++ if (addon == NULL)
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacksPVR - %s - called with a null pointer", __FUNCTION__);
++ return;
++ }
++
++ CAddonCallbacksPVR* addonHelper = addon->GetHelperPVR();
++
++ CStdString strLine1;
++ if (bOnOff)
++ strLine1.Format(g_localizeStrings.Get(19197), addonHelper->m_addon->Name());
++ else
++ strLine1.Format(g_localizeStrings.Get(19198), addonHelper->m_addon->Name());
++
++ CStdString strLine2;
++ if (strName)
++ strLine2 = strName;
++ else if (strFileName)
++ strLine2 = strFileName;
++ else
++ strLine2 = "";
++
++ /* display a notification for 5 seconds */
++ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, strLine1, strLine2, 5000, false);
++
++ CLog::Log(LOGDEBUG, "CAddonCallbacksPVR - %s - recording %s on client '%s'. name='%s' filename='%s'",
++ __FUNCTION__, bOnOff ? "started" : "finished", addonHelper->m_addon->Name().c_str(), strName, strFileName);
++}
++
++void CAddonCallbacksPVR::PVRTriggerChannelUpdate(void *addonData)
++{
++ CAddonCallbacks* addon = (CAddonCallbacks*) addonData;
++ if (addon == NULL)
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacksPVR - %s - called with a null pointer", __FUNCTION__);
++ return;
++ }
++
++ /* update the channels table in the next iteration of the pvrmanager's main loop */
++ g_PVRManager.TriggerChannelsUpdate();
++}
++
++void CAddonCallbacksPVR::PVRTriggerTimerUpdate(void *addonData)
++{
++ CAddonCallbacks* addon = (CAddonCallbacks*) addonData;
++ if (addon == NULL)
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacksPVR - %s - called with a null pointer", __FUNCTION__);
++ return;
++ }
++
++ /* update the timers table in the next iteration of the pvrmanager's main loop */
++ g_PVRManager.TriggerTimersUpdate();
++}
++
++void CAddonCallbacksPVR::PVRTriggerRecordingUpdate(void *addonData)
++{
++ CAddonCallbacks* addon = (CAddonCallbacks*) addonData;
++ if (addon == NULL)
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacksPVR - %s - called with a null pointer", __FUNCTION__);
++ return;
++ }
++
++ /* update the recordings table in the next iteration of the pvrmanager's main loop */
++ g_PVRManager.TriggerRecordingsUpdate();
++}
++
++void CAddonCallbacksPVR::PVRTriggerChannelGroupsUpdate(void *addonData)
++{
++ CAddonCallbacks* addon = (CAddonCallbacks*) addonData;
++ if (addon == NULL)
++ {
++ CLog::Log(LOGERROR, "CAddonCallbacksPVR - %s - called with a null pointer", __FUNCTION__);
++ return;
++ }
++
++ /* update all channel groups in the next iteration of the pvrmanager's main loop */
++ g_PVRManager.TriggerChannelGroupsUpdate();
++}
++
++void CAddonCallbacksPVR::PVRFreeDemuxPacket(void *addonData, DemuxPacket* pPacket)
++{
++ CDVDDemuxUtils::FreeDemuxPacket(pPacket);
++}
++
++DemuxPacket* CAddonCallbacksPVR::PVRAllocateDemuxPacket(void *addonData, int iDataSize)
++{
++ return CDVDDemuxUtils::AllocateDemuxPacket(iDataSize);
++}
++
++}; /* namespace ADDON */
+diff --git a/xbmc/addons/AddonCallbacksPVR.h b/xbmc/addons/AddonCallbacksPVR.h
+new file mode 100644
+index 0000000..d9850bf
+--- /dev/null
++++ b/xbmc/addons/AddonCallbacksPVR.h
+@@ -0,0 +1,153 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "AddonCallbacks.h"
++#include "include/xbmc_pvr_types.h"
++
++namespace ADDON
++{
++
++/*!
++ * Callbacks for a PVR add-on to XBMC.
++ *
++ * Also translates the addon's C structures to XBMC's C++ structures.
++ */
++class CAddonCallbacksPVR
++{
++public:
++ CAddonCallbacksPVR(CAddon* addon);
++ ~CAddonCallbacksPVR(void);
++
++ /*!
++ * @return The callback table.
++ */
++ CB_PVRLib *GetCallbacks() { return m_callbacks; }
++
++ /*!
++ * @brief Transfer a channel group from the add-on to XBMC. The group will be created if it doesn't exist.
++ * @param addonData A pointer to the add-on.
++ * @param handle The handle that initiated this action.
++ * @param group The entry to transfer.
++ */
++ static void PVRTransferChannelGroup(void *addonData, const PVR_HANDLE handle, const PVR_CHANNEL_GROUP *group);
++
++ /*!
++ * @brief Transfer a channel group member from the add-on to XBMC. The channel will be added to the group if the group can be found.
++ * @param addonData A pointer to the add-on.
++ * @param handle The handle that initiated this action.
++ * @param member The entry to transfer.
++ */
++ static void PVRTransferChannelGroupMember(void *addonData, const PVR_HANDLE handle, const PVR_CHANNEL_GROUP_MEMBER *member);
++
++ /*!
++ * @brief Transfer an EPG entry from the add-on to XBMC.
++ * @param addonData A pointer to the add-on.
++ * @param handle The handle that initiated this action.
++ * @param epgentry The entry to transfer.
++ */
++ static void PVRTransferEpgEntry(void *addonData, const PVR_HANDLE handle, const EPG_TAG *epgentry);
++
++ /*!
++ * @brief Transfer a channel entry from the add-on to XBMC.
++ * @param addonData A pointer to the add-on.
++ * @param handle The handle that initiated this action.
++ * @param channel The entry to transfer.
++ */
++ static void PVRTransferChannelEntry(void *addonData, const PVR_HANDLE handle, const PVR_CHANNEL *channel);
++
++ /*!
++ * @brief Transfer a timer entry from the add-on to XBMC.
++ * @param addonData A pointer to the add-on.
++ * @param handle The handle that initiated this action.
++ * @param timer The entry to transfer.
++ */
++ static void PVRTransferTimerEntry(void *addonData, const PVR_HANDLE handle, const PVR_TIMER *timer);
++
++ /*!
++ * @brief Transfer a recording entry from the add-on to XBMC.
++ * @param addonData A pointer to the add-on.
++ * @param handle The handle that initiated this action.
++ * @param recording The entry to transfer.
++ */
++ static void PVRTransferRecordingEntry(void *addonData, const PVR_HANDLE handle, const PVR_RECORDING *recording);
++
++ /*!
++ * @brief Add a menu hook to this add-on table.
++ * @param addonData A pointer to the add-on.
++ * @param hook The hook to add.
++ */
++ static void PVRAddMenuHook(void *addonData, PVR_MENUHOOK *hook);
++
++ /*!
++ * @brief Notify XBMC that a recording has started or stoppped.
++ * @param addonData A pointer to the add-on.
++ * @param strName The name of the recording.
++ * @param strFileName The filename of the recording.
++ * @param bOnOff True if the recording started, false if it stopped.
++ */
++ static void PVRRecording(void *addonData, const char *strName, const char *strFileName, bool bOnOff);
++
++ /*!
++ * @brief Ask the PVRManager to refresh it's channels list.
++ * @param addonData A pointer to the add-on.
++ */
++ static void PVRTriggerChannelUpdate(void *addonData);
++
++ /*!
++ * @brief Ask the PVRManager to refresh it's timers list.
++ * @param addonData A pointer to the add-on.
++ */
++ static void PVRTriggerTimerUpdate(void *addonData);
++
++ /*!
++ * @brief Ask the PVRManager to refresh it's recordings list.
++ * @param addonData A pointer to the add-on.
++ */
++ static void PVRTriggerRecordingUpdate(void *addonData);
++
++ /*!
++ * @brief Ask the PVRManager to refresh it's channel groups list.
++ * @param addonData A pointer to the add-on.
++ */
++ static void PVRTriggerChannelGroupsUpdate(void *addonData);
++
++ /*!
++ * @brief Free an allocated demux packet.
++ * @param addonData A pointer to the add-on.
++ * @param pPacket The packet to free.
++ */
++ static void PVRFreeDemuxPacket(void *addonData, DemuxPacket* pPacket);
++
++ /*!
++ * @brief Allocate a new demux packet.
++ * @param addonData A pointer to the add-on.
++ * @param iDataSize The packet size.
++ * @return The allocated packet.
++ */
++ static DemuxPacket* PVRAllocateDemuxPacket(void *addonData, int iDataSize = 0);
++
++private:
++ CB_PVRLib *m_callbacks; /*!< callback addresses */
++ CAddon *m_addon; /*!< the addon */
++};
++
++}; /* namespace ADDON */
+diff --git a/xbmc/addons/AddonDatabase.cpp b/xbmc/addons/AddonDatabase.cpp
+index fe4e7b4..24276e1 100644
+--- a/xbmc/addons/AddonDatabase.cpp
++++ b/xbmc/addons/AddonDatabase.cpp
+@@ -658,6 +658,14 @@ bool CAddonDatabase::IsAddonDisabled(const CStdString &addonID)
+ return false;
+ }
+
++bool CAddonDatabase::IsSystemPVRAddonEnabled(const CStdString &addonID)
++{
++ CStdString strWhereClause = PrepareSQL("addonID = '%s'", addonID.c_str());
++ CStdString strEnabled = GetSingleValue("pvrenabled", "id", strWhereClause);
++
++ return !strEnabled.IsEmpty();
++}
++
+ CStdString CAddonDatabase::IsAddonBroken(const CStdString &addonID)
+ {
+ try
+diff --git a/xbmc/addons/AddonDatabase.h b/xbmc/addons/AddonDatabase.h
+index 515fa98..34b8ed3 100644
+--- a/xbmc/addons/AddonDatabase.h
++++ b/xbmc/addons/AddonDatabase.h
+@@ -79,6 +79,10 @@ public:
+ \sa DisableAddon, IsAddonDisabled */
+ bool HasDisabledAddons();
+
++ /*! @deprecated only here to allow clean upgrades from earlier pvr versions
++ */
++ bool IsSystemPVRAddonEnabled(const CStdString &addonID);
++
+ /*! \brief Mark an addon as broken
+ Sets a flag that this addon has been marked as broken in the repository.
+ \param addonID id of the addon to mark as broken
+diff --git a/xbmc/addons/AddonDll.h b/xbmc/addons/AddonDll.h
+index bda98e7..2db5d15 100644
+--- a/xbmc/addons/AddonDll.h
++++ b/xbmc/addons/AddonDll.h
+@@ -23,6 +23,7 @@
+ #include "DllAddon.h"
+ #include "AddonManager.h"
+ #include "AddonStatusHandler.h"
++#include "AddonCallbacks.h"
+ #include "settings/GUIDialogSettings.h"
+ #include "utils/URIUtils.h"
+ #include "filesystem/File.h"
+@@ -59,6 +60,7 @@ namespace ADDON
+ virtual bool LoadSettings();
+ TheStruct* m_pStruct;
+ TheProps* m_pInfo;
++ CAddonCallbacks* m_pHelpers;
+
+ private:
+ TheDll* m_pDll;
+@@ -109,6 +111,7 @@ CAddonDll<TheDll, TheStruct, TheProps>::CAddonDll(const AddonProps &props)
+ m_initialized = false;
+ m_pDll = NULL;
+ m_pInfo = NULL;
++ m_pHelpers = NULL;
+ m_needsavedsettings = false;
+ }
+
+@@ -186,9 +189,15 @@ bool CAddonDll<TheDll, TheStruct, TheProps>::Create()
+ if (!LoadDll())
+ return false;
+
++ /* Allocate the helper function class to allow crosstalk over
++ helper libraries */
++ m_pHelpers = new CAddonCallbacks(this);
++
++ /* Call Create to make connections, initializing data or whatever is
++ needed to become the AddOn running */
+ try
+ {
+- ADDON_STATUS status = m_pDll->Create(NULL, m_pInfo);
++ ADDON_STATUS status = m_pDll->Create(m_pHelpers->GetCallbacks(), m_pInfo);
+ if (status == ADDON_STATUS_OK)
+ m_initialized = true;
+ else if ((status == ADDON_STATUS_NEED_SETTINGS) || (status == ADDON_STATUS_NEED_SAVEDSETTINGS))
+@@ -210,6 +219,9 @@ bool CAddonDll<TheDll, TheStruct, TheProps>::Create()
+ HandleException(e, "m_pDll->Create");
+ }
+
++ if (!m_initialized)
++ SAFE_DELETE(m_pHelpers);
++
+ return m_initialized;
+ }
+
+@@ -258,6 +270,8 @@ void CAddonDll<TheDll, TheStruct, TheProps>::Destroy()
+ {
+ HandleException(e, "m_pDll->Unload");
+ }
++ delete m_pHelpers;
++ m_pHelpers = NULL;
+ free(m_pStruct);
+ m_pStruct = NULL;
+ delete m_pDll;
+diff --git a/xbmc/addons/AddonManager.cpp b/xbmc/addons/AddonManager.cpp
+index 4a61c7e..fa99ff1 100644
+--- a/xbmc/addons/AddonManager.cpp
++++ b/xbmc/addons/AddonManager.cpp
+@@ -39,6 +39,10 @@
+ #ifdef HAS_SCREENSAVER
+ #include "ScreenSaver.h"
+ #endif
++#ifdef HAS_PVRCLIENTS
++#include "DllPVRClient.h"
++#include "pvr/addons/PVRClient.h"
++#endif
+ //#ifdef HAS_SCRAPERS
+ #include "Scraper.h"
+ //#endif
+@@ -46,8 +50,11 @@
+ #include "Repository.h"
+ #include "Skin.h"
+ #include "Service.h"
++#include "pvr/PVRManager.h"
++#include "pvr/addons/PVRClients.h"
+
+ using namespace std;
++using namespace PVR;
+
+ namespace ADDON
+ {
+@@ -109,6 +116,7 @@ AddonPtr CAddonMgr::Factory(const cp_extension_t *props)
+ return AddonPtr(new CScraper(props));
+ case ADDON_VIZ:
+ case ADDON_SCREENSAVER:
++ case ADDON_PVRDLL:
+ { // begin temporary platform handling for Dlls
+ // ideally platforms issues will be handled by C-Pluff
+ // this is not an attempt at a solution
+@@ -136,6 +144,12 @@ AddonPtr CAddonMgr::Factory(const cp_extension_t *props)
+ return AddonPtr(new CVisualisation(props));
+ #endif
+ }
++ else if (type == ADDON_PVRDLL)
++ {
++#ifdef HAS_PVRCLIENTS
++ return AddonPtr(new CPVRClient(props));
++#endif
++ }
+ else
+ return AddonPtr(new CScreenSaver(props));
+ }
+@@ -286,14 +300,14 @@ bool CAddonMgr::HasAddons(const TYPE &type, bool enabled /*= true*/)
+ return GetAddons(type, addons, enabled);
+ }
+
+-bool CAddonMgr::GetAllAddons(VECADDONS &addons, bool enabled /*= true*/, bool allowRepos /* = false */)
++bool CAddonMgr::GetAllAddons(VECADDONS &addons, bool enabled /*= true*/, bool allowRepos /* = false */, bool bGetDisabledPVRAddons /* = true */)
+ {
+ for (int i = ADDON_UNKNOWN+1; i < ADDON_VIZ_LIBRARY; ++i)
+ {
+ if (!allowRepos && ADDON_REPOSITORY == (TYPE)i)
+ continue;
+ VECADDONS temp;
+- if (CAddonMgr::Get().GetAddons((TYPE)i, temp, enabled))
++ if (CAddonMgr::Get().GetAddons((TYPE)i, temp, enabled, bGetDisabledPVRAddons))
+ addons.insert(addons.end(), temp.begin(), temp.end());
+ }
+ return !addons.empty();
+@@ -372,8 +386,9 @@ bool CAddonMgr::HasOutdatedAddons(bool enabled /*= true*/)
+ return GetAllOutdatedAddons(dummy,enabled);
+ }
+
+-bool CAddonMgr::GetAddons(const TYPE &type, VECADDONS &addons, bool enabled /* = true */)
++bool CAddonMgr::GetAddons(const TYPE &type, VECADDONS &addons, bool enabled /* = true */, bool bGetDisabledPVRAddons /* = true */)
+ {
++ CStdString xbmcPath = _P("special://xbmc/addons");
+ CSingleLock lock(m_critSection);
+ addons.clear();
+ cp_status_t status;
+@@ -382,9 +397,25 @@ bool CAddonMgr::GetAddons(const TYPE &type, VECADDONS &addons, bool enabled /* =
+ cp_extension_t **exts = m_cpluff->get_extensions_info(m_cp_context, ext_point.c_str(), &status, &num);
+ for(int i=0; i <num; i++)
+ {
+- AddonPtr addon(Factory(exts[i]));
+- if (addon && m_database.IsAddonDisabled(addon->ID()) != enabled)
+- addons.push_back(addon);
++ const cp_extension_t *props = exts[i];
++ bool bIsPVRAddon(TranslateType(props->ext_point_id) == ADDON_PVRDLL);
++
++ if (((bGetDisabledPVRAddons && bIsPVRAddon) || m_database.IsAddonDisabled(props->plugin->identifier) != enabled))
++ {
++ if (bIsPVRAddon && g_PVRManager.IsStarted())
++ {
++ AddonPtr pvrAddon;
++ if (g_PVRClients->GetClient(props->plugin->identifier, pvrAddon))
++ {
++ addons.push_back(pvrAddon);
++ continue;
++ }
++ }
++
++ AddonPtr addon(Factory(props));
++ if (addon)
++ addons.push_back(addon);
++ }
+ }
+ m_cpluff->release_info(m_cp_context, exts);
+ return addons.size() > 0;
+@@ -394,14 +425,26 @@ bool CAddonMgr::GetAddon(const CStdString &str, AddonPtr &addon, const TYPE &typ
+ {
+ CSingleLock lock(m_critSection);
+
++ CStdString xbmcPath = _P("special://xbmc/addons");
+ cp_status_t status;
+ cp_plugin_info_t *cpaddon = m_cpluff->get_plugin_info(m_cp_context, str.c_str(), &status);
+ if (status == CP_OK && cpaddon)
+ {
+ addon = GetAddonFromDescriptor(cpaddon);
+ m_cpluff->release_info(m_cp_context, cpaddon);
+- if (addon.get() && enabledOnly && m_database.IsAddonDisabled(addon->ID()))
+- return false;
++
++ if (addon && addon.get())
++ {
++ if (enabledOnly && m_database.IsAddonDisabled(addon->ID()))
++ return false;
++
++ if (addon->Type() == ADDON_PVRDLL && g_PVRManager.IsStarted())
++ {
++ AddonPtr pvrAddon;
++ if (g_PVRClients->GetClient(addon->ID(), pvrAddon))
++ addon = pvrAddon;
++ }
++ }
+ return NULL != addon.get();
+ }
+ if (cpaddon)
+@@ -489,15 +532,25 @@ CStdString CAddonMgr::GetString(const CStdString &id, const int number)
+
+ void CAddonMgr::FindAddons()
+ {
+- CSingleLock lock(m_critSection);
+- if (m_cpluff && m_cp_context)
+- m_cpluff->scan_plugins(m_cp_context, CP_SP_UPGRADE);
++ {
++ CSingleLock lock(m_critSection);
++ if (m_cpluff && m_cp_context)
++ {
++ m_cpluff->scan_plugins(m_cp_context, CP_SP_UPGRADE);
++ SetChanged();
++ }
++ }
++ NotifyObservers("addons");
+ }
+
+ void CAddonMgr::RemoveAddon(const CStdString& ID)
+ {
+ if (m_cpluff && m_cp_context)
++ {
+ m_cpluff->uninstall_plugin(m_cp_context,ID.c_str());
++ SetChanged();
++ NotifyObservers("addons");
++ }
+ }
+
+ const char *CAddonMgr::GetTranslatedString(const cp_cfg_element_t *root, const char *tag)
+@@ -554,6 +607,8 @@ AddonPtr CAddonMgr::AddonFromProps(AddonProps& addonProps)
+ return AddonPtr(new CScreenSaver(addonProps));
+ case ADDON_VIZ_LIBRARY:
+ return AddonPtr(new CAddonLibrary(addonProps));
++ case ADDON_PVRDLL:
++ return AddonPtr(new CPVRClient(addonProps));
+ case ADDON_REPOSITORY:
+ return AddonPtr(new CRepository(addonProps));
+ default:
+diff --git a/xbmc/addons/AddonManager.h b/xbmc/addons/AddonManager.h
+index 8166974..aaae599 100644
+--- a/xbmc/addons/AddonManager.h
++++ b/xbmc/addons/AddonManager.h
+@@ -22,6 +22,7 @@
+ #include "Addon.h"
+ #include "threads/CriticalSection.h"
+ #include "utils/StdString.h"
++#include "utils/Observer.h"
+ #include <vector>
+ #include <map>
+ #include <deque>
+@@ -44,6 +45,7 @@ namespace ADDON
+ const CStdString ADDON_PYTHON_EXT = "*.py";
+ const CStdString ADDON_SCRAPER_EXT = "*.xml";
+ const CStdString ADDON_SCREENSAVER_EXT = "*.xbs";
++ const CStdString ADDON_PVRDLL_EXT = "*.pvr";
+ const CStdString ADDON_DSP_AUDIO_EXT = "*.adsp";
+ const CStdString ADDON_VERSION_RE = "(?<Major>\\d*)\\.?(?<Minor>\\d*)?\\.?(?<Build>\\d*)?\\.?(?<Revision>\\d*)?";
+
+@@ -67,7 +69,7 @@ namespace ADDON
+ * otherwise. Services the generic callbacks available
+ * to all addon variants.
+ */
+- class CAddonMgr
++ class CAddonMgr : public Observable
+ {
+ public:
+ static CAddonMgr &Get();
+@@ -90,8 +92,8 @@ namespace ADDON
+ */
+ bool GetAddon(const CStdString &id, AddonPtr &addon, const TYPE &type = ADDON_UNKNOWN, bool enabledOnly = true);
+ bool HasAddons(const TYPE &type, bool enabled = true);
+- bool GetAddons(const TYPE &type, VECADDONS &addons, bool enabled = true);
+- bool GetAllAddons(VECADDONS &addons, bool enabled = true, bool allowRepos = false);
++ bool GetAddons(const TYPE &type, VECADDONS &addons, bool enabled = true, bool bGetDisabledPVRAddons = true);
++ bool GetAllAddons(VECADDONS &addons, bool enabled = true, bool allowRepos = false, bool bGetDisabledPVRAddons = true);
+ void AddToUpdateableAddons(AddonPtr &pAddon);
+ void RemoveFromUpdateableAddons(AddonPtr &pAddon);
+ bool ReloadSettings(const CStdString &id);
+diff --git a/xbmc/addons/AddonStatusHandler.cpp b/xbmc/addons/AddonStatusHandler.cpp
+index f7397f9..0bba67d 100644
+--- a/xbmc/addons/AddonStatusHandler.cpp
++++ b/xbmc/addons/AddonStatusHandler.cpp
+@@ -84,7 +84,7 @@ void CAddonStatusHandler::Process()
+ heading.Format("%s: %s", TranslateType(m_addon->Type(), true).c_str(), m_addon->Name().c_str());
+
+ /* AddOn lost connection to his backend (for ones that use Network) */
+- if (m_status == ADDON_STATUS_LOST_CONNECTION)
++ if (m_status == ADDON_STATUS_LOST_CONNECTION && m_addon->Type() != ADDON_PVRDLL) // TODO display a proper message for pvr addons, but don't popup a dialog that requires user action
+ {
+ CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
+ if (!pDialog) return;
+diff --git a/xbmc/addons/DllPVRClient.h b/xbmc/addons/DllPVRClient.h
+new file mode 100644
+index 0000000..82bfcb5
+--- /dev/null
++++ b/xbmc/addons/DllPVRClient.h
+@@ -0,0 +1,30 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2009 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "DllAddon.h"
++#include "include/xbmc_pvr_types.h"
++
++class DllPVRClient : public DllAddon<PVRClient, PVR_PROPERTIES>
++{
++ // this is populated via Macro calls in DllAddon.h
++};
++
+diff --git a/xbmc/addons/GUIDialogAddonInfo.cpp b/xbmc/addons/GUIDialogAddonInfo.cpp
+index 0123aae..49ded35 100644
+--- a/xbmc/addons/GUIDialogAddonInfo.cpp
++++ b/xbmc/addons/GUIDialogAddonInfo.cpp
+@@ -37,6 +37,7 @@
+ #include "utils/StringUtils.h"
+ #include "utils/URIUtils.h"
+ #include "addons/AddonInstaller.h"
++#include "Application.h"
+
+ #define CONTROL_BTN_INSTALL 6
+ #define CONTROL_BTN_ENABLE 7
+@@ -138,11 +139,13 @@ void CGUIDialogAddonInfo::UpdateControls()
+ GrabRollbackVersions();
+
+ // TODO: System addons should be able to be disabled
+- bool canDisable = isInstalled && !isSystem && !m_localAddon->IsInUse();
++ // TODO: the following line will have to be changed later, when the PVR add-ons are no longer part of our source tree
++ bool isPVR = isInstalled && m_localAddon->Type() == ADDON_PVRDLL;
++ bool canDisable = isInstalled && (!isSystem || isPVR) && !m_localAddon->IsInUse();
+ bool canInstall = !isInstalled && m_item->GetProperty("Addon.Broken").empty();
+ bool isRepo = (isInstalled && m_localAddon->Type() == ADDON_REPOSITORY) || (m_addon && m_addon->Type() == ADDON_REPOSITORY);
+
+- CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_INSTALL, canDisable || canInstall);
++ CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_INSTALL, (canDisable || canInstall) && !isPVR);
+ SET_CONTROL_LABEL(CONTROL_BTN_INSTALL, isInstalled ? 24037 : 24038);
+
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_ENABLE, canDisable);
+@@ -208,9 +211,17 @@ void CGUIDialogAddonInfo::OnEnable(bool enable)
+ if (!m_localAddon.get())
+ return;
+
++ CStdString xbmcPath = _P("special://xbmc/addons");
+ CAddonDatabase database;
+ database.Open();
+- database.DisableAddon(m_localAddon->ID(), !enable);
++// if (m_localAddon->Type() == ADDON_PVRDLL && m_localAddon->Path().Left(xbmcPath.size()).Equals(xbmcPath))
++// database.EnableSystemPVRAddon(m_localAddon->ID(), enable);
++// else
++ database.DisableAddon(m_localAddon->ID(), !enable);
++
++ if (m_localAddon->Type() == ADDON_PVRDLL && enable)
++ g_application.StartPVRManager();
++
+ SetItem(m_item);
+ UpdateControls();
+ g_windowManager.SendMessage(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE);
+diff --git a/xbmc/addons/Makefile b/xbmc/addons/Makefile
+index 8848788..952a267 100644
+--- a/xbmc/addons/Makefile
++++ b/xbmc/addons/Makefile
+@@ -1,4 +1,8 @@
+ SRCS=Addon.cpp \
++ AddonCallbacks.cpp \
++ AddonCallbacksAddon.cpp \
++ AddonCallbacksGUI.cpp \
++ AddonCallbacksPVR.cpp \
+ AddonDatabase.cpp \
+ AddonInstaller.cpp \
+ AddonManager.cpp \
+diff --git a/xbmc/addons/Skin.cpp b/xbmc/addons/Skin.cpp
+index fd1b252..62745dc 100644
+--- a/xbmc/addons/Skin.cpp
++++ b/xbmc/addons/Skin.cpp
+@@ -202,6 +202,7 @@ bool CSkinInfo::LoadStartupWindows(const cp_extension_t *ext)
+ {
+ m_startupWindows.clear();
+ m_startupWindows.push_back(CStartupWindow(WINDOW_HOME, "513"));
++ m_startupWindows.push_back(CStartupWindow(WINDOW_PVR, "19180"));
+ m_startupWindows.push_back(CStartupWindow(WINDOW_PROGRAMS, "0"));
+ m_startupWindows.push_back(CStartupWindow(WINDOW_PICTURES, "1"));
+ m_startupWindows.push_back(CStartupWindow(WINDOW_MUSIC, "2"));
+diff --git a/xbmc/addons/include/xbmc_pvr_dll.h b/xbmc/addons/include/xbmc_pvr_dll.h
+new file mode 100644
+index 0000000..8c99416
+--- /dev/null
++++ b/xbmc/addons/include/xbmc_pvr_dll.h
+@@ -0,0 +1,441 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#ifndef __XBMC_PVR_H__
++#define __XBMC_PVR_H__
++
++#include "xbmc_addon_dll.h"
++#include "xbmc_pvr_types.h"
++
++extern "C"
++{
++ // Functions that your PVR client must implement
++
++ /** @name PVR server methods */
++ //@{
++
++ /*!
++ * @brief Query this add-on's capabilities.
++ * @param pCapabilities The add-on properties.
++ * @return PVR_ERROR_NO_ERROR if the properties were fetched successfully.
++ */
++ PVR_ERROR GetAddonCapabilities(PVR_ADDON_CAPABILITIES *pCapabilities);
++
++ /*!
++ * @brief Get the stream properties of the stream that's currently being read.
++ * @param pProperties The properties.
++ * @return PVR_ERROR_NO_ERROR if the properties have been fetched successfully.
++ */
++ PVR_ERROR GetStreamProperties(PVR_STREAM_PROPERTIES *pProperties);
++
++ /*!
++ * @return The name reported by the backend.
++ */
++ const char *GetBackendName(void);
++
++ /*!
++ * @return The version string reported by the backend.
++ */
++ const char *GetBackendVersion(void);
++
++ /*!
++ * @return The connection string reported by the backend.
++ */
++ const char *GetConnectionString(void);
++
++ /*!
++ * @brief Get the disk space reported by the server.
++ * @param iTotal The total disk space.
++ * @param iUsed The used disk space.
++ * @return PVR_ERROR_NO_ERROR if the drive space has been fetched successfully.
++ */
++ PVR_ERROR GetDriveSpace(long long *iTotal, long long *iUsed);
++
++ /*!
++ * @brief Show the channel scan dialog.
++ * @return PVR_ERROR_NO_ERROR if the dialog was displayed successfully.
++ */
++ PVR_ERROR DialogChannelScan(void);
++
++ /*!
++ * @brief Call one of the menu hooks.
++ * @param menuhook The hook to call.
++ * @return PVR_ERROR_NO_ERROR if the hook was called successfully.
++ */
++ PVR_ERROR CallMenuHook(const PVR_MENUHOOK &menuhook);
++
++ //@}
++ /** @name PVR EPG methods */
++ //@{
++
++ /*!
++ * @brief Request an EPG table for a channel from the client.
++ * @param handle Callback handle
++ * @param channel The channel to get the EPG table for.
++ * @param iStart The start time to use.
++ * @param iEnd The end time to use.
++ * @return PVR_ERROR_NO_ERROR if the table has been fetched successfully.
++ */
++ PVR_ERROR GetEPGForChannel(PVR_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd);
++
++ //@}
++ /** @name PVR channel group methods */
++ //@{
++
++ /*!
++ * @return The total amount of channel groups on the server or -1 on error.
++ */
++ int GetChannelGroupsAmount(void);
++
++ /*!
++ * @brief Request the list of all channel groups from the backend.
++ * @param bRadio True to get the radio channel groups, false to get the TV channel groups.
++ * @return PVR_ERROR_NO_ERROR if the list has been fetched successfully.
++ */
++ PVR_ERROR GetChannelGroups(PVR_HANDLE handle, bool bRadio);
++
++ /*!
++ * @brief Request the list of all group members of a group.
++ * @param handle Callback.
++ * @param group The group to get the members for.
++ * @return PVR_ERROR_NO_ERROR if the list has been fetched successfully.
++ */
++ PVR_ERROR GetChannelGroupMembers(PVR_HANDLE handle, const PVR_CHANNEL_GROUP &group);
++
++ //@}
++ /** @name PVR channel methods */
++ //@{
++
++ /*!
++ * @return The total amount of channels on the server or -1 on error.
++ */
++ int GetChannelsAmount(void);
++
++ /*!
++ * @brief Request the list of all channels from the backend.
++ * @param bRadio True to get the radio channels, false to get the TV channels.
++ * @return PVR_ERROR_NO_ERROR if the list has been fetched successfully.
++ */
++ PVR_ERROR GetChannels(PVR_HANDLE handle, bool bRadio);
++
++ /*!
++ * @brief Delete a channel.
++ * @param channel The channel to delete.
++ * @return PVR_ERROR_NO_ERROR if the channel has been deleted successfully.
++ */
++ PVR_ERROR DeleteChannel(const PVR_CHANNEL &channel);
++
++ /*!
++ * @brief Rename a channel.
++ * @param channel The channel to rename, containing the new channel name.
++ * @return PVR_ERROR_NO_ERROR if the channel has been renamed successfully.
++ */
++ PVR_ERROR RenameChannel(const PVR_CHANNEL &channel);
++
++ /*!
++ * @brief Move a channel to another channel number.
++ * @param channel The channel to move, containing the new channel number.
++ * @return PVR_ERROR_NO_ERROR if the channel has been moved successfully.
++ */
++ PVR_ERROR MoveChannel(const PVR_CHANNEL &channel);
++
++ /*!
++ * @brief Show the channel settings dialog.
++ * @param channel The channel to show the dialog for.
++ * @return PVR_ERROR_NO_ERROR if the dialog has been displayed successfully.
++ */
++ PVR_ERROR DialogChannelSettings(const PVR_CHANNEL &channel);
++
++ /*!
++ * @brief Show the dialog to add a channel on the backend.
++ * @param channel The channel to add.
++ * @return PVR_ERROR_NO_ERROR if the channel has been added successfully.
++ */
++ PVR_ERROR DialogAddChannel(const PVR_CHANNEL &channel);
++
++ //@}
++ /** @name PVR recording methods */
++ //@{
++
++ /*!
++ * @return The total amount of channels on the server or -1 on error.
++ */
++ int GetRecordingsAmount(void);
++
++ /*!
++ * @brief Request the list of all recordings from the backend.
++ * @param handle The callback handle.
++ * @return PVR_ERROR_NO_ERROR if the recordings have been fetched successfully.
++ */
++ PVR_ERROR GetRecordings(PVR_HANDLE handle);
++
++ /*!
++ * @brief Delete a recording on the backend.
++ * @param recording The recording to delete.
++ * @return PVR_ERROR_NO_ERROR if the recording has been deleted successfully.
++ */
++ PVR_ERROR DeleteRecording(const PVR_RECORDING &recording);
++
++ /*!
++ * @brief Rename a recording on the backend.
++ * @param recording The recording to rename, containing the new name.
++ * @return PVR_ERROR_NO_ERROR if the recording has been renamed successfully.
++ */
++ PVR_ERROR RenameRecording(const PVR_RECORDING &recording);
++
++ //@}
++ /** @name PVR timer methods */
++ //@{
++
++ /*!
++ * @return The total amount of timers on the backend or -1 on error.
++ */
++ int GetTimersAmount(void);
++
++ /*!
++ * @brief Request the list of all timers from the backend.
++ * @param handle The callback handle.
++ * @return PVR_ERROR_NO_ERROR if the list has been fetched successfully.
++ */
++ PVR_ERROR GetTimers(PVR_HANDLE handle);
++
++ /*!
++ * @brief Add a timer on the backend.
++ * @param timer The timer to add.
++ * @return PVR_ERROR_NO_ERROR if the timer has been added successfully.
++ */
++ PVR_ERROR AddTimer(const PVR_TIMER &timer);
++
++ /*!
++ * @brief Delete a timer on the backend.
++ * @param timer The timer to delete.
++ * @param bForceDelete Set to true to delete a timer that is currently recording a program.
++ * @return PVR_ERROR_NO_ERROR if the timer has been deleted successfully.
++ */
++ PVR_ERROR DeleteTimer(const PVR_TIMER &timer, bool bForceDelete);
++
++ /*!
++ * @brief Update the timer information on the server.
++ * @param timer The timer to update.
++ * @return PVR_ERROR_NO_ERROR if the timer has been updated successfully.
++ */
++ PVR_ERROR UpdateTimer(const PVR_TIMER &timer);
++
++ //@}
++ /** @name PVR live stream methods */
++ //@{
++
++ /*!
++ * @brief Open a live stream on the server.
++ * @param channel The channel to stream.
++ * @return True if the stream opened successfully, false otherwise.
++ */
++ bool OpenLiveStream(const PVR_CHANNEL &channel);
++
++ /*!
++ * @brief Close an open live stream.
++ */
++ void CloseLiveStream(void);
++
++ /*!
++ * @brief Read from an open live stream.
++ * @param pBuffer The buffer to store the data in.
++ * @param iBufferSize The amount of bytes to read.
++ * @return The amount of bytes that were actually read from the stream.
++ */
++ int ReadLiveStream(unsigned char *pBuffer, unsigned int iBufferSize);
++
++ /*!
++ * @brief Seek in a live stream on a backend that supports timeshifting.
++ * @param iPosition The position to seek to.
++ * @param iWhence ?
++ * @return The new position.
++ */
++ long long SeekLiveStream(long long iPosition, int iWhence = SEEK_SET);
++
++ /*!
++ * @return The position in the stream that's currently being read.
++ */
++ long long PositionLiveStream(void);
++
++ /*!
++ * @return The total length of the stream that's currently being read.
++ */
++ long long LengthLiveStream(void);
++
++ /*!
++ * @return The channel number on the server of the live stream that's currently being read.
++ */
++ int GetCurrentClientChannel(void);
++
++ /*!
++ * @brief Switch to another channel. Only to be called when a live stream has already been opened.
++ * @param channel The channel to switch to.
++ * @return True if the switch was successful, false otherwise.
++ */
++ bool SwitchChannel(const PVR_CHANNEL &channel);
++
++ /*!
++ * @brief Get the signal status of the stream that's currently open.
++ * @param signalStatus The signal status.
++ * @return True if the signal status has been read successfully, false otherwise.
++ */
++ PVR_ERROR SignalStatus(PVR_SIGNAL_STATUS &signalStatus);
++
++ /*!
++ * @brief Get the stream URL for a channel from the server. Used by the MediaPortal add-on.
++ * @param channel The channel to get the stream URL for.
++ * @return The requested URL.
++ */
++ const char *GetLiveStreamURL(const PVR_CHANNEL &channel);
++
++ //@}
++ /** @name PVR recording stream methods */
++ //@{
++
++ /*!
++ * @brief Open a recording on the server.
++ * @param recording The recording to open.
++ * @return True if the stream has been opened succesfully, false otherwise.
++ */
++ bool OpenRecordedStream(const PVR_RECORDING &recording);
++
++ /*!
++ * @brief Close an open stream from a recording.
++ */
++ void CloseRecordedStream(void);
++
++ /*!
++ * @brief Read from a recording.
++ * @param pBuffer The buffer to store the data in.
++ * @param iBufferSize The amount of bytes to read.
++ * @return The amount of bytes that were actually read from the stream.
++ */
++ int ReadRecordedStream(unsigned char *pBuffer, unsigned int iBufferSize);
++
++ /*!
++ * @brief Seek in a recorded stream.
++ * @param iPosition The position to seek to.
++ * @param iWhence ?
++ * @return The new position.
++ */
++ long long SeekRecordedStream(long long iPosition, int iWhence = SEEK_SET);
++
++ /*!
++ * @return The position in the stream that's currently being read.
++ */
++ long long PositionRecordedStream(void);
++
++ /*!
++ * @return The total length of the stream that's currently being read.
++ */
++ long long LengthRecordedStream(void);
++
++ //@}
++ /** @name PVR demultiplexer methods */
++ //@{
++
++ /*!
++ * @brief Reset the demultiplexer in the add-on.
++ */
++ void DemuxReset(void);
++
++ /*!
++ * @brief Abort the demultiplexer thread in the add-on.
++ */
++ void DemuxAbort(void);
++
++ /*!
++ * @brief Flush all data that's currently in the demultiplexer buffer in the add-on.
++ */
++ void DemuxFlush(void);
++
++ /*!
++ * @brief Read a packet from the demultiplexer.
++ * @return The packet.
++ */
++ DemuxPacket *DemuxRead(void);
++
++ //@}
++
++ // function to export the above structure to XBMC
++ void __declspec(dllexport) get_addon(struct PVRClient *pClient)
++ {
++ pClient->GetAddonCapabilities = GetAddonCapabilities;
++ pClient->GetStreamProperties = GetStreamProperties;
++ pClient->GetConnectionString = GetConnectionString;
++ pClient->GetBackendName = GetBackendName;
++ pClient->GetBackendVersion = GetBackendVersion;
++ pClient->GetDriveSpace = GetDriveSpace;
++ pClient->DialogChannelScan = DialogChannelScan;
++ pClient->MenuHook = CallMenuHook;
++
++ pClient->GetEpg = GetEPGForChannel;
++
++ pClient->GetChannelGroupsAmount = GetChannelGroupsAmount;
++ pClient->GetChannelGroups = GetChannelGroups;
++ pClient->GetChannelGroupMembers = GetChannelGroupMembers;
++
++ pClient->GetChannelsAmount = GetChannelsAmount;
++ pClient->GetChannels = GetChannels;
++ pClient->DeleteChannel = DeleteChannel;
++ pClient->RenameChannel = RenameChannel;
++ pClient->MoveChannel = MoveChannel;
++ pClient->DialogChannelSettings = DialogChannelSettings;
++ pClient->DialogAddChannel = DialogAddChannel;
++
++ pClient->GetRecordingsAmount = GetRecordingsAmount;
++ pClient->GetRecordings = GetRecordings;
++ pClient->DeleteRecording = DeleteRecording;
++ pClient->RenameRecording = RenameRecording;
++
++ pClient->GetTimersAmount = GetTimersAmount;
++ pClient->GetTimers = GetTimers;
++ pClient->AddTimer = AddTimer;
++ pClient->DeleteTimer = DeleteTimer;
++ pClient->UpdateTimer = UpdateTimer;
++
++ pClient->OpenLiveStream = OpenLiveStream;
++ pClient->CloseLiveStream = CloseLiveStream;
++ pClient->ReadLiveStream = ReadLiveStream;
++ pClient->SeekLiveStream = SeekLiveStream;
++ pClient->PositionLiveStream = PositionLiveStream;
++ pClient->LengthLiveStream = LengthLiveStream;
++ pClient->GetCurrentClientChannel = GetCurrentClientChannel;
++ pClient->SwitchChannel = SwitchChannel;
++ pClient->SignalStatus = SignalStatus;
++ pClient->GetLiveStreamURL = GetLiveStreamURL;
++
++ pClient->OpenRecordedStream = OpenRecordedStream;
++ pClient->CloseRecordedStream = CloseRecordedStream;
++ pClient->ReadRecordedStream = ReadRecordedStream;
++ pClient->SeekRecordedStream = SeekRecordedStream;
++ pClient->PositionRecordedStream = PositionRecordedStream;
++ pClient->LengthRecordedStream = LengthRecordedStream;
++
++ pClient->DemuxReset = DemuxReset;
++ pClient->DemuxAbort = DemuxAbort;
++ pClient->DemuxFlush = DemuxFlush;
++ pClient->DemuxRead = DemuxRead;
++ };
++};
++
++#endif
+diff --git a/xbmc/addons/include/xbmc_pvr_types.h b/xbmc/addons/include/xbmc_pvr_types.h
+new file mode 100644
+index 0000000..add0996
+--- /dev/null
++++ b/xbmc/addons/include/xbmc_pvr_types.h
+@@ -0,0 +1,412 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#ifndef __PVRCLIENT_TYPES_H__
++#define __PVRCLIENT_TYPES_H__
++
++#ifdef _WIN32
++#include <windows.h>
++#else
++#ifndef __cdecl
++#define __cdecl
++#endif
++#ifndef __declspec
++#define __declspec(X)
++#endif
++#endif
++#include <string.h>
++
++/*! @note Define "USE_DEMUX" on compile time if demuxing inside pvr
++ * addon is used. Also XBMC's "DVDDemuxPacket.h" file must be inside
++ * the include path of the pvr addon.
++ */
++#ifdef USE_DEMUX
++#include "DVDDemuxPacket.h"
++#else
++struct DemuxPacket;
++#endif
++
++#undef ATTRIBUTE_PACKED
++#undef PRAGMA_PACK_BEGIN
++#undef PRAGMA_PACK_END
++
++#if defined(__GNUC__)
++#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
++#define ATTRIBUTE_PACKED __attribute__ ((packed))
++#define PRAGMA_PACK 0
++#endif
++#endif
++
++#if !defined(ATTRIBUTE_PACKED)
++#define ATTRIBUTE_PACKED
++#define PRAGMA_PACK 1
++#endif
++
++/*! @name PVR entry content event types */
++//@{
++/* These IDs come from the DVB-SI EIT table "content descriptor"
++ * Also known under the name "E-book genre assignments"
++ */
++#define EPG_EVENT_CONTENTMASK_UNDEFINED 0x00
++#define EPG_EVENT_CONTENTMASK_MOVIEDRAMA 0x10
++#define EPG_EVENT_CONTENTMASK_NEWSCURRENTAFFAIRS 0x20
++#define EPG_EVENT_CONTENTMASK_SHOW 0x30
++#define EPG_EVENT_CONTENTMASK_SPORTS 0x40
++#define EPG_EVENT_CONTENTMASK_CHILDRENYOUTH 0x50
++#define EPG_EVENT_CONTENTMASK_MUSICBALLETDANCE 0x60
++#define EPG_EVENT_CONTENTMASK_ARTSCULTURE 0x70
++#define EPG_EVENT_CONTENTMASK_SOCIALPOLITICALECONOMICS 0x80
++#define EPG_EVENT_CONTENTMASK_EDUCATIONALSCIENCE 0x90
++#define EPG_EVENT_CONTENTMASK_LEISUREHOBBIES 0xA0
++#define EPG_EVENT_CONTENTMASK_SPECIAL 0xB0
++#define EPG_EVENT_CONTENTMASK_USERDEFINED 0xF0
++//@}
++/* Set EPGTAG.iGenreType to EPG_GENRE_USE_STRING to transfer genre strings to XBMC */
++#define EPG_GENRE_USE_STRING 0x100
++
++/* using the default avformat's MAX_STREAMS value to be safe */
++#define PVR_STREAM_MAX_STREAMS 20
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++ /*!
++ * @brief Handle used to return data from the PVR add-on to CPVRClient
++ */
++ struct PVR_HANDLE_STRUCT
++ {
++ void *callerAddress; /*!< address of the caller */
++ void *dataAddress; /*!< address to store data in */
++ int dataIdentifier; /*!< parameter to pass back when calling the callback */
++ };
++ typedef PVR_HANDLE_STRUCT *PVR_HANDLE;
++
++ /*! \brief PVR Client Error Codes
++ */
++ typedef enum
++ {
++ PVR_ERROR_NO_ERROR = 0,
++ PVR_ERROR_UNKNOWN = -1,
++ PVR_ERROR_NOT_IMPLEMENTED = -2,
++ PVR_ERROR_SERVER_ERROR = -3,
++ PVR_ERROR_SERVER_TIMEOUT = -4,
++ PVR_ERROR_NOT_SYNC = -5,
++ PVR_ERROR_NOT_DELETED = -6,
++ PVR_ERROR_NOT_SAVED = -7,
++ PVR_ERROR_RECORDING_RUNNING = -8,
++ PVR_ERROR_ALREADY_PRESENT = -9,
++ PVR_ERROR_NOT_POSSIBLE = -10
++ } PVR_ERROR;
++
++ /*!
++ * @brief PVR timer states
++ */
++ typedef enum
++ {
++ PVR_TIMER_STATE_INVALID = 0,
++ PVR_TIMER_STATE_SCHEDULED = 1, /*!< @brief the timer is scheduled for recording */
++ PVR_TIMER_STATE_RECORDING = 2, /*!< @brief the timer is currently recordings */
++ PVR_TIMER_STATE_COMPLETED = 3, /*!< @brief the recording completed successfully */
++ PVR_TIMER_STATE_ABORTED = 4, /*!< @brief recording started, but was aborted */
++ PVR_TIMER_STATE_CANCELLED = 5 /*!< @brief the timer was scheduled, but was cancelled */
++ } PVR_TIMER_STATE;
++
++ /*!
++ * @brief Properties passed to the Create() method of an add-on.
++ */
++ typedef struct PVR_PROPERTIES
++ {
++ int iClientId; /*!< @brief (required) database ID of the client */
++ const char *strUserPath; /*!< @brief (required) path to the user profile */
++ const char *strClientPath; /*!< @brief (required) path to this add-on */
++ } PVR_PROPERTIES;
++
++ /*!
++ * @brief PVR add-on capabilities. All capabilities are set to "false" as default.
++ */
++ typedef struct PVR_ADDON_CAPABILITIES
++ {
++ bool bSupportsChannelSettings; /*!< @brief (optional) true if this add-on supports changing channel settings on the backend */
++ bool bSupportsTimeshift; /*!< @brief (optional) true if the backend will handle timeshift. false if XBMC should handle it. */
++ bool bSupportsEPG; /*!< @brief (optional) true if the add-on provides EPG information */
++ bool bSupportsTV; /*!< @brief (optional) true if this add-on provides TV channels */
++ bool bSupportsRadio; /*!< @brief (optional) true if this add-on supports radio channels */
++ bool bSupportsRecordings; /*!< @brief (optional) true if this add-on supports playback of recordings stored on the backend */
++ bool bSupportsTimers; /*!< @brief (optional) true if this add-on supports the creation and editing of timers */
++ bool bSupportsChannelGroups; /*!< @brief (optional) true if this add-on supports channel groups */
++ bool bSupportsChannelScan; /*!< @brief (optional) true if this add-on support scanning for new channels on the backend */
++ bool bHandlesInputStream; /*!< @brief (optional) true if this add-on provides an input stream. false if XBMC handles the stream. */
++ bool bHandlesDemuxing; /*!< @brief (optional) true if this add-on demultiplexes packets. */
++ bool bSupportsRecordingFolders; /*!< @brief (optional) true if the backend supports timers / recordings in folders. */
++ } ATTRIBUTE_PACKED PVR_ADDON_CAPABILITIES;
++
++ /*!
++ * @brief PVR stream properties
++ */
++ typedef struct PVR_STREAM_PROPERTIES
++ {
++ unsigned int iStreamCount;
++ struct PVR_STREAM
++ {
++ unsigned int iStreamIndex; /*!< @brief (required) stream index */
++ unsigned int iPhysicalId; /*!< @brief (required) physical index */
++ unsigned int iCodecType; /*!< @brief (required) codec type id */
++ unsigned int iCodecId; /*!< @brief (required) codec id */
++ char strLanguage[4]; /*!< @brief (required) language id */
++ unsigned int iIdentifier; /*!< @brief (required) stream id */
++ unsigned int iFPSScale; /*!< @brief (required) scale of 1000 and a rate of 29970 will result in 29.97 fps */
++ unsigned int iFPSRate; /*!< @brief (required) FPS rate */
++ unsigned int iHeight; /*!< @brief (required) height of the stream reported by the demuxer */
++ unsigned int iWidth; /*!< @brief (required) width of the stream reported by the demuxer */
++ float fAspect; /*!< @brief (required) display aspect ratio of the stream */
++ unsigned int iChannels; /*!< @brief (required) amount of channels */
++ unsigned int iSampleRate; /*!< @brief (required) sample rate */
++ unsigned int iBlockAlign; /*!< @brief (required) block alignment */
++ unsigned int iBitRate; /*!< @brief (required) bit rate */
++ unsigned int iBitsPerSample; /*!< @brief (required) bits per sample */
++ } stream[PVR_STREAM_MAX_STREAMS]; /*!< @brief (required) the streams */
++ } ATTRIBUTE_PACKED PVR_STREAM_PROPERTIES;
++
++ /*!
++ * @brief Signal status information
++ */
++ typedef struct PVR_SIGNAL_STATUS
++ {
++ char strAdapterName[1024]; /*!< @brief (optional) name of the adapter that's being used */
++ char strAdapterStatus[1024]; /*!< @brief (optional) status of the adapter that's being used */
++ int iSNR; /*!< @brief (optional) signal/noise ratio */
++ int iSignal; /*!< @brief (optional) signal strength */
++ long iBER; /*!< @brief (optional) bit error rate */
++ long iUNC; /*!< @brief (optional) uncorrected blocks */
++ double dVideoBitrate; /*!< @brief (optional) video bitrate */
++ double dAudioBitrate; /*!< @brief (optional) audio bitrate */
++ double dDolbyBitrate; /*!< @brief (optional) dolby bitrate */
++ } ATTRIBUTE_PACKED PVR_SIGNAL_STATUS;
++
++ /*!
++ * @brief Menu hooks that are available in the context menus while playing a stream via this add-on.
++ */
++ typedef struct PVR_MENUHOOK
++ {
++ unsigned int iHookId; /*!< @brief (required) this hook's identifier */
++ unsigned int iLocalizedStringId; /*!< @brief (required) the id of the label for this hook in g_localizeStrings */
++ } ATTRIBUTE_PACKED PVR_MENUHOOK;
++
++ /*!
++ * @brief Representation of a TV or radio channel.
++ */
++ typedef struct PVR_CHANNEL
++ {
++ unsigned int iUniqueId; /*!< @brief (required) unique identifier for this channel */
++ bool bIsRadio; /*!< @brief (required) true if this is a radio channel, false if it's a TV channel */
++ unsigned int iChannelNumber; /*!< @brief (optional) channel number of this channel on the backend */
++ const char * strChannelName; /*!< @brief (optional) channel name given to this channel */
++ const char * strInputFormat; /*!< @brief (optional) input format type. types can be found in ffmpeg/libavformat/allformats.c
++ leave empty if unknown */
++ const char * strStreamURL; /*!< @brief (optional) the URL to use to access this channel.
++ leave empty to use this add-on to access the stream.
++ set to a path that's supported by XBMC otherwise. */
++ unsigned int iEncryptionSystem; /*!< @brief (optional) the encryption ID or CaID of this channel */
++ const char * strIconPath; /*!< @brief (optional) path to the channel icon (if present) */
++ bool bIsHidden; /*!< @brief (optional) true if this channel is marked as hidden */
++ } ATTRIBUTE_PACKED PVR_CHANNEL;
++
++ typedef struct PVR_CHANNEL_GROUP
++ {
++ const char * strGroupName; /*!< @brief (required) name of this channel group */
++ bool bIsRadio; /*!< @brief (required) true if this is a radio channel group, false otherwise. */
++ } ATTRIBUTE_PACKED PVR_CHANNEL_GROUP;
++
++ typedef struct PVR_CHANNEL_GROUP_MEMBER
++ {
++ const char * strGroupName; /*!< @brief (required) name of the channel group to add the channel to */
++ unsigned int iChannelUniqueId; /*!< @brief (required) unique id of the member */
++ unsigned int iChannelNumber; /*!< @brief (optional) channel number within the group */
++ } ATTRIBUTE_PACKED PVR_CHANNEL_GROUP_MEMBER;
++
++ /*!
++ * @brief Representation of an EPG event.
++ */
++ typedef struct EPG_TAG {
++ unsigned int iUniqueBroadcastId; /*!< @brief (required) identifier for this event */
++ const char * strTitle; /*!< @brief (required) this event's title */
++ unsigned int iChannelNumber; /*!< @brief (required) the number of the channel this event occurs on */
++ time_t startTime; /*!< @brief (required) start time in UTC */
++ time_t endTime; /*!< @brief (required) end time in UTC */
++ const char * strPlotOutline; /*!< @brief (optional) plot outline */
++ const char * strPlot; /*!< @brief (optional) plot */
++ const char * strIconPath; /*!< @brief (optional) icon path */
++ int iGenreType; /*!< @brief (optional) genre type */
++ int iGenreSubType; /*!< @brief (optional) genre sub type */
++ const char * strGenreDescription; /*!< @brief (optional) genre. Will be used only when iGenreType = EPG_GENRE_USE_STRING */
++ time_t firstAired; /*!< @brief (optional) first aired in UTC */
++ int iParentalRating; /*!< @brief (optional) parental rating */
++ int iStarRating; /*!< @brief (optional) star rating */
++ bool bNotify; /*!< @brief (optional) notify the user when this event starts */
++ int iSeriesNumber; /*!< @brief (optional) series number */
++ int iEpisodeNumber; /*!< @brief (optional) episode number */
++ int iEpisodePartNumber; /*!< @brief (optional) episode part number */
++ const char * strEpisodeName; /*!< @brief (optional) episode name */
++ } ATTRIBUTE_PACKED EPG_TAG;
++
++ /*!
++ * @brief Representation of a timer event.
++ */
++ typedef struct PVR_TIMER {
++ unsigned int iClientIndex; /*!< @brief (required) the index of this timer given by the client */
++ int iClientChannelUid; /*!< @brief (required) unique identifier of the channel to record on */
++ time_t startTime; /*!< @brief (required) start time of the recording in UTC. instant timers that are sent to the add-on by xbmc will have this value set to 0 */
++ time_t endTime; /*!< @brief (required) end time of the recording in UTC */
++ PVR_TIMER_STATE state; /*!< @brief (required) the state of this timer */
++ const char * strTitle; /*!< @brief (optional) title of this timer */
++ const char * strDirectory; /*!< @brief (optional) the directory where the recording will be stored in */
++ const char * strSummary; /*!< @brief (optional) the summary for this timer */
++ int iPriority; /*!< @brief (optional) the priority of this timer */
++ int iLifetime; /*!< @brief (optional) lifetimer of this timer in days */
++ bool bIsRepeating; /*!< @brief (optional) true if this is a recurring timer */
++ time_t firstDay; /*!< @brief (optional) the first day this recording is active in case of a repeating event */
++ int iWeekdays; /*!< @brief (optional) weekday mask */
++ int iEpgUid; /*!< @brief (optional) epg event id */
++ unsigned int iMarginStart; /*!< @brief (optional) if set, the backend starts the recording iMarginStart minutes before startTime. */
++ unsigned int iMarginEnd; /*!< @brief (optional) if set, the backend ends the recording iMarginEnd minutes after endTime. */
++ int iGenreType; /*!< @brief (optional) genre type */
++ int iGenreSubType; /*!< @brief (optional) genre sub type */
++ } ATTRIBUTE_PACKED PVR_TIMER;
++
++ /*!
++ * @brief Representation of a recording.
++ */
++ typedef struct PVR_RECORDING {
++ const char * strRecordingId; /*!< @brief (required) unique id of the recording on the client. */
++ const char * strTitle; /*!< @brief (required) the title of this recording */
++ const char * strStreamURL; /*!< @brief (required) stream URL to access this recording */
++ const char * strDirectory; /*!< @brief (optional) directory of this recording on the client */
++ const char * strPlotOutline; /*!< @brief (optional) plot outline */
++ const char * strPlot; /*!< @brief (optional) plot */
++ const char * strChannelName; /*!< @brief (optional) channel name */
++ time_t recordingTime; /*!< @brief (optional) start time of the recording */
++ int iDuration; /*!< @brief (optional) duration of the recording in seconds */
++ int iPriority; /*!< @brief (optional) priority of this recording (from 0 - 100) */
++ int iLifetime; /*!< @brief (optional) life time in days of this recording */
++ int iGenreType; /*!< @brief (optional) genre type */
++ int iGenreSubType; /*!< @brief (optional) genre sub type */
++ const char * strIconPath; /*!< @brief (optional) icon path */
++ const char * strDefFanart; /*!< @brief (optional) fanart path */
++ } ATTRIBUTE_PACKED PVR_RECORDING;
++
++ /*!
++ * @brief Structure to transfer the PVR functions to XBMC
++ */
++ typedef struct PVRClient
++ {
++ /** @name PVR server methods */
++ //@{
++ PVR_ERROR (__cdecl* GetAddonCapabilities)(PVR_ADDON_CAPABILITIES *pCapabilities);
++ PVR_ERROR (__cdecl* GetStreamProperties)(PVR_STREAM_PROPERTIES *pProperties);
++ const char * (__cdecl* GetBackendName)(void);
++ const char * (__cdecl* GetBackendVersion)(void);
++ const char * (__cdecl* GetConnectionString)(void);
++ PVR_ERROR (__cdecl* GetDriveSpace)(long long *iTotal, long long *iUsed);
++ PVR_ERROR (__cdecl* DialogChannelScan)(void);
++ PVR_ERROR (__cdecl* MenuHook)(const PVR_MENUHOOK &menuhook);
++ //@}
++
++ /** @name PVR EPG methods */
++ //@{
++ PVR_ERROR (__cdecl* GetEpg)(PVR_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd);
++ //@}
++
++ /** @name PVR channel group methods */
++ //@{
++ int (__cdecl* GetChannelGroupsAmount)(void);
++ PVR_ERROR (__cdecl* GetChannelGroups)(PVR_HANDLE handle, bool bRadio);
++ PVR_ERROR (__cdecl* GetChannelGroupMembers)(PVR_HANDLE handle, const PVR_CHANNEL_GROUP &group);
++ //@}
++
++ /** @name PVR channel methods */
++ //@{
++ int (__cdecl* GetChannelsAmount)(void);
++ PVR_ERROR (__cdecl* GetChannels)(PVR_HANDLE handle, bool bRadio);
++ PVR_ERROR (__cdecl* DeleteChannel)(const PVR_CHANNEL &channel);
++ PVR_ERROR (__cdecl* RenameChannel)(const PVR_CHANNEL &channel);
++ PVR_ERROR (__cdecl* MoveChannel)(const PVR_CHANNEL &channel);
++ PVR_ERROR (__cdecl* DialogChannelSettings)(const PVR_CHANNEL &channel);
++ PVR_ERROR (__cdecl* DialogAddChannel)(const PVR_CHANNEL &channel);
++ //@}
++
++ /** @name PVR recording methods */
++ //@{
++ int (__cdecl* GetRecordingsAmount)(void);
++ PVR_ERROR (__cdecl* GetRecordings)(PVR_HANDLE handle);
++ PVR_ERROR (__cdecl* DeleteRecording)(const PVR_RECORDING &recording);
++ PVR_ERROR (__cdecl* RenameRecording)(const PVR_RECORDING &recording);
++ //@}
++
++ /** @name PVR timer methods */
++ //@{
++ int (__cdecl* GetTimersAmount)(void);
++ PVR_ERROR (__cdecl* GetTimers)(PVR_HANDLE handle);
++ PVR_ERROR (__cdecl* AddTimer)(const PVR_TIMER &timer);
++ PVR_ERROR (__cdecl* DeleteTimer)(const PVR_TIMER &timer, bool bForceDelete);
++ PVR_ERROR (__cdecl* UpdateTimer)(const PVR_TIMER &timer);
++ //@}
++
++ /** @name PVR live stream methods */
++ //@{
++ bool (__cdecl* OpenLiveStream)(const PVR_CHANNEL &channel);
++ void (__cdecl* CloseLiveStream)(void);
++ int (__cdecl* ReadLiveStream)(unsigned char *pBuffer, unsigned int iBufferSize);
++ long long (__cdecl* SeekLiveStream)(long long iPosition, int iWhence /* = SEEK_SET */);
++ long long (__cdecl* PositionLiveStream)(void);
++ long long (__cdecl* LengthLiveStream)(void);
++ int (__cdecl* GetCurrentClientChannel)(void);
++ bool (__cdecl* SwitchChannel)(const PVR_CHANNEL &channel);
++ PVR_ERROR (__cdecl* SignalStatus)(PVR_SIGNAL_STATUS &signalStatus);
++ const char* (__cdecl* GetLiveStreamURL)(const PVR_CHANNEL &channel);
++ //@}
++
++ /** @name PVR recording stream methods */
++ //@{
++ bool (__cdecl* OpenRecordedStream)(const PVR_RECORDING &recording);
++ void (__cdecl* CloseRecordedStream)(void);
++ int (__cdecl* ReadRecordedStream)(unsigned char *pBuffer, unsigned int iBufferSize);
++ long long (__cdecl* SeekRecordedStream)(long long iPosition, int iWhence /* = SEEK_SET */);
++ long long (__cdecl* PositionRecordedStream)(void);
++ long long (__cdecl* LengthRecordedStream)(void);
++ //@}
++
++ /** @name PVR demultiplexer methods */
++ //@{
++ void (__cdecl* DemuxReset)(void);
++ void (__cdecl* DemuxAbort)(void);
++ void (__cdecl* DemuxFlush)(void);
++ DemuxPacket* (__cdecl* DemuxRead)(void);
++ //@}
++
++ } PVRClient;
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif //__PVRCLIENT_TYPES_H__
+diff --git a/xbmc/cores/DllLoader/DllLoaderContainer.cpp b/xbmc/cores/DllLoader/DllLoaderContainer.cpp
+index 3e02492..f70facc 100644
+--- a/xbmc/cores/DllLoader/DllLoaderContainer.cpp
++++ b/xbmc/cores/DllLoader/DllLoaderContainer.cpp
+@@ -231,7 +231,7 @@ LibraryLoader* DllLoaderContainer::LoadDll(const char* sName, bool bLoadSymbols)
+ LibraryLoader* pLoader;
+ #ifdef _LINUX
+ if (strstr(sName, ".so") != NULL || strstr(sName, ".vis") != NULL || strstr(sName, ".xbs") != NULL
+- || strstr(sName, ".mvis") != NULL || strstr(sName, ".dylib") != NULL || strstr(sName, ".framework") != NULL)
++ || strstr(sName, ".mvis") != NULL || strstr(sName, ".dylib") != NULL || strstr(sName, ".framework") != NULL || strstr(sName, ".pvr") != NULL)
+ pLoader = new SoLoader(sName, bLoadSymbols);
+ else
+ #elif defined(_WIN32)
+diff --git a/xbmc/cores/DllLoader/Win32DllLoader.cpp b/xbmc/cores/DllLoader/Win32DllLoader.cpp
+index a7af019..fd918e4 100644
+--- a/xbmc/cores/DllLoader/Win32DllLoader.cpp
++++ b/xbmc/cores/DllLoader/Win32DllLoader.cpp
+@@ -331,6 +331,9 @@ bool Win32DllLoader::NeedsHooking(const char *dllName)
+ CStdStringW strdllNameW;
+ g_charsetConverter.utf8ToW(_P(dllName), strdllNameW, false);
+ HMODULE hModule = GetModuleHandleW(strdllNameW.c_str());
++ if (hModule == NULL)
++ return false;
++
+ wchar_t filepathW[MAX_PATH];
+ GetModuleFileNameW(hModule, filepathW, MAX_PATH);
+ CStdString dllPath;
+diff --git a/xbmc/cores/IPlayer.h b/xbmc/cores/IPlayer.h
+index 91655a5..b44f56b 100644
+--- a/xbmc/cores/IPlayer.h
++++ b/xbmc/cores/IPlayer.h
+@@ -30,6 +30,11 @@ class TiXmlElement;
+ class CStreamDetails;
+ class CAction;
+
++namespace PVR
++{
++ class CPVRChannel;
++}
++
+ class IPlayerCallback
+ {
+ public:
+@@ -170,6 +175,7 @@ public:
+
+ virtual CStdString GetPlayingTitle() { return ""; };
+
++ virtual bool SwitchChannel(const PVR::CPVRChannel &channel) { return false; }
+ protected:
+ IPlayerCallback& m_callback;
+ };
+diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.cpp
+index ea1f0bd..0e67af3 100644
+--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.cpp
++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.cpp
+@@ -531,7 +531,8 @@ bool CVDPAU::Supports(VdpVideoMixerFeature feature)
+ bool CVDPAU::Supports(EINTERLACEMETHOD method)
+ {
+ if(method == VS_INTERLACEMETHOD_VDPAU_BOB
+- || method == VS_INTERLACEMETHOD_AUTO)
++ || method == VS_INTERLACEMETHOD_AUTO
++ || method == VS_INTERLACEMETHOD_AUTO_ION)
+ return true;
+
+ for(SInterlaceMapping* p = g_interlace_mapping; p->method != VS_INTERLACEMETHOD_NONE; p++)
+@@ -657,8 +658,21 @@ void CVDPAU::SetDeinterlacing()
+ }
+ else
+ {
+- if (method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL
+- || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF)
++ if (method == VS_INTERLACEMETHOD_AUTO_ION)
++ {
++ if (vid_height <= 576)
++ {
++ VdpBool enabled[]={1,1,0};
++ vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled);
++ }
++ else if (vid_height > 576)
++ {
++ VdpBool enabled[]={1,0,0};
++ vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled);
++ }
++ }
++ else if (method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL
++ || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF)
+ {
+ VdpBool enabled[]={1,0,0};
+ vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled);
+@@ -1387,14 +1401,16 @@ int CVDPAU::Decode(AVCodecContext *avctx, AVFrame *pFrame)
+ if (mode == VS_DEINTERLACEMODE_FORCE
+ || (mode == VS_DEINTERLACEMODE_AUTO && m_DVDVideoPics.front().iFlags & DVP_FLAG_INTERLACED))
+ {
+- if((method == VS_INTERLACEMETHOD_VDPAU_BOB
++ if((method == VS_INTERLACEMETHOD_AUTO_ION
++ || method == VS_INTERLACEMETHOD_VDPAU_BOB
+ || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL
+ || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF
+ || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL
+ || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF
+ || method == VS_INTERLACEMETHOD_VDPAU_INVERSE_TELECINE ))
+ {
+- if(method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF
++ if((method == VS_INTERLACEMETHOD_AUTO_ION && vid_height > 576)
++ || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF
+ || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF
+ || avctx->skip_frame == AVDISCARD_NONREF)
+ m_mixerstep = 0;
+diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h
+index dd8d977..2642a81 100644
+--- a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h
++++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h
+@@ -23,6 +23,7 @@
+
+ #include "utils/StdString.h"
+ #include "system.h"
++#include "DVDDemuxPacket.h"
+
+ class CDVDInputStream;
+
+@@ -219,19 +220,6 @@ public:
+ virtual void GetStreamInfo(std::string& strInfo);
+ };
+
+-typedef struct DemuxPacket
+-{
+- BYTE* pData; // data
+- int iSize; // data size
+- int iStreamId; // integer representing the stream index
+- int iGroupId; // the group this data belongs to, used to group data from different streams together
+-
+- double pts; // pts in DVD_TIME_BASE
+- double dts; // dts in DVD_TIME_BASE
+- double duration; // duration in DVD_TIME_BASE if available
+-} DemuxPacket;
+-
+-
+ class CDVDDemux
+ {
+ public:
+diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
+index 46287a8..55138fa 100644
+--- a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
++++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
+@@ -34,6 +34,7 @@
+ #include "DVDInputStreams/DVDInputStream.h"
+ #include "DVDInputStreams/DVDInputStreamNavigator.h"
+ #include "DVDInputStreams/DVDInputStreamBluray.h"
++#include "DVDInputStreams/DVDInputStreamPVRManager.h"
+ #include "DVDDemuxUtils.h"
+ #include "DVDClock.h" // for DVD_TIME_BASE
+ #include "utils/Win32Exception.h"
+diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxHTSP.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxHTSP.cpp
+index 2737f19..38ca397 100644
+--- a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxHTSP.cpp
++++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxHTSP.cpp
+@@ -284,6 +284,7 @@ void CDVDDemuxHTSP::SubscriptionStart (htsmsg_t *m)
+ CDemuxStreamAudio* a;
+ CDemuxStreamVideo* v;
+ CDemuxStreamSubtitle* s;
++ CDemuxStreamTeletext* t;
+ } st;
+
+ CLog::Log(LOGDEBUG, "CDVDDemuxHTSP::SubscriptionStart - id: %d, type: %s", index, type);
+@@ -320,6 +321,9 @@ void CDVDDemuxHTSP::SubscriptionStart (htsmsg_t *m)
+ } else if(!strcmp(type, "TEXTSUB")) {
+ st.s = new CDemuxStreamSubtitle();
+ st.s->codec = CODEC_ID_TEXT;
++ } else if(!strcmp(type, "TELETEXT")) {
++ st.t = new CDemuxStreamTeletext();
++ st.t->codec = CODEC_ID_DVB_TELETEXT;
+ } else {
+ continue;
+ }
+diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.cpp
+new file mode 100644
+index 0000000..9a57eb1
+--- /dev/null
++++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.cpp
+@@ -0,0 +1,317 @@
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "DVDInputStreams/DVDInputStream.h"
++#include "DVDDemuxPVRClient.h"
++#include "DVDDemuxUtils.h"
++#include "utils/log.h"
++#include "pvr/PVRManager.h"
++#include "pvr/addons/PVRClients.h"
++
++using namespace PVR;
++
++void CDemuxStreamVideoPVRClient::GetStreamInfo(std::string& strInfo)
++{
++ switch (codec)
++ {
++ case CODEC_ID_MPEG2VIDEO:
++ strInfo = "mpeg2video";
++ break;
++ case CODEC_ID_H264:
++ strInfo = "h264";
++ break;
++ default:
++ break;
++ }
++}
++
++void CDemuxStreamAudioPVRClient::GetStreamInfo(std::string& strInfo)
++{
++ switch (codec)
++ {
++ case CODEC_ID_AC3:
++ strInfo = "ac3";
++ break;
++ case CODEC_ID_EAC3:
++ strInfo = "eac3";
++ break;
++ case CODEC_ID_MP2:
++ strInfo = "mpeg2audio";
++ break;
++ case CODEC_ID_AAC:
++ strInfo = "aac";
++ break;
++ case CODEC_ID_DTS:
++ strInfo = "dts";
++ break;
++ default:
++ break;
++ }
++}
++
++void CDemuxStreamSubtitlePVRClient::GetStreamInfo(std::string& strInfo)
++{
++}
++
++CDVDDemuxPVRClient::CDVDDemuxPVRClient() : CDVDDemux()
++{
++ m_pInput = NULL;
++ for (int i = 0; i < MAX_STREAMS; i++) m_streams[i] = NULL;
++}
++
++CDVDDemuxPVRClient::~CDVDDemuxPVRClient()
++{
++ Dispose();
++}
++
++bool CDVDDemuxPVRClient::Open(CDVDInputStream* pInput)
++{
++ Abort();
++ m_pInput = pInput;
++ RequestStreams();
++ return true;
++}
++
++void CDVDDemuxPVRClient::Dispose()
++{
++ for (int i = 0; i < MAX_STREAMS; i++)
++ {
++ if (m_streams[i])
++ {
++ if (m_streams[i]->ExtraData)
++ delete[] (BYTE*)(m_streams[i]->ExtraData);
++ delete m_streams[i];
++ }
++ m_streams[i] = NULL;
++ }
++ m_pInput = NULL;
++}
++
++void CDVDDemuxPVRClient::Reset()
++{
++ if(m_pInput && g_PVRManager.IsStarted())
++ g_PVRClients->DemuxReset();
++
++ CDVDInputStream* pInputStream = m_pInput;
++ Dispose();
++ Open(pInputStream);
++}
++
++void CDVDDemuxPVRClient::Abort()
++{
++ if(m_pInput)
++ g_PVRClients->DemuxAbort();
++}
++
++void CDVDDemuxPVRClient::Flush()
++{
++ if(m_pInput && g_PVRManager.IsStarted())
++ g_PVRClients->DemuxFlush();
++}
++
++DemuxPacket* CDVDDemuxPVRClient::Read()
++{
++ if (!g_PVRManager.IsStarted())
++ return CDVDDemuxUtils::AllocateDemuxPacket(0);
++
++ DemuxPacket* pPacket = g_PVRClients->ReadDemuxStream();
++ if (!pPacket)
++ {
++ if (m_pInput)
++ m_pInput->Close();
++ return NULL;
++ }
++
++ if (pPacket->iStreamId == DMX_SPECIALID_STREAMINFO)
++ {
++ UpdateStreams((PVR_STREAM_PROPERTIES*)pPacket->pData);
++ CDVDDemuxUtils::FreeDemuxPacket(pPacket);
++ return CDVDDemuxUtils::AllocateDemuxPacket(0);
++ }
++ else if (pPacket->iStreamId == DMX_SPECIALID_STREAMCHANGE)
++ {
++ Reset();
++ }
++
++ return pPacket;
++}
++
++CDemuxStream* CDVDDemuxPVRClient::GetStream(int iStreamId)
++{
++ if (iStreamId < 0 || iStreamId >= MAX_STREAMS) return NULL;
++ return m_streams[iStreamId];
++}
++
++void CDVDDemuxPVRClient::RequestStreams()
++{
++ if (!g_PVRManager.IsStarted())
++ return;
++
++ PVR_STREAM_PROPERTIES *props = g_PVRClients->GetCurrentStreamProperties();
++
++ for (unsigned int i = 0; i < props->iStreamCount; ++i)
++ {
++ if (props->stream[i].iCodecType == AVMEDIA_TYPE_AUDIO)
++ {
++ CDemuxStreamAudioPVRClient* st = new CDemuxStreamAudioPVRClient(this);
++ st->iChannels = props->stream[i].iChannels;
++ st->iSampleRate = props->stream[i].iSampleRate;
++ st->iBlockAlign = props->stream[i].iBlockAlign;
++ st->iBitRate = props->stream[i].iBitRate;
++ st->iBitsPerSample = props->stream[i].iBitsPerSample;
++ m_streams[props->stream[i].iStreamIndex] = st;
++ }
++ else if (props->stream[i].iCodecType == AVMEDIA_TYPE_VIDEO)
++ {
++ CDemuxStreamVideoPVRClient* st = new CDemuxStreamVideoPVRClient(this);
++ st->iFpsScale = props->stream[i].iFPSScale;
++ st->iFpsRate = props->stream[i].iFPSRate;
++ st->iHeight = props->stream[i].iHeight;
++ st->iWidth = props->stream[i].iWidth;
++ st->fAspect = props->stream[i].fAspect;
++ m_streams[props->stream[i].iStreamIndex] = st;
++ }
++ else if (props->stream[i].iCodecId == CODEC_ID_DVB_TELETEXT)
++ {
++ m_streams[props->stream[i].iStreamIndex] = new CDemuxStreamTeletext();
++ }
++ else if (props->stream[i].iCodecType == AVMEDIA_TYPE_SUBTITLE)
++ {
++ CDemuxStreamSubtitlePVRClient* st = new CDemuxStreamSubtitlePVRClient(this);
++ st->identifier = props->stream[i].iIdentifier;
++ m_streams[props->stream[i].iStreamIndex] = st;
++ }
++ else
++ m_streams[props->stream[i].iStreamIndex] = new CDemuxStream();
++
++ m_streams[props->stream[i].iStreamIndex]->codec = (CodecID)props->stream[i].iCodecId;
++ m_streams[props->stream[i].iStreamIndex]->iId = props->stream[i].iStreamIndex;
++ m_streams[props->stream[i].iStreamIndex]->iPhysicalId = props->stream[i].iPhysicalId;
++ m_streams[props->stream[i].iStreamIndex]->language[0] = props->stream[i].strLanguage[0];
++ m_streams[props->stream[i].iStreamIndex]->language[1] = props->stream[i].strLanguage[1];
++ m_streams[props->stream[i].iStreamIndex]->language[2] = props->stream[i].strLanguage[2];
++ m_streams[props->stream[i].iStreamIndex]->language[3] = props->stream[i].strLanguage[3];
++
++ CLog::Log(LOGDEBUG,"CDVDDemuxPVRClient::RequestStreams(): added stream %d:%d with codec_id %d",
++ m_streams[props->stream[i].iStreamIndex]->iId,
++ m_streams[props->stream[i].iStreamIndex]->iPhysicalId,
++ m_streams[props->stream[i].iStreamIndex]->codec);
++ }
++}
++
++void CDVDDemuxPVRClient::UpdateStreams(PVR_STREAM_PROPERTIES *props)
++{
++ bool bGotVideoStream(false);
++
++ for (unsigned int i = 0; i < props->iStreamCount; ++i)
++ {
++ if (m_streams[props->stream[i].iStreamIndex] == NULL ||
++ m_streams[props->stream[i].iStreamIndex]->codec != (CodecID)props->stream[i].iCodecId)
++ {
++ CLog::Log(LOGERROR,"Invalid stream inside UpdateStreams");
++ continue;
++ }
++
++ if (m_streams[props->stream[i].iStreamIndex]->type == STREAM_AUDIO)
++ {
++ CDemuxStreamAudioPVRClient* st = (CDemuxStreamAudioPVRClient*) m_streams[props->stream[i].iStreamIndex];
++ st->iChannels = props->stream[i].iChannels;
++ st->iSampleRate = props->stream[i].iSampleRate;
++ st->iBlockAlign = props->stream[i].iBlockAlign;
++ st->iBitRate = props->stream[i].iBitRate;
++ st->iBitsPerSample = props->stream[i].iBitsPerSample;
++ }
++ else if (m_streams[props->stream[i].iStreamIndex]->type == STREAM_VIDEO)
++ {
++ if (bGotVideoStream)
++ {
++ CLog::Log(LOGDEBUG, "CDVDDemuxPVRClient - %s - skip video stream", __FUNCTION__);
++ continue;
++ }
++
++ CDemuxStreamVideoPVRClient* st = (CDemuxStreamVideoPVRClient*) m_streams[props->stream[i].iStreamIndex];
++ if (st->iWidth <= 0 || st->iHeight <= 0)
++ {
++ CLog::Log(LOGWARNING, "CDVDDemuxPVRClient - %s - invalid stream data", __FUNCTION__);
++ continue;
++ }
++
++ st->iFpsScale = props->stream[i].iFPSScale;
++ st->iFpsRate = props->stream[i].iFPSRate;
++ st->iHeight = props->stream[i].iHeight;
++ st->iWidth = props->stream[i].iWidth;
++ st->fAspect = props->stream[i].fAspect;
++ bGotVideoStream = true;
++ }
++ else if (m_streams[props->stream[i].iStreamIndex]->type == STREAM_SUBTITLE)
++ {
++ CDemuxStreamSubtitlePVRClient* st = (CDemuxStreamSubtitlePVRClient*) m_streams[props->stream[i].iStreamIndex];
++ st->identifier = props->stream[i].iIdentifier;
++ }
++
++ m_streams[props->stream[i].iStreamIndex]->language[0] = props->stream[i].strLanguage[0];
++ m_streams[props->stream[i].iStreamIndex]->language[1] = props->stream[i].strLanguage[1];
++ m_streams[props->stream[i].iStreamIndex]->language[2] = props->stream[i].strLanguage[2];
++ m_streams[props->stream[i].iStreamIndex]->language[3] = props->stream[i].strLanguage[3];
++
++ CLog::Log(LOGDEBUG,"CDVDDemuxPVRClient::UpdateStreams(): update stream %d:%d with codec_id %d",
++ m_streams[props->stream[i].iStreamIndex]->iId,
++ m_streams[props->stream[i].iStreamIndex]->iPhysicalId,
++ m_streams[props->stream[i].iStreamIndex]->codec);
++ }
++}
++
++int CDVDDemuxPVRClient::GetNrOfStreams()
++{
++ int i = 0;
++ while (i < MAX_STREAMS && m_streams[i]) i++;
++ return i;
++}
++
++std::string CDVDDemuxPVRClient::GetFileName()
++{
++ if(m_pInput)
++ return m_pInput->GetFileName();
++ else
++ return "";
++}
++
++void CDVDDemuxPVRClient::GetStreamCodecName(int iStreamId, CStdString &strName)
++{
++ CDemuxStream *stream = GetStream(iStreamId);
++ if (stream)
++ {
++ if (stream->codec == CODEC_ID_AC3)
++ strName = "ac3";
++ else if (stream->codec == CODEC_ID_MP2)
++ strName = "mp2";
++ else if (stream->codec == CODEC_ID_AAC)
++ strName = "aac";
++ else if (stream->codec == CODEC_ID_DTS)
++ strName = "dca";
++ else if (stream->codec == CODEC_ID_MPEG2VIDEO)
++ strName = "mpeg2video";
++ else if (stream->codec == CODEC_ID_H264)
++ strName = "h264";
++ else if (stream->codec == CODEC_ID_EAC3)
++ strName = "eac3";
++ }
++}
+diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.h
+new file mode 100644
+index 0000000..ec0fbd5
+--- /dev/null
++++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.h
+@@ -0,0 +1,108 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "DVDDemux.h"
++#include <map>
++
++#ifndef _LINUX
++#include <libavformat/avformat.h>
++#else
++extern "C" {
++#if (defined USE_EXTERNAL_FFMPEG)
++ #if (defined HAVE_LIBAVFORMAT_AVFORMAT_H)
++ #include <libavformat/avformat.h>
++ #elif (defined HAVE_FFMPEG_AVFORMAT_H)
++ #include <ffmpeg/avformat.h>
++ #endif
++#else
++ #include "libavformat/avformat.h"
++#endif
++}
++#endif
++
++class CDVDDemuxPVRClient;
++struct PVR_STREAM_PROPERTIES;
++
++class CDemuxStreamVideoPVRClient : public CDemuxStreamVideo
++{
++ CDVDDemuxPVRClient *m_parent;
++public:
++ CDemuxStreamVideoPVRClient(CDVDDemuxPVRClient *parent)
++ : m_parent(parent)
++ {}
++ virtual void GetStreamInfo(std::string& strInfo);
++};
++
++class CDemuxStreamAudioPVRClient : public CDemuxStreamAudio
++{
++ CDVDDemuxPVRClient *m_parent;
++public:
++ CDemuxStreamAudioPVRClient(CDVDDemuxPVRClient *parent)
++ : m_parent(parent)
++ {}
++ virtual void GetStreamInfo(std::string& strInfo);
++};
++
++class CDemuxStreamSubtitlePVRClient : public CDemuxStreamSubtitle
++{
++ CDVDDemuxPVRClient *m_parent;
++public:
++ CDemuxStreamSubtitlePVRClient(CDVDDemuxPVRClient *parent)
++ : m_parent(parent)
++ {}
++ virtual void GetStreamInfo(std::string& strInfo);
++};
++
++
++class CDVDDemuxPVRClient : public CDVDDemux
++{
++public:
++
++ CDVDDemuxPVRClient();
++ ~CDVDDemuxPVRClient();
++
++ bool Open(CDVDInputStream* pInput);
++ void Dispose();
++ void Reset();
++ void Abort();
++ void Flush();
++ DemuxPacket* Read();
++ bool SeekTime(int time, bool backwords = false, double* startpts = NULL) { return false; }
++ void SetSpeed(int iSpeed) {};
++ int GetStreamLength() { return 0; }
++ CDemuxStream* GetStream(int iStreamId);
++ int GetNrOfStreams();
++ std::string GetFileName();
++ virtual void GetStreamCodecName(int iStreamId, CStdString &strName);
++
++protected:
++ CDVDInputStream* m_pInput;
++#ifndef MAX_STREAMS
++ #define MAX_STREAMS 100
++#endif
++ CDemuxStream* m_streams[MAX_STREAMS]; // maximum number of streams that ffmpeg can handle
++
++private:
++ void RequestStreams();
++ void UpdateStreams(PVR_STREAM_PROPERTIES *props);
++};
++
+diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPacket.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPacket.h
+new file mode 100644
+index 0000000..beadfcd
+--- /dev/null
++++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPacket.h
+@@ -0,0 +1,37 @@
++#pragma once
++
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#define DMX_SPECIALID_STREAMINFO -10
++#define DMX_SPECIALID_STREAMCHANGE -11
++
++ typedef struct DemuxPacket
++{
++ unsigned char* pData; // data
++ int iSize; // data size
++ int iStreamId; // integer representing the stream index
++ int iGroupId; // the group this data belongs to, used to group data from different streams together
++
++ double pts; // pts in DVD_TIME_BASE
++ double dts; // dts in DVD_TIME_BASE
++ double duration; // duration in DVD_TIME_BASE if available
++} DemuxPacket;
+diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.h
+index e7d5f41..5d28810 100644
+--- a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.h
++++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.h
+@@ -21,7 +21,7 @@
+ *
+ */
+
+-#include "DVDDemux.h"
++#include "DVDDemuxPacket.h"
+
+ class CDVDDemuxUtils
+ {
+diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.cpp
+index 7021661..c2ff089 100644
+--- a/xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.cpp
++++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.cpp
+@@ -24,14 +24,18 @@
+
+ #include "DVDInputStreams/DVDInputStream.h"
+ #include "DVDInputStreams/DVDInputStreamHttp.h"
++#include "DVDInputStreams/DVDInputStreamPVRManager.h"
+
+ #include "DVDDemuxFFmpeg.h"
+ #include "DVDDemuxShoutcast.h"
+ #ifdef HAS_FILESYSTEM_HTSP
+ #include "DVDDemuxHTSP.h"
+ #endif
++#include "DVDDemuxPVRClient.h"
++#include "pvr/PVRManager.h"
+
+ using namespace std;
++using namespace PVR;
+
+ CDVDDemux* CDVDFactoryDemuxer::CreateDemuxer(CDVDInputStream* pInputStream)
+ {
+@@ -62,6 +66,38 @@ CDVDDemux* CDVDFactoryDemuxer::CreateDemuxer(CDVDInputStream* pInputStream)
+ }
+ #endif
+
++ if (pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER))
++ {
++ CDVDInputStreamPVRManager* pInputStreamPVR = (CDVDInputStreamPVRManager*)pInputStream;
++ CDVDInputStream* pOtherStream = pInputStreamPVR->GetOtherStream();
++ if(pOtherStream)
++ {
++ /* Used for MediaPortal PVR addon (uses PVR otherstream for playback of rtsp streams) */
++ if (pOtherStream->IsStreamType(DVDSTREAM_TYPE_FFMPEG))
++ {
++ auto_ptr<CDVDDemuxFFmpeg> demuxer(new CDVDDemuxFFmpeg());
++ if(demuxer->Open(pOtherStream))
++ return demuxer.release();
++ else
++ return NULL;
++ }
++ }
++
++ std::string filename = pInputStream->GetFileName();
++ /* Use PVR demuxer only for live streams */
++ if (filename.substr(0, 14) == "pvr://channels")
++ {
++ if (g_PVRManager.GetCurrentAddonCapabilities().bHandlesDemuxing)
++ {
++ auto_ptr<CDVDDemuxPVRClient> demuxer(new CDVDDemuxPVRClient());
++ if(demuxer->Open(pInputStream))
++ return demuxer.release();
++ else
++ return NULL;
++ }
++ }
++ }
++
+ auto_ptr<CDVDDemuxFFmpeg> demuxer(new CDVDDemuxFFmpeg());
+ if(demuxer->Open(pInputStream))
+ return demuxer.release();
+diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/Makefile.in b/xbmc/cores/dvdplayer/DVDDemuxers/Makefile.in
+index dc6f959..be2e392 100644
+--- a/xbmc/cores/dvdplayer/DVDDemuxers/Makefile.in
++++ b/xbmc/cores/dvdplayer/DVDDemuxers/Makefile.in
+@@ -3,6 +3,7 @@ INCLUDES+=-I@abs_top_srcdir@/xbmc/cores/dvdplayer
+ SRCS= DVDDemux.cpp \
+ DVDDemuxFFmpeg.cpp \
+ DVDDemuxHTSP.cpp \
++ DVDDemuxPVRClient.cpp \
+ DVDDemuxShoutcast.cpp \
+ DVDDemuxUtils.cpp \
+ DVDDemuxVobsub.cpp \
+diff --git a/xbmc/cores/dvdplayer/DVDInputStreams/DVDFactoryInputStream.cpp b/xbmc/cores/dvdplayer/DVDInputStreams/DVDFactoryInputStream.cpp
+index e4e7065..151daeb 100644
+--- a/xbmc/cores/dvdplayer/DVDInputStreams/DVDFactoryInputStream.cpp
++++ b/xbmc/cores/dvdplayer/DVDInputStreams/DVDFactoryInputStream.cpp
+@@ -26,6 +26,7 @@
+ #include "DVDInputStreamNavigator.h"
+ #include "DVDInputStreamHttp.h"
+ #include "DVDInputStreamFFmpeg.h"
++#include "DVDInputStreamPVRManager.h"
+ #include "DVDInputStreamTV.h"
+ #include "DVDInputStreamRTMP.h"
+ #ifdef HAVE_LIBBLURAY
+@@ -52,6 +53,8 @@ CDVDInputStream* CDVDFactoryInputStream::CreateInputStream(IDVDPlayer* pPlayer,
+ {
+ return (new CDVDInputStreamNavigator(pPlayer));
+ }
++ else if(file.substr(0, 6) == "pvr://")
++ return new CDVDInputStreamPVRManager(pPlayer);
+ #ifdef HAVE_LIBBLURAY
+ else if (item.IsType(".bdmv") || item.IsType(".mpls") || content == "bluray/iso")
+ return new CDVDInputStreamBluray();
+diff --git a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStream.h b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStream.h
+index 5cd64ea..d07fc22 100644
+--- a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStream.h
++++ b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStream.h
+@@ -39,6 +39,7 @@ enum DVDStreamType
+ DVDSTREAM_TYPE_HTSP = 8,
+ DVDSTREAM_TYPE_MPLS = 10,
+ DVDSTREAM_TYPE_BLURAY = 11,
++ DVDSTREAM_TYPE_PVRMANAGER = 12,
+ };
+
+ #define SEEK_POSSIBLE 0x10 // flag used to check if protocol allows seeks
+@@ -53,10 +54,17 @@ public:
+ {
+ public:
+ virtual ~IChannel() {};
+- virtual bool NextChannel() = 0;
+- virtual bool PrevChannel() = 0;
+- virtual bool SelectChannel(unsigned int channel) = 0;
++ virtual bool NextChannel(bool preview = false) = 0;
++ virtual bool PrevChannel(bool preview = false) = 0;
++ virtual bool SelectChannelByNumber(unsigned int channel) = 0;
++ virtual bool SelectChannel(const PVR::CPVRChannel &channel) { return false; };
++ virtual bool GetSelectedChannel(PVR::CPVRChannel *) { return false; };
++ virtual int GetTotalTime() = 0;
++ virtual int GetStartTime() = 0;
+ virtual bool UpdateItem(CFileItem& item) = 0;
++ virtual bool CanRecord() = 0;
++ virtual bool IsRecording() = 0;
++ virtual bool Record(bool bOnOff) = 0;
+ };
+
+ class IDisplayTime
+diff --git a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamHTSP.cpp b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamHTSP.cpp
+index 80d2abe..5ae7737 100644
+--- a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamHTSP.cpp
++++ b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamHTSP.cpp
+@@ -195,7 +195,7 @@ bool CDVDInputStreamHTSP::GetChannels(SChannelV &channels, SChannelV::iterator &
+ return false;
+ }
+
+-bool CDVDInputStreamHTSP::NextChannel()
++bool CDVDInputStreamHTSP::NextChannel(bool preview/* = false*/)
+ {
+ SChannelV channels;
+ SChannelV::iterator it;
+@@ -209,7 +209,7 @@ bool CDVDInputStreamHTSP::NextChannel()
+ return SetChannel(circ->id);
+ }
+
+-bool CDVDInputStreamHTSP::PrevChannel()
++bool CDVDInputStreamHTSP::PrevChannel(bool preview/* = false*/)
+ {
+ SChannelV channels;
+ SChannelV::iterator it;
+@@ -223,7 +223,7 @@ bool CDVDInputStreamHTSP::PrevChannel()
+ return SetChannel(circ->id);
+ }
+
+-bool CDVDInputStreamHTSP::SelectChannel(unsigned int channel)
++bool CDVDInputStreamHTSP::SelectChannelByNumber(unsigned int channel)
+ {
+ return SetChannel(channel);
+ }
+@@ -256,6 +256,33 @@ int CDVDInputStreamHTSP::GetTotalTime()
+ {
+ if(m_event.id == 0)
+ return 0;
++
++ long duration = (time_t)m_event.stop - (time_t)m_event.start;
++ CDateTimeSpan time = CDateTimeSpan(0, 0, duration / 60, duration % 60);
++
++ return time.GetDays() * 1000 * 60 * 60 * 24
++ + time.GetHours() * 1000 * 60 * 60
++ + time.GetMinutes() * 1000 * 60
++ + time.GetSeconds() * 1000;
++}
++
++int CDVDInputStreamHTSP::GetStartTime()
++{
++ if(m_event.id == 0)
++ return 0;
++
++ time_t time_c;
++
++ CDateTime::GetCurrentDateTime().GetAsTime(time_c);
++
++ return (m_event.start - time_c) * 1000;
++}
++
++/*
++int CDVDInputStreamHTSP::GetTotalTime()
++{
++ if(m_event.id == 0)
++ return 0;
+ return (m_event.stop - m_event.start) * 1000;
+ }
+
+@@ -270,6 +297,7 @@ int CDVDInputStreamHTSP::GetTime()
+ + time.GetMinutes() * 1000 * 60
+ + time.GetSeconds() * 1000;
+ }
++*/
+
+ void CDVDInputStreamHTSP::Abort()
+ {
+diff --git a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamHTSP.h b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamHTSP.h
+index 287c8ac..83012e7 100644
+--- a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamHTSP.h
++++ b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamHTSP.h
+@@ -26,7 +26,6 @@
+ class CDVDInputStreamHTSP
+ : public CDVDInputStream
+ , public CDVDInputStream::IChannel
+- , public CDVDInputStream::IDisplayTime
+ {
+ public:
+ CDVDInputStreamHTSP();
+@@ -43,13 +42,19 @@ public:
+
+ virtual void Abort();
+
+- bool NextChannel();
+- bool PrevChannel();
+- bool SelectChannel(unsigned int channel);
++ bool NextChannel(bool preview = false);
++ bool PrevChannel(bool preview = false);
++ bool SelectChannelByNumber(unsigned int channel);
++ bool SelectChannel(const PVR::CPVRChannel &channel) { return false; }
++ bool GetSelectedChannel(PVR::CPVRChannel *channel) {return false; }
+ bool UpdateItem(CFileItem& item);
+
++ bool CanRecord() { return false; }
++ bool IsRecording() { return false; }
++ bool Record(bool bOnOff) { return false; }
++
+ int GetTotalTime();
+- int GetTime();
++ int GetStartTime();
+
+ htsmsg_t* ReadStream();
+
+diff --git a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamPVRManager.cpp b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamPVRManager.cpp
+new file mode 100644
+index 0000000..a1d61ad
+--- /dev/null
++++ b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamPVRManager.cpp
+@@ -0,0 +1,294 @@
++/*
++ * Copyright (C) 2005-2009 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "DVDFactoryInputStream.h"
++#include "DVDInputStreamPVRManager.h"
++#include "filesystem/PVRFile.h"
++#include "URL.h"
++#include "pvr/PVRManager.h"
++#include "pvr/channels/PVRChannel.h"
++#include "utils/log.h"
++#include "utils/StringUtils.h"
++#include "pvr/addons/PVRClients.h"
++
++using namespace XFILE;
++using namespace PVR;
++
++/************************************************************************
++ * Description: Class constructor, initialize member variables
++ * public class is CDVDInputStream
++ */
++CDVDInputStreamPVRManager::CDVDInputStreamPVRManager(IDVDPlayer* pPlayer) : CDVDInputStream(DVDSTREAM_TYPE_PVRMANAGER)
++{
++ m_pPlayer = pPlayer;
++ m_pFile = NULL;
++ m_pRecordable = NULL;
++ m_pLiveTV = NULL;
++ m_pOtherStream = NULL;
++ m_eof = true;
++}
++
++/************************************************************************
++ * Description: Class destructor
++ */
++CDVDInputStreamPVRManager::~CDVDInputStreamPVRManager()
++{
++ Close();
++}
++
++bool CDVDInputStreamPVRManager::IsEOF()
++{
++ if (m_pOtherStream)
++ return m_pOtherStream->IsEOF();
++ else
++ return !m_pFile || m_eof;
++}
++
++bool CDVDInputStreamPVRManager::Open(const char* strFile, const std::string& content)
++{
++ /* Open PVR File for both cases, to have access to ILiveTVInterface and
++ * IRecordable
++ */
++ m_pFile = new CPVRFile();
++ m_pLiveTV = ((CPVRFile*)m_pFile)->GetLiveTV();
++ m_pRecordable = ((CPVRFile*)m_pFile)->GetRecordable();
++
++ CURL url(strFile);
++ if (!CDVDInputStream::Open(strFile, content)) return false;
++ if (!m_pFile->Open(url))
++ {
++ delete m_pFile;
++ m_pFile = NULL;
++ return false;
++ }
++ m_eof = false;
++
++ /*
++ * Translate the "pvr://....." entry.
++ * The PVR Client can use http or whatever else is supported by DVDPlayer.
++ * to access streams.
++ * If after translation the file protocol is still "pvr://" use this class
++ * to read the stream data over the CPVRFile class and the PVR Library itself.
++ * Otherwise call CreateInputStream again with the translated filename and looks again
++ * for the right protocol stream handler and swap every call to this input stream
++ * handler.
++ */
++ std::string transFile = XFILE::CPVRFile::TranslatePVRFilename(strFile);
++ if(transFile.substr(0, 6) != "pvr://")
++ {
++ m_pOtherStream = CDVDFactoryInputStream::CreateInputStream(m_pPlayer, transFile, content);
++ if (!m_pOtherStream)
++ {
++ CLog::Log(LOGERROR, "CDVDInputStreamPVRManager::Open - unable to create input stream for [%s]", transFile.c_str());
++ return false;
++ }
++ else
++ m_pOtherStream->SetFileItem(m_item);
++
++ if (!m_pOtherStream->Open(transFile.c_str(), content))
++ {
++ CLog::Log(LOGERROR, "CDVDInputStreamPVRManager::Open - error opening [%s]", transFile.c_str());
++ delete m_pFile;
++ m_pFile = NULL;
++ delete m_pOtherStream;
++ m_pOtherStream = NULL;
++ return false;
++ }
++ }
++
++ return true;
++}
++
++// close file and reset everyting
++void CDVDInputStreamPVRManager::Close()
++{
++ if (m_pOtherStream)
++ {
++ m_pOtherStream->Close();
++ delete m_pOtherStream;
++ }
++
++ if (m_pFile)
++ {
++ m_pFile->Close();
++ delete m_pFile;
++ }
++
++ CDVDInputStream::Close();
++
++ m_pPlayer = NULL;
++ m_pFile = NULL;
++ m_pLiveTV = NULL;
++ m_pRecordable = NULL;
++ m_pOtherStream = NULL;
++ m_eof = true;
++}
++
++int CDVDInputStreamPVRManager::Read(BYTE* buf, int buf_size)
++{
++ if(!m_pFile) return -1;
++
++ if (m_pOtherStream)
++ {
++ return m_pOtherStream->Read(buf, buf_size);
++ }
++ else
++ {
++ unsigned int ret = m_pFile->Read(buf, buf_size);
++
++ /* we currently don't support non completing reads */
++ if( ret <= 0 ) m_eof = true;
++
++ return (int)(ret & 0xFFFFFFFF);
++ }
++}
++
++__int64 CDVDInputStreamPVRManager::Seek(__int64 offset, int whence)
++{
++ if (!m_pFile)
++ return -1;
++
++ if (whence == SEEK_POSSIBLE)
++ return m_pFile->IoControl(IOCTRL_SEEK_POSSIBLE, NULL);
++
++ if (m_pOtherStream)
++ {
++ return m_pOtherStream->Seek(offset, whence);
++ }
++ else
++ {
++ __int64 ret = m_pFile->Seek(offset, whence);
++
++ /* if we succeed, we are not eof anymore */
++ if( ret >= 0 ) m_eof = false;
++
++ return ret;
++ }
++}
++
++__int64 CDVDInputStreamPVRManager::GetLength()
++{
++ if(!m_pFile) return -1;
++
++ if (m_pOtherStream)
++ return m_pOtherStream->GetLength();
++ else
++ return m_pFile->GetLength();
++}
++
++int CDVDInputStreamPVRManager::GetTotalTime()
++{
++ if (m_pLiveTV)
++ return m_pLiveTV->GetTotalTime();
++ return 0;
++}
++
++int CDVDInputStreamPVRManager::GetStartTime()
++{
++ if (m_pLiveTV)
++ return m_pLiveTV->GetStartTime();
++ return 0;
++}
++
++bool CDVDInputStreamPVRManager::NextChannel(bool preview/* = false*/)
++{
++ if (m_pLiveTV)
++ return m_pLiveTV->NextChannel(preview);
++ return false;
++}
++
++bool CDVDInputStreamPVRManager::PrevChannel(bool preview/* = false*/)
++{
++ if (m_pLiveTV)
++ return m_pLiveTV->PrevChannel(preview);
++ return false;
++}
++
++bool CDVDInputStreamPVRManager::SelectChannelByNumber(unsigned int channel)
++{
++ if (m_pLiveTV)
++ return m_pLiveTV->SelectChannel(channel);
++ return false;
++}
++
++bool CDVDInputStreamPVRManager::SelectChannel(const CPVRChannel &channel)
++{
++ if (m_pLiveTV)
++ return m_pLiveTV->SelectChannel(channel.ChannelNumber());
++ return false;
++}
++
++bool CDVDInputStreamPVRManager::GetSelectedChannel(CPVRChannel &channel) const
++{
++ return g_PVRManager.GetCurrentChannel(channel);
++}
++
++bool CDVDInputStreamPVRManager::UpdateItem(CFileItem& item)
++{
++ if (m_pLiveTV)
++ return m_pLiveTV->UpdateItem(item);
++ return false;
++}
++
++bool CDVDInputStreamPVRManager::NextStream()
++{
++ if(!m_pFile) return false;
++
++ if (m_pOtherStream)
++ return m_pOtherStream->NextStream();
++ else
++ {
++ if(m_pFile->SkipNext())
++ {
++ m_eof = false;
++ return true;
++ }
++ }
++ return false;
++}
++
++bool CDVDInputStreamPVRManager::CanRecord()
++{
++ if (m_pRecordable)
++ return m_pRecordable->CanRecord();
++ return false;
++}
++
++bool CDVDInputStreamPVRManager::IsRecording()
++{
++ if (m_pRecordable)
++ return m_pRecordable->IsRecording();
++ return false;
++}
++
++bool CDVDInputStreamPVRManager::Record(bool bOnOff)
++{
++ if (m_pRecordable)
++ return m_pRecordable->Record(bOnOff);
++ return false;
++}
++
++CStdString CDVDInputStreamPVRManager::GetInputFormat()
++{
++ if (!m_pOtherStream && g_PVRManager.IsStarted())
++ return g_PVRClients->GetCurrentInputFormat();
++ return StringUtils::EmptyString;
++}
+diff --git a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamPVRManager.h b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamPVRManager.h
+new file mode 100644
+index 0000000..6a3688e
+--- /dev/null
++++ b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamPVRManager.h
+@@ -0,0 +1,107 @@
++#pragma once
++
++/*
++ * Copyright (C) 2005-2008 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++/*
++* for DESCRIPTION see 'DVDInputStreamPVRManager.cpp'
++*/
++
++#include "DVDInputStream.h"
++#include "FileItem.h"
++
++namespace XFILE {
++class IFile;
++class ILiveTVInterface;
++class IRecordable;
++}
++
++class IDVDPlayer;
++
++class CDVDInputStreamPVRManager
++ : public CDVDInputStream
++ , public CDVDInputStream::IChannel
++{
++public:
++ CDVDInputStreamPVRManager(IDVDPlayer* pPlayer);
++ virtual ~CDVDInputStreamPVRManager();
++ virtual bool Open(const char* strFile, const std::string &content);
++ virtual void Close();
++ virtual int Read(BYTE* buf, int buf_size);
++ virtual __int64 Seek(__int64 offset, int whence);
++ virtual bool Pause(double dTime) { return false; }
++ virtual bool IsEOF();
++ virtual __int64 GetLength();
++
++ virtual bool NextStream();
++
++ bool SelectChannelByNumber(unsigned int iChannel);
++ bool SelectChannel(const PVR::CPVRChannel &channel);
++ bool NextChannel(bool preview = false);
++ bool PrevChannel(bool preview = false);
++ bool GetSelectedChannel(PVR::CPVRChannel &channel) const;
++
++ int GetTotalTime();
++ int GetStartTime();
++
++ bool CanRecord();
++ bool IsRecording();
++ bool Record(bool bOnOff);
++
++ bool UpdateItem(CFileItem& item);
++
++ /* overloaded is streamtype to support m_pOtherStream */
++ bool IsStreamType(DVDStreamType type) const;
++
++ /*! \brief Get the input format from the Backend
++ If it is empty ffmpeg scanning the stream to find the right input format.
++ See "xbmc/cores/dvdplayer/Codecs/ffmpeg/libavformat/allformats.c" for a
++ list of the input formats.
++ \return The name of the input format
++ */
++ CStdString GetInputFormat();
++
++ /* returns m_pOtherStream */
++ CDVDInputStream* GetOtherStream();
++
++protected:
++ IDVDPlayer* m_pPlayer;
++ CDVDInputStream* m_pOtherStream;
++ XFILE::IFile* m_pFile;
++ XFILE::ILiveTVInterface* m_pLiveTV;
++ XFILE::IRecordable* m_pRecordable;
++ bool m_eof;
++};
++
++
++inline bool CDVDInputStreamPVRManager::IsStreamType(DVDStreamType type) const
++{
++ if (m_pOtherStream)
++ return m_pOtherStream->IsStreamType(type);
++
++ return m_streamType == type;
++}
++
++inline CDVDInputStream* CDVDInputStreamPVRManager::GetOtherStream()
++{
++ return m_pOtherStream;
++};
++
+diff --git a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamTV.cpp b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamTV.cpp
+index 5ed50a9..606e78b 100644
+--- a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamTV.cpp
++++ b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamTV.cpp
+@@ -21,8 +21,9 @@
+
+ #include "DVDInputStreamTV.h"
+ #include "filesystem/MythFile.h"
+-#include "filesystem/VTPFile.h"
+ #include "filesystem/Slingbox.h"
++#include "filesystem/VTPFile.h"
++#include "pvr/channels/PVRChannel.h"
+ #include "URL.h"
+
+ using namespace XFILE;
+@@ -137,19 +138,19 @@ int CDVDInputStreamTV::GetStartTime()
+ return m_pLiveTV->GetStartTime();
+ }
+
+-bool CDVDInputStreamTV::NextChannel()
++bool CDVDInputStreamTV::NextChannel(bool preview/* = false*/)
+ {
+ if(!m_pLiveTV) return false;
+ return m_pLiveTV->NextChannel();
+ }
+
+-bool CDVDInputStreamTV::PrevChannel()
++bool CDVDInputStreamTV::PrevChannel(bool preview/* = false*/)
+ {
+ if(!m_pLiveTV) return false;
+ return m_pLiveTV->PrevChannel();
+ }
+
+-bool CDVDInputStreamTV::SelectChannel(unsigned int channel)
++bool CDVDInputStreamTV::SelectChannelByNumber(unsigned int channel)
+ {
+ if(!m_pLiveTV) return false;
+ return m_pLiveTV->SelectChannel(channel);
+diff --git a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamTV.h b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamTV.h
+index f19244a..f2db586 100644
+--- a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamTV.h
++++ b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamTV.h
+@@ -47,10 +47,9 @@ public:
+ virtual bool NextStream();
+ virtual int GetBlockSize();
+
+-
+- bool NextChannel();
+- bool PrevChannel();
+- bool SelectChannel(unsigned int channel);
++ bool NextChannel(bool preview = false);
++ bool PrevChannel(bool preview = false);
++ bool SelectChannelByNumber(unsigned int channel);
+
+ int GetTotalTime();
+ int GetStartTime();
+diff --git a/xbmc/cores/dvdplayer/DVDInputStreams/Makefile b/xbmc/cores/dvdplayer/DVDInputStreams/Makefile
+index b522e05..4607ed3 100644
+--- a/xbmc/cores/dvdplayer/DVDInputStreams/Makefile
++++ b/xbmc/cores/dvdplayer/DVDInputStreams/Makefile
+@@ -11,6 +11,7 @@ SRCS= DVDFactoryInputStream.cpp \
+ DVDInputStreamMemory.cpp \
+ DVDInputStreamNavigator.cpp \
+ DVDInputStreamRTMP.cpp \
++ DVDInputStreamPVRManager.cpp \
+ DVDInputStreamStack.cpp \
+ DVDInputStreamTV.cpp \
+ DVDStateSerializer.cpp \
+diff --git a/xbmc/cores/dvdplayer/DVDMessage.h b/xbmc/cores/dvdplayer/DVDMessage.h
+index 706e09e..bd54732 100644
+--- a/xbmc/cores/dvdplayer/DVDMessage.h
++++ b/xbmc/cores/dvdplayer/DVDMessage.h
+@@ -70,7 +70,8 @@ public:
+
+ PLAYER_CHANNEL_NEXT, // switches to next playback channel
+ PLAYER_CHANNEL_PREV, // switches to previous playback channel
+- PLAYER_CHANNEL_SELECT, // switches to given playback channel
++ PLAYER_CHANNEL_SELECT_NUMBER, // switches to the channel with the provided channel number
++ PLAYER_CHANNEL_SELECT, // switches to the provided channel
+ PLAYER_STARTED, // sent whenever a sub player has finished it's first frame after open
+
+ // demuxer related messages
+diff --git a/xbmc/cores/dvdplayer/DVDPlayer.cpp b/xbmc/cores/dvdplayer/DVDPlayer.cpp
+index b9b271e..65cf071 100644
+--- a/xbmc/cores/dvdplayer/DVDPlayer.cpp
++++ b/xbmc/cores/dvdplayer/DVDPlayer.cpp
+@@ -28,6 +28,7 @@
+ #include "DVDInputStreams/DVDFactoryInputStream.h"
+ #include "DVDInputStreams/DVDInputStreamNavigator.h"
+ #include "DVDInputStreams/DVDInputStreamTV.h"
++#include "DVDInputStreams/DVDInputStreamPVRManager.h"
+
+ #include "DVDDemuxers/DVDDemux.h"
+ #include "DVDDemuxers/DVDDemuxUtils.h"
+@@ -67,6 +68,11 @@
+ #include "utils/log.h"
+ #include "utils/TimeUtils.h"
+ #include "utils/StreamDetails.h"
++#include "pvr/PVRManager.h"
++#include "pvr/channels/PVRChannel.h"
++#include "pvr/windows/GUIWindowPVR.h"
++#include "filesystem/PVRFile.h"
++#include "video/dialogs/GUIDialogFullScreenInfo.h"
+ #include "utils/StreamUtils.h"
+ #include "utils/Variant.h"
+ #include "storage/MediaManager.h"
+@@ -76,6 +82,7 @@
+ #include "Util.h"
+
+ using namespace std;
++using namespace PVR;
+
+ void CSelectionStreams::Clear(StreamType type, StreamSource source)
+ {
+@@ -354,6 +361,7 @@ bool CDVDPlayer::OpenFile(const CFileItem& file, const CPlayerOptions &options)
+ m_item = file;
+ m_mimetype = file.GetMimeType();
+ m_filename = file.GetPath();
++ m_scanStart = 0;
+
+ m_ready.Reset();
+
+@@ -482,6 +490,7 @@ retry:
+
+ // find any available external subtitles for non dvd files
+ if (!m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)
++ && !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER)
+ && !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV)
+ && !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_HTSP))
+ {
+@@ -520,6 +529,7 @@ retry:
+ m_clock.Reset();
+ m_dvd.Clear();
+ m_errorCount = 0;
++ m_iChannelEntryTimeOut = 0;
+
+ return true;
+ }
+@@ -533,11 +543,15 @@ bool CDVDPlayer::OpenDemuxStream()
+
+ try
+ {
+- int attempts = 10;
++ int attempts = m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER) ? 1000 : 10;
+ while(!m_bStop && attempts-- > 0)
+ {
+ m_pDemuxer = CDVDFactoryDemuxer::CreateDemuxer(m_pInputStream);
+- if(!m_pDemuxer && m_pInputStream->NextStream())
++ if(!m_pDemuxer && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER))
++ {
++ continue;
++ }
++ else if(!m_pDemuxer && m_pInputStream->NextStream())
+ {
+ CLog::Log(LOGDEBUG, "%s - New stream available from input, retry open", __FUNCTION__);
+ continue;
+@@ -736,7 +750,6 @@ void CDVDPlayer::OpenDefaultStreams()
+ m_dvdPlayerVideo.EnableSubtitle(true);
+ else
+ m_dvdPlayerVideo.EnableSubtitle(false);
+-
+ // open teletext data stream
+ count = m_SelectionStreams.Count(STREAM_TELETEXT);
+ valid = false;
+@@ -761,6 +774,15 @@ bool CDVDPlayer::ReadPacket(DemuxPacket*& packet, CDemuxStream*& stream)
+
+ if(packet)
+ {
++ if(packet->iStreamId == DMX_SPECIALID_STREAMCHANGE)
++ {
++ // reset the caching state for pvr streams
++ if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER))
++ SetCaching(CACHESTATE_PVR);
++ CDVDDemuxUtils::FreeDemuxPacket(packet);
++ return true;
++ }
++
+ if(packet->iStreamId < 0)
+ return true;
+
+@@ -877,6 +899,30 @@ bool CDVDPlayer::IsBetterStream(CCurrentStream& current, CDemuxStream* stream)
+ if(current.type == STREAM_VIDEO && current.id < 0)
+ return true;
+ }
++ else if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER))
++ {
++ if(stream->source == current.source &&
++ stream->iId == current.id)
++ return false;
++
++ if(stream->disabled)
++ return false;
++
++ if(stream->type != current.type)
++ return false;
++
++ if(current.type == STREAM_AUDIO && stream->iPhysicalId == m_dvd.iSelectedAudioStream)
++ return true;
++
++ if(current.type == STREAM_SUBTITLE && stream->iPhysicalId == m_dvd.iSelectedSPUStream)
++ return true;
++
++ if(current.type == STREAM_TELETEXT)
++ return true;
++
++ if(current.id < 0)
++ return true;
++ }
+ else
+ {
+ if(stream->source == current.source
+@@ -1000,6 +1046,10 @@ void CDVDPlayer::Process()
+ }
+ }
+
++ // make sure all selected stream have data on startup
++ if (CachePVRStream())
++ SetCaching(CACHESTATE_PVR);
++
+ // make sure application know our info
+ UpdateApplication(0);
+ UpdatePlayState(0);
+@@ -1010,7 +1060,8 @@ void CDVDPlayer::Process()
+ // we are done initializing now, set the readyevent
+ m_ready.Set();
+
+- SetCaching(CACHESTATE_FLUSH);
++ if (!CachePVRStream())
++ SetCaching(CACHESTATE_FLUSH);
+
+ while (!m_bAbortRequest)
+ {
+@@ -1046,6 +1097,10 @@ void CDVDPlayer::Process()
+ }
+
+ OpenDefaultStreams();
++
++ if (CachePVRStream())
++ SetCaching(CACHESTATE_PVR);
++
+ UpdateApplication(0);
+ UpdatePlayState(0);
+ }
+@@ -1059,9 +1114,12 @@ void CDVDPlayer::Process()
+ // update application with our state
+ UpdateApplication(1000);
+
++ if (CheckDelayedChannelEntry())
++ continue;
++
+ // if the queues are full, no need to read more
+- if ((!m_dvdPlayerAudio.AcceptsData() && m_CurrentAudio.id >= 0)
+- || (!m_dvdPlayerVideo.AcceptsData() && m_CurrentVideo.id >= 0))
++ if ((!m_dvdPlayerAudio.AcceptsData() && m_CurrentAudio.id >= 0) ||
++ (!m_dvdPlayerVideo.AcceptsData() && m_CurrentVideo.id >= 0))
+ {
+ Sleep(10);
+ continue;
+@@ -1132,6 +1190,23 @@ void CDVDPlayer::Process()
+ Sleep(100);
+ continue;
+ }
++ else if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER))
++ {
++ CDVDInputStreamPVRManager* pStream = static_cast<CDVDInputStreamPVRManager*>(m_pInputStream);
++ unsigned int iTimeout = (unsigned int) g_guiSettings.GetInt("pvrplayback.scantime");
++ if (m_scanStart && XbmcThreads::SystemClockMillis() - m_scanStart >= iTimeout*1000)
++ {
++ CLog::Log(LOGERROR,"CDVDPlayer - %s - no video or audio data available after %i seconds, playback stopped",
++ __FUNCTION__, iTimeout);
++ break;
++ }
++
++ if (pStream->IsEOF())
++ break;
++
++ Sleep(100);
++ continue;
++ }
+
+ // make sure we tell all players to finish it's data
+ if(m_CurrentAudio.inited)
+@@ -1191,6 +1266,23 @@ void CDVDPlayer::Process()
+ }
+ }
+
++bool CDVDPlayer::CheckDelayedChannelEntry(void)
++{
++ bool bReturn(false);
++
++ if (m_iChannelEntryTimeOut > 0 && XbmcThreads::SystemClockMillis() >= m_iChannelEntryTimeOut)
++ {
++ CFileItem currentFile(g_application.CurrentFileItem());
++ CPVRChannel *currentChannel = currentFile.GetPVRChannelInfoTag();
++ SwitchChannel(*currentChannel);
++
++ bReturn = true;
++ m_iChannelEntryTimeOut = 0;
++ }
++
++ return bReturn;
++}
++
+ void CDVDPlayer::ProcessPacket(CDemuxStream* pStream, DemuxPacket* pPacket)
+ {
+ /* process packet if it belongs to selected stream. for dvd's don't allow automatic opening of streams*/
+@@ -1436,6 +1528,42 @@ void CDVDPlayer::HandlePlaySpeed()
+ }
+ }
+
++ if (caching == CACHESTATE_PVR)
++ {
++ bool bGotAudio(m_pDemuxer->GetNrOfAudioStreams() > 0);
++ bool bGotVideo(m_pDemuxer->GetNrOfVideoStreams() > 0);
++ bool bAudioLevelOk(m_dvdPlayerAudio.m_messageQueue.GetLevel() > g_advancedSettings.m_iPVRMinAudioCacheLevel);
++ bool bVideoLevelOk(m_dvdPlayerVideo.m_messageQueue.GetLevel() > g_advancedSettings.m_iPVRMinVideoCacheLevel);
++ bool bAudioFull(!m_dvdPlayerAudio.AcceptsData());
++ bool bVideoFull(!m_dvdPlayerVideo.AcceptsData());
++
++ if (/* if all streams got at least g_advancedSettings.m_iPVRMinCacheLevel in their buffers, we're done */
++ ((bGotVideo || bGotAudio) && (!bGotAudio || bAudioLevelOk) && (!bGotVideo || bVideoLevelOk)) ||
++ /* or if one of the buffers is full */
++ (bAudioFull || bVideoFull))
++ {
++ CLog::Log(LOGDEBUG, "set caching from pvr to done. audio (%d) = %d. video (%d) = %d",
++ bGotAudio, m_dvdPlayerAudio.m_messageQueue.GetLevel(),
++ bGotVideo, m_dvdPlayerVideo.m_messageQueue.GetLevel());
++
++ CFileItem currentItem(g_application.CurrentFileItem());
++ if (currentItem.HasPVRChannelInfoTag())
++ g_PVRManager.LoadCurrentChannelSettings();
++
++ caching = CACHESTATE_DONE;
++ SAFE_RELEASE(m_CurrentAudio.startsync);
++ SAFE_RELEASE(m_CurrentVideo.startsync);
++ }
++ else
++ {
++ /* ensure that automatically started players are stopped while caching */
++ if (m_CurrentAudio.started)
++ m_dvdPlayerAudio.SetSpeed(DVD_PLAYSPEED_PAUSE);
++ if (m_CurrentVideo.started)
++ m_dvdPlayerVideo.SetSpeed(DVD_PLAYSPEED_PAUSE);
++ }
++ }
++
+ if(caching == CACHESTATE_PLAY)
+ {
+ // if all enabled streams have started playing we are done
+@@ -1499,21 +1627,26 @@ bool CDVDPlayer::CheckStartCaching(CCurrentStream& current)
+ if((current.type == STREAM_AUDIO && m_dvdPlayerAudio.IsStalled())
+ || (current.type == STREAM_VIDEO && m_dvdPlayerVideo.IsStalled()))
+ {
++ if (CachePVRStream())
++ {
++ if ((current.type == STREAM_AUDIO && current.started && m_dvdPlayerAudio.m_messageQueue.GetLevel() == 0) ||
++ (current.type == STREAM_VIDEO && current.started && m_dvdPlayerVideo.m_messageQueue.GetLevel() == 0))
++ {
++ CLog::Log(LOGDEBUG, "%s stream stalled. start buffering", current.type == STREAM_AUDIO ? "audio" : "video");
++ SetCaching(CACHESTATE_PVR);
++ }
++ return true;
++ }
++
+ // don't start caching if it's only a single stream that has run dry
+- if(m_dvdPlayerAudio.m_messageQueue.GetLevel() > 50
+- || m_dvdPlayerVideo.m_messageQueue.GetLevel() > 50)
++ if(m_dvdPlayerAudio.m_messageQueue.GetLevel() > 50 ||
++ m_dvdPlayerVideo.m_messageQueue.GetLevel() > 50)
+ return false;
+
+- if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_HTSP)
+- || m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV))
+- SetCaching(CACHESTATE_INIT);
++ if(current.inited)
++ SetCaching(CACHESTATE_FULL);
+ else
+- {
+- if(current.inited)
+- SetCaching(CACHESTATE_FULL);
+- else
+- SetCaching(CACHESTATE_INIT);
+- }
++ SetCaching(CACHESTATE_INIT);
+ return true;
+ }
+ return false;
+@@ -1621,7 +1754,7 @@ void CDVDPlayer::CheckContinuity(CCurrentStream& current, DemuxPacket* pPacket)
+ #if 0
+ // these checks seem to cause more harm, than good
+ // looping stillframes are not common in normal files
+- // and a better fix for this behaviour would be to
++ // and a better fix for this behaviour would be to
+ // correct the timestamps with some offset
+
+ if (current.type == STREAM_VIDEO
+@@ -1697,6 +1830,7 @@ void CDVDPlayer::CheckContinuity(CCurrentStream& current, DemuxPacket* pPacket)
+ /* normally don't need to sync players since video player will keep playing at normal fps */
+ /* after a discontinuity */
+ //SynchronizePlayers(dts, pts, MSGWAIT_ALL);
++
+ m_CurrentAudio.inited = false;
+ m_CurrentVideo.inited = false;
+ m_CurrentSubtitle.inited = false;
+@@ -2101,8 +2235,9 @@ void CDVDPlayer::HandleMessages()
+ }
+ else if (pMsg->IsType(CDVDMsg::PLAYER_SET_RECORD))
+ {
+- if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV))
+- static_cast<CDVDInputStreamTV*>(m_pInputStream)->Record(*(CDVDMsgBool*)pMsg);
++ CDVDInputStream::IChannel* input = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
++ if(input)
++ input->Record(*(CDVDMsgBool*)pMsg);
+ }
+ else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH))
+ {
+@@ -2144,30 +2279,71 @@ void CDVDPlayer::HandleMessages()
+ if(m_pDemuxer)
+ m_pDemuxer->SetSpeed(speed);
+ }
+- else if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_NEXT) ||
+- pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_PREV) ||
+- (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_SELECT) && m_messenger.GetPacketCount(CDVDMsg::PLAYER_CHANNEL_SELECT) == 0))
++ else if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_SELECT_NUMBER) && m_messenger.GetPacketCount(CDVDMsg::PLAYER_CHANNEL_SELECT_NUMBER) == 0)
++ {
++ FlushBuffers(false);
++ CDVDInputStream::IChannel* input = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
++ if(input && input->SelectChannelByNumber(static_cast<CDVDMsgInt*>(pMsg)->m_value))
++ {
++ SAFE_DELETE(m_pDemuxer);
++ }else
++ {
++ CLog::Log(LOGWARNING, "%s - failed to switch channel. playback stopped", __FUNCTION__);
++ g_application.getApplicationMessenger().MediaStop(false);
++ }
++ }
++ else if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_SELECT) && m_messenger.GetPacketCount(CDVDMsg::PLAYER_CHANNEL_SELECT) == 0)
++ {
++ FlushBuffers(false);
++ CDVDInputStream::IChannel* input = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
++ if(input && input->SelectChannel(static_cast<CDVDMsgType <CPVRChannel> *>(pMsg)->m_value))
++ {
++ SAFE_DELETE(m_pDemuxer);
++ }else
++ {
++ CLog::Log(LOGWARNING, "%s - failed to switch channel. playback stopped", __FUNCTION__);
++ g_application.getApplicationMessenger().MediaStop(false);
++ }
++ }
++ else if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_NEXT) || pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_PREV))
+ {
+ CDVDInputStream::IChannel* input = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
+ if(input)
+ {
+- g_infoManager.SetDisplayAfterSeek(100000);
++ bool bSwitchSuccessful(false);
++ bool bShowPreview(g_guiSettings.GetInt("pvrplayback.channelentrytimeout") > 0);
+
+- bool result;
+- if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_SELECT))
+- result = input->SelectChannel(static_cast<CDVDMsgInt*>(pMsg)->m_value);
+- else if(pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_NEXT))
+- result = input->NextChannel();
+- else
+- result = input->PrevChannel();
+-
+- if(result)
++ if (!bShowPreview)
+ {
++ g_infoManager.SetDisplayAfterSeek(100000);
+ FlushBuffers(false);
+- SAFE_DELETE(m_pDemuxer);
+ }
+
+- g_infoManager.SetDisplayAfterSeek();
++ if(pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_NEXT))
++ bSwitchSuccessful = input->NextChannel(bShowPreview);
++ else
++ bSwitchSuccessful = input->PrevChannel(bShowPreview);
++
++ if(bSwitchSuccessful)
++ {
++ if (bShowPreview)
++ {
++ UpdateApplication(0);
++ m_iChannelEntryTimeOut = XbmcThreads::SystemClockMillis() + g_guiSettings.GetInt("pvrplayback.channelentrytimeout");
++ }
++ else
++ {
++ m_iChannelEntryTimeOut = 0;
++ SAFE_DELETE(m_pDemuxer);
++
++ g_infoManager.SetDisplayAfterSeek();
++ }
++ }
++ else
++ {
++ CLog::Log(LOGWARNING, "%s - failed to switch channel. playback stopped", __FUNCTION__);
++ g_application.getApplicationMessenger().MediaStop(false);
++ }
+ }
+ }
+ else if (pMsg->IsType(CDVDMsg::GENERAL_GUI_ACTION))
+@@ -2208,13 +2384,17 @@ void CDVDPlayer::SetCaching(ECacheState state)
+
+ CLog::Log(LOGDEBUG, "CDVDPlayer::SetCaching - caching state %d", state);
+ if(state == CACHESTATE_FULL
+- || state == CACHESTATE_INIT)
++ || state == CACHESTATE_INIT
++ || state == CACHESTATE_PVR)
+ {
+ m_clock.SetSpeed(DVD_PLAYSPEED_PAUSE);
+ m_dvdPlayerAudio.SetSpeed(DVD_PLAYSPEED_PAUSE);
+ m_dvdPlayerAudio.SendMessage(new CDVDMsg(CDVDMsg::PLAYER_STARTED), 1);
+ m_dvdPlayerVideo.SetSpeed(DVD_PLAYSPEED_PAUSE);
+ m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::PLAYER_STARTED), 1);
++
++ if (state == CACHESTATE_PVR)
++ m_scanStart = XbmcThreads::SystemClockMillis();
+ }
+
+ if(state == CACHESTATE_PLAY
+@@ -2223,6 +2403,7 @@ void CDVDPlayer::SetCaching(ECacheState state)
+ m_clock.SetSpeed(m_playSpeed);
+ m_dvdPlayerAudio.SetSpeed(m_playSpeed);
+ m_dvdPlayerVideo.SetSpeed(m_playSpeed);
++ m_scanStart = 0;
+ }
+ m_caching = state;
+ }
+@@ -2237,7 +2418,7 @@ void CDVDPlayer::SetPlaySpeed(int speed)
+
+ void CDVDPlayer::Pause()
+ {
+- if(m_playSpeed != DVD_PLAYSPEED_PAUSE && m_caching == CACHESTATE_FULL)
++ if(m_playSpeed != DVD_PLAYSPEED_PAUSE && (m_caching == CACHESTATE_FULL || m_caching == CACHESTATE_PVR))
+ {
+ SetCaching(CACHESTATE_DONE);
+ return;
+@@ -2258,7 +2439,7 @@ void CDVDPlayer::Pause()
+
+ bool CDVDPlayer::IsPaused() const
+ {
+- return (m_playSpeed == DVD_PLAYSPEED_PAUSE) || m_caching == CACHESTATE_FULL;
++ return m_playSpeed == DVD_PLAYSPEED_PAUSE || m_caching == CACHESTATE_FULL || m_caching == CACHESTATE_PVR;
+ }
+
+ bool CDVDPlayer::HasVideo() const
+@@ -3231,6 +3412,21 @@ int CDVDPlayer::OnDVDNavResult(void* pData, int iMessage)
+ return NAVRESULT_NOP;
+ }
+
++bool CDVDPlayer::ShowPVRChannelInfo(void)
++{
++ bool bReturn(false);
++
++ if (g_guiSettings.GetBool("pvrmenu.infoswitch"))
++ {
++ int iTimeout = g_guiSettings.GetBool("pvrmenu.infotimeout") ? g_guiSettings.GetInt("pvrmenu.infotime") : 0;
++ g_PVRManager.ShowPlayerInfo(iTimeout);
++
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
+ bool CDVDPlayer::OnAction(const CAction &action)
+ {
+ #define THREAD_ACTION(action) \
+@@ -3416,17 +3612,21 @@ bool CDVDPlayer::OnAction(const CAction &action)
+ {
+ switch (action.GetID())
+ {
++ case ACTION_MOVE_UP:
+ case ACTION_NEXT_ITEM:
+ case ACTION_PAGE_UP:
+ m_messenger.Put(new CDVDMsg(CDVDMsg::PLAYER_CHANNEL_NEXT));
+ g_infoManager.SetDisplayAfterSeek();
++ ShowPVRChannelInfo();
+ return true;
+ break;
+
++ case ACTION_MOVE_DOWN:
+ case ACTION_PREV_ITEM:
+ case ACTION_PAGE_DOWN:
+ m_messenger.Put(new CDVDMsg(CDVDMsg::PLAYER_CHANNEL_PREV));
+ g_infoManager.SetDisplayAfterSeek();
++ ShowPVRChannelInfo();
+ return true;
+ break;
+
+@@ -3434,8 +3634,9 @@ bool CDVDPlayer::OnAction(const CAction &action)
+ {
+ // Offset from key codes back to button number
+ int channel = action.GetAmount();
+- m_messenger.Put(new CDVDMsgInt(CDVDMsg::PLAYER_CHANNEL_SELECT, channel));
++ m_messenger.Put(new CDVDMsgInt(CDVDMsg::PLAYER_CHANNEL_SELECT_NUMBER, channel));
+ g_infoManager.SetDisplayAfterSeek();
++ ShowPVRChannelInfo();
+ return true;
+ }
+ break;
+@@ -3668,6 +3869,11 @@ void CDVDPlayer::UpdatePlayState(double timeout)
+ state.canrecord = static_cast<CDVDInputStreamTV*>(m_pInputStream)->CanRecord();
+ state.recording = static_cast<CDVDInputStreamTV*>(m_pInputStream)->IsRecording();
+ }
++ else if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER))
++ {
++ state.canrecord = static_cast<CDVDInputStreamPVRManager*>(m_pInputStream)->CanRecord();
++ state.recording = static_cast<CDVDInputStreamPVRManager*>(m_pInputStream)->IsRecording();
++ }
+
+ CDVDInputStream::IDisplayTime* pDisplayTime = dynamic_cast<CDVDInputStream::IDisplayTime*>(m_pInputStream);
+ if (pDisplayTime)
+@@ -3690,7 +3896,6 @@ void CDVDPlayer::UpdatePlayState(double timeout)
+ else
+ state.player_state = "";
+
+-
+ if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV))
+ {
+ if(((CDVDInputStreamTV*)m_pInputStream)->GetTotalTime() > 0)
+@@ -3699,6 +3904,15 @@ void CDVDPlayer::UpdatePlayState(double timeout)
+ state.time_total = ((CDVDInputStreamTV*)m_pInputStream)->GetTotalTime();
+ }
+ }
++ else if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER))
++ {
++ if(((CDVDInputStreamPVRManager*)m_pInputStream)->GetTotalTime() > 0 &&
++ ((CDVDInputStreamPVRManager*)m_pInputStream)->GetStartTime() > 0)
++ {
++ state.time = ((CDVDInputStreamPVRManager*)m_pInputStream)->GetStartTime();
++ state.time_total = ((CDVDInputStreamPVRManager*)m_pInputStream)->GetTotalTime();
++ }
++ }
+ }
+
+ if (m_Edl.HasCut())
+@@ -3792,7 +4006,8 @@ bool CDVDPlayer::IsRecording()
+
+ bool CDVDPlayer::Record(bool bOnOff)
+ {
+- if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV))
++ if (m_pInputStream && (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV) ||
++ m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER)) )
+ {
+ m_messenger.Put(new CDVDMsgBool(CDVDMsg::PLAYER_SET_RECORD, bOnOff));
+ return true;
+@@ -3874,3 +4089,30 @@ CStdString CDVDPlayer::GetPlayingTitle()
+
+ return "";
+ }
++
++bool CDVDPlayer::SwitchChannel(const CPVRChannel &channel)
++{
++ /* set GUI info */
++ if (!g_PVRManager.PerformChannelSwitch(channel, true))
++ return false;
++
++ UpdateApplication(0);
++ UpdatePlayState(0);
++
++ /* make sure the pvr window is updated */
++ CGUIWindowPVR *pWindow = (CGUIWindowPVR *) g_windowManager.GetWindow(WINDOW_PVR);
++ if (pWindow)
++ pWindow->SetInvalid();
++
++ /* select the new channel */
++ m_messenger.Put(new CDVDMsgType<CPVRChannel>(CDVDMsg::PLAYER_CHANNEL_SELECT, channel));
++
++ return true;
++}
++
++bool CDVDPlayer::CachePVRStream(void) const
++{
++ return m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER) &&
++ !g_PVRManager.IsPlayingRecording() &&
++ g_advancedSettings.m_bPVRCacheInDvdPlayer;
++}
+diff --git a/xbmc/cores/dvdplayer/DVDPlayer.h b/xbmc/cores/dvdplayer/DVDPlayer.h
+index 4afb5e2..2424ee3 100644
+--- a/xbmc/cores/dvdplayer/DVDPlayer.h
++++ b/xbmc/cores/dvdplayer/DVDPlayer.h
+@@ -49,6 +49,11 @@ class CDemuxStreamVideo;
+ class CDemuxStreamAudio;
+ class CStreamInfo;
+
++namespace PVR
++{
++ class CPVRChannel;
++}
++
+ #define DVDSTATE_NORMAL 0x00000001 // normal dvd state
+ #define DVDSTATE_STILL 0x00000002 // currently displaying a still frame
+ #define DVDSTATE_WAIT 0x00000003 // waiting for demuxer read error
+@@ -223,15 +228,19 @@ public:
+
+ virtual CStdString GetPlayingTitle();
+
++ virtual bool SwitchChannel(const PVR::CPVRChannel &channel);
++ virtual bool CachePVRStream(void) const;
++
+ enum ECacheState
+ { CACHESTATE_DONE = 0
+ , CACHESTATE_FULL // player is filling up the demux queue
++ , CACHESTATE_PVR // player is waiting for some data in each buffer
+ , CACHESTATE_INIT // player is waiting for first packet of each stream
+ , CACHESTATE_PLAY // player is waiting for players to not be stalled
+ , CACHESTATE_FLUSH // temporary state player will choose startup between init or full
+ };
+
+- virtual bool IsCaching() const { return m_caching == CACHESTATE_FULL; }
++ virtual bool IsCaching() const { return m_caching == CACHESTATE_FULL || m_caching == CACHESTATE_PVR; }
+ virtual int GetCacheLevel() const ;
+
+ virtual int OnDVDNavResult(void* pData, int iMessage);
+@@ -263,6 +272,8 @@ protected:
+ void ProcessSubData(CDemuxStream* pStream, DemuxPacket* pPacket);
+ void ProcessTeletextData(CDemuxStream* pStream, DemuxPacket* pPacket);
+
++ bool ShowPVRChannelInfo();
++
+ int AddSubtitleFile(const std::string& filename, const std::string& subfilename = "", CDemuxStream::EFlags flags = CDemuxStream::FLAG_NONE);
+
+ /**
+@@ -297,6 +308,7 @@ protected:
+ bool ReadPacket(DemuxPacket*& packet, CDemuxStream*& stream);
+ bool IsValidStream(CCurrentStream& stream);
+ bool IsBetterStream(CCurrentStream& current, CDemuxStream* stream);
++ bool CheckDelayedChannelEntry(void);
+
+ bool OpenInputStream();
+ bool OpenDemuxStream();
+@@ -308,10 +320,12 @@ protected:
+
+ bool m_bAbortRequest;
+
+- std::string m_filename; // holds the actual filename
+- std::string m_mimetype; // hold a hint to what content file contains (mime type)
+- ECacheState m_caching;
+- CFileItem m_item;
++ std::string m_filename; // holds the actual filename
++ std::string m_mimetype; // hold a hint to what content file contains (mime type)
++ ECacheState m_caching;
++ CFileItem m_item;
++ unsigned int m_scanStart;
++ unsigned int m_iChannelEntryTimeOut;
+
+
+ CCurrentStream m_CurrentAudio;
+diff --git a/xbmc/cores/dvdplayer/DVDSubtitles/SamiTagConvertor.cpp b/xbmc/cores/dvdplayer/DVDSubtitles/SamiTagConvertor.cpp
+new file mode 100644
+index 0000000..6199aaa
+--- /dev/null
++++ b/xbmc/cores/dvdplayer/DVDSubtitles/SamiTagConvertor.cpp
+@@ -0,0 +1,242 @@
++/*
++ * Copyright (C) 2005-2008 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "SamiTagConvertor.h"
++#include "DVDSubtitleStream.h"
++#include "DVDCodecs/Overlay/DVDOverlayText.h"
++#include "utils/RegExp.h"
++
++SamiTagConvertor::~SamiTagConvertor()
++{
++ delete m_tags;
++ delete m_tagOptions;
++}
++
++bool SamiTagConvertor::Init()
++{
++ m_tags = new CRegExp(true);
++ if (!m_tags->RegComp("(<[^>]*>)"))
++ return false;
++
++ m_tagOptions = new CRegExp(true);
++ if (!m_tagOptions->RegComp("([a-z]+)[ \t]*=[ \t]*(?:[\"'])?([^\"'> ]+)(?:[\"'])?(?:>)?"))
++ return false;
++
++ return true;
++}
++
++void SamiTagConvertor::ConvertLine(CDVDOverlayText* pOverlay, const char* line, int len, const char* lang)
++{
++ CStdStringA strUTF8;
++ strUTF8.assign(line, len);
++
++ int pos = 0;
++ int del_start = 0;
++ while ((pos=m_tags->RegFind(strUTF8.c_str(), pos)) >= 0)
++ {
++ // Parse Tags
++ CStdString fullTag = m_tags->GetMatch(0);
++ fullTag.ToLower();
++ strUTF8.erase(pos, fullTag.length());
++ if (fullTag == "<b>")
++ {
++ tag_flag[FLAG_BOLD] = true;
++ strUTF8.insert(pos, "[B]");
++ pos += 3;
++ }
++ else if (fullTag == "</b>" && tag_flag[FLAG_BOLD])
++ {
++ tag_flag[FLAG_BOLD] = false;
++ strUTF8.insert(pos, "[/B]");
++ pos += 4;
++ }
++ else if (fullTag == "<i>")
++ {
++ tag_flag[FLAG_ITALIC] = true;
++ strUTF8.insert(pos, "[I]");
++ pos += 3;
++ }
++ else if (fullTag == "</i>" && tag_flag[FLAG_ITALIC])
++ {
++ tag_flag[FLAG_ITALIC] = false;
++ strUTF8.insert(pos, "[/I]");
++ pos += 4;
++ }
++ else if (fullTag == "</font>" && tag_flag[FLAG_COLOR])
++ {
++ tag_flag[FLAG_COLOR] = false;
++ strUTF8.insert(pos, "[/COLOR]");
++ pos += 8;
++ }
++ else if (fullTag.Left(5) == "<font")
++ {
++ int pos2 = 5;
++ while ((pos2 = m_tagOptions->RegFind(fullTag.c_str(), pos2)) >= 0)
++ {
++ CStdString tagOptionName = m_tagOptions->GetMatch(1);
++ CStdString tagOptionValue = m_tagOptions->GetMatch(2);
++ pos2 += tagOptionName.length() + tagOptionValue.length();
++ if (tagOptionName == "color")
++ {
++ tag_flag[FLAG_COLOR] = true;
++ CStdString tempColorTag = "[COLOR ";
++ if (tagOptionValue[0] == '#')
++ {
++ tagOptionValue.erase(0, 1);
++ tempColorTag += "FF";
++ }
++ else if( tagOptionValue.size() == 6 )
++ {
++ bool bHex = true;
++ for( int i=0 ; i<6 ; i++ )
++ {
++ char temp = tagOptionValue[i];
++ if( !(('0' <= temp && temp <= '9') ||
++ ('a' <= temp && temp <= 'f') ||
++ ('A' <= temp && temp <= 'F') ))
++ {
++ bHex = false;
++ break;
++ }
++ }
++ if( bHex ) tempColorTag += "FF";
++ }
++ tempColorTag += tagOptionValue;
++ tempColorTag += "]";
++ strUTF8.insert(pos, tempColorTag);
++ pos += tempColorTag.length();
++ }
++ }
++ }
++ else if (lang && (fullTag.Left(3) == "<p "))
++ {
++ int pos2 = 3;
++ while ((pos2 = m_tagOptions->RegFind(fullTag.c_str(), pos2)) >= 0)
++ {
++ CStdString tagOptionName = m_tagOptions->GetMatch(1);
++ CStdString tagOptionValue = m_tagOptions->GetMatch(2);
++ pos2 += tagOptionName.length() + tagOptionValue.length();
++ if (tagOptionName == "class")
++ {
++ if (tag_flag[FLAG_LANGUAGE])
++ {
++ strUTF8.erase(del_start, pos - del_start);
++ pos = del_start;
++ }
++ if (!tagOptionValue.Compare(lang))
++ {
++ tag_flag[FLAG_LANGUAGE] = false;
++ }
++ else
++ {
++ tag_flag[FLAG_LANGUAGE] = true;
++ del_start = pos;
++ }
++ break;
++ }
++ }
++ }
++ else if (fullTag == "</p>" && tag_flag[FLAG_LANGUAGE])
++ {
++ strUTF8.erase(del_start, pos - del_start);
++ pos = del_start;
++ tag_flag[FLAG_LANGUAGE] = false;
++ }
++ else if (fullTag == "<br>" && !strUTF8.IsEmpty())
++ {
++ strUTF8.Insert(pos, "\n");
++ pos += 1;
++ }
++ }
++
++ if(tag_flag[FLAG_LANGUAGE])
++ strUTF8.erase(del_start);
++
++ if (strUTF8.IsEmpty())
++ return;
++
++ if( strUTF8[strUTF8.size()-1] == '\n' )
++ strUTF8.Delete(strUTF8.size()-1);
++
++ // add a new text element to our container
++ pOverlay->AddElement(new CDVDOverlayText::CElementText(strUTF8.c_str()));
++}
++
++void SamiTagConvertor::CloseTag(CDVDOverlayText* pOverlay)
++{
++ if (tag_flag[FLAG_BOLD])
++ {
++ pOverlay->AddElement(new CDVDOverlayText::CElementText("[/B]"));
++ tag_flag[FLAG_BOLD] = false;
++ }
++ if (tag_flag[FLAG_ITALIC])
++ {
++ pOverlay->AddElement(new CDVDOverlayText::CElementText("[/I]"));
++ tag_flag[FLAG_ITALIC] = false;
++ }
++ if (tag_flag[FLAG_COLOR])
++ {
++ pOverlay->AddElement(new CDVDOverlayText::CElementText("[/COLOR]"));
++ tag_flag[FLAG_COLOR] = false;
++ }
++ tag_flag[FLAG_LANGUAGE] = false;
++}
++
++void SamiTagConvertor::LoadHead(CDVDSubtitleStream* samiStream)
++{
++ char line[1024];
++ bool inSTYLE = false;
++ CRegExp reg(true);
++ if (!reg.RegComp("\\.([a-z]+)[ \t]*\\{[ \t]*name:([^;]*?);[ \t]*lang:([^;]*?);[ \t]*SAMIType:([^;]*?);[ \t]*\\}"))
++ return;
++
++ while (samiStream->ReadLine(line, sizeof(line)))
++ {
++ if (!strnicmp(line, "<BODY>", 6))
++ break;
++ if (inSTYLE)
++ {
++ if (!strnicmp(line, "</STYLE>", 8))
++ break;
++ else
++ {
++ if (reg.RegFind(line) > -1)
++ {
++ SLangclass lc;
++ lc.ID = reg.GetMatch(1);
++ lc.Name = reg.GetMatch(2);
++ lc.Lang = reg.GetMatch(3);
++ lc.SAMIType = reg.GetMatch(4);
++ lc.Name.Trim();
++ lc.Lang.Trim();
++ lc.SAMIType.Trim();
++ m_Langclass.push_back(lc);
++ }
++ }
++ }
++ else
++ {
++ if (!strnicmp(line, "<STYLE TYPE=\"text/css\">", 23))
++ inSTYLE = true;
++ }
++ }
++}
++
+diff --git a/xbmc/cores/dvdplayer/DVDSubtitles/SamiTagConvertor.h b/xbmc/cores/dvdplayer/DVDSubtitles/SamiTagConvertor.h
+new file mode 100644
+index 0000000..93ee191
+--- /dev/null
++++ b/xbmc/cores/dvdplayer/DVDSubtitles/SamiTagConvertor.h
+@@ -0,0 +1,68 @@
++#pragma once
++
++/*
++ * Copyright (C) 2005-2008 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++#include <stdio.h>
++#include "StdString.h"
++
++#define FLAG_BOLD 0
++#define FLAG_ITALIC 1
++#define FLAG_COLOR 2
++#define FLAG_LANGUAGE 3
++
++class CDVDOverlayText;
++class CDVDSubtitleStream;
++class CRegExp;
++
++class SamiTagConvertor
++{
++public:
++ SamiTagConvertor()
++ {
++ m_tags = NULL;
++ m_tagOptions = NULL;
++ tag_flag[FLAG_BOLD] = false;
++ tag_flag[FLAG_ITALIC] = false;
++ tag_flag[FLAG_COLOR] = false;
++ tag_flag[FLAG_LANGUAGE] = false; //set to true when classID != lang
++ }
++ virtual ~SamiTagConvertor();
++ bool Init();
++ void ConvertLine(CDVDOverlayText* pOverlay, const char* line, int len, const char* lang = NULL);
++ void CloseTag(CDVDOverlayText* pOverlay);
++ void LoadHead(CDVDSubtitleStream* samiStream);
++
++ typedef struct
++ {
++ CStdString ID;
++ CStdString Name;
++ CStdString Lang;
++ CStdString SAMIType;
++ } SLangclass;
++
++ std::vector<SLangclass> m_Langclass;
++
++private:
++ CRegExp *m_tags;
++ CRegExp *m_tagOptions;
++ bool tag_flag[4];
++};
++
+diff --git a/xbmc/cores/dvdplayer/Edl.cpp b/xbmc/cores/dvdplayer/Edl.cpp
+index 1e6ce3f..8021d38 100644
+--- a/xbmc/cores/dvdplayer/Edl.cpp
++++ b/xbmc/cores/dvdplayer/Edl.cpp
+@@ -112,8 +112,9 @@ bool CEdl::ReadEditDecisionLists(const CStdString& strMovie, const float fFrameR
+ * Only check for edit decision lists if the movie is on the local hard drive, or accessed over a
+ * network share.
+ */
+- if (URIUtils::IsHD(strMovie)
+- || URIUtils::IsSmb(strMovie))
++ if ((URIUtils::IsHD(strMovie) || URIUtils::IsSmb(strMovie)) &&
++ !URIUtils::IsPVRRecording(strMovie) &&
++ !URIUtils::IsInternetStream(strMovie))
+ {
+ CLog::Log(LOGDEBUG, "%s - Checking for edit decision lists (EDL) on local drive or remote share for: %s",
+ __FUNCTION__, strMovie.c_str());
+diff --git a/xbmc/cores/paplayer/CodecFactory.cpp b/xbmc/cores/paplayer/CodecFactory.cpp
+index bc30c63..52d8d37 100644
+--- a/xbmc/cores/paplayer/CodecFactory.cpp
++++ b/xbmc/cores/paplayer/CodecFactory.cpp
+@@ -65,7 +65,8 @@ ICodec* CodecFactory::CreateCodec(const CStdString& strFileType)
+ else if (strFileType.Equals("wav"))
+ return new DVDPlayerCodec();
+ else if (strFileType.Equals("dts") || strFileType.Equals("ac3") ||
+- strFileType.Equals("m4a") || strFileType.Equals("aac"))
++ strFileType.Equals("m4a") || strFileType.Equals("aac") ||
++ strFileType.Equals("pvr"))
+ return new DVDPlayerCodec();
+ else if (strFileType.Equals("wv"))
+ return new DVDPlayerCodec();
+diff --git a/xbmc/dialogs/GUIDialogContextMenu.h b/xbmc/dialogs/GUIDialogContextMenu.h
+index 9a9600f..f661614 100644
+--- a/xbmc/dialogs/GUIDialogContextMenu.h
++++ b/xbmc/dialogs/GUIDialogContextMenu.h
+@@ -100,10 +100,30 @@ enum CONTEXT_BUTTON { CONTEXT_BUTTON_CANCELLED = 0,
+ CONTEXT_BUTTON_SCRIPT_SETTINGS,
+ CONTEXT_BUTTON_LASTFM_UNLOVE_ITEM,
+ CONTEXT_BUTTON_LASTFM_UNBAN_ITEM,
++ CONTEXT_BUTTON_HIDE,
++ CONTEXT_BUTTON_SHOW_HIDDEN,
++ CONTEXT_BUTTON_ADD,
++ CONTEXT_BUTTON_ACTIVATE,
++ CONTEXT_BUTTON_START_RECORD,
++ CONTEXT_BUTTON_STOP_RECORD,
++ CONTEXT_BUTTON_GROUP_MANAGER,
++ CONTEXT_BUTTON_CHANNEL_MANAGER,
++ CONTEXT_BUTTON_FILTER,
+ CONTEXT_BUTTON_SET_MOVIESET_THUMB,
++ CONTEXT_BUTTON_BEGIN,
++ CONTEXT_BUTTON_END,
++ CONTEXT_BUTTON_FIND,
+ CONTEXT_BUTTON_SET_MOVIESET_FANART,
+ CONTEXT_BUTTON_DELETE_PLUGIN,
++ CONTEXT_BUTTON_SORTASC,
++ CONTEXT_BUTTON_SORTBY,
++ CONTEXT_BUTTON_SORTBY_CHANNEL,
++ CONTEXT_BUTTON_SORTBY_NAME,
++ CONTEXT_BUTTON_SORTBY_DATE,
++ CONTEXT_BUTTON_MENU_HOOKS,
+ CONTEXT_BUTTON_PLAY_AND_QUEUE,
++ CONTEXT_BUTTON_UPDATE_EPG,
++ CONTEXT_BUTTON_RECORD_ITEM,
+ CONTEXT_BUTTON_USER1,
+ CONTEXT_BUTTON_USER2,
+ CONTEXT_BUTTON_USER3,
+diff --git a/xbmc/dialogs/GUIDialogExtendedProgressBar.cpp b/xbmc/dialogs/GUIDialogExtendedProgressBar.cpp
+new file mode 100644
+index 0000000..6000b6f
+--- /dev/null
++++ b/xbmc/dialogs/GUIDialogExtendedProgressBar.cpp
+@@ -0,0 +1,103 @@
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "GUIDialogExtendedProgressBar.h"
++#include "guilib/GUIProgressControl.h"
++#include "guilib/GUISliderControl.h"
++#include "threads/SingleLock.h"
++
++#define CONTROL_LABELHEADER 30
++#define CONTROL_LABELTITLE 31
++#define CONTROL_PROGRESS 32
++
++CGUIDialogExtendedProgressBar::CGUIDialogExtendedProgressBar(void)
++ : CGUIDialog(WINDOW_DIALOG_EXT_PROGRESS, "DialogExtendedProgressBar.xml")
++{
++ m_loadOnDemand = false;
++}
++
++bool CGUIDialogExtendedProgressBar::OnMessage(CGUIMessage& message)
++{
++ switch (message.GetMessage())
++ {
++ case GUI_MSG_WINDOW_INIT:
++ {
++ CGUIDialog::OnMessage(message);
++
++ m_strTitle.Empty();
++ m_strHeader.Empty();
++ m_fPercentDone = -1.0f;
++
++ UpdateState();
++ return true;
++ }
++ break;
++ }
++
++ return CGUIDialog::OnMessage(message);
++}
++
++void CGUIDialogExtendedProgressBar::Render()
++{
++ if (m_active)
++ UpdateState();
++
++ CGUIDialog::Render();
++}
++
++void CGUIDialogExtendedProgressBar::SetHeader(const CStdString& strHeader)
++{
++ CSingleLock lock (m_critical);
++
++ m_strHeader = strHeader;
++}
++
++void CGUIDialogExtendedProgressBar::SetTitle(const CStdString& strTitle)
++{
++ CSingleLock lock (m_critical);
++
++ m_strTitle = strTitle;
++}
++
++void CGUIDialogExtendedProgressBar::SetProgress(int currentItem, int itemCount)
++{
++ CSingleLock lock (m_critical);
++
++ m_fPercentDone = (float)((currentItem*100)/itemCount);
++ if (m_fPercentDone > 100.0F)
++ m_fPercentDone = 100.0F;
++}
++
++void CGUIDialogExtendedProgressBar::UpdateState()
++{
++ CSingleLock lock (m_critical);
++
++ SET_CONTROL_LABEL(CONTROL_LABELHEADER, m_strHeader);
++ SET_CONTROL_LABEL(CONTROL_LABELTITLE, m_strTitle);
++
++ if (m_fPercentDone > -1.0f)
++ {
++ SET_CONTROL_VISIBLE(CONTROL_PROGRESS);
++ CGUIProgressControl* pProgressCtrl=(CGUIProgressControl*)GetControl(CONTROL_PROGRESS);
++ if (pProgressCtrl) pProgressCtrl->SetPercentage(m_fPercentDone);
++ }
++}
++
+diff --git a/xbmc/dialogs/GUIDialogExtendedProgressBar.h b/xbmc/dialogs/GUIDialogExtendedProgressBar.h
+new file mode 100644
+index 0000000..ae11ad9
+--- /dev/null
++++ b/xbmc/dialogs/GUIDialogExtendedProgressBar.h
+@@ -0,0 +1,44 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "guilib/GUIDialog.h"
++
++class CGUIDialogExtendedProgressBar : public CGUIDialog
++{
++public:
++ CGUIDialogExtendedProgressBar(void);
++ virtual ~CGUIDialogExtendedProgressBar(void) {}
++ virtual bool OnMessage(CGUIMessage& message);
++ virtual void Render();
++ void SetProgress(int currentItem, int itemCount);
++ void SetHeader(const CStdString& strHeader);
++ void SetTitle(const CStdString& strTitle);
++ void UpdateState();
++
++protected:
++ CStdString m_strTitle;
++ CStdString m_strHeader;
++ CCriticalSection m_critical;
++ float m_fPercentDone;
++ int m_currentItem;
++ int m_itemCount;
++};
+diff --git a/xbmc/dialogs/GUIDialogMediaSource.cpp b/xbmc/dialogs/GUIDialogMediaSource.cpp
+index ffbe20f..0d446ef 100644
+--- a/xbmc/dialogs/GUIDialogMediaSource.cpp
++++ b/xbmc/dialogs/GUIDialogMediaSource.cpp
+@@ -28,6 +28,8 @@
+ #include "Util.h"
+ #include "utils/URIUtils.h"
+ #include "filesystem/Directory.h"
++#include "filesystem/PluginDirectory.h"
++#include "filesystem/PVRDirectory.h"
+ #include "GUIDialogYesNo.h"
+ #include "FileItem.h"
+ #include "settings/Settings.h"
+@@ -263,6 +265,14 @@ void CGUIDialogMediaSource::OnPathBrowse(int item)
+ share1.strPath = "sap://";
+ share1.strName = "SAP Streams";
+ extraShares.push_back(share1);
++
++ // add the recordings dir as needed
++ if (CPVRDirectory::HasRecordings())
++ {
++ share1.strPath = "pvr://recordings/";
++ share1.strName = g_localizeStrings.Get(19017); // TV Recordings
++ extraShares.push_back(share1);
++ }
+ }
+ else if (m_type == "pictures")
+ {
+diff --git a/xbmc/dialogs/GUIDialogNumeric.cpp b/xbmc/dialogs/GUIDialogNumeric.cpp
+index fb930be..558cd77 100644
+--- a/xbmc/dialogs/GUIDialogNumeric.cpp
++++ b/xbmc/dialogs/GUIDialogNumeric.cpp
+@@ -283,6 +283,8 @@ void CGUIDialogNumeric::FrameMove()
+
+ void CGUIDialogNumeric::OnNumber(unsigned int num)
+ {
++ ResetAutoClose();
++
+ if (m_mode == INPUT_NUMBER || m_mode == INPUT_PASSWORD)
+ {
+ m_number += num + '0';
+@@ -566,16 +568,19 @@ bool CGUIDialogNumeric::ShowAndGetIPAddress(CStdString &IPAddress, const CStdStr
+ return true;
+ }
+
+-bool CGUIDialogNumeric::ShowAndGetNumber(CStdString& strInput, const CStdString &strHeading)
++bool CGUIDialogNumeric::ShowAndGetNumber(CStdString& strInput, const CStdString &strHeading, unsigned int iAutoCloseTimeoutMs /* = 0 */)
+ {
+ // Prompt user for password input
+ CGUIDialogNumeric *pDialog = (CGUIDialogNumeric *)g_windowManager.GetWindow(WINDOW_DIALOG_NUMERIC);
+ pDialog->SetHeading( strHeading );
+
+ pDialog->SetMode(INPUT_NUMBER, (void *)&strInput);
++ if (iAutoCloseTimeoutMs)
++ pDialog->SetAutoClose(iAutoCloseTimeoutMs);
++
+ pDialog->DoModal();
+
+- if (!pDialog->IsConfirmed() || pDialog->IsCanceled())
++ if (!pDialog->IsAutoClosed() && (!pDialog->IsConfirmed() || pDialog->IsCanceled()))
+ return false;
+ pDialog->GetOutput(&strInput);
+ return true;
+diff --git a/xbmc/dialogs/GUIDialogNumeric.h b/xbmc/dialogs/GUIDialogNumeric.h
+index ef1517b..517e87f 100644
+--- a/xbmc/dialogs/GUIDialogNumeric.h
++++ b/xbmc/dialogs/GUIDialogNumeric.h
+@@ -49,7 +49,7 @@ public:
+ static bool ShowAndGetTime(SYSTEMTIME &time, const CStdString &heading);
+ static bool ShowAndGetDate(SYSTEMTIME &date, const CStdString &heading);
+ static bool ShowAndGetIPAddress(CStdString &IPAddress, const CStdString &heading);
+- static bool ShowAndGetNumber(CStdString& strInput, const CStdString &strHeading);
++ static bool ShowAndGetNumber(CStdString& strInput, const CStdString &strHeading, unsigned int iAutoCloseTimeoutMs = 0);
+ static bool ShowAndGetSeconds(CStdString& timeString, const CStdString &heading);
+
+ protected:
+diff --git a/xbmc/dialogs/GUIDialogSeekBar.cpp b/xbmc/dialogs/GUIDialogSeekBar.cpp
+index a774b19..d20c477 100644
+--- a/xbmc/dialogs/GUIDialogSeekBar.cpp
++++ b/xbmc/dialogs/GUIDialogSeekBar.cpp
+@@ -25,6 +25,8 @@
+ #include "Application.h"
+ #include "GUIInfoManager.h"
+ #include "utils/TimeUtils.h"
++#include "FileItem.h"
++#include "settings/GUISettings.h"
+ #include "utils/StringUtils.h"
+
+ #define SEEK_BAR_DISPLAY_TIME 2000L
+diff --git a/xbmc/dialogs/Makefile b/xbmc/dialogs/Makefile
+index 49ae5f2..9182f06 100644
+--- a/xbmc/dialogs/Makefile
++++ b/xbmc/dialogs/Makefile
+@@ -3,6 +3,7 @@ SRCS=GUIDialogBoxBase.cpp \
+ GUIDialogButtonMenu.cpp \
+ GUIDialogCache.cpp \
+ GUIDialogContextMenu.cpp \
++ GUIDialogExtendedProgressBar.cpp \
+ GUIDialogFavourites.cpp \
+ GUIDialogFileBrowser.cpp \
+ GUIDialogGamepad.cpp \
+diff --git a/xbmc/epg/Epg.cpp b/xbmc/epg/Epg.cpp
+new file mode 100644
+index 0000000..8569214
+--- /dev/null
++++ b/xbmc/epg/Epg.cpp
+@@ -0,0 +1,999 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "guilib/LocalizeStrings.h"
++#include "settings/AdvancedSettings.h"
++#include "settings/GUISettings.h"
++#include "threads/SingleLock.h"
++#include "utils/log.h"
++#include "utils/TimeUtils.h"
++
++#include "EpgDatabase.h"
++#include "EpgContainer.h"
++#include "pvr/PVRManager.h"
++#include "pvr/addons/PVRClients.h"
++#include "pvr/channels/PVRChannelGroupsContainer.h"
++#include "utils/StringUtils.h"
++
++#include "../addons/include/xbmc_pvr_types.h" // TODO extract the epg specific stuff
++
++using namespace PVR;
++using namespace EPG;
++using namespace std;
++
++CEpg::CEpg(int iEpgID, const CStdString &strName /* = "" */, const CStdString &strScraperName /* = "" */, bool bLoadedFromDb /* = false */) :
++ m_bChanged(!bLoadedFromDb),
++ m_bTagsChanged(false),
++ m_bLoaded(false),
++ m_bUpdatePending(false),
++ m_iEpgID(iEpgID),
++ m_strName(strName),
++ m_strScraperName(strScraperName),
++ m_iPVRChannelId(-1),
++ m_iPVRChannelNumber(-1)
++{
++}
++
++CEpg::CEpg(CPVRChannel *channel, bool bLoadedFromDb /* = false */) :
++ m_bChanged(!bLoadedFromDb),
++ m_bTagsChanged(false),
++ m_bLoaded(false),
++ m_bUpdatePending(false),
++ m_iEpgID(channel->EpgID()),
++ m_strName(channel->ChannelName()),
++ m_strScraperName(channel->EPGScraper()),
++ m_iPVRChannelId(channel->ChannelID()),
++ m_iPVRChannelNumber(channel->ChannelNumber())
++{
++}
++
++CEpg::CEpg(void) :
++ m_bChanged(false),
++ m_bTagsChanged(false),
++ m_bLoaded(false),
++ m_bUpdatePending(false),
++ m_iEpgID(0),
++ m_strName(StringUtils::EmptyString),
++ m_strScraperName(StringUtils::EmptyString),
++ m_iPVRChannelId(-1),
++ m_iPVRChannelNumber(-1)
++{
++}
++
++CEpg::~CEpg(void)
++{
++ Clear();
++}
++
++CEpg &CEpg::operator =(const CEpg &right)
++{
++ m_bChanged = right.m_bChanged;
++ m_bTagsChanged = right.m_bTagsChanged;
++ m_bLoaded = right.m_bLoaded;
++ m_bUpdatePending = right.m_bUpdatePending;
++ m_iEpgID = right.m_iEpgID;
++ m_strName = right.m_strName;
++ m_strScraperName = right.m_strScraperName;
++ m_nowActiveStart = right.m_nowActiveStart;
++ m_lastScanTime = right.m_lastScanTime;
++ m_iPVRChannelId = right.m_iPVRChannelId;
++ m_iPVRChannelNumber = right.m_iPVRChannelNumber;
++
++ for (map<CDateTime, CEpgInfoTag *>::const_iterator it = right.m_tags.begin(); it != right.m_tags.end(); it++)
++ m_tags.insert(make_pair(it->first, new CEpgInfoTag(*it->second)));
++
++ return *this;
++}
++
++/** @name Public methods */
++//@{
++
++void CEpg::SetName(const CStdString &strName)
++{
++ CSingleLock lock(m_critSection);
++
++ if (!m_strName.Equals(strName))
++ {
++ m_bChanged = true;
++ m_strName = strName;
++ }
++}
++
++void CEpg::SetScraperName(const CStdString &strScraperName)
++{
++ CSingleLock lock(m_critSection);
++
++ if (!m_strScraperName.Equals(strScraperName))
++ {
++ m_bChanged = true;
++ m_strScraperName = strScraperName;
++ }
++}
++
++void CEpg::SetUpdatePending(bool bUpdatePending /* = true */)
++{
++ {
++ CSingleLock lock(m_critSection);
++ m_bUpdatePending = bUpdatePending;
++ }
++
++ if (bUpdatePending)
++ g_EpgContainer.SetHasPendingUpdates(true);
++}
++
++void CEpg::ForceUpdate(void)
++{
++ SetUpdatePending();
++}
++
++bool CEpg::HasValidEntries(void) const
++{
++ CSingleLock lock(m_critSection);
++
++ return (m_iEpgID > 0 && /* valid EPG ID */
++ m_tags.size() > 0 && /* contains at least 1 tag */
++ m_tags.rbegin()->second->EndAsUTC() >= CDateTime::GetCurrentDateTime().GetAsUTCDateTime()); /* the last end time hasn't passed yet */
++}
++
++void CEpg::Clear(void)
++{
++ CSingleLock lock(m_critSection);
++
++ for (map<CDateTime, CEpgInfoTag *>::iterator it = m_tags.begin(); it != m_tags.end(); it++)
++ delete it->second;
++ m_tags.clear();
++}
++
++void CEpg::Cleanup(void)
++{
++ CDateTime cleanupTime = CDateTime::GetCurrentDateTime().GetAsUTCDateTime() -
++ CDateTimeSpan(0, g_advancedSettings.m_iEpgLingerTime / 60, g_advancedSettings.m_iEpgLingerTime % 60, 0);
++ Cleanup(cleanupTime);
++}
++
++void CEpg::Cleanup(const CDateTime &Time)
++{
++ bool bTagsChanged(false);
++ CSingleLock lock(m_critSection);
++ for (map<CDateTime, CEpgInfoTag *>::iterator it = m_tags.begin(); it != m_tags.end(); it != m_tags.end() ? it++ : it)
++ {
++ if (it->second->EndAsUTC() < Time)
++ {
++ if (m_nowActiveStart == it->first)
++ m_nowActiveStart.SetValid(false);
++
++ delete it->second;
++ m_tags.erase(it++);
++ bTagsChanged = true;
++ }
++ }
++}
++
++bool CEpg::InfoTagNow(CEpgInfoTag &tag, bool bUpdateIfNeeded /* = true */)
++{
++ CSingleLock lock(m_critSection);
++ if (m_nowActiveStart.IsValid())
++ {
++ map<CDateTime, CEpgInfoTag *>::const_iterator it = m_tags.find(m_nowActiveStart);
++ if (it != m_tags.end() && it->second->IsActive())
++ {
++ tag = *it->second;
++ return true;
++ }
++ }
++
++ if (bUpdateIfNeeded)
++ {
++ CDateTime lastActiveTag;
++
++ /* one of the first items will always match if the list is sorted */
++ for (map<CDateTime, CEpgInfoTag *>::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
++ {
++ if (it->second->IsActive())
++ {
++ m_nowActiveStart = it->first;
++ tag = *it->second;
++ return true;
++ }
++ else if (it->second->WasActive())
++ lastActiveTag = it->first;
++ }
++
++ /* there might be a gap between the last and next event. just return the last if found */
++ map<CDateTime, CEpgInfoTag *>::const_iterator it = m_tags.find(lastActiveTag);
++ if (it != m_tags.end())
++ {
++ tag = *it->second;
++ return true;
++ }
++ }
++
++ return false;
++}
++
++bool CEpg::InfoTagNext(CEpgInfoTag &tag)
++{
++ CEpgInfoTag nowTag;
++ if (InfoTagNow(nowTag))
++ {
++ CSingleLock lock(m_critSection);
++ map<CDateTime, CEpgInfoTag *>::const_iterator it = m_tags.find(nowTag.StartAsUTC());
++ if (it != m_tags.end() && ++it != m_tags.end())
++ {
++ tag = *it->second;
++ return true;
++ }
++ }
++ else if (Size() > 0)
++ {
++ /* return the first event that is in the future */
++ for (map<CDateTime, CEpgInfoTag *>::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
++ {
++ if (it->second->InTheFuture())
++ {
++ tag = *it->second;
++ return true;
++ }
++ }
++ }
++
++ return false;
++}
++
++bool CEpg::CheckPlayingEvent(void)
++{
++ bool bReturn(false);
++ CEpgInfoTag previousTag, newTag;
++ bool bGotPreviousTag = InfoTagNow(previousTag, false);
++ bool bGotCurrentTag = InfoTagNow(newTag);
++
++ if (!bGotPreviousTag || (bGotCurrentTag && previousTag != newTag))
++ {
++ NotifyObservers("epg-current-event");
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++CEpgInfoTag *CEpg::GetTag(int uniqueID, const CDateTime &StartTime) const // TODO remove uid param
++{
++ CEpgInfoTag *returnTag = NULL;
++
++ CSingleLock lock(m_critSection);
++ map<CDateTime, CEpgInfoTag *>::const_iterator it = m_tags.find(StartTime);
++ if (it != m_tags.end())
++ returnTag = it->second;
++
++ return returnTag;
++}
++
++const CEpgInfoTag *CEpg::GetTagBetween(const CDateTime &beginTime, const CDateTime &endTime) const
++{
++ CEpgInfoTag *returnTag = NULL;
++
++ CSingleLock lock(m_critSection);
++
++ for (map<CDateTime, CEpgInfoTag *>::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
++ {
++ if (it->second->StartAsUTC() >= beginTime && it->second->EndAsUTC() <= endTime)
++ {
++ returnTag = it->second;
++ break;
++ }
++ }
++
++ return returnTag;
++}
++
++const CEpgInfoTag *CEpg::GetTagAround(const CDateTime &time) const
++{
++ CEpgInfoTag *returnTag = NULL;
++
++ CSingleLock lock(m_critSection);
++
++ for (map<CDateTime, CEpgInfoTag *>::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
++ {
++ if ((it->second->StartAsUTC() <= time) && (it->second->EndAsUTC() >= time))
++ {
++ returnTag = it->second;
++ break;
++ }
++ }
++
++ return returnTag;
++}
++
++void CEpg::AddEntry(const CEpgInfoTag &tag)
++{
++ CEpgInfoTag *newTag(NULL);
++ {
++ CSingleLock lock(m_critSection);
++ map<CDateTime, CEpgInfoTag*>::iterator itr = m_tags.find(tag.StartAsUTC());
++ if (itr != m_tags.end())
++ newTag = itr->second;
++ else
++ {
++ newTag = new CEpgInfoTag(m_iEpgID, m_iPVRChannelNumber, m_iPVRChannelId, m_strName);
++ m_tags.insert(make_pair(tag.StartAsUTC(), newTag));
++ }
++ }
++
++ if (newTag)
++ {
++ newTag->Update(tag);
++ newTag->m_iEpgId = m_iEpgID;
++ newTag->m_iPVRChannelNumber = m_iPVRChannelNumber;
++ newTag->m_iPVRChannelID = m_iPVRChannelId;
++ newTag->m_strTableName = m_strName;
++ newTag->m_bChanged = false;
++ }
++}
++
++bool CEpg::UpdateEntry(const CEpgInfoTag &tag, bool bUpdateDatabase /* = false */, bool bSort /* = true */)
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ CEpgInfoTag *infoTag(NULL);
++ map<CDateTime, CEpgInfoTag *>::iterator it = m_tags.find(tag.StartAsUTC());
++ bool bNewTag(false);
++ if (it != m_tags.end())
++ {
++ infoTag = it->second;
++ }
++ else
++ {
++ /* create a new tag if no tag with this ID exists */
++ infoTag = new CEpgInfoTag(m_iEpgID, m_iPVRChannelNumber, m_iPVRChannelId, m_strName);
++ infoTag->SetUniqueBroadcastID(tag.UniqueBroadcastID());
++ m_tags.insert(make_pair(tag.StartAsUTC(), infoTag));
++ bNewTag = true;
++ }
++
++ infoTag->Update(tag, bNewTag);
++ infoTag->m_iEpgId = m_iEpgID;
++ infoTag->m_iPVRChannelNumber = m_iPVRChannelNumber;
++ infoTag->m_iPVRChannelID = m_iPVRChannelId;
++ infoTag->m_strTableName = m_strName;
++
++ if (bUpdateDatabase)
++ bReturn = infoTag->Persist();
++ else
++ bReturn = true;
++
++ return bReturn;
++}
++
++bool CEpg::Load(void)
++{
++ bool bReturn(false);
++ CEpgDatabase *database = g_EpgContainer.GetDatabase();
++
++ if (!database || !database->IsOpen())
++ {
++ CLog::Log(LOGERROR, "Epg - %s - could not open the database", __FUNCTION__);
++ return bReturn;
++ }
++
++ CSingleLock lock(m_critSection);
++ int iEntriesLoaded = database->Get(*this);
++ if (iEntriesLoaded <= 0)
++ {
++ CLog::Log(LOGNOTICE, "Epg - %s - no database entries found for table '%s'.",
++ __FUNCTION__, m_strName.c_str());
++ }
++ else
++ {
++ m_lastScanTime = GetLastScanTime();
++ CLog::Log(LOGDEBUG, "Epg - %s - %d entries loaded for table '%s'.",
++ __FUNCTION__, (int) m_tags.size(), m_strName.c_str());
++ bReturn = true;
++ }
++
++ m_bLoaded = true;
++
++ return bReturn;
++}
++
++bool CEpg::UpdateEntries(const CEpg &epg, bool bStoreInDb /* = true */)
++{
++ bool bReturn(false);
++ CEpgDatabase *database = g_EpgContainer.GetDatabase();
++
++ CSingleLock lock(m_critSection);
++
++ if (epg.m_tags.size() > 0)
++ {
++ if (bStoreInDb)
++ {
++ if (!database || !database->IsOpen())
++ {
++ CLog::Log(LOGERROR, "%s - could not open the database", __FUNCTION__);
++ return bReturn;
++ }
++ database->BeginTransaction();
++ }
++ CLog::Log(LOGDEBUG, "%s - %u entries in memory before merging", __FUNCTION__, m_tags.size());
++ /* copy over tags */
++ for (map<CDateTime, CEpgInfoTag *>::const_iterator it = epg.m_tags.begin(); it != epg.m_tags.end(); it++)
++ UpdateEntry(*it->second, bStoreInDb, false);
++
++ CLog::Log(LOGDEBUG, "%s - %u entries in memory after merging and before fixing", __FUNCTION__, m_tags.size());
++ FixOverlappingEvents(bStoreInDb);
++ CLog::Log(LOGDEBUG, "%s - %u entries in memory after fixing", __FUNCTION__, m_tags.size());
++ /* update the last scan time of this table */
++ m_lastScanTime = CDateTime::GetCurrentDateTime().GetAsUTCDateTime();
++
++ //m_bTagsChanged = true;
++ /* persist changes */
++ if (bStoreInDb)
++ {
++ bReturn = database->CommitTransaction();
++ if (bReturn)
++ Persist(true);
++ }
++ else
++ bReturn = true;
++ }
++ else
++ {
++ if (bStoreInDb)
++ bReturn = Persist(true);
++ else
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++CDateTime CEpg::GetLastScanTime(void)
++{
++ CDateTime lastScanTime;
++ {
++ CSingleLock lock(m_critSection);
++
++ if (!m_lastScanTime.IsValid())
++ {
++ if (!g_guiSettings.GetBool("epg.ignoredbforclient"))
++ {
++ CEpgDatabase *database = g_EpgContainer.GetDatabase();
++ CDateTime dtReturn; dtReturn.SetValid(false);
++
++ if (database && database->IsOpen())
++ database->GetLastEpgScanTime(m_iEpgID, &m_lastScanTime);
++ }
++
++ if (!m_lastScanTime.IsValid())
++ {
++ m_lastScanTime.SetDateTime(0, 0, 0, 0, 0, 0);
++ m_lastScanTime.SetValid(true);
++ }
++ }
++ lastScanTime = m_lastScanTime;
++ }
++
++ return m_lastScanTime;
++}
++
++bool CEpg::Update(const time_t start, const time_t end, int iUpdateTime, bool bForceUpdate /* = false */)
++{
++ bool bGrabSuccess(true);
++ bool bUpdate(false);
++
++ /* load the entries from the db first */
++ if (!m_bLoaded && !g_EpgContainer.IgnoreDB())
++ Load();
++
++ /* clean up if needed */
++ if (m_bLoaded)
++ Cleanup();
++
++ /* get the last update time from the database */
++ CDateTime lastScanTime = GetLastScanTime();
++
++ if (!bForceUpdate)
++ {
++ /* check if we have to update */
++ time_t iNow = 0;
++ time_t iLastUpdate = 0;
++ CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(iNow);
++ lastScanTime.GetAsTime(iLastUpdate);
++ bUpdate = (iNow > iLastUpdate + iUpdateTime);
++ }
++ else
++ bUpdate = true;
++
++ if (bUpdate)
++ bGrabSuccess = LoadFromClients(start, end);
++
++ if (bGrabSuccess)
++ {
++ g_PVRManager.ResetPlayingTag();
++ m_bLoaded = true;
++ }
++ else
++ CLog::Log(LOGERROR, "EPG - %s - failed to update table '%s'", __FUNCTION__, Name().c_str());
++
++ CSingleLock lock(m_critSection);
++ m_bUpdatePending = false;
++
++ return bGrabSuccess;
++}
++
++int CEpg::Get(CFileItemList &results) const
++{
++ int iInitialSize = results.Size();
++
++ CSingleLock lock(m_critSection);
++
++ for (map<CDateTime, CEpgInfoTag *>::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
++ {
++ CDateTime localStartTime;
++ localStartTime.SetFromUTCDateTime(it->first);
++
++ CFileItemPtr entry(new CFileItem(*it->second));
++ entry->SetLabel2(localStartTime.GetAsLocalizedDateTime(false, false));
++ results.Add(entry);
++ }
++
++ return results.Size() - iInitialSize;
++}
++
++int CEpg::Get(CFileItemList &results, const EpgSearchFilter &filter) const
++{
++ int iInitialSize = results.Size();
++
++ if (!HasValidEntries())
++ return -1;
++
++ CSingleLock lock(m_critSection);
++
++ for (map<CDateTime, CEpgInfoTag *>::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
++ {
++ if (filter.FilterEntry(*it->second))
++ {
++ CDateTime localStartTime;
++ localStartTime.SetFromUTCDateTime(it->first);
++
++ CFileItemPtr entry(new CFileItem(*it->second));
++ entry->SetLabel2(localStartTime.GetAsLocalizedDateTime(false, false));
++ results.Add(entry);
++ }
++ }
++
++ return results.Size() - iInitialSize;
++}
++
++bool CEpg::Persist(bool bUpdateLastScanTime /* = false */)
++{
++ if (g_guiSettings.GetBool("epg.ignoredbforclient"))
++ return true;
++
++ CEpgDatabase *database = g_EpgContainer.GetDatabase();
++
++ if (!database || !database->IsOpen())
++ {
++ CLog::Log(LOGERROR, "%s - could not open the database", __FUNCTION__);
++ return false;
++ }
++
++ CEpg epgCopy;
++ {
++ CSingleLock lock(m_critSection);
++ epgCopy = *this;
++ m_bChanged = false;
++ m_bTagsChanged = false;
++ }
++
++ database->BeginTransaction();
++
++ if (epgCopy.m_iEpgID <= 0 || epgCopy.m_bChanged)
++ {
++ int iId = database->Persist(epgCopy);
++ if (iId > 0)
++ {
++ epgCopy.m_iEpgID = iId;
++ epgCopy.m_bChanged = false;
++ if (m_iEpgID != epgCopy.m_iEpgID)
++ {
++ CSingleLock lock(m_critSection);
++ m_iEpgID = epgCopy.m_iEpgID;
++ }
++ }
++ }
++
++ bool bReturn(true);
++
++ if (bUpdateLastScanTime)
++ bReturn = database->PersistLastEpgScanTime(epgCopy.m_iEpgID);
++
++ database->CommitTransaction();
++
++ return bReturn;
++}
++
++CDateTime CEpg::GetFirstDate(void) const
++{
++ CDateTime first;
++
++ CSingleLock lock(m_critSection);
++ if (m_tags.size() > 0)
++ first = m_tags.begin()->second->StartAsUTC();
++
++ return first;
++}
++
++CDateTime CEpg::GetLastDate(void) const
++{
++ CDateTime last;
++
++ CSingleLock lock(m_critSection);
++ if (m_tags.size() > 0)
++ last = m_tags.rbegin()->second->StartAsUTC();
++
++ return last;
++}
++
++//@}
++
++/** @name Protected methods */
++//@{
++
++bool CEpg::UpdateMetadata(const CEpg &epg, bool bUpdateDb /* = false */)
++{
++ bool bReturn = true;
++ CSingleLock lock(m_critSection);
++
++ m_strName = epg.m_strName;
++ m_strScraperName = epg.m_strScraperName;
++ if (m_iPVRChannelId == -1 || epg.m_iPVRChannelId != -1)
++ {
++ m_iPVRChannelId = epg.m_iPVRChannelId;
++ m_iPVRChannelNumber = epg.m_iPVRChannelNumber;
++
++ /* Copy the new channel information to all child tags */
++ for (map<CDateTime, CEpgInfoTag *>::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
++ {
++ it->second->SetPVRChannelID(m_iPVRChannelId);
++ it->second->SetPVRChannelNumber(m_iPVRChannelNumber);
++ }
++ }
++
++ if (bUpdateDb)
++ bReturn = Persist();
++
++ return bReturn;
++}
++
++//@}
++
++/** @name Private methods */
++//@{
++
++bool CEpg::FixOverlappingEvents(bool bUpdateDb /* = false */)
++{
++ bool bReturn(true);
++ CEpgInfoTag *previousTag(NULL), *currentTag(NULL);
++ CEpgDatabase *database(NULL);
++ if (bUpdateDb)
++ {
++ database = g_EpgContainer.GetDatabase();
++ if (!database || !database->IsOpen())
++ {
++ CLog::Log(LOGERROR, "%s - could not open the database", __FUNCTION__);
++ return false;
++ }
++ }
++
++ for (map<CDateTime, CEpgInfoTag *>::iterator it = m_tags.begin(); it != m_tags.end(); it != m_tags.end() ? it++ : it)
++ {
++ if (!previousTag)
++ {
++ previousTag = it->second;
++ continue;
++ }
++ currentTag = it->second;
++
++ if (previousTag->EndAsUTC() >= currentTag->EndAsUTC())
++ {
++ // delete the current tag. it's completely overlapped
++ if (bUpdateDb)
++ bReturn &= database->Delete(*currentTag);
++
++ if (m_nowActiveStart == it->first)
++ m_nowActiveStart.SetValid(false);
++
++ delete currentTag;
++ m_tags.erase(it++);
++ }
++ else if (previousTag->EndAsUTC() > currentTag->StartAsUTC())
++ {
++ currentTag->SetStartFromUTC(previousTag->EndAsUTC());
++ if (bUpdateDb)
++ bReturn &= currentTag->Persist();
++
++ previousTag = it->second;
++ }
++ else if (previousTag->EndAsUTC() < currentTag->StartAsUTC())
++ {
++ time_t start, end, middle;
++ previousTag->EndAsUTC().GetAsTime(start);
++ currentTag->StartAsUTC().GetAsTime(end);
++ middle = start + ((end - start) / 2);
++ CDateTime newTime(middle);
++
++ currentTag->SetStartFromUTC(newTime);
++ previousTag->SetEndFromUTC(newTime);
++
++ if (m_nowActiveStart == it->first)
++ m_nowActiveStart = currentTag->StartAsUTC();
++
++ if (bUpdateDb)
++ {
++ bReturn &= currentTag->Persist();
++ bReturn &= previousTag->Persist();
++ }
++
++ previousTag = it->second;
++ }
++ else
++ {
++ previousTag = it->second;
++ }
++ }
++
++ return bReturn;
++}
++
++bool CEpg::UpdateFromScraper(time_t start, time_t end)
++{
++ bool bGrabSuccess = false;
++ if (ScraperName() == "client")
++ {
++ CPVRChannel *channel = Channel();
++ if (!channel)
++ CLog::Log(LOGINFO, "%s - channel not found, can't update", __FUNCTION__);
++ else if (!channel->EPGEnabled())
++ CLog::Log(LOGINFO, "%s - EPG updating disabled in the channel configuration", __FUNCTION__);
++ else if (!g_PVRClients->GetAddonCapabilities(channel->ClientID()).bSupportsEPG)
++ CLog::Log(LOGINFO, "%s - the backend for channel '%s' on client '%i' does not support EPGs", __FUNCTION__, channel->ChannelName().c_str(), channel->ClientID());
++ else
++ {
++ CLog::Log(LOGINFO, "%s - updating EPG for channel '%s' from client '%i'", __FUNCTION__, channel->ChannelName().c_str(), channel->ClientID());
++ PVR_ERROR error;
++ g_PVRClients->GetEPGForChannel(*channel, this, start, end, &error);
++ bGrabSuccess = error == PVR_ERROR_NO_ERROR;
++ }
++ }
++ else if (m_strScraperName.IsEmpty()) /* no grabber defined */
++ CLog::Log(LOGERROR, "EPG - %s - no EPG scraper defined for table '%s'", __FUNCTION__, m_strName.c_str());
++ else
++ {
++ CLog::Log(LOGINFO, "EPG - %s - updating EPG table '%s' with scraper '%s'", __FUNCTION__, m_strName.c_str(), m_strScraperName.c_str());
++ CLog::Log(LOGERROR, "loading the EPG via scraper has not been implemented yet");
++ // TODO: Add Support for Web EPG Scrapers here
++ }
++
++ return bGrabSuccess;
++}
++
++bool CEpg::PersistTags(void) const
++{
++ bool bReturn = false;
++ CEpgDatabase *database = g_EpgContainer.GetDatabase();
++
++ if (!database || !database->IsOpen())
++ {
++ CLog::Log(LOGERROR, "EPG - %s - could not load the database", __FUNCTION__);
++ return bReturn;
++ }
++
++ CDateTime first = GetFirstDate();
++ CDateTime last = GetLastDate();
++
++ time_t iStart(0), iEnd(0);
++ if (first.IsValid())
++ first.GetAsTime(iStart);
++ if (last.IsValid())
++ last.GetAsTime(iEnd);
++ database->Delete(*this, iStart, iEnd);
++
++ if (m_tags.size() > 0)
++ {
++ for (map<CDateTime, CEpgInfoTag *>::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
++ {
++ if (!it->second->Persist())
++ {
++ CLog::Log(LOGERROR, "failed to persist epg tag %d", it->second->UniqueBroadcastID());
++ bReturn = false;
++ }
++ }
++ }
++ else
++ {
++ /* Return true if we have no tags, so that no error is logged */
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++//@}
++
++const CStdString &CEpg::ConvertGenreIdToString(int iID, int iSubID)
++{
++ unsigned int iLabelId = 19499;
++ switch (iID)
++ {
++ case EPG_EVENT_CONTENTMASK_MOVIEDRAMA:
++ iLabelId = (iSubID <= 8) ? 19500 + iSubID : 19500;
++ break;
++ case EPG_EVENT_CONTENTMASK_NEWSCURRENTAFFAIRS:
++ iLabelId = (iSubID <= 4) ? 19516 + iSubID : 19516;
++ break;
++ case EPG_EVENT_CONTENTMASK_SHOW:
++ iLabelId = (iSubID <= 3) ? 19532 + iSubID : 19532;
++ break;
++ case EPG_EVENT_CONTENTMASK_SPORTS:
++ iLabelId = (iSubID <= 11) ? 19548 + iSubID : 19548;
++ break;
++ case EPG_EVENT_CONTENTMASK_CHILDRENYOUTH:
++ iLabelId = (iSubID <= 5) ? 19564 + iSubID : 19564;
++ break;
++ case EPG_EVENT_CONTENTMASK_MUSICBALLETDANCE:
++ iLabelId = (iSubID <= 6) ? 19580 + iSubID : 19580;
++ break;
++ case EPG_EVENT_CONTENTMASK_ARTSCULTURE:
++ iLabelId = (iSubID <= 11) ? 19596 + iSubID : 19596;
++ break;
++ case EPG_EVENT_CONTENTMASK_SOCIALPOLITICALECONOMICS:
++ iLabelId = (iSubID <= 3) ? 19612 + iSubID : 19612;
++ break;
++ case EPG_EVENT_CONTENTMASK_EDUCATIONALSCIENCE:
++ iLabelId = (iSubID <= 7) ? 19628 + iSubID : 19628;
++ break;
++ case EPG_EVENT_CONTENTMASK_LEISUREHOBBIES:
++ iLabelId = (iSubID <= 7) ? 19644 + iSubID : 19644;
++ break;
++ case EPG_EVENT_CONTENTMASK_SPECIAL:
++ iLabelId = (iSubID <= 3) ? 19660 + iSubID : 19660;
++ break;
++ case EPG_EVENT_CONTENTMASK_USERDEFINED:
++ iLabelId = (iSubID <= 3) ? 19676 + iSubID : 19676;
++ break;
++ default:
++ break;
++ }
++
++ return g_localizeStrings.Get(iLabelId);
++}
++
++bool CEpg::UpdateEntry(const EPG_TAG *data, bool bUpdateDatabase /* = false */)
++{
++ if (!data)
++ return false;
++
++ CEpgInfoTag tag(*data);
++ return UpdateEntry(tag, bUpdateDatabase);
++}
++
++bool CEpg::IsRadio(void) const
++{
++ CPVRChannel *channel = Channel();
++ return channel ? channel->IsRadio() : false;
++}
++
++bool CEpg::IsRemovableTag(const CEpgInfoTag *tag) const
++{
++ return (!tag || !tag->HasTimer());
++}
++
++bool CEpg::LoadFromClients(time_t start, time_t end)
++{
++ bool bReturn(false);
++ CPVRChannel *channel = Channel();
++ if (channel)
++ {
++ CEpg tmpEpg(channel);
++ if (tmpEpg.UpdateFromScraper(start, end))
++ bReturn = UpdateEntries(tmpEpg, !g_guiSettings.GetBool("epg.ignoredbforclient"));
++ }
++ else
++ {
++ CEpg tmpEpg(m_iEpgID, m_strName, m_strScraperName);
++ if (tmpEpg.UpdateFromScraper(start, end))
++ bReturn = UpdateEntries(tmpEpg, !g_guiSettings.GetBool("epg.ignoredbforclient"));
++ }
++
++ return bReturn;
++}
++
++const CEpgInfoTag *CEpg::GetNextEvent(const CEpgInfoTag& tag) const
++{
++ CSingleLock lock(m_critSection);
++ map<CDateTime, CEpgInfoTag *>::const_iterator it = m_tags.find(tag.StartAsUTC());
++ if (it != m_tags.end() && ++it != m_tags.end())
++ return it->second;
++ return NULL;
++}
++
++const CEpgInfoTag *CEpg::GetPreviousEvent(const CEpgInfoTag& tag) const
++{
++ CSingleLock lock(m_critSection);
++ map<CDateTime, CEpgInfoTag *>::const_iterator it = m_tags.find(tag.StartAsUTC());
++ if (it != m_tags.end() && it != m_tags.begin())
++ {
++ it--;
++ return it->second;
++ }
++ return NULL;
++}
++
++CPVRChannel *CEpg::Channel(void) const
++{
++ int iChannelId(-1);
++ {
++ CSingleLock lock(m_critSection);
++ iChannelId = m_iPVRChannelId;
++ }
++
++ if (iChannelId != -1 && g_PVRManager.IsStarted())
++ return g_PVRChannelGroups->GetByChannelIDFromAll(iChannelId);
++
++ return NULL;
++}
++
++int CEpg::ChannelID(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_iPVRChannelId;
++}
++
++int CEpg::ChannelNumber(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_iPVRChannelNumber;
++}
++
++void CEpg::SetChannel(PVR::CPVRChannel *channel)
++{
++ CSingleLock lock(m_critSection);
++ m_iPVRChannelId = channel->ChannelID();
++ m_iPVRChannelNumber = channel->ChannelNumber();
++ for (map<CDateTime, CEpgInfoTag *>::iterator it = m_tags.begin(); it != m_tags.end(); it++)
++ {
++ it->second->m_iPVRChannelID = m_iPVRChannelId;
++ it->second->m_iPVRChannelNumber = m_iPVRChannelNumber;
++ }
++}
++
++bool CEpg::HasPVRChannel(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_iPVRChannelId != -1;
++}
++
++bool CEpg::UpdatePending(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_bUpdatePending;
++}
+diff --git a/xbmc/epg/Epg.h b/xbmc/epg/Epg.h
+new file mode 100644
+index 0000000..11ffe48
+--- /dev/null
++++ b/xbmc/epg/Epg.h
+@@ -0,0 +1,367 @@
++#pragma once
++
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "FileItem.h"
++
++#include "threads/CriticalSection.h"
++
++#include "EpgInfoTag.h"
++#include "EpgSearchFilter.h"
++#include "utils/Observer.h"
++
++namespace PVR
++{
++ class CPVRChannel;
++}
++
++/** EPG container for CEpgInfoTag instances */
++namespace EPG
++{
++ class CEpg : public Observable
++ {
++ friend class CEpgDatabase;
++
++ public:
++ /*!
++ * @brief Create a new EPG instance.
++ * @param iEpgID The ID of this table or <= 0 to create a new ID.
++ * @param strName The name of this table.
++ * @param strScraperName The name of the scraper to use.
++ * @param bLoadedFromDb True if this table was loaded from the database, false otherwise.
++ */
++ CEpg(int iEpgID, const CStdString &strName = "", const CStdString &strScraperName = "", bool bLoadedFromDb = false);
++
++ /*!
++ * @brief Create a new EPG instance for a channel.
++ * @param channel The channel to create the EPG for.
++ * @param bLoadedFromDb True if this table was loaded from the database, false otherwise.
++ */
++ CEpg(PVR::CPVRChannel *channel, bool bLoadedFromDb = false);
++
++ /*!
++ * @brief Destroy this EPG instance.
++ */
++ virtual ~CEpg(void);
++
++ CEpg &operator =(const CEpg &right);
++
++ /*!
++ * @brief Update this table's info with the given info. Doesn't change the EpgID.
++ * @param epg The new info.
++ * @param bUpdateDb If true, persist the changes.
++ * @return True if the update was successful, false otherwise.
++ */
++ virtual bool UpdateMetadata(const CEpg &epg, bool bUpdateDb = false);
++
++ /*!
++ * @brief Load all entries for this table from the database.
++ * @return True if any entries were loaded, false otherwise.
++ */
++ virtual bool Load(void);
++
++ /*!
++ * @brief The channel this EPG belongs to.
++ * @return The channel this EPG belongs to
++ */
++ virtual PVR::CPVRChannel *Channel(void) const;
++
++ virtual int ChannelID(void) const;
++ virtual int ChannelNumber(void) const;
++
++ /*!
++ * @brief Channel the channel tag linked to this EPG table.
++ * @param channel The new channel tag.
++ */
++ virtual void SetChannel(PVR::CPVRChannel *channel);
++
++ /*!
++ * @brief Get the name of the scraper to use for this table.
++ * @return The name of the scraper to use for this table.
++ */
++ virtual const CStdString &ScraperName(void) const { return m_strScraperName; }
++
++ /*!
++ * @brief Change the name of the scraper to use.
++ * @param strScraperName The new scraper.
++ */
++ virtual void SetScraperName(const CStdString &strScraperName);
++
++ /*!
++ * @brief Specify if EPG should be manually updated on the next cycle
++ * @param bUpdatePending True if EPG should be manually updated
++ */
++ virtual void SetUpdatePending(bool bUpdatePending = true);
++
++ /*!
++ * @brief Returns if there is a manual update pending for this EPG
++ * @returns True if there are is a manual update pending, false otherwise
++ */
++ virtual bool UpdatePending(void) const;
++
++ /*!
++ * @brief Clear the current tag and schedule manual update
++ */
++ virtual void ForceUpdate(void);
++
++ /*!
++ * @brief Get the name of this table.
++ * @return The name of this table.
++ */
++ virtual const CStdString &Name(void) const { return m_strName; }
++
++ /*!
++ * @brief Changed the name of this table.
++ * @param strName The new name.
++ */
++ virtual void SetName(const CStdString &strName);
++
++ /*!
++ * @brief Get the database ID of this table.
++ * @return The database ID of this table.
++ */
++ virtual int EpgID(void) const { return m_iEpgID; }
++
++ /*!
++ * @brief Check whether this EPG contains valid entries.
++ * @return True if it has valid entries, false if not.
++ */
++ virtual bool HasValidEntries(void) const;
++
++ /*!
++ * @return True if this EPG has a PVR channel set, false otherwise.
++ */
++ virtual bool HasPVRChannel(void) const;
++
++ /*!
++ * @brief Remove all entries from this EPG that finished before the given time
++ * and that have no timers set.
++ * @param Time Delete entries with an end time before this time in UTC.
++ */
++ virtual void Cleanup(const CDateTime &Time);
++
++ /*!
++ * @brief Remove all entries from this EPG that finished before the given time
++ * and that have no timers set.
++ */
++ virtual void Cleanup(void);
++
++ /*!
++ * @brief Remove all entries from this EPG.
++ */
++ virtual void Clear(void);
++
++ /*!
++ * @brief Get the event that is occurring now.
++ * @return The current event.
++ */
++ virtual bool InfoTagNow(CEpgInfoTag &tag, bool bUpdateIfNeeded = true);
++
++ /*!
++ * @brief Get the event that will occur next.
++ * @return The next event.
++ */
++ virtual bool InfoTagNext(CEpgInfoTag &tag);
++
++ /*!
++ * @brief Get the event that occurs at the given time.
++ * @param time The time in UTC to find the event for.
++ * @return The found tag or NULL if it wasn't found.
++ */
++ virtual const CEpgInfoTag *GetTagAround(const CDateTime &time) const;
++
++ /*!
++ * Get the event that occurs between the given begin and end time.
++ * @param beginTime Minimum start time in UTC of the event.
++ * @param endTime Maximum end time in UTC of the event.
++ * @return The found tag or NULL if it wasn't found.
++ */
++ virtual const CEpgInfoTag *GetTagBetween(const CDateTime &beginTime, const CDateTime &endTime) const;
++
++ /*!
++ * @brief Get the infotag with the given ID.
++ *
++ * Get the infotag with the given ID.
++ * If it wasn't found, try finding the event with the given start time
++ *
++ * @param uniqueID The unique ID of the event to find.
++ * @param beginTime The start time in UTC of the event to find if it wasn't found by it's unique ID.
++ * @return The found tag or NULL if it wasn't found.
++ */
++ virtual CEpgInfoTag *GetTag(int uniqueID, const CDateTime &beginTime) const;
++
++ /*!
++ * @brief Update an entry in this EPG.
++ * @param tag The tag to update.
++ * @param bUpdateDatabase If set to true, this event will be persisted in the database.
++ * @param bSort If set to false, epg entries will not be sorted after updating; used for mass updates
++ * @return True if it was updated successfully, false otherwise.
++ */
++ virtual bool UpdateEntry(const CEpgInfoTag &tag, bool bUpdateDatabase = false, bool bSort = true);
++
++ /*!
++ * @brief Update the EPG from 'start' till 'end'.
++ * @param start The start time.
++ * @param end The end time.
++ * @param iUpdateTime Update the table after the given amount of time has passed.
++ * @param bForceUpdate Force update from client even if it's not the time to
++ * @return True if the update was successful, false otherwise.
++ */
++ virtual bool Update(const time_t start, const time_t end, int iUpdateTime, bool bForceUpdate = false);
++
++ /*!
++ * @brief Get all EPG entries.
++ * @param results The file list to store the results in.
++ * @return The amount of entries that were added.
++ */
++ virtual int Get(CFileItemList &results) const;
++
++ /*!
++ * @brief Get all EPG entries that and apply a filter.
++ * @param results The file list to store the results in.
++ * @param filter The filter to apply.
++ * @return The amount of entries that were added.
++ */
++ virtual int Get(CFileItemList &results, const EpgSearchFilter &filter) const;
++
++ /*!
++ * @brief Persist this table in the database.
++ * @param bUpdateLastScanTime True to update the last scan time in the db, false otherwise.
++ * @return True if the table was persisted, false otherwise.
++ */
++ virtual bool Persist(bool bUpdateLastScanTime = false);
++
++ /*!
++ * @brief Get the start time of the first entry in this table.
++ * @return The first date in UTC.
++ */
++ virtual CDateTime GetFirstDate(void) const;
++
++ /*!
++ * @brief Get the end time of the last entry in this table.
++ * @return The last date in UTC.
++ */
++ virtual CDateTime GetLastDate(void) const;
++
++ /*!
++ * @return The last time this table was scanned.
++ */
++ virtual CDateTime GetLastScanTime(void);
++
++ /*!
++ * @brief Notify observers when the currently active tag changed.
++ */
++ virtual bool CheckPlayingEvent(void);
++
++ /*!
++ * @brief Convert a genre id and subid to a human readable name.
++ * @param iID The genre ID.
++ * @param iSubID The genre sub ID.
++ * @return A human readable name.
++ */
++ static const CStdString &ConvertGenreIdToString(int iID, int iSubID);
++
++ /*!
++ * @brief Update an entry in this EPG.
++ * @param data The tag to update.
++ * @param bUpdateDatabase If set to true, this event will be persisted in the database.
++ * @return True if it was updated successfully, false otherwise.
++ */
++ virtual bool UpdateEntry(const EPG_TAG *data, bool bUpdateDatabase = false);
++
++ /*!
++ * @return True if this is an EPG table for a radio channel, false otherwise.
++ */
++ virtual bool IsRadio(void) const;
++
++ virtual const CEpgInfoTag *GetNextEvent(const CEpgInfoTag& tag) const;
++ virtual const CEpgInfoTag *GetPreviousEvent(const CEpgInfoTag& tag) const;
++
++ virtual size_t Size(void) const { return m_tags.size(); }
++
++ protected:
++ CEpg(void);
++
++ /*!
++ * @brief Update the EPG from a scraper set in the channel tag.
++ * TODO: not implemented yet for non-pvr EPGs
++ * @param start Get entries with a start date after this time.
++ * @param end Get entries with an end date before this time.
++ * @return True if the update was successful, false otherwise.
++ */
++ virtual bool UpdateFromScraper(time_t start, time_t end);
++
++ /*!
++ * @brief Persist all tags in this container.
++ * @return True if all tags were persisted, false otherwise.
++ */
++ virtual bool PersistTags(void) const;
++
++ /*!
++ * @brief Fix overlapping events from the tables.
++ * @param bUpdateDb If set to yes, any changes to tags during fixing will be persisted to database
++ * @return True if anything changed, false otherwise.
++ */
++ virtual bool FixOverlappingEvents(bool bUpdateDb = false);
++
++ /*!
++ * @brief Add an infotag to this container.
++ * @param tag The tag to add.
++ */
++ virtual void AddEntry(const CEpgInfoTag &tag);
++
++ /*!
++ * @brief Load all EPG entries from clients into a temporary table and update this table with the contents of that temporary table.
++ * @param start Only get entries after this start time. Use 0 to get all entries before "end".
++ * @param end Only get entries before this end time. Use 0 to get all entries after "begin". If both "begin" and "end" are 0, all entries will be updated.
++ * @return True if the update was successful, false otherwise.
++ */
++ virtual bool LoadFromClients(time_t start, time_t end);
++
++ /*!
++ * @brief Update the contents of this table with the contents provided in "epg"
++ * @param epg The updated contents.
++ * @param bStoreInDb True to store the updated contents in the db, false otherwise.
++ * @return True if the update was successful, false otherwise.
++ */
++ virtual bool UpdateEntries(const CEpg &epg, bool bStoreInDb = true);
++
++ virtual bool IsRemovableTag(const EPG::CEpgInfoTag *tag) const;
++
++ std::map<CDateTime, CEpgInfoTag*> m_tags;
++ bool m_bChanged; /*!< true if anything changed that needs to be persisted, false otherwise */
++ bool m_bTagsChanged; /*!< true when any tags are changed and not persisted, false otherwise */
++ bool m_bLoaded; /*!< true when the initial entries have been loaded */
++ bool m_bUpdatePending; /*!< true if manual update is pending */
++ int m_iEpgID; /*!< the database ID of this table */
++ CStdString m_strName; /*!< the name of this table */
++ CStdString m_strScraperName; /*!< the name of the scraper to use */
++ CDateTime m_nowActiveStart; /*!< the start time of the tag that is currently active */
++
++ CDateTime m_lastScanTime; /*!< the last time the EPG has been updated */
++
++ int m_iPVRChannelId; /*!< the channel this EPG belongs to */
++ int m_iPVRChannelNumber; /*!< the channel number in the "all channels" group. set on create and not updated */
++
++ CCriticalSection m_critSection; /*!< critical section for changes in this table */
++ };
++}
+diff --git a/xbmc/epg/EpgContainer.cpp b/xbmc/epg/EpgContainer.cpp
+new file mode 100644
+index 0000000..770e7e4
+--- /dev/null
++++ b/xbmc/epg/EpgContainer.cpp
+@@ -0,0 +1,624 @@
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "Application.h"
++#include "threads/SingleLock.h"
++#include "settings/AdvancedSettings.h"
++#include "settings/GUISettings.h"
++#include "dialogs/GUIDialogExtendedProgressBar.h"
++#include "dialogs/GUIDialogProgress.h"
++#include "guilib/GUIWindowManager.h"
++#include "guilib/LocalizeStrings.h"
++#include "utils/log.h"
++#include "pvr/PVRManager.h"
++#include "pvr/channels/PVRChannelGroupsContainer.h"
++#include "pvr/timers/PVRTimers.h"
++
++#include "EpgContainer.h"
++#include "Epg.h"
++#include "EpgInfoTag.h"
++#include "EpgSearchFilter.h"
++
++using namespace std;
++using namespace EPG;
++using namespace PVR;
++
++typedef std::map<int, CEpg*>::iterator EPGITR;
++
++CEpgContainer::CEpgContainer(void) :
++ CThread("EPG updater")
++{
++ m_progressDialog = NULL;
++ m_bStop = true;
++ m_bIsUpdating = false;
++ m_bIsInitialising = true;
++ m_iNextEpgId = 0;
++ m_bPreventUpdates = false;
++ m_updateEvent.Reset();
++ m_bLoaded = false;
++ m_bHasPendingUpdates = false;
++
++ m_database.Open();
++}
++
++CEpgContainer::~CEpgContainer(void)
++{
++ Unload();
++ m_database.Close();
++}
++
++CEpgContainer &CEpgContainer::Get(void)
++{
++ static CEpgContainer epgInstance;
++ return epgInstance;
++}
++
++void CEpgContainer::Unload(void)
++{
++ Stop();
++ Clear(false);
++}
++
++unsigned int CEpgContainer::NextEpgId(void)
++{
++ CSingleLock lock(m_critSection);
++ return ++m_iNextEpgId;
++}
++
++void CEpgContainer::Clear(bool bClearDb /* = false */)
++{
++ /* make sure the update thread is stopped */
++ bool bThreadRunning = !m_bStop;
++ if (bThreadRunning && !Stop())
++ {
++ CLog::Log(LOGERROR, "%s - cannot stop the update thread", __FUNCTION__);
++ return;
++ }
++
++ {
++ CSingleLock lock(m_critSection);
++ /* clear all epg tables and remove pointers to epg tables on channels */
++ for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++)
++ delete it->second;
++ m_epgs.clear();
++ m_iNextEpgUpdate = 0;
++ m_bIsInitialising = true;
++ }
++
++ /* clear the database entries */
++ if (bClearDb && !m_bIgnoreDbForClient)
++ {
++ if (m_database.IsOpen())
++ m_database.DeleteEpg();
++ }
++
++ SetChanged();
++ NotifyObservers("epg", true);
++
++ if (bThreadRunning)
++ Start();
++}
++
++void CEpgContainer::Start(void)
++{
++ CSingleLock lock(m_critSection);
++
++ m_bIsInitialising = true;
++ m_bStop = false;
++ g_guiSettings.RegisterObserver(this);
++ LoadSettings();
++
++ m_iNextEpgUpdate = 0;
++ m_iNextEpgActiveTagCheck = 0;
++
++ Create();
++ SetPriority(-1);
++ CLog::Log(LOGNOTICE, "%s - EPG thread started", __FUNCTION__);
++}
++
++bool CEpgContainer::Stop(void)
++{
++ StopThread();
++ return true;
++}
++
++void CEpgContainer::Notify(const Observable &obs, const CStdString& msg)
++{
++ /* settings were updated */
++ if (msg == "settings")
++ LoadSettings();
++}
++
++void CEpgContainer::LoadFromDB(void)
++{
++ bool bLoaded(true);
++ unsigned int iCounter(0);
++ if (!m_bIgnoreDbForClient && m_database.IsOpen())
++ {
++ ShowProgressDialog(false);
++
++ m_database.DeleteOldEpgEntries();
++ m_database.Get(*this);
++
++ for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++)
++ {
++ if (InterruptUpdate())
++ {
++ bLoaded = false;
++ break;
++ }
++ UpdateProgressDialog(++iCounter, m_epgs.size(), it->second->Name());
++ it->second->Load();
++ }
++
++ CloseProgressDialog();
++ }
++
++ CSingleLock lock(m_critSection);
++ m_bLoaded = bLoaded;
++}
++
++bool CEpgContainer::PersistTables(void)
++{
++ return m_database.Persist(*this);
++}
++
++bool CEpgContainer::PersistAll(void)
++{
++ bool bReturn(true);
++ CSingleLock lock(m_critSection);
++ for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++)
++ {
++ CEpg *epg = it->second;
++ lock.Leave();
++ if (epg)
++ bReturn &= epg->Persist(false);
++ lock.Enter();
++ }
++
++ return bReturn;
++}
++
++void CEpgContainer::Process(void)
++{
++ time_t iNow = 0;
++
++ bool bUpdateEpg(true);
++ bool bHasPendingUpdates(false);
++
++ if (!m_bLoaded)
++ {
++ CSingleLock lock(m_critSection);
++ LoadFromDB();
++ CheckPlayingEvents();
++ }
++
++ while (!m_bStop && !g_application.m_bStop)
++ {
++ CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(iNow);
++ {
++ CSingleLock lock(m_critSection);
++ bUpdateEpg = (iNow >= m_iNextEpgUpdate);
++ }
++
++ /* update the EPG */
++ if (!InterruptUpdate() && bUpdateEpg && UpdateEPG())
++ m_bIsInitialising = false;
++
++ /* clean up old entries */
++ if (!m_bStop && iNow >= m_iLastEpgCleanup)
++ RemoveOldEntries();
++
++ /* check for pending manual EPG updates */
++ if (!m_bStop)
++ {
++ {
++ CSingleLock lock(m_critSection);
++ bHasPendingUpdates = m_bHasPendingUpdates;
++ }
++
++ if (bHasPendingUpdates)
++ UpdateEPG(true);
++ }
++
++ /* check for updated active tag */
++ if (!m_bStop)
++ CheckPlayingEvents();
++
++ Sleep(1000);
++ }
++
++ g_guiSettings.UnregisterObserver(this);
++}
++
++CEpg *CEpgContainer::GetById(int iEpgId) const
++{
++ if (iEpgId < 0)
++ return 0;
++
++ CSingleLock lock(m_critSection);
++ map<unsigned int, CEpg *>::const_iterator it = m_epgs.find((unsigned int) iEpgId);
++ return it != m_epgs.end() ? it->second : NULL;
++}
++
++CEpg *CEpgContainer::GetByChannel(const CPVRChannel &channel) const
++{
++ CSingleLock lock(m_critSection);
++ for (map<unsigned int, CEpg *>::const_iterator it = m_epgs.begin(); it != m_epgs.end(); it++)
++ if (channel.ChannelID() == it->second->ChannelID())
++ return it->second;
++
++ return NULL;
++}
++
++bool CEpgContainer::UpdateEntry(const CEpg &entry, bool bUpdateDatabase /* = false */)
++{
++ CEpg *epg(NULL);
++ bool bReturn(false);
++ WaitForUpdateFinish(true);
++
++ CSingleLock lock(m_critSection);
++ epg = entry.EpgID() > 0 ? GetById(entry.EpgID()) : NULL;
++ if (!epg)
++ {
++ /* table does not exist yet, create a new one */
++ unsigned int iEpgId = m_bIgnoreDbForClient || entry.EpgID() <= 0 ? NextEpgId() : entry.EpgID();
++ epg = CreateEpg(iEpgId);
++ if (epg)
++ {
++ bReturn = epg->UpdateMetadata(entry, bUpdateDatabase);
++ m_epgs.insert(make_pair((unsigned int)epg->EpgID(), epg));
++ }
++ }
++ else
++ {
++ bReturn = epg->UpdateMetadata(entry, bUpdateDatabase);
++ }
++
++ m_bPreventUpdates = false;
++ CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(m_iNextEpgUpdate);
++
++ return bReturn;
++}
++
++bool CEpgContainer::LoadSettings(void)
++{
++ m_bIgnoreDbForClient = g_guiSettings.GetBool("epg.ignoredbforclient");
++ m_iUpdateTime = g_guiSettings.GetInt ("epg.epgupdate") * 60;
++ m_iDisplayTime = g_guiSettings.GetInt ("epg.daystodisplay") * 24 * 60 * 60;
++
++ return true;
++}
++
++bool CEpgContainer::RemoveOldEntries(void)
++{
++ CLog::Log(LOGINFO, "EpgContainer - %s - removing old EPG entries",
++ __FUNCTION__);
++
++ CDateTime now = CDateTime::GetUTCDateTime() -
++ CDateTimeSpan(0, g_advancedSettings.m_iEpgLingerTime / 60, g_advancedSettings.m_iEpgLingerTime % 60, 0);
++
++ /* call Cleanup() on all known EPG tables */
++ for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++)
++ it->second->Cleanup(now);
++
++ /* remove the old entries from the database */
++ if (!m_bIgnoreDbForClient && m_database.IsOpen())
++ m_database.DeleteOldEpgEntries();
++
++ CSingleLock lock(m_critSection);
++ CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(m_iLastEpgCleanup);
++ m_iLastEpgCleanup += g_advancedSettings.m_iEpgCleanupInterval;
++
++ return true;
++}
++
++CEpg *CEpgContainer::CreateEpg(int iEpgId)
++{
++ if (g_PVRManager.IsStarted())
++ {
++ CPVRChannel *channel = g_PVRChannelGroups->GetChannelByEpgId(iEpgId);
++ if (channel)
++ {
++ CEpg *epg = new CEpg(channel, true);
++ channel->Persist();
++ return epg;
++ }
++ }
++
++ return new CEpg(iEpgId);
++}
++
++bool CEpgContainer::DeleteEpg(const CEpg &epg, bool bDeleteFromDatabase /* = false */)
++{
++ if (epg.EpgID() < 0)
++ return false;
++
++ CSingleLock lock(m_critSection);
++
++ map<unsigned int, CEpg *>::iterator it = m_epgs.find((unsigned int)epg.EpgID());
++ if (it == m_epgs.end())
++ return false;
++
++ if (bDeleteFromDatabase && !m_bIgnoreDbForClient && m_database.IsOpen())
++ m_database.Delete(*it->second);
++
++ delete it->second;
++ m_epgs.erase(it);
++
++ return true;
++}
++
++void CEpgContainer::CloseProgressDialog(void)
++{
++ if (m_progressDialog)
++ {
++ m_progressDialog->Close(true, 0, true, false);
++ m_progressDialog = NULL;
++ }
++}
++
++void CEpgContainer::ShowProgressDialog(bool bUpdating /* = true */)
++{
++ if (!m_progressDialog)
++ {
++ m_progressDialog = (CGUIDialogExtendedProgressBar *)g_windowManager.GetWindow(WINDOW_DIALOG_EXT_PROGRESS);
++ m_progressDialog->Show();
++ m_progressDialog->SetHeader(bUpdating ? g_localizeStrings.Get(19004) : g_localizeStrings.Get(19250));
++ }
++}
++
++void CEpgContainer::UpdateProgressDialog(int iCurrent, int iMax, const CStdString &strText)
++{
++ if (!m_progressDialog)
++ ShowProgressDialog();
++
++ if (m_progressDialog)
++ {
++ m_progressDialog->SetProgress(iCurrent, iMax);
++ m_progressDialog->SetTitle(strText);
++ m_progressDialog->UpdateState();
++ }
++}
++
++bool CEpgContainer::InterruptUpdate(void) const
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++ bReturn = g_application.m_bStop || m_bStop || m_bPreventUpdates;
++ lock.Leave();
++
++ return bReturn ||
++ (g_guiSettings.GetBool("epg.preventupdateswhileplayingtv") &&
++ g_PVRManager.IsStarted() &&
++ g_PVRManager.IsPlaying());
++}
++
++void CEpgContainer::WaitForUpdateFinish(bool bInterrupt /* = true */)
++{
++ {
++ CSingleLock lock(m_critSection);
++ if (bInterrupt)
++ m_bPreventUpdates = true;
++
++ if (!m_bIsUpdating)
++ return;
++
++ m_updateEvent.Reset();
++ }
++
++ m_updateEvent.Wait();
++}
++
++bool CEpgContainer::UpdateEPG(bool bOnlyPending /* = false */)
++{
++ bool bInterrupted(false);
++ unsigned int iUpdatedTables(0);
++ bool bShowProgress(false);
++
++ /* set start and end time */
++ time_t start;
++ time_t end;
++ CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(start);
++ end = start + m_iDisplayTime;
++ start -= g_advancedSettings.m_iEpgLingerTime * 60;
++ bShowProgress = g_advancedSettings.m_bEpgDisplayUpdatePopup && (m_bIsInitialising || g_advancedSettings.m_bEpgDisplayIncrementalUpdatePopup);
++
++ {
++ CSingleLock lock(m_critSection);
++ if (m_bIsUpdating || InterruptUpdate())
++ return false;
++ m_bIsUpdating = true;
++ }
++
++ if (bShowProgress && !bOnlyPending)
++ ShowProgressDialog();
++
++ /* open the database */
++ if (!m_bIgnoreDbForClient && !m_database.IsOpen())
++ {
++ CLog::Log(LOGERROR, "EpgContainer - %s - could not open the database", __FUNCTION__);
++ return false;
++ }
++
++ /* load or update all EPG tables */
++ CEpg *epg;
++ unsigned int iCounter(0);
++ for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++)
++ {
++ if (InterruptUpdate())
++ {
++ bInterrupted = true;
++ break;
++ }
++
++ epg = it->second;
++ if (!epg)
++ continue;
++
++ if (bShowProgress && !bOnlyPending)
++ UpdateProgressDialog(++iCounter, m_epgs.size(), epg->Name());
++
++ if ((!bOnlyPending || epg->UpdatePending()) && epg->Update(start, end, m_iUpdateTime, bOnlyPending))
++ ++iUpdatedTables;
++ }
++
++ if (!bInterrupted)
++ {
++ if (bInterrupted)
++ {
++ /* the update has been interrupted. try again later */
++ time_t iNow;
++ CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(iNow);
++ m_iNextEpgUpdate = iNow + g_advancedSettings.m_iEpgRetryInterruptedUpdateInterval;
++ }
++ else
++ {
++ CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(m_iNextEpgUpdate);
++ m_iNextEpgUpdate += g_advancedSettings.m_iEpgUpdateCheckInterval;
++ m_bHasPendingUpdates = false;
++ }
++ }
++
++ if (bShowProgress && !bOnlyPending)
++ CloseProgressDialog();
++
++ /* notify observers */
++ if (iUpdatedTables > 0)
++ {
++ SetChanged();
++ NotifyObservers("epg", true);
++ }
++
++ CSingleLock lock(m_critSection);
++ m_bIsUpdating = false;
++ m_updateEvent.Set();
++
++ return !bInterrupted;
++}
++
++int CEpgContainer::GetEPGAll(CFileItemList &results)
++{
++ int iInitialSize = results.Size();
++
++ CSingleLock lock(m_critSection);
++ for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++)
++ it->second->Get(results);
++
++ return results.Size() - iInitialSize;
++}
++
++const CDateTime CEpgContainer::GetFirstEPGDate(void)
++{
++ CDateTime returnValue;
++
++ CSingleLock lock(m_critSection);
++ for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++)
++ {
++ lock.Leave();
++ CDateTime entry = it->second->GetFirstDate();
++ if (entry.IsValid() && (!returnValue.IsValid() || entry < returnValue))
++ returnValue = entry;
++ lock.Enter();
++ }
++
++ return returnValue;
++}
++
++const CDateTime CEpgContainer::GetLastEPGDate(void)
++{
++ CDateTime returnValue;
++
++ CSingleLock lock(m_critSection);
++ for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++)
++ {
++ lock.Leave();
++ CDateTime entry = it->second->GetLastDate();
++ if (entry.IsValid() && (!returnValue.IsValid() || entry > returnValue))
++ returnValue = entry;
++ lock.Enter();
++ }
++
++ return returnValue;
++}
++
++int CEpgContainer::GetEPGSearch(CFileItemList &results, const EpgSearchFilter &filter)
++{
++ int iInitialSize = results.Size();
++
++ /* get filtered results from all tables */
++ {
++ CSingleLock lock(m_critSection);
++ for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++)
++ it->second->Get(results, filter);
++ }
++
++ /* remove duplicate entries */
++ if (filter.m_bPreventRepeats)
++ EpgSearchFilter::RemoveDuplicates(results);
++
++ return results.Size() - iInitialSize;
++}
++
++bool CEpgContainer::CheckPlayingEvents(void)
++{
++ bool bReturn(false);
++ time_t iNow;
++ CSingleLock lock(m_critSection);
++
++ CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(iNow);
++ if (iNow >= m_iNextEpgActiveTagCheck)
++ {
++ bool bFoundChanges(false);
++ CSingleLock lock(m_critSection);
++
++ for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++)
++ bFoundChanges = it->second->CheckPlayingEvent() || bFoundChanges;
++ CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(m_iNextEpgActiveTagCheck);
++ m_iNextEpgActiveTagCheck += g_advancedSettings.m_iEpgActiveTagCheckInterval;
++
++ if (bFoundChanges)
++ {
++ SetChanged();
++ NotifyObservers("epg-now", true);
++ }
++
++ /* pvr tags always start on the full minute */
++ if (g_PVRManager.IsStarted())
++ m_iNextEpgActiveTagCheck -= m_iNextEpgActiveTagCheck % 60;
++
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CEpgContainer::IsInitialising(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_bIsInitialising;
++}
++
++void CEpgContainer::SetHasPendingUpdates(bool bHasPendingUpdates /* = true */)
++{
++ CSingleLock lock(m_critSection);
++ m_bHasPendingUpdates = bHasPendingUpdates;
++}
+diff --git a/xbmc/epg/EpgContainer.h b/xbmc/epg/EpgContainer.h
+new file mode 100644
+index 0000000..2af7539
+--- /dev/null
++++ b/xbmc/epg/EpgContainer.h
+@@ -0,0 +1,296 @@
++#pragma once
++
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "XBDateTime.h"
++#include "threads/CriticalSection.h"
++#include "threads/Thread.h"
++#include "utils/Observer.h"
++
++#include "Epg.h"
++#include "EpgDatabase.h"
++
++#include <map>
++
++class CFileItemList;
++class CGUIDialogExtendedProgressBar;
++
++namespace EPG
++{
++ #define g_EpgContainer CEpgContainer::Get()
++
++ class CEpgContainer : public Observer,
++ public Observable,
++ private CThread
++ {
++ friend class CEpgDatabase;
++
++ public:
++ /*!
++ * @brief Destroy this instance.
++ */
++ virtual ~CEpgContainer(void);
++
++ /*!
++ * @return An instance of this singleton.
++ */
++ static CEpgContainer &Get(void);
++
++ /*!
++ * @brief Get a pointer to the database instance.
++ * @return A pointer to the database instance.
++ */
++ CEpgDatabase *GetDatabase(void) { return &m_database; }
++
++ /*!
++ * @brief Start the EPG update thread.
++ */
++ virtual void Start(void);
++
++ /*!
++ * @brief Stop the EPG update thread.
++ * @return
++ */
++ virtual bool Stop(void);
++
++ /*!
++ * @brief Clear all EPG entries.
++ * @param bClearDb Clear the database too if true.
++ */
++ virtual void Clear(bool bClearDb = false);
++
++ /*!
++ * @brief Stop the update thread and unload all data.
++ */
++ virtual void Unload(void);
++
++ /*!
++ * @brief Clear the EPG and all it's database entries.
++ */
++ virtual void Reset(void) { Clear(true); }
++
++ /*!
++ * @brief Delete an EPG table from this container.
++ * @param epg The table to delete.
++ * @param bDeleteFromDatabase Delete this table from the database too if true.
++ * @return
++ */
++ virtual bool DeleteEpg(const CEpg &epg, bool bDeleteFromDatabase = false);
++
++ /*!
++ * @brief Process a notification from an observable.
++ * @param obs The observable that sent the update.
++ * @param msg The update message.
++ */
++ virtual void Notify(const Observable &obs, const CStdString& msg);
++
++ /*!
++ * @brief Update an entry in this container.
++ * @param tag The table to update.
++ * @param bUpdateDatabase If set to true, this table will be persisted in the database.
++ * @return The updated epg table or NULL if it couldn't be found.
++ */
++ virtual bool UpdateEntry(const CEpg &entry, bool bUpdateDatabase = false);
++
++ /*!
++ * @brief Get all EPG tables and apply a filter.
++ * @param results The fileitem list to store the results in.
++ * @param filter The filter to apply.
++ * @return The amount of entries that were added.
++ */
++ virtual int GetEPGSearch(CFileItemList &results, const EpgSearchFilter &filter);
++
++ /*!
++ * @brief Get all EPG tables.
++ * @param results The fileitem list to store the results in.
++ * @return The amount of entries that were added.
++ */
++ virtual int GetEPGAll(CFileItemList &results);
++
++ /*!
++ * @brief Get the start time of the first entry.
++ * @return The start time.
++ */
++ virtual const CDateTime GetFirstEPGDate(void);
++
++ /*!
++ * @brief Get the end time of the last entry.
++ * @return The end time.
++ */
++ virtual const CDateTime GetLastEPGDate(void);
++
++ /*!
++ * @brief Get an EPG table given it's ID.
++ * @param iEpgId The database ID of the table.
++ * @return The table or NULL if it wasn't found.
++ */
++ virtual CEpg *GetById(int iEpgId) const;
++
++ /*!
++ * @brief Get an EPG table given a PVR channel.
++ * @param channel The channel to get the EPG table for.
++ * @return The table or NULL if it wasn't found.
++ */
++ virtual CEpg *GetByChannel(const PVR::CPVRChannel &channel) const;
++
++ /*!
++ * @brief Notify EPG table observers when the currently active tag changed.
++ * @return True if the check was done, false if it was not the right time to check
++ */
++ virtual bool CheckPlayingEvents(void);
++
++ /*!
++ * @brief The next EPG ID to be given to a table when the db isn't being used.
++ * @return The next ID.
++ */
++ unsigned int NextEpgId(void);
++
++ /*!
++ * @brief Close the progress bar if it's visible.
++ */
++ virtual void CloseProgressDialog(void);
++
++ /*!
++ * @brief Show the progress bar
++ * @param bUpdating True if updating epg entries, false if just loading them from db
++ */
++ virtual void ShowProgressDialog(bool bUpdating = true);
++
++ /*!
++ * @brief Update the progress bar.
++ * @param iCurrent The current position.
++ * @param iMax The maximum position.
++ * @param strText The text to display.
++ */
++ virtual void UpdateProgressDialog(int iCurrent, int iMax, const CStdString &strText);
++
++ /*!
++ * @return True to not to store EPG entries in the database.
++ */
++ virtual bool IgnoreDB(void) const { return m_bIgnoreDbForClient; }
++
++ /*!
++ * @brief Wait for an EPG update to finish.
++ * @param bInterrupt True to interrupt a running update.
++ */
++ void WaitForUpdateFinish(bool bInterrupt = true);
++
++ /*!
++ * @brief Set to true to prevent updates.
++ * @param bSetTo The new value.
++ */
++ void PreventUpdates(bool bSetTo = true) { m_bPreventUpdates = bSetTo; }
++
++ /*!
++ * @brief Notify EPG container that there are pending manual EPG updates
++ * @param bHasPendingUpdates The new value
++ */
++ void SetHasPendingUpdates(bool bHasPendingUpdates = true);
++
++ /*!
++ * @return True while being initialised.
++ */
++ bool IsInitialising(void) const;
++
++ /*!
++ * @brief Call Persist() on each table
++ * @return True when they all were persisted, false otherwise.
++ */
++ bool PersistAll(void);
++
++ bool PersistTables(void);
++
++ protected:
++ /*!
++ * @brief Load the EPG settings.
++ * @return True if the settings were loaded successfully, false otherwise.
++ */
++ virtual bool LoadSettings(void);
++
++ /*!
++ * @brief Remove old EPG entries.
++ * @return True if the old entries were removed successfully, false otherwise.
++ */
++ virtual bool RemoveOldEntries(void);
++
++ /*!
++ * @brief Load and update the EPG data.
++ * @param bOnlyPending Only check and update EPG tables with pending manual updates
++ * @return True if the update has not been interrupted, false otherwise.
++ */
++ virtual bool UpdateEPG(bool bOnlyPending = false);
++
++ /*!
++ * @return True if a running update should be interrupted, false otherwise.
++ */
++ virtual bool InterruptUpdate(void) const;
++
++ /*!
++ * @brief Create a new EPG table.
++ * @param iEpgId The table ID or -1 to create a new one.
++ * @return The new table.
++ */
++ virtual CEpg *CreateEpg(int iEpgId);
++
++ /*!
++ * @brief EPG update thread
++ */
++ virtual void Process(void);
++
++ /*!
++ * @brief Create a new EPG table container.
++ */
++ CEpgContainer(void);
++
++ /*!
++ * @brief Load all tables from the database
++ */
++ void LoadFromDB(void);
++
++ CEpgDatabase m_database; /*!< the EPG database */
++
++ /** @name Configuration */
++ //@{
++ bool m_bIgnoreDbForClient; /*!< don't save the EPG data in the database */
++ int m_iDisplayTime; /*!< hours of EPG data to fetch */
++ int m_iUpdateTime; /*!< update the full EPG after this period */
++ //@}
++
++ /** @name Class state properties */
++ //@{
++ bool m_bIsUpdating; /*!< true while an update is running */
++ bool m_bIsInitialising; /*!< true while the epg manager hasn't loaded all tables */
++ bool m_bLoaded; /*!< true after epg data is initially loaded from the database */
++ bool m_bPreventUpdates; /*!< true to prevent EPG updates */
++ bool m_bHasPendingUpdates; /*!< true if there are manual updates pending */
++ time_t m_iLastEpgCleanup; /*!< the time the EPG was cleaned up */
++ time_t m_iNextEpgUpdate; /*!< the time the EPG will be updated */
++ time_t m_iNextEpgActiveTagCheck; /*!< the time the EPG will be checked for active tag updates */
++ unsigned int m_iNextEpgId; /*!< the next epg ID that will be given to a new table when the db isn't being used */
++ std::map<unsigned int, CEpg*> m_epgs; /*!< the EPGs in this container */
++ //@}
++
++ CGUIDialogExtendedProgressBar *m_progressDialog; /*!< the progress dialog that is visible when updating the first time */
++ CCriticalSection m_critSection; /*!< a critical section for changes to this container */
++ CEvent m_updateEvent; /*!< trigger when an update finishes */
++ };
++}
+diff --git a/xbmc/epg/EpgDatabase.cpp b/xbmc/epg/EpgDatabase.cpp
+new file mode 100644
+index 0000000..4f55dbc
+--- /dev/null
++++ b/xbmc/epg/EpgDatabase.cpp
+@@ -0,0 +1,449 @@
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "dbwrappers/dataset.h"
++#include "settings/AdvancedSettings.h"
++#include "settings/VideoSettings.h"
++#include "utils/log.h"
++#include "threads/SingleLock.h"
++#include "addons/include/xbmc_pvr_types.h"
++
++#include "EpgDatabase.h"
++#include "EpgContainer.h"
++
++using namespace std;
++using namespace dbiplus;
++using namespace EPG;
++
++bool CEpgDatabase::Open(void)
++{
++ CSingleLock lock(m_critSection);
++ return CDatabase::Open(g_advancedSettings.m_databaseEpg);
++}
++
++bool CEpgDatabase::CreateTables(void)
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ try
++ {
++ CDatabase::CreateTables();
++
++ BeginTransaction();
++
++ CLog::Log(LOGINFO, "EpgDB - %s - creating tables", __FUNCTION__);
++
++ CLog::Log(LOGDEBUG, "EpgDB - %s - creating table 'epg'", __FUNCTION__);
++ m_pDS->exec(
++ "CREATE TABLE epg ("
++ "idEpg integer primary key, "
++ "sName varchar(64),"
++ "sScraperName varchar(32)"
++ ")"
++ );
++
++ CLog::Log(LOGDEBUG, "EpgDB - %s - creating table 'epgtags'", __FUNCTION__);
++ m_pDS->exec(
++ "CREATE TABLE epgtags ("
++ "idBroadcast integer primary key, "
++ "iBroadcastUid integer, "
++ "idEpg integer, "
++ "sTitle varchar(128), "
++ "sPlotOutline text, "
++ "sPlot text, "
++ "iStartTime integer, "
++ "iEndTime integer, "
++ "iGenreType integer, "
++ "iGenreSubType integer, "
++ "sGenre varchar(128), "
++ "iFirstAired integer, "
++ "iParentalRating integer, "
++ "iStarRating integer, "
++ "bNotify bool, "
++ "iSeriesId integer, "
++ "iEpisodeId integer, "
++ "iEpisodePart integer, "
++ "sEpisodeName varchar(128)"
++ ")"
++ );
++ m_pDS->exec("CREATE UNIQUE INDEX idx_epg_idEpg_iStartTime on epgtags(idEpg, iStartTime desc);");
++ m_pDS->exec("CREATE INDEX idx_epg_iEndTime on epgtags(iEndTime);");
++
++ CLog::Log(LOGDEBUG, "EpgDB - %s - creating table 'lastepgscan'", __FUNCTION__);
++ m_pDS->exec("CREATE TABLE lastepgscan ("
++ "idEpg integer primary key, "
++ "sLastScan varchar(20)"
++ ")"
++ );
++
++ CommitTransaction();
++
++ bReturn = true;
++ }
++ catch (...)
++ {
++ CLog::Log(LOGERROR, "EpgDB - %s - unable to create EPG tables:%i",
++ __FUNCTION__, (int)GetLastError());
++ RollbackTransaction();
++ bReturn = false;
++ }
++
++ return bReturn;
++}
++
++bool CEpgDatabase::UpdateOldVersion(int iVersion)
++{
++ bool bReturn = true;
++
++ if (iVersion < 4)
++ {
++ CLog::Log(LOGERROR, "EpgDB - %s - updating from table versions < 4 not supported. please delete '%s'", __FUNCTION__, GetBaseDBName());
++ return false;
++ }
++
++ BeginTransaction();
++
++ try
++ {
++ if (iVersion < 5)
++ m_pDS->exec("ALTER TABLE epgtags ADD sGenre varchar(128);");
++ if (iVersion < 6)
++ {
++ m_pDS->exec("DROP INDEX idx_epg_iBroadcastUid;");
++ m_pDS->exec("DROP INDEX idx_epg_idEpg;");
++ m_pDS->exec("DROP INDEX idx_epg_iStartTime;");
++ m_pDS->exec("DROP INDEX idx_epg_iEndTime;");
++ }
++ if (iVersion < 7)
++ {
++ m_pDS->exec("CREATE INDEX idx_epg_iEndTime on epgtags(iEndTime);");
++ }
++ }
++ catch (...)
++ {
++ CLog::Log(LOGERROR, "Error attempting to update the database version!");
++ bReturn = false;
++ }
++
++ if (bReturn)
++ CommitTransaction();
++ else
++ RollbackTransaction();
++
++ return bReturn;
++}
++
++bool CEpgDatabase::DeleteEpg(void)
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++ CLog::Log(LOGDEBUG, "EpgDB - %s - deleting all EPG data from the database", __FUNCTION__);
++
++ bReturn = DeleteValues("epg") || bReturn;
++ bReturn = DeleteValues("epgtags") || bReturn;
++ bReturn = DeleteValues("lastepgscan") || bReturn;
++
++ return bReturn;
++}
++
++bool CEpgDatabase::Delete(const CEpg &table, const time_t start /* = 0 */, const time_t end /* = 0 */)
++{
++ /* invalid channel */
++ if (table.EpgID() <= 0)
++ {
++ CLog::Log(LOGERROR, "EpgDB - %s - invalid channel id: %d",
++ __FUNCTION__, table.EpgID());
++ return false;
++ }
++
++ CStdString strWhereClause;
++ strWhereClause = FormatSQL("idEpg = %u", table.EpgID());
++
++ if (start != 0)
++ strWhereClause.append(FormatSQL(" AND iStartTime >= %u", start).c_str());
++
++ if (end != 0)
++ strWhereClause.append(FormatSQL(" AND iEndTime <= %u", end).c_str());
++
++ CSingleLock lock(m_critSection);
++ return DeleteValues("epgtags", strWhereClause);
++}
++
++bool CEpgDatabase::DeleteOldEpgEntries(void)
++{
++ time_t iCleanupTime;
++ CDateTime cleanupTime = CDateTime::GetCurrentDateTime().GetAsUTCDateTime() -
++ CDateTimeSpan(0, g_advancedSettings.m_iEpgLingerTime / 60, g_advancedSettings.m_iEpgLingerTime % 60, 0);
++ cleanupTime.GetAsTime(iCleanupTime);
++
++ CStdString strWhereClause = FormatSQL("iEndTime < %u", iCleanupTime);
++
++ CSingleLock lock(m_critSection);
++ return DeleteValues("epgtags", strWhereClause);
++}
++
++bool CEpgDatabase::Delete(const CEpgInfoTag &tag)
++{
++ /* tag without a database ID was not persisted */
++ if (tag.BroadcastId() <= 0)
++ return false;
++
++ CStdString strWhereClause = FormatSQL("idBroadcast = %u", tag.BroadcastId());
++
++ CSingleLock lock(m_critSection);
++ return DeleteValues("epgtags", strWhereClause);
++}
++
++int CEpgDatabase::Get(CEpgContainer &container)
++{
++ int iReturn(-1);
++ CSingleLock lock(m_critSection);
++
++ CStdString strQuery = FormatSQL("SELECT idEpg, sName, sScraperName FROM epg;");
++ if (ResultQuery(strQuery))
++ {
++ iReturn = 0;
++
++ try
++ {
++ while (!m_pDS->eof())
++ {
++ int iEpgID = m_pDS->fv("idEpg").get_asInt();
++ CStdString strName = m_pDS->fv("sName").get_asString().c_str();
++ CStdString strScraperName = m_pDS->fv("sScraperName").get_asString().c_str();
++
++ CEpg newEpg(iEpgID, strName, strScraperName, true);
++ if (container.UpdateEntry(newEpg))
++ ++iReturn;
++ else
++ {
++ CLog::Log(LOGERROR, "%s - deleting EPG table %d from the database",
++ __FUNCTION__, iEpgID);
++
++ CStdString strWhereClause = FormatSQL("idEpg = %u", iEpgID);
++ DeleteValues("lastepgscan", strWhereClause);
++ DeleteValues("epgtags", strWhereClause);
++ DeleteValues("epg", strWhereClause);
++ }
++
++ m_pDS->next();
++ }
++ m_pDS->close();
++ }
++ catch (...)
++ {
++ CLog::Log(LOGERROR, "%s - couldn't load EPG data from the database", __FUNCTION__);
++ }
++ }
++
++ return iReturn;
++}
++
++int CEpgDatabase::Get(CEpg &epg)
++{
++ int iReturn(-1);
++ CSingleLock lock(m_critSection);
++
++ CStdString strQuery = FormatSQL("SELECT * FROM epgtags WHERE idEpg = %u;", epg.EpgID());
++ if (ResultQuery(strQuery))
++ {
++ iReturn = 0;
++ try
++ {
++ while (!m_pDS->eof())
++ {
++ CEpgInfoTag newTag;
++
++ time_t iStartTime, iEndTime, iFirstAired;
++ iStartTime = (time_t) m_pDS->fv("iStartTime").get_asInt();
++ CDateTime startTime(iStartTime);
++ newTag.m_startTime = startTime;
++
++ iEndTime = (time_t) m_pDS->fv("iEndTime").get_asInt();
++ CDateTime endTime(iEndTime);
++ newTag.m_endTime = endTime;
++
++ iFirstAired = (time_t) m_pDS->fv("iFirstAired").get_asInt();
++ CDateTime firstAired(iFirstAired);
++ newTag.m_firstAired = firstAired;
++
++ newTag.m_iUniqueBroadcastID = m_pDS->fv("iBroadcastUid").get_asInt();
++ newTag.m_iBroadcastId = m_pDS->fv("idBroadcast").get_asInt();
++ newTag.m_strTitle = m_pDS->fv("sTitle").get_asString().c_str();
++ newTag.m_strPlotOutline = m_pDS->fv("sPlotOutline").get_asString().c_str();
++ newTag.m_strPlot = m_pDS->fv("sPlot").get_asString().c_str();
++ newTag.m_iGenreType = m_pDS->fv("iGenreType").get_asInt();
++ newTag.m_iGenreSubType = m_pDS->fv("iGenreSubType").get_asInt();
++ newTag.m_strGenre = m_pDS->fv("sGenre").get_asString().c_str();
++ newTag.m_iParentalRating = m_pDS->fv("iParentalRating").get_asInt();
++ newTag.m_iStarRating = m_pDS->fv("iStarRating").get_asInt();
++ newTag.m_bNotify = m_pDS->fv("bNotify").get_asBool();
++ newTag.m_iEpisodeNumber = m_pDS->fv("iEpisodeId").get_asInt();
++ newTag.m_iEpisodePart = m_pDS->fv("iEpisodePart").get_asInt();
++ newTag.m_strEpisodeName = m_pDS->fv("sEpisodeName").get_asString().c_str();
++ newTag.m_iSeriesNumber = m_pDS->fv("iSeriesId").get_asInt();
++
++ epg.AddEntry(newTag);
++ ++iReturn;
++
++ m_pDS->next();
++ }
++ m_pDS->close();
++ }
++ catch (...)
++ {
++ CLog::Log(LOGERROR, "%s - couldn't load EPG data from the database", __FUNCTION__);
++ }
++ }
++ return iReturn;
++}
++
++bool CEpgDatabase::GetLastEpgScanTime(int iEpgId, CDateTime *lastScan)
++{
++ bool bReturn = false;
++ CStdString strWhereClause = FormatSQL("idEpg = %u", iEpgId);
++ CSingleLock lock(m_critSection);
++ CStdString strValue = GetSingleValue("lastepgscan", "sLastScan", strWhereClause);
++
++ if (!strValue.IsEmpty())
++ {
++ lastScan->SetFromDBDateTime(strValue.c_str());
++ bReturn = true;
++ }
++ else
++ {
++ lastScan->SetValid(false);
++ }
++
++ return bReturn;
++}
++
++bool CEpgDatabase::PersistLastEpgScanTime(int iEpgId /* = 0 */, bool bQueueWrite /* = false */)
++{
++ CStdString strQuery = FormatSQL("REPLACE INTO lastepgscan(idEpg, sLastScan) VALUES (%u, '%s');",
++ iEpgId, CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsDBDateTime().c_str());
++
++ CSingleLock lock(m_critSection);
++ return bQueueWrite ? QueueInsertQuery(strQuery) : ExecuteQuery(strQuery);
++}
++
++bool CEpgDatabase::Persist(const CEpgContainer &epg)
++{
++ for (map<unsigned int, CEpg *>::const_iterator it = epg.m_epgs.begin(); it != epg.m_epgs.end(); it++)
++ {
++ CEpg *epg = it->second;
++ if (epg)
++ Persist(*epg, true);
++ }
++
++ return CommitInsertQueries();
++}
++
++int CEpgDatabase::Persist(const CEpg &epg, bool bQueueWrite /* = false */)
++{
++ int iReturn(-1);
++
++ CStdString strQuery;
++ if (epg.EpgID() > 0)
++ strQuery = FormatSQL("REPLACE INTO epg (idEpg, sName, sScraperName) "
++ "VALUES (%u, '%s', '%s');", epg.EpgID(), epg.Name().c_str(), epg.ScraperName().c_str());
++ else
++ strQuery = FormatSQL("INSERT INTO epg (sName, sScraperName) "
++ "VALUES ('%s', '%s');", epg.Name().c_str(), epg.ScraperName().c_str());
++
++ CSingleLock lock(m_critSection);
++ if (bQueueWrite)
++ {
++ if (QueueInsertQuery(strQuery))
++ iReturn = epg.EpgID() <= 0 ? 0 : epg.EpgID();
++ }
++ else
++ {
++ if (ExecuteQuery(strQuery))
++ iReturn = epg.EpgID() <= 0 ? (int) m_pDS->lastinsertid() : epg.EpgID();
++ }
++
++ return iReturn;
++}
++
++int CEpgDatabase::Persist(const CEpgInfoTag &tag, bool bSingleUpdate /* = true */)
++{
++ int iReturn(-1);
++
++ if (tag.EpgID() <= 0)
++ {
++ CLog::Log(LOGERROR, "%s - tag '%s' does not have a valid table", __FUNCTION__, tag.Title().c_str());
++ return iReturn;
++ }
++
++ time_t iStartTime, iEndTime, iFirstAired;
++ tag.StartAsUTC().GetAsTime(iStartTime);
++ tag.EndAsUTC().GetAsTime(iEndTime);
++ tag.FirstAiredAsUTC().GetAsTime(iFirstAired);
++
++ int iBroadcastId = tag.BroadcastId();
++ CSingleLock lock(m_critSection);
++ CStdString strQuery;
++
++ /* Only store the genre string when needed */
++ CStdString strGenre = (tag.GenreType() == EPG_GENRE_USE_STRING) ? tag.Genre() : "";
++
++ if (iBroadcastId < 0)
++ {
++ strQuery = FormatSQL("INSERT INTO epgtags (idEpg, iStartTime, "
++ "iEndTime, sTitle, sPlotOutline, sPlot, iGenreType, iGenreSubType, sGenre, "
++ "iFirstAired, iParentalRating, iStarRating, bNotify, iSeriesId, "
++ "iEpisodeId, iEpisodePart, sEpisodeName, iBroadcastUid) "
++ "VALUES (%u, %u, %u, '%s', '%s', '%s', %i, %i, '%s', %u, %i, %i, %i, %i, %i, %i, '%s', %i);",
++ tag.EpgID(), iStartTime, iEndTime,
++ tag.Title().c_str(), tag.PlotOutline().c_str(), tag.Plot().c_str(), tag.GenreType(), tag.GenreSubType(), strGenre.c_str(),
++ iFirstAired, tag.ParentalRating(), tag.StarRating(), tag.Notify(),
++ tag.SeriesNum(), tag.EpisodeNum(), tag.EpisodePart(), tag.EpisodeName().c_str(),
++ tag.UniqueBroadcastID());
++ }
++ else
++ {
++ strQuery = FormatSQL("REPLACE INTO epgtags (idEpg, iStartTime, "
++ "iEndTime, sTitle, sPlotOutline, sPlot, iGenreType, iGenreSubType, sGenre, "
++ "iFirstAired, iParentalRating, iStarRating, bNotify, iSeriesId, "
++ "iEpisodeId, iEpisodePart, sEpisodeName, iBroadcastUid, idBroadcast) "
++ "VALUES (%u, %u, %u, '%s', '%s', '%s', %i, %i, '%s', %u, %i, %i, %i, %i, %i, %i, '%s', %i, %i);",
++ tag.EpgID(), iStartTime, iEndTime,
++ tag.Title().c_str(), tag.PlotOutline().c_str(), tag.Plot().c_str(), tag.GenreType(), tag.GenreSubType(), strGenre.c_str(),
++ iFirstAired, tag.ParentalRating(), tag.StarRating(), tag.Notify(),
++ tag.SeriesNum(), tag.EpisodeNum(), tag.EpisodePart(), tag.EpisodeName().c_str(),
++ tag.UniqueBroadcastID(), iBroadcastId);
++ }
++
++ if (bSingleUpdate)
++ {
++ if (ExecuteQuery(strQuery))
++ iReturn = (int) m_pDS->lastinsertid();
++ }
++ else
++ {
++ QueueInsertQuery(strQuery);
++ iReturn = 0;
++ }
++
++ return iReturn;
++}
+diff --git a/xbmc/epg/EpgDatabase.h b/xbmc/epg/EpgDatabase.h
+new file mode 100644
+index 0000000..d7ef018
+--- /dev/null
++++ b/xbmc/epg/EpgDatabase.h
+@@ -0,0 +1,163 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "dbwrappers/Database.h"
++#include "XBDateTime.h"
++#include "threads/CriticalSection.h"
++
++namespace EPG
++{
++ class CEpg;
++ class CEpgInfoTag;
++ class CEpgContainer;
++
++ /** The EPG database */
++
++ class CEpgDatabase : public CDatabase
++ {
++ public:
++ /*!
++ * @brief Create a new instance of the EPG database.
++ */
++ CEpgDatabase(void) {};
++
++ /*!
++ * @brief Destroy this instance.
++ */
++ virtual ~CEpgDatabase(void) {};
++
++ /*!
++ * @brief Open the database.
++ * @return True if it was opened successfully, false otherwise.
++ */
++ virtual bool Open(void);
++
++ /*!
++ * @brief Get the minimal database version that is required to operate correctly.
++ * @return The minimal database version.
++ */
++ virtual int GetMinVersion(void) const { return 7; };
++
++ /*!
++ * @brief Get the default sqlite database filename.
++ * @return The default filename.
++ */
++ const char *GetBaseDBName(void) const { return "Epg"; };
++
++ /*! @name EPG methods */
++ //@{
++
++ /*!
++ * @brief Remove all EPG information from the database
++ * @return True if the EPG information was erased, false otherwise.
++ */
++ virtual bool DeleteEpg(void);
++
++ /*!
++ * @brief Erase all EPG entries for a table.
++ * @param table The table to remove the EPG entries for.
++ * @param start Remove entries after this time if set.
++ * @param end Remove entries before this time if set.
++ * @return True if the entries were removed successfully, false otherwise.
++ */
++ virtual bool Delete(const CEpg &table, const time_t start = 0, const time_t end = 0);
++
++ /*!
++ * @brief Erase all EPG entries older than 1 day.
++ * @return True if the entries were removed successfully, false otherwise.
++ */
++ virtual bool DeleteOldEpgEntries(void);
++
++ /*!
++ * @brief Remove a single EPG entry.
++ * @param tag The entry to remove.
++ * @return True if it was removed successfully, false otherwise.
++ */
++ virtual bool Delete(const CEpgInfoTag &tag);
++
++ /*!
++ * @brief Get all EPG tables from the database. Does not get the EPG tables' entries.
++ * @param container The container to fill.
++ * @return The amount of entries that was added.
++ */
++ virtual int Get(CEpgContainer &container);
++
++ /*!
++ * @brief Get all EPG entries for a table.
++ * @param epg The EPG table to get the entries for.
++ * @return The amount of entries that was added.
++ */
++ virtual int Get(CEpg &epg);
++
++ /*!
++ * @brief Get the last stored EPG scan time.
++ * @param iEpgId The table to update the time for. Use 0 for a global value.
++ * @param lastScan The last scan time or -1 if it wasn't found.
++ * @return True if the time was fetched successfully, false otherwise.
++ */
++ virtual bool GetLastEpgScanTime(int iEpgId, CDateTime *lastScan);
++
++ /*!
++ * @brief Update the last scan time.
++ * @param iEpgId The table to update the time for. Use 0 for a global value.
++ * @param bQueueWrite Don't execute the query immediately but queue it if true.
++ * @return True if it was updated successfully, false otherwise.
++ */
++ virtual bool PersistLastEpgScanTime(int iEpgId = 0, bool bQueueWrite = false);
++
++ bool Persist(const CEpgContainer &epg);
++
++ /*!
++ * @brief Persist an EPG table. It's entries are not persisted.
++ * @param epg The table to persist.
++ * @param bQueueWrite Don't execute the query immediately but queue it if true.
++ * @return The database ID of this entry or 0 if bSingleUpdate is false and the query was queued.
++ */
++ virtual int Persist(const CEpg &epg, bool bQueueWrite = false);
++
++ /*!
++ * @brief Persist an infotag.
++ * @param tag The tag to persist.
++ * @param bSingleUpdate If true, this is a single update and the query will be executed immediately.
++ * @return The database ID of this entry or 0 if bSingleUpdate is false and the query was queued.
++ */
++ virtual int Persist(const CEpgInfoTag &tag, bool bSingleUpdate = true);
++
++ //@}
++
++ protected:
++ /*!
++ * @brief Create the EPG database tables.
++ * @return True if the tables were created successfully, false otherwise.
++ */
++ virtual bool CreateTables(void);
++
++ /*!
++ * @brief Update an old version of the database.
++ * @param version The version to update the database from.
++ * @return True if it was updated successfully, false otherwise.
++ */
++ virtual bool UpdateOldVersion(int version);
++
++ CCriticalSection m_critSection;
++ };
++}
+diff --git a/xbmc/epg/EpgInfoTag.cpp b/xbmc/epg/EpgInfoTag.cpp
+new file mode 100644
+index 0000000..df1c93e
+--- /dev/null
++++ b/xbmc/epg/EpgInfoTag.cpp
+@@ -0,0 +1,974 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "guilib/LocalizeStrings.h"
++#include "Epg.h"
++#include "EpgInfoTag.h"
++#include "EpgContainer.h"
++#include "EpgDatabase.h"
++#include "pvr/channels/PVRChannelGroupsContainer.h"
++#include "pvr/timers/PVRTimers.h"
++#include "pvr/PVRManager.h"
++#include "settings/AdvancedSettings.h"
++#include "utils/log.h"
++#include "addons/include/xbmc_pvr_types.h"
++
++using namespace std;
++using namespace EPG;
++using namespace PVR;
++
++CEpgInfoTag::CEpgInfoTag(int iEpgId /* = -1 */, int iPVRChannelNumber /* = -1 */, int iPVRChannelID /* = -1 */, const CStdString &strTableName /* = StringUtils::EmptyString */) :
++ m_bNotify(false),
++ m_bChanged(false),
++ m_iBroadcastId(-1),
++ m_iGenreType(0),
++ m_iGenreSubType(0),
++ m_iParentalRating(0),
++ m_iStarRating(0),
++ m_iSeriesNumber(0),
++ m_iEpisodeNumber(0),
++ m_iEpisodePart(0),
++ m_iUniqueBroadcastID(-1),
++ m_strTitle(""),
++ m_strPlotOutline(""),
++ m_strPlot(""),
++ m_strGenre(""),
++ m_strEpisodeName(""),
++ m_strIconPath(""),
++ m_strFileNameAndPath(""),
++ m_iTimerId(-1),
++ m_iEpgId(iEpgId),
++ m_iPVRChannelNumber(iPVRChannelNumber),
++ m_iPVRChannelID(iPVRChannelID),
++ m_strTableName(strTableName)
++{
++}
++
++CEpgInfoTag::CEpgInfoTag(const EPG_TAG &data) :
++ m_bNotify(false),
++ m_bChanged(false),
++ m_iBroadcastId(-1),
++ m_iGenreType(0),
++ m_iGenreSubType(0),
++ m_iParentalRating(0),
++ m_iStarRating(0),
++ m_iSeriesNumber(0),
++ m_iEpisodeNumber(0),
++ m_iEpisodePart(0),
++ m_iUniqueBroadcastID(-1),
++ m_strTitle(""),
++ m_strPlotOutline(""),
++ m_strPlot(""),
++ m_strGenre(""),
++ m_strEpisodeName(""),
++ m_strIconPath(""),
++ m_strFileNameAndPath(""),
++ m_iTimerId(-1),
++ m_iEpgId(-1),
++ m_iPVRChannelNumber(-1),
++ m_iPVRChannelID(-1)
++{
++ Update(data);
++}
++
++CEpgInfoTag::CEpgInfoTag(const CEpgInfoTag &tag) :
++ m_bNotify(tag.m_bNotify),
++ m_bChanged(tag.m_bChanged),
++ m_iBroadcastId(tag.m_iBroadcastId),
++ m_iGenreType(tag.m_iGenreType),
++ m_iGenreSubType(tag.m_iGenreSubType),
++ m_iParentalRating(tag.m_iParentalRating),
++ m_iStarRating(tag.m_iStarRating),
++ m_iSeriesNumber(tag.m_iSeriesNumber),
++ m_iEpisodeNumber(tag.m_iEpisodeNumber),
++ m_iEpisodePart(tag.m_iEpisodePart),
++ m_iUniqueBroadcastID(tag.m_iUniqueBroadcastID),
++ m_strTitle(tag.m_strTitle),
++ m_strPlotOutline(tag.m_strPlotOutline),
++ m_strPlot(tag.m_strPlot),
++ m_strGenre(tag.m_strGenre),
++ m_strEpisodeName(tag.m_strEpisodeName),
++ m_strIconPath(tag.m_strIconPath),
++ m_strFileNameAndPath(tag.m_strFileNameAndPath),
++ m_startTime(tag.m_startTime),
++ m_endTime(tag.m_endTime),
++ m_firstAired(tag.m_firstAired),
++ m_iTimerId(tag.m_iTimerId),
++ m_iEpgId(tag.m_iEpgId),
++ m_iPVRChannelNumber(tag.m_iPVRChannelNumber),
++ m_iPVRChannelID(tag.m_iPVRChannelID),
++ m_strTableName(tag.m_strTableName)
++{
++}
++
++CEpgInfoTag::~CEpgInfoTag()
++{
++ CPVRTimerInfoTag* tag = Timer();
++ if (tag)
++ tag->OnEpgTagDeleted();
++}
++
++bool CEpgInfoTag::operator ==(const CEpgInfoTag& right) const
++{
++ if (this == &right) return true;
++
++ CSingleLock lock(m_critSection);
++ return (m_bNotify == right.m_bNotify &&
++ m_bChanged == right.m_bChanged &&
++ m_iBroadcastId == right.m_iBroadcastId &&
++ m_iGenreType == right.m_iGenreType &&
++ m_iGenreSubType == right.m_iGenreSubType &&
++ m_iParentalRating == right.m_iParentalRating &&
++ m_firstAired == right.m_firstAired &&
++ m_iStarRating == right.m_iStarRating &&
++ m_iSeriesNumber == right.m_iSeriesNumber &&
++ m_iEpisodeNumber == right.m_iEpisodeNumber &&
++ m_iEpisodePart == right.m_iEpisodePart &&
++ m_iUniqueBroadcastID == right.m_iUniqueBroadcastID &&
++ m_strTitle == right.m_strTitle &&
++ m_strPlotOutline == right.m_strPlotOutline &&
++ m_strPlot == right.m_strPlot &&
++ m_strGenre == right.m_strGenre &&
++ m_strEpisodeName == right.m_strEpisodeName &&
++ m_strIconPath == right.m_strIconPath &&
++ m_strFileNameAndPath == right.m_strFileNameAndPath &&
++ m_startTime == right.m_startTime &&
++ m_endTime == right.m_endTime &&
++ m_iPVRChannelNumber == right.m_iPVRChannelNumber &&
++ m_iPVRChannelID == right.m_iPVRChannelID &&
++ m_strTableName == right.m_strTableName);
++}
++
++bool CEpgInfoTag::operator !=(const CEpgInfoTag& right) const
++{
++ if (this == &right) return false;
++
++ return !(*this == right);
++}
++
++CEpgInfoTag &CEpgInfoTag::operator =(const CEpgInfoTag &other)
++{
++ CSingleLock lock(other.m_critSection);
++
++ m_bNotify = other.m_bNotify;
++ m_bChanged = other.m_bChanged;
++ m_iBroadcastId = other.m_iBroadcastId;
++ m_iGenreType = other.m_iGenreType;
++ m_iGenreSubType = other.m_iGenreSubType;
++ m_iParentalRating = other.m_iParentalRating;
++ m_iStarRating = other.m_iStarRating;
++ m_iSeriesNumber = other.m_iSeriesNumber;
++ m_iEpisodeNumber = other.m_iEpisodeNumber;
++ m_iEpisodePart = other.m_iEpisodePart;
++ m_iUniqueBroadcastID = other.m_iUniqueBroadcastID;
++ m_strTitle = other.m_strTitle;
++ m_strPlotOutline = other.m_strPlotOutline;
++ m_strPlot = other.m_strPlot;
++ m_strGenre = other.m_strGenre;
++ m_strEpisodeName = other.m_strEpisodeName;
++ m_strIconPath = other.m_strIconPath;
++ m_strFileNameAndPath = other.m_strFileNameAndPath;
++ m_startTime = other.m_startTime;
++ m_endTime = other.m_endTime;
++ m_firstAired = other.m_firstAired;
++ m_iTimerId = other.m_iTimerId;
++ m_timerStart = other.m_timerStart;
++ m_iEpgId = other.m_iEpgId;
++ m_iPVRChannelNumber = other.m_iPVRChannelNumber;
++ m_iPVRChannelID = other.m_iPVRChannelID;
++ m_strTableName = other.m_strTableName;
++
++ return *this;
++}
++
++bool CEpgInfoTag::Changed(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_bChanged;
++}
++
++bool CEpgInfoTag::IsActive(void) const
++{
++ CDateTime now = CDateTime::GetUTCDateTime();
++ CSingleLock lock(m_critSection);
++ return (m_startTime <= now && m_endTime > now);
++}
++
++bool CEpgInfoTag::WasActive(void) const
++{
++ CDateTime now = CDateTime::GetUTCDateTime();
++ CSingleLock lock(m_critSection);
++ return (m_endTime < now);
++}
++
++bool CEpgInfoTag::InTheFuture(void) const
++{
++ CDateTime now = CDateTime::GetUTCDateTime();
++ CSingleLock lock(m_critSection);
++ return (m_startTime > now);
++}
++
++float CEpgInfoTag::ProgressPercentage(void) const
++{
++ float fReturn(0);
++ int iDuration;
++ time_t currentTime, startTime, endTime;
++ CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(currentTime);
++
++ CSingleLock lock(m_critSection);
++ m_startTime.GetAsTime(startTime);
++ m_endTime.GetAsTime(endTime);
++ iDuration = endTime - startTime > 0 ? endTime - startTime : 3600;
++
++ if (currentTime >= startTime && currentTime <= endTime)
++ fReturn = ((float) currentTime - startTime) / iDuration * 100;
++ else if (currentTime > endTime)
++ fReturn = 100;
++
++ return fReturn;
++}
++
++const CEpgInfoTag *CEpgInfoTag::GetNextEvent(void) const
++{
++ CSingleLock lock(m_critSection);
++ return GetTable()->GetNextEvent(*this);
++}
++
++const CEpgInfoTag *CEpgInfoTag::GetPreviousEvent(void) const
++{
++ CSingleLock lock(m_critSection);
++ return GetTable()->GetPreviousEvent(*this);
++}
++
++void CEpgInfoTag::SetUniqueBroadcastID(int iUniqueBroadcastID)
++{
++ bool bUpdate(false);
++ {
++ CSingleLock lock(m_critSection);
++ if (m_iUniqueBroadcastID != iUniqueBroadcastID)
++ {
++ m_iUniqueBroadcastID = iUniqueBroadcastID;
++ m_bChanged = true;
++ bUpdate = true;
++ }
++ }
++ if (bUpdate)
++ UpdatePath();
++}
++
++int CEpgInfoTag::UniqueBroadcastID(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_iUniqueBroadcastID;
++}
++
++void CEpgInfoTag::SetBroadcastId(int iId)
++{
++ bool bUpdate(false);
++ {
++ CSingleLock lock(m_critSection);
++ if (m_iBroadcastId != iId)
++ {
++ m_iBroadcastId = iId;
++ m_bChanged = true;
++ bUpdate = true;
++ }
++ }
++ if (bUpdate)
++ UpdatePath();
++}
++
++int CEpgInfoTag::BroadcastId(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_iBroadcastId;
++}
++
++CDateTime CEpgInfoTag::StartAsUTC(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_startTime;
++}
++
++CDateTime CEpgInfoTag::StartAsLocalTime(void) const
++{
++ CDateTime retVal;
++ CSingleLock lock(m_critSection);
++ retVal.SetFromUTCDateTime(m_startTime);
++ return retVal;
++}
++
++void CEpgInfoTag::SetStartFromUTC(const CDateTime &start)
++{
++ bool bUpdate(false);
++ {
++ CSingleLock lock(m_critSection);
++ if (m_startTime != start)
++ {
++ m_startTime = start;
++ m_bChanged = true;
++ bUpdate = true;
++ }
++ }
++ if (bUpdate)
++ UpdatePath();
++}
++
++void CEpgInfoTag::SetStartFromLocalTime(const CDateTime &start)
++{
++ CDateTime tmp = start.GetAsUTCDateTime();
++ SetStartFromUTC(tmp);
++}
++
++CDateTime CEpgInfoTag::EndAsUTC(void) const
++{
++ CDateTime retVal;
++ CSingleLock lock(m_critSection);
++ retVal = m_endTime;
++ return retVal;
++}
++
++CDateTime CEpgInfoTag::EndAsLocalTime(void) const
++{
++ CDateTime retVal;
++ CSingleLock lock(m_critSection);
++ retVal.SetFromUTCDateTime(m_endTime);
++ return retVal;
++}
++
++void CEpgInfoTag::SetEndFromUTC(const CDateTime &end)
++{
++ bool bUpdate(false);
++ {
++ CSingleLock lock(m_critSection);
++ if (m_endTime != end)
++ {
++ m_endTime = end;
++ m_bChanged = true;
++ bUpdate = true;
++ }
++ }
++ if (bUpdate)
++ UpdatePath();
++}
++
++void CEpgInfoTag::SetEndFromLocalTime(const CDateTime &end)
++{
++ CDateTime tmp = end.GetAsUTCDateTime();
++ SetEndFromUTC(tmp);
++}
++
++int CEpgInfoTag::GetDuration(void) const
++{
++ time_t start, end;
++ CSingleLock lock(m_critSection);
++ m_startTime.GetAsTime(start);
++ m_endTime.GetAsTime(end);
++ return end - start > 0 ? end - start : 3600;
++}
++
++void CEpgInfoTag::SetTitle(const CStdString &strTitle)
++{
++ bool bUpdate(false);
++ {
++ CSingleLock lock(m_critSection);
++ if (m_strTitle != strTitle)
++ {
++ m_strTitle = strTitle;
++ m_bChanged = true;
++ bUpdate = true;
++ }
++ }
++ if (bUpdate)
++ UpdatePath();
++}
++
++CStdString CEpgInfoTag::Title(void) const
++{
++ CStdString retVal;
++ CSingleLock lock(m_critSection);
++ retVal = (m_strTitle.IsEmpty()) ?
++ g_localizeStrings.Get(19055) :
++ m_strTitle;
++ return retVal;
++}
++
++void CEpgInfoTag::SetPlotOutline(const CStdString &strPlotOutline)
++{
++ bool bUpdate(false);
++ {
++ CSingleLock lock(m_critSection);
++ if (m_strPlotOutline != strPlotOutline)
++ {
++ m_strPlotOutline = strPlotOutline;
++ m_bChanged = true;
++ bUpdate = true;
++ }
++ }
++ if (bUpdate)
++ UpdatePath();
++}
++
++CStdString CEpgInfoTag::PlotOutline(void) const
++{
++ CStdString retVal;
++ CSingleLock lock(m_critSection);
++ retVal = m_strPlotOutline;
++ return retVal;
++}
++
++void CEpgInfoTag::SetPlot(const CStdString &strPlot)
++{
++ bool bUpdate(false);
++ {
++ CSingleLock lock(m_critSection);
++ CStdString strPlotClean = (m_strPlotOutline.length() > 0 && strPlot.Left(m_strPlotOutline.length()).Equals(m_strPlotOutline)) ?
++ strPlot.Right(strPlot.length() - m_strPlotOutline.length()) :
++ strPlot;
++
++ if (m_strPlot != strPlotClean)
++ {
++ m_strPlot = strPlotClean;
++ m_bChanged = true;
++ bUpdate = true;
++ }
++ }
++ if (bUpdate)
++ UpdatePath();
++}
++
++CStdString CEpgInfoTag::Plot(void) const
++{
++ CStdString retVal;
++ CSingleLock lock(m_critSection);
++ retVal = m_strPlot;
++ return retVal;
++}
++
++void CEpgInfoTag::SetGenre(int iID, int iSubID, const char* strGenre)
++{
++ bool bUpdate(false);
++ {
++ CSingleLock lock(m_critSection);
++ if (m_iGenreType != iID || m_iGenreSubType != iSubID)
++ {
++ m_iGenreType = iID;
++ m_iGenreSubType = iSubID;
++ if ((iID == EPG_GENRE_USE_STRING) && (strGenre != NULL) && (strlen(strGenre) > 0))
++ {
++ /* Type and sub type are not given. No EPG color coding possible
++ * Use the provided genre description as backup. */
++ m_strGenre = strGenre;
++ }
++ else
++ {
++ /* Determine the genre description from the type and subtype IDs */
++ m_strGenre = CEpg::ConvertGenreIdToString(iID, iSubID);
++ }
++ m_bChanged = true;
++ bUpdate = true;
++ }
++ }
++ if (bUpdate)
++ UpdatePath();
++}
++
++int CEpgInfoTag::GenreType(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_iGenreType;
++}
++
++int CEpgInfoTag::GenreSubType(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_iGenreSubType;
++}
++
++CStdString CEpgInfoTag::Genre(void) const
++{
++ CStdString retVal;
++ CSingleLock lock(m_critSection);
++ retVal = m_strGenre;
++ return retVal;
++}
++
++CDateTime CEpgInfoTag::FirstAiredAsUTC(void) const
++{
++ CDateTime retVal;
++ CSingleLock lock(m_critSection);
++ retVal = m_firstAired;
++ return retVal;
++}
++
++CDateTime CEpgInfoTag::FirstAiredAsLocalTime(void) const
++{
++ CDateTime retVal;
++ CSingleLock lock(m_critSection);
++ retVal.SetFromUTCDateTime(m_firstAired);
++ return retVal;
++}
++
++void CEpgInfoTag::SetFirstAiredFromUTC(const CDateTime &firstAired)
++{
++ bool bUpdate(false);
++ {
++ CSingleLock lock(m_critSection);
++ if (m_firstAired != firstAired)
++ {
++ m_firstAired = firstAired;
++ m_bChanged = true;
++ bUpdate = true;
++ }
++ }
++ if (bUpdate)
++ UpdatePath();
++}
++
++void CEpgInfoTag::SetFirstAiredFromLocalTime(const CDateTime &firstAired)
++{
++ CDateTime tmp = firstAired.GetAsUTCDateTime();
++ SetStartFromUTC(tmp);
++}
++
++void CEpgInfoTag::SetParentalRating(int iParentalRating)
++{
++ bool bUpdate(false);
++ {
++ CSingleLock lock(m_critSection);
++ if (m_iParentalRating != iParentalRating)
++ {
++ m_iParentalRating = iParentalRating;
++ m_bChanged = true;
++ bUpdate = true;
++ }
++ }
++ if (bUpdate)
++ UpdatePath();
++}
++
++int CEpgInfoTag::ParentalRating(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_iParentalRating;
++}
++
++void CEpgInfoTag::SetStarRating(int iStarRating)
++{
++ bool bUpdate(false);
++ {
++ CSingleLock lock(m_critSection);
++ if (m_iStarRating != iStarRating)
++ {
++ m_iStarRating = iStarRating;
++ m_bChanged = true;
++ bUpdate = true;
++ }
++ }
++ if (bUpdate)
++ UpdatePath();
++}
++
++int CEpgInfoTag::StarRating(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_iStarRating;
++}
++
++void CEpgInfoTag::SetNotify(bool bNotify)
++{
++ bool bUpdate(false);
++ {
++ CSingleLock lock(m_critSection);
++ if (m_bNotify != bNotify)
++ {
++ m_bNotify = bNotify;
++ m_bChanged = true;
++ bUpdate = true;
++ }
++ }
++ if (bUpdate)
++ UpdatePath();
++}
++
++bool CEpgInfoTag::Notify(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_bNotify;
++}
++
++void CEpgInfoTag::SetSeriesNum(int iSeriesNum)
++{
++ bool bUpdate(false);
++ {
++ CSingleLock lock(m_critSection);
++ if (m_iSeriesNumber != iSeriesNum)
++ {
++ m_iSeriesNumber = iSeriesNum;
++ m_bChanged = true;
++ bUpdate = true;
++ }
++ }
++ if (bUpdate)
++ UpdatePath();
++}
++
++int CEpgInfoTag::SeriesNum(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_iSeriesNumber;
++}
++
++void CEpgInfoTag::SetEpisodeNum(int iEpisodeNum)
++{
++ bool bUpdate(false);
++ {
++ CSingleLock lock(m_critSection);
++ if (m_iEpisodeNumber != iEpisodeNum)
++ {
++ m_iEpisodeNumber = iEpisodeNum;
++ m_bChanged = true;
++ bUpdate = true;
++ }
++ }
++ if (bUpdate)
++ UpdatePath();
++}
++
++int CEpgInfoTag::EpisodeNum(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_iEpisodeNumber;
++}
++
++void CEpgInfoTag::SetEpisodePart(int iEpisodePart)
++{
++ bool bUpdate(false);
++ {
++ CSingleLock lock(m_critSection);
++ if (m_iEpisodePart != iEpisodePart)
++ {
++ m_iEpisodePart = iEpisodePart;
++ m_bChanged = true;
++ bUpdate = true;
++ }
++ }
++ if (bUpdate)
++ UpdatePath();
++}
++
++int CEpgInfoTag::EpisodePart(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_iEpisodePart;
++}
++
++void CEpgInfoTag::SetEpisodeName(const CStdString &strEpisodeName)
++{
++ bool bUpdate(false);
++ {
++ CSingleLock lock(m_critSection);
++ if (m_strEpisodeName != strEpisodeName)
++ {
++ m_strEpisodeName = strEpisodeName;
++ m_bChanged = true;
++ bUpdate = true;
++ }
++ }
++ if (bUpdate)
++ UpdatePath();
++}
++
++CStdString CEpgInfoTag::EpisodeName(void) const
++{
++ CStdString retVal;
++ CSingleLock lock(m_critSection);
++ retVal = m_strEpisodeName;
++ return retVal;
++}
++
++void CEpgInfoTag::SetIcon(const CStdString &strIconPath)
++{
++ bool bUpdate(false);
++ {
++ CSingleLock lock(m_critSection);
++ if (m_strIconPath != strIconPath)
++ {
++ m_strIconPath = strIconPath;
++ m_bChanged = true;
++ bUpdate = true;
++ }
++ }
++ if (bUpdate)
++ UpdatePath();
++}
++
++CStdString CEpgInfoTag::Icon(void) const
++{
++ CStdString retVal;
++ CEpg *epg = g_EpgContainer.GetById(m_iEpgId);
++
++ CSingleLock lock(m_critSection);
++ retVal = m_strIconPath;
++ if (retVal.IsEmpty() && epg && epg->HasPVRChannel())
++ retVal = epg->Channel()->IconPath();
++ return retVal;
++}
++
++void CEpgInfoTag::SetPath(const CStdString &strFileNameAndPath)
++{
++ CSingleLock lock(m_critSection);
++ if (m_strFileNameAndPath != strFileNameAndPath)
++ {
++ m_strFileNameAndPath = strFileNameAndPath;
++ m_bChanged = true;
++ }
++}
++
++CStdString CEpgInfoTag::Path(void) const
++{
++ CStdString retVal;
++ CSingleLock lock(m_critSection);
++ retVal = m_strFileNameAndPath;
++ return retVal;
++}
++
++void CEpgInfoTag::SetTimer(CPVRTimerInfoTag *newTimer)
++{
++ CPVRTimerInfoTag *oldTimer(NULL);
++ {
++ CSingleLock lock(m_critSection);
++ if (g_PVRManager.IsStarted())
++ oldTimer = Timer();
++ m_timerStart = newTimer->StartAsUTC();
++ m_iTimerId = newTimer->m_iClientIndex;
++ }
++ if (oldTimer)
++ oldTimer->SetEpgInfoTag(NULL);
++}
++
++void CEpgInfoTag::OnTimerDeleted(void)
++{
++ CSingleLock lock(m_critSection);
++ m_iTimerId = -1;
++}
++
++bool CEpgInfoTag::HasTimer(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_iTimerId != -1;
++}
++
++CPVRTimerInfoTag *CEpgInfoTag::Timer(void) const
++{
++ CPVRTimerInfoTag* tag(NULL);
++ CSingleLock lock(m_critSection);
++ if (m_iTimerId >= 0)
++ tag = g_PVRTimers->GetTimer(m_timerStart, m_iTimerId);
++
++ return tag;
++}
++
++void CEpgInfoTag::SetPVRChannelID(int iPVRChannelID)
++{
++ CSingleLock lock(m_critSection);
++ m_iPVRChannelID = iPVRChannelID;
++}
++
++void CEpgInfoTag::SetPVRChannelNumber(int iPVRChannelNumber)
++{
++ CSingleLock lock(m_critSection);
++ m_iPVRChannelNumber = iPVRChannelNumber;
++}
++
++bool CEpgInfoTag::HasPVRChannel(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_iPVRChannelID != -1;
++}
++
++int CEpgInfoTag::PVRChannelNumber(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_iPVRChannelNumber;
++}
++
++CStdString CEpgInfoTag::PVRChannelName(void) const
++{
++ CStdString strReturn;
++ CSingleLock lock(m_critSection);
++ strReturn = m_strTableName;
++ return strReturn;
++}
++
++const PVR::CPVRChannel *CEpgInfoTag::ChannelTag(void) const
++{
++ int iChannelId(-1);
++ {
++ CSingleLock lock(m_critSection);
++ iChannelId = m_iPVRChannelID;
++ }
++
++ return (iChannelId != -1) ?
++ g_PVRChannelGroups->GetByChannelIDFromAll(iChannelId) :
++ NULL;
++}
++
++void CEpgInfoTag::Update(const EPG_TAG &tag)
++{
++ CSingleLock lock(m_critSection);
++ SetStartFromUTC(tag.startTime + g_advancedSettings.m_iPVRTimeCorrection);
++ SetEndFromUTC(tag.endTime + g_advancedSettings.m_iPVRTimeCorrection);
++ SetTitle(tag.strTitle);
++ SetPlotOutline(tag.strPlotOutline);
++ SetPlot(tag.strPlot);
++ SetGenre(tag.iGenreType, tag.iGenreSubType, tag.strGenreDescription);
++ SetParentalRating(tag.iParentalRating);
++ SetUniqueBroadcastID(tag.iUniqueBroadcastId);
++ SetNotify(tag.bNotify);
++ SetFirstAiredFromUTC(tag.firstAired + g_advancedSettings.m_iPVRTimeCorrection);
++ SetEpisodeNum(tag.iEpisodeNumber);
++ SetEpisodePart(tag.iEpisodePartNumber);
++ SetEpisodeName(tag.strEpisodeName);
++ SetStarRating(tag.iStarRating);
++ SetIcon(tag.strIconPath);
++}
++
++bool CEpgInfoTag::Update(const CEpgInfoTag &tag, bool bUpdateBroadcastId /* = true */)
++{
++ bool bChanged(false);
++ {
++ CSingleLock lock(m_critSection);
++ bChanged = (
++ m_strTitle != tag.m_strTitle ||
++ m_strPlotOutline != tag.m_strPlotOutline ||
++ m_strPlot != tag.m_strPlot ||
++ m_startTime != tag.m_startTime ||
++ m_endTime != tag.m_endTime ||
++ m_iGenreType != tag.m_iGenreType ||
++ m_iGenreSubType != tag.m_iGenreSubType ||
++ m_firstAired != tag.m_firstAired ||
++ m_iParentalRating != tag.m_iParentalRating ||
++ m_iStarRating != tag.m_iStarRating ||
++ m_bNotify != tag.m_bNotify ||
++ m_iEpisodeNumber != tag.m_iEpisodeNumber ||
++ m_iEpisodePart != tag.m_iEpisodePart ||
++ m_iSeriesNumber != tag.m_iSeriesNumber ||
++ m_strEpisodeName != tag.m_strEpisodeName ||
++ m_iUniqueBroadcastID != tag.m_iUniqueBroadcastID ||
++ m_iEpgId != tag.m_iEpgId ||
++ m_iPVRChannelID != tag.m_iPVRChannelID ||
++ m_iPVRChannelNumber != tag.m_iPVRChannelNumber ||
++ m_strTableName != tag.m_strTableName ||
++ ( tag.m_strGenre.length() > 0 && m_strGenre != tag.m_strGenre )
++ );
++ if (bUpdateBroadcastId)
++ bChanged = bChanged || m_iBroadcastId != tag.m_iBroadcastId;
++
++ if (bChanged)
++ {
++ if (bUpdateBroadcastId)
++ m_iBroadcastId = tag.m_iBroadcastId;
++
++ m_strTitle = tag.m_strTitle;
++ m_strPlotOutline = tag.m_strPlotOutline;
++ m_strPlot = tag.m_strPlot;
++ m_startTime = tag.m_startTime;
++ m_endTime = tag.m_endTime;
++ m_iGenreType = tag.m_iGenreType;
++ m_iGenreSubType = tag.m_iGenreSubType;
++ m_iEpgId = tag.m_iEpgId;
++ m_iPVRChannelID = tag.m_iPVRChannelID;
++ m_iPVRChannelNumber = tag.m_iPVRChannelNumber;
++ m_strTableName = tag.m_strTableName;
++ if (m_iGenreType == EPG_GENRE_USE_STRING && tag.m_strGenre.length() > 0)
++ {
++ /* No type/subtype. Use the provided description */
++ m_strGenre = tag.m_strGenre;
++ }
++ else
++ {
++ /* Determine genre description by type/subtype */
++ m_strGenre = CEpg::ConvertGenreIdToString(tag.m_iGenreType, tag.m_iGenreSubType);
++ }
++ m_firstAired = tag.m_firstAired;
++ m_iParentalRating = tag.m_iParentalRating;
++ m_iStarRating = tag.m_iStarRating;
++ m_bNotify = tag.m_bNotify;
++ m_iEpisodeNumber = tag.m_iEpisodeNumber;
++ m_iEpisodePart = tag.m_iEpisodePart;
++ m_iSeriesNumber = tag.m_iSeriesNumber;
++ m_strEpisodeName = tag.m_strEpisodeName;
++ m_iUniqueBroadcastID = tag.m_iUniqueBroadcastID;
++
++ m_bChanged = true;
++ }
++ }
++ if (bChanged)
++ UpdatePath();
++
++ return bChanged;
++}
++
++bool CEpgInfoTag::Persist(bool bSingleUpdate /* = true */)
++{
++ bool bReturn = false;
++ CSingleLock lock(m_critSection);
++ if (!m_bChanged)
++ return true;
++ CLog::Log(LOGDEBUG, "Epg - %s - Infotag '%s' %s, persisting...", __FUNCTION__, m_strTitle.c_str(), m_iBroadcastId > 0 ? "has changes" : "is new");
++ CEpgDatabase *database = g_EpgContainer.GetDatabase();
++ if (!database || (bSingleUpdate && !database->IsOpen()))
++ {
++ CLog::Log(LOGERROR, "%s - could not open the database", __FUNCTION__);
++ return bReturn;
++ }
++
++ int iId = database->Persist(*this, bSingleUpdate);
++ if (iId >= 0)
++ {
++ bReturn = true;
++
++ if (iId > 0)
++ {
++ m_iBroadcastId = iId;
++ m_bChanged = false;
++ }
++ }
++
++ return bReturn;
++}
++
++void CEpgInfoTag::UpdatePath(void)
++{
++ CStdString path;
++ {
++ CSingleLock lock(m_critSection);
++ path.Format("pvr://guide/%04i/%s.epg", m_iEpgId, m_startTime.GetAsDBDateTime().c_str());
++ }
++
++ SetPath(path);
++}
++
++const CEpg *CEpgInfoTag::GetTable() const
++{
++ return g_EpgContainer.GetById(m_iEpgId);
++}
+diff --git a/xbmc/epg/EpgInfoTag.h b/xbmc/epg/EpgInfoTag.h
+new file mode 100644
+index 0000000..f1c6a57
+--- /dev/null
++++ b/xbmc/epg/EpgInfoTag.h
+@@ -0,0 +1,469 @@
++#pragma once
++
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "addons/include/xbmc_pvr_types.h"
++#include "XBDateTime.h"
++#include "Epg.h"
++#include "utils/StringUtils.h"
++
++namespace PVR
++{
++ class CPVRTimerInfoTag;
++}
++
++/** an EPG info tag */
++namespace EPG
++{
++ class CEpg;
++
++ class CEpgInfoTag
++ {
++ friend class CEpg;
++ friend class CEpgDatabase;
++ friend class PVR::CPVRTimerInfoTag;
++
++ public:
++ /*!
++ * @brief Create a new empty event without a unique ID.
++ */
++ CEpgInfoTag(int iEpgId = -1, int iPVRChannelNumber = -1, int iPVRChannelID = -1, const CStdString &strTableName = StringUtils::EmptyString);
++
++ /*!
++ * @brief Create a new EPG infotag with 'data' as content.
++ * @param data The tag's content.
++ */
++ CEpgInfoTag(const EPG_TAG &data);
++
++ /*!
++ * @brief Create a new EPG infotag with 'tag' as content.
++ * @param tag The tag's content.
++ */
++ CEpgInfoTag(const CEpgInfoTag &tag);
++
++ /*!
++ * @brief Destroy this instance.
++ */
++ virtual ~CEpgInfoTag();
++
++ bool operator ==(const CEpgInfoTag& right) const;
++ bool operator !=(const CEpgInfoTag& right) const;
++ CEpgInfoTag &operator =(const CEpgInfoTag &other);
++
++ /*!
++ * @brief Check whether this tag has changed and unsaved values.
++ * @return True if it has unsaved values, false otherwise.
++ */
++ virtual bool Changed(void) const;
++
++ /*!
++ * @brief Check if this event is currently active.
++ * @return True if it's active, false otherwise.
++ */
++ virtual bool IsActive(void) const;
++
++ /*!
++ * @return True when this event has already passed, false otherwise.
++ */
++ virtual bool WasActive(void) const;
++
++ /*!
++ * @return True when this event is in the future, false otherwise.
++ */
++ virtual bool InTheFuture(void) const;
++
++ /*!
++ * @return The current progress of this tag.
++ */
++ virtual float ProgressPercentage(void) const;
++
++ /*!
++ * @brief Get a pointer to the next event. Set by CEpg in a call to Sort()
++ * @return A pointer to the next event or NULL if it's not set.
++ */
++ virtual const CEpgInfoTag *GetNextEvent(void) const;
++
++ /*!
++ * @brief Get a pointer to the previous event. Set by CEpg in a call to Sort()
++ * @return A pointer to the previous event or NULL if it's not set.
++ */
++ virtual const CEpgInfoTag *GetPreviousEvent(void) const;
++
++ /*!
++ * @brief The table this event belongs to
++ * @return The table this event belongs to
++ */
++ virtual const CEpg *GetTable() const;
++
++ virtual const int EpgID(void) const { return m_iEpgId; }
++
++ /*!
++ * @brief Change the unique broadcast ID of this event.
++ * @param iUniqueBroadcastId The new unique broadcast ID.
++ */
++ virtual void SetUniqueBroadcastID(int iUniqueBroadcastID);
++
++ /*!
++ * @brief Get the unique broadcast ID.
++ * @return The unique broadcast ID.
++ */
++ virtual int UniqueBroadcastID(void) const;
++
++ /*!
++ * @brief Change the event's database ID.
++ * @param iId The new database ID.
++ */
++ virtual void SetBroadcastId(int iId);
++
++ /*!
++ * @brief Get the event's database ID.
++ * @return The database ID.
++ */
++ virtual int BroadcastId(void) const;
++
++ /*!
++ * @brief Get the event's start time.
++ * @return The new start time.
++ */
++ virtual CDateTime StartAsUTC(void) const;
++ virtual CDateTime StartAsLocalTime(void) const;
++
++ /*!
++ * @brief Change the event's start time.
++ * @param start The new start time.
++ */
++ virtual void SetStartFromUTC(const CDateTime &start);
++ void SetStartFromLocalTime(const CDateTime &start);
++
++ /*!
++ * @brief Get the event's end time.
++ * @return The new start time.
++ */
++ virtual CDateTime EndAsUTC(void) const;
++ virtual CDateTime EndAsLocalTime(void) const;
++
++ /*!
++ * @brief Change the event's end time.
++ * @param end The new end time.
++ */
++ virtual void SetEndFromUTC(const CDateTime &end);
++ virtual void SetEndFromLocalTime(const CDateTime &end);
++
++ /*!
++ * @brief Get the duration of this event in seconds.
++ * @return The duration in seconds.
++ */
++ virtual int GetDuration(void) const;
++
++ /*!
++ * @brief Change the title of this event.
++ * @param strTitle The new title.
++ */
++ virtual void SetTitle(const CStdString &strTitle);
++
++ /*!
++ * @brief Get the title of this event.
++ * @return The title.
++ */
++ virtual CStdString Title(void) const;
++
++ /*!
++ * @brief Change the plot outline of this event.
++ * @param strPlotOutline The new plot outline.
++ */
++ virtual void SetPlotOutline(const CStdString &strPlotOutline);
++
++ /*!
++ * @brief Get the plot outline of this event.
++ * @return The plot outline.
++ */
++ virtual CStdString PlotOutline(void) const;
++
++ /*!
++ * @brief Change the plot of this event.
++ * @param strPlot The new plot.
++ */
++ virtual void SetPlot(const CStdString &strPlot);
++
++ /*!
++ * @brief Get the plot of this event.
++ * @return The plot.
++ */
++ virtual CStdString Plot(void) const;
++
++ /*!
++ * @brief Change the genre of this event.
++ * @param iID The genre type ID.
++ * @param iSubID The genre subtype ID.
++ */
++ virtual void SetGenre(int iID, int iSubID, const char* strGenre);
++
++ /*!
++ * @brief Get the genre type ID of this event.
++ * @return The genre type ID.
++ */
++ virtual int GenreType(void) const;
++
++ /*!
++ * @brief Get the genre subtype ID of this event.
++ * @return The genre subtype ID.
++ */
++ virtual int GenreSubType(void) const;
++
++ /*!
++ * @brief Get the genre as human readable string.
++ * @return The genre.
++ */
++ virtual CStdString Genre(void) const;
++
++ /*!
++ * @brief Change the first air date of this event.
++ * @param firstAired The new first air date.
++ */
++ virtual void SetFirstAiredFromUTC(const CDateTime &firstAired);
++ virtual void SetFirstAiredFromLocalTime(const CDateTime &firstAired);
++
++ /*!
++ * @brief Get the first air date of this event.
++ * @return The first air date.
++ */
++ virtual CDateTime FirstAiredAsUTC(void) const;
++ virtual CDateTime FirstAiredAsLocalTime(void) const;
++
++ /*!
++ * @brief Change the parental rating of this event.
++ * @param iParentalRating The new parental rating.
++ */
++ virtual void SetParentalRating(int iParentalRating);
++
++ /*!
++ * @brief Get the parental rating of this event.
++ * @return The parental rating.
++ */
++ virtual int ParentalRating(void) const;
++
++ /*!
++ * @brief Change the star rating of this event.
++ * @param iStarRating The new star rating.
++ */
++ virtual void SetStarRating(int iStarRating);
++
++ /*!
++ * @brief Get the star rating of this event.
++ * @return The star rating.
++ */
++ virtual int StarRating(void) const;
++
++ /*!
++ * @brief Change the value of notify on start.
++ * @param bNotify The new value.
++ */
++ virtual void SetNotify(bool bNotify);
++
++ /*!
++ * @brief Notify on start if true.
++ * @return Notify on start.
++ */
++ virtual bool Notify(void) const;
++
++ /*!
++ * @brief Change the series number of this event.
++ * @param iSeriesNum The new series number.
++ */
++ virtual void SetSeriesNum(int iSeriesNum);
++
++ /*!
++ * @brief The series number of this event.
++ * @return The series number.
++ */
++ virtual int SeriesNum(void) const;
++
++ /*!
++ * @brief Change the episode number of this event.
++ * @param iEpisodeNum The new episode number.
++ */
++ virtual void SetEpisodeNum(int iEpisodeNum);
++
++ /*!
++ * @brief The episode number of this event.
++ * @return The episode number.
++ */
++ virtual int EpisodeNum(void) const;
++
++ /*!
++ * @brief Change the episode part number of this event.
++ * @param iEpisodePart The new episode part number.
++ */
++ virtual void SetEpisodePart(int iEpisodePart);
++
++ /*!
++ * @brief The episode part number of this event.
++ * @return The episode part number.
++ */
++ virtual int EpisodePart(void) const;
++
++ /*!
++ * @brief Change the episode name of this event.
++ * @param strEpisodeName The new episode name.
++ */
++ virtual void SetEpisodeName(const CStdString &strEpisodeName);
++
++ /*!
++ * @brief The episode name of this event.
++ * @return The episode name.
++ */
++ virtual CStdString EpisodeName(void) const;
++
++ /*!
++ * @brief Change the path to the icon for this event.
++ * @param strIconPath The new path.
++ */
++ virtual void SetIcon(const CStdString &strIconPath);
++
++ /*!
++ * @brief Get the path to the icon for this event.
++ * @return The path to the icon
++ */
++ virtual CStdString Icon(void) const;
++
++ /*!
++ * @brief Change the path to this event.
++ * @param strFileNameAndPath The new path.
++ */
++ virtual void SetPath(const CStdString &strFileNameAndPath);
++
++ /*!
++ * @brief The path to this event.
++ * @return The path.
++ */
++ virtual CStdString Path(void) const;
++
++ /*!
++ * @brief Set a timer for this event or NULL to clear it.
++ * @param newTimer The new timer value.
++ */
++ virtual void SetTimer(PVR::CPVRTimerInfoTag *newTimer);
++
++ /*!
++ * @brief Check whether this event has an active timer tag.
++ * @return True if it has an active timer tag, false if not.
++ */
++ virtual bool HasTimer(void) const;
++
++ /*!
++ * @brief Get a pointer to the timer for event or NULL if there is none.
++ * @return A pointer to the timer for event or NULL if there is none.
++ */
++ virtual PVR::CPVRTimerInfoTag *Timer(void) const;
++
++ /*!
++ * @brief Set the PVR channel ID of the tag
++ * @param The new value
++ */
++ virtual void SetPVRChannelID(int iPVRChannelID);
++
++ /*!
++ * @brief Set the PVR channel number of the tag
++ * @param The new value
++ */
++ virtual void SetPVRChannelNumber(int iPVRChannelNumber);
++
++ /*!
++ * @return True if this tag has a PVR channel set.
++ */
++ virtual bool HasPVRChannel(void) const;
++
++ virtual int PVRChannelNumber(void) const;
++
++ virtual CStdString PVRChannelName(void) const;
++
++ /*!
++ * @brief Get the channel that plays this event.
++ * @return a pointer to the channel.
++ */
++ virtual const PVR::CPVRChannel *ChannelTag(void) const;
++
++ /*!
++ * @brief Persist this tag in the database.
++ * @param bSingleUpdate True if this is a single update, false if more updates will follow.
++ * @return True if the tag was persisted correctly, false otherwise.
++ */
++ virtual bool Persist(bool bSingleUpdate = true);
++
++ /*!
++ * @brief Update the information in this tag with the info in the given tag.
++ * @param tag The new info.
++ */
++ virtual void Update(const EPG_TAG &tag);
++
++ /*!
++ * @brief Update the information in this tag with the info in the given tag.
++ * @param tag The new info.
++ * @param bUpdateBroadcastId If set to false, the tag BroadcastId (locally unique) will not be chacked/updated
++ * @return True if something changed, false otherwise.
++ */
++ virtual bool Update(const CEpgInfoTag &tag, bool bUpdateBroadcastId = true);
++
++ protected:
++ /*!
++ * @brief Hook that is called when the start date changed.
++ */
++ virtual void UpdatePath(void);
++
++ /*!
++ * @brief Called by the CPVRTimerInfoTag destructor
++ */
++ virtual void OnTimerDeleted(void);
++
++ bool m_bNotify; /*!< notify on start */
++ bool m_bChanged; /*!< keep track of changes to this entry */
++
++ int m_iBroadcastId; /*!< database ID */
++ int m_iGenreType; /*!< genre type */
++ int m_iGenreSubType; /*!< genre subtype */
++ int m_iParentalRating; /*!< parental rating */
++ int m_iStarRating; /*!< star rating */
++ int m_iSeriesNumber; /*!< series number */
++ int m_iEpisodeNumber; /*!< episode number */
++ int m_iEpisodePart; /*!< episode part number */
++ int m_iUniqueBroadcastID; /*!< unique broadcast ID */
++ CStdString m_strTitle; /*!< title */
++ CStdString m_strPlotOutline; /*!< plot outline */
++ CStdString m_strPlot; /*!< plot */
++ CStdString m_strGenre; /*!< genre */
++ CStdString m_strEpisodeName; /*!< episode name */
++ CStdString m_strIconPath; /*!< the path to the icon */
++ CStdString m_strFileNameAndPath; /*!< the filename and path */
++ CDateTime m_startTime; /*!< event start time */
++ CDateTime m_endTime; /*!< event end time */
++ CDateTime m_firstAired; /*!< first airdate */
++
++ CDateTime m_timerStart; /*!< the start time of the timer (if any) */
++ int m_iTimerId; /*!< the id of the timer (if any) */
++ int m_iEpgId; /*!< the ID of the schedule that this event belongs to */
++
++ int m_iPVRChannelNumber; /*!< the channel number in the "all channels" group */
++ int m_iPVRChannelID; /*!< the ID of the PVR channel */
++ CStdString m_strTableName; /*!< the name of the EPG table (or PVR channel, if it's a PVR epg table */
++ CCriticalSection m_critSection;
++ };
++}
+diff --git a/xbmc/epg/EpgSearchFilter.cpp b/xbmc/epg/EpgSearchFilter.cpp
+new file mode 100644
+index 0000000..1603735
+--- /dev/null
++++ b/xbmc/epg/EpgSearchFilter.cpp
+@@ -0,0 +1,246 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "guilib/LocalizeStrings.h"
++#include "utils/TextSearch.h"
++#include "utils/log.h"
++#include "FileItem.h"
++#include "../addons/include/xbmc_pvr_types.h"
++
++#include "EpgSearchFilter.h"
++#include "EpgContainer.h"
++
++#include "pvr/PVRManager.h"
++#include "pvr/channels/PVRChannelGroupsContainer.h"
++#include "pvr/recordings/PVRRecordings.h"
++#include "pvr/timers/PVRTimers.h"
++
++using namespace std;
++using namespace EPG;
++using namespace PVR;
++
++void EpgSearchFilter::Reset()
++{
++ m_strSearchTerm = "";
++ m_bIsCaseSensitive = false;
++ m_bSearchInDescription = false;
++ m_iGenreType = EPG_SEARCH_UNSET;
++ m_iGenreSubType = EPG_SEARCH_UNSET;
++ m_iMinimumDuration = EPG_SEARCH_UNSET;
++ m_iMaximumDuration = EPG_SEARCH_UNSET;
++ m_startDateTime.SetFromUTCDateTime(g_EpgContainer.GetFirstEPGDate());
++ m_endDateTime.SetFromUTCDateTime(g_EpgContainer.GetLastEPGDate());
++ m_bIncludeUnknownGenres = false;
++ m_bIgnorePresentTimers = false;
++ m_bIgnorePresentRecordings = false;
++ m_bPreventRepeats = false;
++
++ /* pvr specific filters */
++ m_iChannelNumber = EPG_SEARCH_UNSET;
++ m_bFTAOnly = false;
++ m_iChannelGroup = EPG_SEARCH_UNSET;
++ m_bIgnorePresentTimers = true;
++ m_bIgnorePresentRecordings = true;
++}
++
++bool EpgSearchFilter::MatchGenre(const CEpgInfoTag &tag) const
++{
++ bool bReturn(true);
++
++ if (m_iGenreType != EPG_SEARCH_UNSET)
++ {
++ bool bIsUnknownGenre(tag.GenreType() > EPG_EVENT_CONTENTMASK_USERDEFINED ||
++ tag.GenreType() < EPG_EVENT_CONTENTMASK_MOVIEDRAMA);
++ bReturn = ((m_bIncludeUnknownGenres && bIsUnknownGenre) || tag.GenreType() == m_iGenreType);
++ }
++
++ return bReturn;
++}
++
++bool EpgSearchFilter::MatchDuration(const CEpgInfoTag &tag) const
++{
++ bool bReturn(true);
++
++ if (m_iMinimumDuration != EPG_SEARCH_UNSET)
++ bReturn = (tag.GetDuration() > m_iMinimumDuration * 60);
++
++ if (bReturn && m_iMaximumDuration != EPG_SEARCH_UNSET)
++ bReturn = (tag.GetDuration() < m_iMaximumDuration * 60);
++
++ return bReturn;
++}
++
++bool EpgSearchFilter::MatchStartAndEndTimes(const CEpgInfoTag &tag) const
++{
++ return (tag.StartAsLocalTime() >= m_startDateTime && tag.EndAsLocalTime() <= m_endDateTime);
++}
++
++bool EpgSearchFilter::MatchSearchTerm(const CEpgInfoTag &tag) const
++{
++ bool bReturn(true);
++
++ if (!m_strSearchTerm.IsEmpty())
++ {
++ CTextSearch search(m_strSearchTerm, m_bIsCaseSensitive, SEARCH_DEFAULT_OR);
++ bReturn = search.Search(tag.Title()) ||
++ search.Search(tag.PlotOutline());
++ }
++
++ return bReturn;
++}
++
++bool EpgSearchFilter::FilterEntry(const CEpgInfoTag &tag) const
++{
++ return (MatchGenre(tag) &&
++ MatchDuration(tag) &&
++ MatchStartAndEndTimes(tag) &&
++ MatchSearchTerm(tag)) &&
++ (!tag.HasPVRChannel() ||
++ (MatchChannelNumber(tag) &&
++ MatchChannelGroup(tag) &&
++ (!m_bFTAOnly || !tag.ChannelTag()->IsEncrypted())));
++}
++
++int EpgSearchFilter::RemoveDuplicates(CFileItemList &results)
++{
++ unsigned int iSize = results.Size();
++
++ for (unsigned int iResultPtr = 0; iResultPtr < iSize; iResultPtr++)
++ {
++ const CEpgInfoTag *epgentry_1 = results.Get(iResultPtr)->GetEPGInfoTag();
++ for (unsigned int iTagPtr = 0; iTagPtr < iSize; iTagPtr++)
++ {
++ const CEpgInfoTag *epgentry_2 = results.Get(iTagPtr)->GetEPGInfoTag();
++ if (iResultPtr == iTagPtr)
++ continue;
++
++ if (epgentry_1->Title() != epgentry_2->Title() ||
++ epgentry_1->Plot() != epgentry_2->Plot() ||
++ epgentry_1->PlotOutline() != epgentry_2->PlotOutline())
++ continue;
++
++ results.Remove(iTagPtr);
++ iResultPtr--;
++ iTagPtr--;
++ iSize--;
++ }
++ }
++
++ return iSize;
++}
++
++
++bool EpgSearchFilter::MatchChannelNumber(const CEpgInfoTag &tag) const
++{
++ bool bReturn(true);
++
++ if (m_iChannelNumber != EPG_SEARCH_UNSET && g_PVRManager.IsStarted())
++ {
++ const CPVRChannelGroup *group = (m_iChannelGroup != EPG_SEARCH_UNSET) ? g_PVRChannelGroups->GetByIdFromAll(m_iChannelGroup) : g_PVRChannelGroups->GetGroupAllTV();
++ if (!group)
++ group = CPVRManager::Get().ChannelGroups()->GetGroupAllTV();
++
++ bReturn = (m_iChannelNumber == (int) group->GetChannelNumber(*tag.ChannelTag()));
++ }
++
++ return bReturn;
++}
++
++bool EpgSearchFilter::MatchChannelGroup(const CEpgInfoTag &tag) const
++{
++ bool bReturn(true);
++
++ if (m_iChannelGroup != EPG_SEARCH_UNSET && g_PVRManager.IsStarted())
++ {
++ const CPVRChannelGroup *group = g_PVRChannelGroups->GetByIdFromAll(m_iChannelGroup);
++ bReturn = (group && group->IsGroupMember(*tag.ChannelTag()));
++ }
++
++ return bReturn;
++}
++
++int EpgSearchFilter::FilterRecordings(CFileItemList &results)
++{
++ int iRemoved(0);
++ if (!g_PVRManager.IsStarted())
++ return iRemoved;
++
++ CPVRRecordings *recordings = CPVRManager::Get().Recordings();
++
++ // TODO not thread safe and inefficient!
++ for (unsigned int iRecordingPtr = 0; iRecordingPtr < recordings->size(); iRecordingPtr++)
++ {
++ CPVRRecording *recording = recordings->at(iRecordingPtr);
++ if (!recording)
++ continue;
++
++ for (int iResultPtr = 0; iResultPtr < results.Size(); iResultPtr++)
++ {
++ const CEpgInfoTag *epgentry = results.Get(iResultPtr)->GetEPGInfoTag();
++
++ /* no match */
++ if (!epgentry ||
++ epgentry->Title() != recording->m_strTitle ||
++ epgentry->Plot() != recording->m_strPlot)
++ continue;
++
++ results.Remove(iResultPtr);
++ iResultPtr--;
++ ++iRemoved;
++ }
++ }
++
++ return iRemoved;
++}
++
++int EpgSearchFilter::FilterTimers(CFileItemList &results)
++{
++ int iRemoved(0);
++ if (!g_PVRManager.IsStarted())
++ return iRemoved;
++
++ vector<CPVRTimerInfoTag *> timers;
++ g_PVRTimers->GetActiveTimers(&timers);
++
++ // TODO not thread safe and inefficient!
++ for (unsigned int iTimerPtr = 0; iTimerPtr < timers.size(); iTimerPtr++)
++ {
++ CPVRTimerInfoTag *timer = timers.at(iTimerPtr);
++ if (!timer)
++ continue;
++
++ for (int iResultPtr = 0; iResultPtr < results.Size(); iResultPtr++)
++ {
++ const CEpgInfoTag *epgentry = results.Get(iResultPtr)->GetEPGInfoTag();
++ if (!epgentry ||
++ *epgentry->ChannelTag() != *timer->m_channel ||
++ epgentry->StartAsUTC() < timer->StartAsUTC() ||
++ epgentry->EndAsUTC() > timer->EndAsUTC())
++ continue;
++
++ results.Remove(iResultPtr);
++ iResultPtr--;
++ ++iRemoved;
++ }
++ }
++
++ return iRemoved;
++}
+diff --git a/xbmc/epg/EpgSearchFilter.h b/xbmc/epg/EpgSearchFilter.h
+new file mode 100644
+index 0000000..160b8ac
+--- /dev/null
++++ b/xbmc/epg/EpgSearchFilter.h
+@@ -0,0 +1,81 @@
++#pragma once
++
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "XBDateTime.h"
++
++class CFileItemList;
++
++namespace EPG
++{
++ class CEpgInfoTag;
++
++ #define EPG_SEARCH_UNSET (-1)
++
++ /** Filter to apply with on a CEpgInfoTag */
++
++ struct EpgSearchFilter
++ {
++ static int FilterRecordings(CFileItemList &results);
++ static int FilterTimers(CFileItemList &results);
++
++ /*!
++ * @brief Clear this filter.
++ */
++ virtual void Reset();
++
++ /*!
++ * @brief Check if a tag will be filtered or not.
++ * @param tag The tag to check.
++ * @return True if this tag matches the filter, false if not.
++ */
++ virtual bool FilterEntry(const CEpgInfoTag &tag) const;
++
++ virtual bool MatchGenre(const CEpgInfoTag &tag) const;
++ virtual bool MatchDuration(const CEpgInfoTag &tag) const;
++ virtual bool MatchStartAndEndTimes(const CEpgInfoTag &tag) const;
++ virtual bool MatchSearchTerm(const CEpgInfoTag &tag) const;
++ virtual bool MatchChannelNumber(const CEpgInfoTag &tag) const;
++ virtual bool MatchChannelGroup(const CEpgInfoTag &tag) const;
++
++ static int RemoveDuplicates(CFileItemList &results);
++
++ CStdString m_strSearchTerm; /*!< The term to search for */
++ bool m_bIsCaseSensitive; /*!< Do a case sensitive search */
++ bool m_bSearchInDescription; /*!< Search for strSearchTerm in the description too */
++ int m_iGenreType; /*!< The genre type for an entry */
++ int m_iGenreSubType; /*!< The genre subtype for an entry */
++ int m_iMinimumDuration; /*!< The minimum duration for an entry */
++ int m_iMaximumDuration; /*!< The maximum duration for an entry */
++ CDateTime m_startDateTime; /*!< The minimum start time for an entry */
++ CDateTime m_endDateTime; /*!< The maximum end time for an entry */
++ bool m_bIncludeUnknownGenres; /*!< Include unknown genres or not */
++ bool m_bPreventRepeats; /*!< True to remove repeating events, false if not */
++
++ /* PVR specific filters */
++ int m_iChannelNumber; /*!< The channel number in the selected channel group */
++ bool m_bFTAOnly; /*!< Free to air only or not */
++ int m_iChannelGroup; /*!< The group this channel belongs to */
++ bool m_bIgnorePresentTimers; /*!< True to ignore currently present timers (future recordings), false if not */
++ bool m_bIgnorePresentRecordings; /*!< True to ignore currently active recordings, false if not */
++ };
++}
+diff --git a/xbmc/epg/GUIEPGGridContainer.cpp b/xbmc/epg/GUIEPGGridContainer.cpp
+new file mode 100644
+index 0000000..77a5463
+--- /dev/null
++++ b/xbmc/epg/GUIEPGGridContainer.cpp
+@@ -0,0 +1,1915 @@
++/*
++* Copyright (C) 2005-2008 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, write to
++* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++* http://www.gnu.org/copyleft/gpl.html
++*
++*/
++
++#include "guilib/Key.h"
++#include "guilib/GUIControlFactory.h"
++#include "guilib/GUIListItem.h"
++#include "guilib/GUIFontManager.h"
++#include "guilib/LocalizeStrings.h"
++#include "guilib/DirtyRegion.h"
++#include "lib/tinyXML/tinyxml.h"
++#include "utils/log.h"
++#include "utils/Variant.h"
++#include "threads/SystemClock.h"
++#include "GUIInfoManager.h"
++
++#include "epg/EpgInfoTag.h"
++#include "pvr/channels/PVRChannel.h"
++
++#include "GUIEPGGridContainer.h"
++
++using namespace PVR;
++using namespace EPG;
++using namespace std;
++
++#define SHORTGAP 5 // how many blocks is considered a short-gap in nav logic
++#define MINSPERBLOCK 5 /// would be nice to offer zooming of busy schedules /// performance cost to increase resolution 5 fold?
++#define BLOCKJUMP 4 // how many blocks are jumped with each analogue scroll action
++
++CGUIEPGGridContainer::CGUIEPGGridContainer(int parentID, int controlID, float posX, float posY, float width,
++ float height, ORIENTATION orientation, int scrollTime,
++ int preloadItems, int timeBlocks, int rulerUnit)
++ : CGUIControl(parentID, controlID, posX, posY, width, height)
++{
++ ControlType = GUICONTAINER_EPGGRID;
++ m_blocksPerPage = timeBlocks;
++ m_rulerUnit = rulerUnit;
++ m_channelCursor = 0;
++ m_blockCursor = 0;
++ m_channelOffset = 0;
++ m_blockOffset = 0;
++ m_channelScrollOffset = 0;
++ m_channelScrollSpeed = 0;
++ m_channelScrollLastTime = 0;
++ m_programmeScrollOffset = 0;
++ m_programmeScrollSpeed = 0;
++ m_programmeScrollLastTime = 0;
++ m_scrollTime = scrollTime ? scrollTime : 1;
++ m_renderTime = 0;
++ m_item = NULL;
++ m_lastItem = NULL;
++ m_lastChannel = NULL;
++ m_channelWrapAround = true; /// get from settings?
++ m_orientation = orientation;
++ m_programmeLayout = NULL;
++ m_focusedProgrammeLayout= NULL;
++ m_channelLayout = NULL;
++ m_focusedChannelLayout = NULL;
++ m_rulerLayout = NULL;
++ m_rulerPosX = 0;
++ m_rulerPosY = 0;
++ m_rulerHeight = 0;
++ m_rulerWidth = 0;
++ m_channelPosX = 0;
++ m_channelPosY = 0;
++ m_channelHeight = 0;
++ m_channelWidth = 0;
++ m_gridPosX = 0;
++ m_gridPosY = 0;
++ m_gridWidth = 0;
++ m_gridHeight = 0;
++ m_blockSize = 0;
++ m_analogScrollCount = 0;
++ m_cacheChannelItems = preloadItems;
++ m_cacheRulerItems = preloadItems;
++ m_cacheProgrammeItems = preloadItems;
++ m_gridIndex = NULL;
++}
++
++CGUIEPGGridContainer::~CGUIEPGGridContainer(void)
++{
++ Reset();
++}
++
++void CGUIEPGGridContainer::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
++{
++ bool changed = false;
++ m_renderTime = currentTime;
++
++ changed = true;
++
++ if (changed)
++ MarkDirtyRegion();
++
++ CGUIControl::Process(currentTime, dirtyregions);
++}
++
++void CGUIEPGGridContainer::Render()
++{
++ ValidateOffset();
++
++ if (m_bInvalidated)
++ UpdateLayout();
++
++ if (!m_focusedChannelLayout || !m_channelLayout || !m_rulerLayout || !m_focusedProgrammeLayout || !m_programmeLayout || m_rulerItems.size()<=1 || (m_gridEnd - m_gridStart) == CDateTimeSpan(0, 0, 0, 0))
++ return;
++
++ UpdateScrollOffset();
++
++ int chanOffset = (int)floorf(m_channelScrollOffset / m_programmeLayout->Size(m_orientation));
++ int blockOffset = (int)floorf(m_programmeScrollOffset / m_blockSize);
++ int rulerOffset = (int)floorf(m_programmeScrollOffset / m_blockSize);
++
++ /// Render channel names
++ int cacheBeforeChannel, cacheAfterChannel;
++ GetChannelCacheOffsets(cacheBeforeChannel, cacheAfterChannel);
++
++ // Free memory not used on screen
++ if ((int)m_channelItems.size() > m_channelsPerPage + cacheBeforeChannel + cacheAfterChannel)
++ FreeChannelMemory(CorrectOffset(chanOffset - cacheBeforeChannel, 0), CorrectOffset(chanOffset + m_channelsPerPage + 1 + cacheAfterChannel, 0));
++
++ if (m_orientation == VERTICAL)
++ g_graphicsContext.SetClipRegion(m_channelPosX, m_channelPosY, m_channelWidth, m_gridHeight);
++ else
++ g_graphicsContext.SetClipRegion(m_channelPosX, m_channelPosY, m_gridWidth, m_channelHeight);
++
++ CPoint originChannel = CPoint(m_channelPosX, m_channelPosY) + m_renderOffset;
++ float pos = (m_orientation == VERTICAL) ? originChannel.y : originChannel.x;
++ float end = (m_orientation == VERTICAL) ? m_posY + m_height : m_posX + m_width;
++
++ // we offset our draw position to take into account scrolling and whether or not our focused
++ // item is offscreen "above" the list.
++ float drawOffset = (chanOffset - cacheBeforeChannel) * m_channelLayout->Size(m_orientation) - m_channelScrollOffset;
++ if (m_channelOffset + m_channelCursor < chanOffset)
++ drawOffset += m_focusedChannelLayout->Size(m_orientation) - m_channelLayout->Size(m_orientation);
++ pos += drawOffset;
++ end += cacheAfterChannel * m_channelLayout->Size(m_orientation);
++
++ float focusedPos = 0;
++ CGUIListItemPtr focusedItem;
++ int current = chanOffset;// - cacheBeforeChannel;
++ while (pos < end && (int)m_channelItems.size())
++ {
++ int itemNo = CorrectOffset(current, 0);
++ if (itemNo >= (int)m_channelItems.size())
++ break;
++ bool focused = (current == m_channelOffset + m_channelCursor);
++ if (itemNo >= 0)
++ {
++ CGUIListItemPtr item = m_channelItems[itemNo];
++ // render our item
++ if (focused)
++ {
++ focusedPos = pos;
++ focusedItem = item;
++ }
++ else
++ {
++ if (m_orientation == VERTICAL)
++ RenderChannelItem(originChannel.x, pos, item.get(), false);
++ else
++ RenderChannelItem(pos, originChannel.y, item.get(), false);
++ }
++ }
++ // increment our position
++ pos += focused ? m_focusedChannelLayout->Size(m_orientation) : m_channelLayout->Size(m_orientation);
++ current++;
++ }
++ // render focused item last so it can overlap other items
++ if (focusedItem)
++ {
++ if (m_orientation == VERTICAL)
++ RenderChannelItem(originChannel.x, focusedPos, focusedItem.get(), true);
++ else
++ RenderChannelItem(focusedPos, originChannel.y, focusedItem.get(), true);
++ }
++ g_graphicsContext.RestoreClipRegion();
++
++ /// Render the ruler items
++ g_graphicsContext.SetClipRegion(m_posX, m_posY, m_width, m_height);
++ CGUIListItemPtr item = m_rulerItems[0];
++ g_graphicsContext.SetOrigin(m_posX, m_posY);
++ item->SetLabel(m_rulerItems[rulerOffset/m_rulerUnit+1]->GetLabel2());
++ if (!item->GetLayout())
++ {
++ CGUIListItemLayout *layout = new CGUIListItemLayout(*m_rulerLayout);
++ if (m_orientation == VERTICAL)
++ layout->SetWidth(m_channelWidth);
++ else
++ layout->SetHeight(m_channelHeight);
++ item->SetLayout(layout);
++ }
++ if (item->GetLayout())
++ {
++ CDirtyRegionList dirtyRegions;
++ item->GetLayout()->Process(item.get(),m_parentID,m_renderTime,dirtyRegions);
++ item->GetLayout()->Render(item.get(), m_parentID);
++ }
++ g_graphicsContext.RestoreOrigin();
++
++ int cacheBeforeRuler, cacheAfterRuler;
++ GetRulerCacheOffsets(cacheBeforeRuler, cacheAfterRuler);
++
++ g_graphicsContext.RestoreClipRegion();
++
++ // Free memory not used on screen
++ if ((int)m_rulerItems.size() > m_blocksPerPage + cacheBeforeRuler + cacheAfterRuler)
++ FreeRulerMemory(CorrectOffset(rulerOffset - cacheBeforeRuler, 0), CorrectOffset(rulerOffset + m_blocksPerPage + 1 + cacheAfterRuler, 0));
++
++ if (m_orientation == VERTICAL)
++ g_graphicsContext.SetClipRegion(m_rulerPosX, m_rulerPosY, m_gridWidth, m_rulerHeight);
++ else
++ g_graphicsContext.SetClipRegion(m_rulerPosX, m_rulerPosY, m_rulerWidth, m_gridHeight);
++
++ CPoint originRuler = CPoint(m_rulerPosX, m_rulerPosY) + m_renderOffset;
++ pos = (m_orientation == VERTICAL) ? originRuler.x : originRuler.y;
++ end = (m_orientation == VERTICAL) ? m_posX + m_width : m_posY + m_height;
++ drawOffset = (rulerOffset - cacheBeforeRuler) * m_blockSize - m_programmeScrollOffset;
++ pos += drawOffset;
++ end += cacheAfterRuler * m_rulerLayout->Size(m_orientation == VERTICAL ? HORIZONTAL : VERTICAL);
++
++ if (rulerOffset % m_rulerUnit != 0)
++ {
++ /* first ruler marker starts before current view */
++ int startBlock = rulerOffset - 1;
++
++ while (startBlock % m_rulerUnit != 0)
++ startBlock--;
++
++ int missingSection = rulerOffset - startBlock;
++
++ pos -= missingSection * m_blockSize;
++ }
++ while (pos < end && m_rulerItems.size())
++ {
++ item = m_rulerItems[rulerOffset/m_rulerUnit+1];
++ if (m_orientation == VERTICAL)
++ {
++ g_graphicsContext.SetOrigin(pos, originRuler.y);
++ pos += m_rulerWidth;
++ }
++ else
++ {
++ g_graphicsContext.SetOrigin(originRuler.x, pos);
++ pos += m_rulerHeight;
++ }
++ if (!item->GetLayout())
++ {
++ CGUIListItemLayout *layout = new CGUIListItemLayout(*m_rulerLayout);
++ if (m_orientation == VERTICAL)
++ layout->SetWidth(m_rulerWidth);
++ else
++ layout->SetHeight(m_rulerHeight);
++
++ item->SetLayout(layout);
++ }
++ if (item->GetLayout())
++ {
++ CDirtyRegionList dirtyRegions;
++ item->GetLayout()->Process(item.get(),m_parentID,m_renderTime,dirtyRegions);
++ item->GetLayout()->Render(item.get(), m_parentID);
++ }
++ g_graphicsContext.RestoreOrigin();
++
++ rulerOffset += m_rulerUnit;
++ }
++ g_graphicsContext.RestoreClipRegion();
++
++ /// Render programmes
++ int cacheBeforeProgramme, cacheAfterProgramme;
++ GetProgrammeCacheOffsets(cacheBeforeProgramme, cacheAfterProgramme);
++
++ // Free memory not used on screen
++ if ((int)m_programmeItems.size() > m_ProgrammesPerPage + cacheBeforeProgramme + cacheAfterProgramme)
++ FreeProgrammeMemory(CorrectOffset(blockOffset - cacheBeforeProgramme, 0), CorrectOffset(blockOffset + m_ProgrammesPerPage + 1 + cacheAfterProgramme, 0));
++
++ g_graphicsContext.SetClipRegion(m_gridPosX, m_gridPosY, m_gridWidth, m_gridHeight);
++ CPoint originProgramme = CPoint(m_gridPosX, m_gridPosY) + m_renderOffset;
++ float posA = (m_orientation != VERTICAL) ? originProgramme.y : originProgramme.x;
++ float endA = (m_orientation != VERTICAL) ? m_posY + m_height : m_posX + m_width;
++ float posB = (m_orientation == VERTICAL) ? originProgramme.y : originProgramme.x;
++ float endB = (m_orientation == VERTICAL) ? m_gridPosY + m_gridHeight : m_posX + m_width;
++ endA += cacheAfterProgramme * m_blockSize;
++
++ float DrawOffsetA = blockOffset * m_blockSize - m_programmeScrollOffset;
++ posA += DrawOffsetA;
++ float DrawOffsetB = (chanOffset - cacheBeforeProgramme) * m_channelLayout->Size(m_orientation) - m_channelScrollOffset;
++ posB += DrawOffsetB;
++
++ int channel = chanOffset;
++
++ float focusedPosX = 0;
++ float focusedPosY = 0;
++ float focusedwidth = 0;
++ float focusedheight = 0;
++ while (posB < endB && m_channelItems.size())
++ {
++ if (channel >= (int)m_channelItems.size())
++ break;
++
++ int block = blockOffset;
++ float posA2 = posA;
++
++ CGUIListItemPtr item = m_gridIndex[channel][block].item;
++ if (blockOffset > 0 && item == m_gridIndex[channel][blockOffset-1].item)
++ {
++ /* first program starts before current view */
++ int startBlock = blockOffset - 1;
++ while (m_gridIndex[channel][startBlock].item == item)
++ startBlock--;
++
++ block = startBlock + 1;
++ int missingSection = blockOffset - block;
++ posA2 -= missingSection * m_blockSize;
++ }
++
++ while (posA2 < endA && m_programmeItems.size()) // FOR EACH ITEM ///////////////
++ {
++ item = m_gridIndex[channel][block].item;
++ if (!item || !item.get()->IsFileItem())
++ break;
++
++ bool focused = (channel == m_channelOffset + m_channelCursor) && (item == m_gridIndex[m_channelOffset + m_channelCursor][m_blockOffset + m_blockCursor].item);
++
++ // render our item
++ if (focused)
++ {
++ if (m_orientation == VERTICAL)
++ {
++ focusedPosX = posA2;
++ focusedPosY = posB;
++ }
++ else
++ {
++ focusedPosX = posB;
++ focusedPosY = posA2;
++ }
++ focusedItem = item;
++ focusedwidth = m_gridIndex[channel][block].width;
++ focusedheight = m_gridIndex[channel][block].height;
++ }
++ else
++ {
++ if (m_orientation == VERTICAL)
++ RenderProgrammeItem(posA2, posB, m_gridIndex[channel][block].width, m_gridIndex[channel][block].height, item.get(), focused);
++ else
++ RenderProgrammeItem(posB, posA2, m_gridIndex[channel][block].width, m_gridIndex[channel][block].height, item.get(), focused);
++ }
++
++ // increment our X position
++ if (m_orientation == VERTICAL)
++ {
++ posA2 += m_gridIndex[channel][block].width; // assumes focused & unfocused layouts have equal length
++ block += (int)(m_gridIndex[channel][block].width / m_blockSize);
++ }
++ else
++ {
++ posA2 += m_gridIndex[channel][block].height; // assumes focused & unfocused layouts have equal length
++ block += (int)(m_gridIndex[channel][block].height / m_blockSize);
++ }
++ }
++
++ // increment our Y position
++ channel++;
++ posB += m_orientation == VERTICAL ? m_channelHeight : m_channelWidth;
++ }
++
++ // and render the focused item last (for overlapping purposes)
++ if (focusedItem)
++ RenderProgrammeItem(focusedPosX, focusedPosY, focusedwidth, focusedheight, focusedItem.get(), true);
++
++ g_graphicsContext.RestoreClipRegion();
++
++ CGUIControl::Render();
++}
++
++void CGUIEPGGridContainer::RenderChannelItem(float posX, float posY, CGUIListItem *item, bool focused)
++{
++ if (!m_focusedChannelLayout || !m_channelLayout) return;
++
++ // set the origin
++ g_graphicsContext.SetOrigin(posX, posY);
++
++ if (m_bInvalidated)
++ item->SetInvalid();
++ if (focused)
++ {
++ if (!item->GetFocusedLayout())
++ {
++ CGUIListItemLayout *layout = new CGUIListItemLayout(*m_focusedChannelLayout);
++ item->SetFocusedLayout(layout);
++ }
++ if (item->GetFocusedLayout())
++ {
++ if (item != m_lastChannel || !HasFocus())
++ {
++ item->GetFocusedLayout()->SetFocusedItem(0);
++ }
++ if (item != m_lastChannel && HasFocus())
++ {
++ item->GetFocusedLayout()->ResetAnimation(ANIM_TYPE_UNFOCUS);
++ unsigned int subItem = 1;
++ if (m_lastChannel && m_lastChannel->GetFocusedLayout())
++ subItem = m_lastChannel->GetFocusedLayout()->GetFocusedItem();
++ item->GetFocusedLayout()->SetFocusedItem(subItem ? subItem : 1);
++ }
++ CDirtyRegionList dirtyRegions;
++ item->GetFocusedLayout()->Process(item,m_parentID,m_renderTime,dirtyRegions);
++ item->GetFocusedLayout()->Render(item, m_parentID);
++ }
++ m_lastChannel = item;
++ }
++ else
++ {
++ if (item->GetFocusedLayout())
++ item->GetFocusedLayout()->SetFocusedItem(0); // focus is not set
++ if (!item->GetLayout())
++ {
++ CGUIListItemLayout *layout = new CGUIListItemLayout(*m_channelLayout);
++ item->SetLayout(layout);
++ }
++ if (item->GetFocusedLayout() && item->GetFocusedLayout()->IsAnimating(ANIM_TYPE_UNFOCUS))
++ {
++ CDirtyRegionList dirtyRegions;
++ item->GetFocusedLayout()->Process(item,m_parentID,m_renderTime,dirtyRegions);
++ item->GetFocusedLayout()->Render(item, m_parentID);
++ }
++ else if (item->GetLayout())
++ {
++ CDirtyRegionList dirtyRegions;
++ item->GetLayout()->Process(item,m_parentID,m_renderTime,dirtyRegions);
++ item->GetLayout()->Render(item, m_parentID);
++ }
++ }
++ g_graphicsContext.RestoreOrigin();
++}
++
++void CGUIEPGGridContainer::RenderProgrammeItem(float posX, float posY, float width, float height, CGUIListItem *item, bool focused)
++{
++ if (!m_focusedProgrammeLayout || !m_programmeLayout) return;
++
++ // set the origin
++ g_graphicsContext.SetOrigin(posX, posY);
++
++ if (m_bInvalidated)
++ item->SetInvalid();
++ if (focused)
++ {
++ if (!item->GetFocusedLayout())
++ {
++ CGUIListItemLayout *layout = new CGUIListItemLayout(*m_focusedProgrammeLayout);
++ CFileItem *fileItem = item->IsFileItem() ? (CFileItem *)item : NULL;
++ if (fileItem)
++ {
++ const CEpgInfoTag* tag = fileItem->GetEPGInfoTag();
++ if (m_orientation == VERTICAL)
++ layout->SetWidth(width);
++ else
++ layout->SetHeight(height);
++
++ item->SetProperty("GenreType", tag->GenreType());
++ }
++ item->SetFocusedLayout(layout);
++ }
++ if (item->GetFocusedLayout())
++ {
++ if (item != m_lastItem || !HasFocus())
++ {
++ item->GetFocusedLayout()->SetFocusedItem(0);
++ }
++ if (item != m_lastItem && HasFocus())
++ {
++ item->GetFocusedLayout()->ResetAnimation(ANIM_TYPE_UNFOCUS);
++ unsigned int subItem = 1;
++ if (m_lastItem && m_lastItem->GetFocusedLayout())
++ subItem = m_lastItem->GetFocusedLayout()->GetFocusedItem();
++ item->GetFocusedLayout()->SetFocusedItem(subItem ? subItem : 1);
++ }
++ CDirtyRegionList dirtyRegions;
++ item->GetFocusedLayout()->Process(item,m_parentID,m_renderTime,dirtyRegions);
++ item->GetFocusedLayout()->Render(item, m_parentID);
++ }
++ m_lastItem = item;
++ }
++ else
++ {
++ if (item->GetFocusedLayout())
++ item->GetFocusedLayout()->SetFocusedItem(0); // focus is not set
++ if (!item->GetLayout())
++ {
++ CGUIListItemLayout *layout = new CGUIListItemLayout(*m_programmeLayout);
++ CFileItem *fileItem = item->IsFileItem() ? (CFileItem *)item : NULL;
++ if (fileItem)
++ {
++ const CEpgInfoTag* tag = fileItem->GetEPGInfoTag();
++ if (m_orientation == VERTICAL)
++ layout->SetWidth(width);
++ else
++ layout->SetHeight(height);
++
++ item->SetProperty("GenreType", tag->GenreType());
++ }
++ item->SetLayout(layout);
++ }
++ if (item->GetFocusedLayout() && item->GetFocusedLayout()->IsAnimating(ANIM_TYPE_UNFOCUS))
++ {
++ CDirtyRegionList dirtyRegions;
++ item->GetFocusedLayout()->Process(item,m_parentID,m_renderTime,dirtyRegions);
++ item->GetFocusedLayout()->Render(item, m_parentID);
++ }
++ else if (item->GetLayout())
++ {
++ CDirtyRegionList dirtyRegions;
++ item->GetLayout()->Process(item,m_parentID,m_renderTime,dirtyRegions);
++ item->GetLayout()->Render(item, m_parentID);
++ }
++ }
++ g_graphicsContext.RestoreOrigin();
++}
++
++bool CGUIEPGGridContainer::OnAction(const CAction &action)
++{
++ switch (action.GetID())
++ {
++ case ACTION_MOVE_LEFT:
++ case ACTION_MOVE_RIGHT:
++ case ACTION_MOVE_DOWN:
++ case ACTION_MOVE_UP:
++ { // use base class implementation
++
++ return CGUIControl::OnAction(action);
++ }
++
++ break;
++ case ACTION_PAGE_UP:
++ {
++ if (m_orientation == VERTICAL)
++ {
++ if (m_channelOffset == 0)
++ { // already on the first page, so move to the first item
++ SetChannel(0);
++ }
++ else
++ { // scroll up to the previous page
++ ChannelScroll(-m_channelsPerPage);
++ }
++ }
++ else
++ ProgrammesScroll(-m_blocksPerPage/4);
++
++ return true;
++ }
++
++ break;
++ case ACTION_PAGE_DOWN:
++ {
++ if (m_orientation == VERTICAL)
++ {
++ if (m_channelOffset == m_channels - m_channelsPerPage || m_channels < m_channelsPerPage)
++ { // already at the last page, so move to the last item.
++ SetChannel(m_channels - m_channelOffset - 1);
++ }
++ else
++ { // scroll down to the next page
++ ChannelScroll(m_channelsPerPage);
++ }
++ }
++ else
++ ProgrammesScroll(m_blocksPerPage/4);
++
++ return true;
++ }
++
++ break;
++
++ // smooth scrolling (for analog controls)
++ case ACTION_TELETEXT_RED:
++ case ACTION_TELETEXT_GREEN:
++ case ACTION_SCROLL_UP: // left horizontal scrolling
++ {
++ int blocksToJump = action.GetID() == ACTION_TELETEXT_RED ? m_blocksPerPage/2 : m_blocksPerPage/4;
++
++ m_analogScrollCount += action.GetAmount() * action.GetAmount();
++ bool handled = false;
++
++ while (m_analogScrollCount > 0.4)
++ {
++ handled = true;
++ m_analogScrollCount -= 0.4f;
++
++ if (m_blockOffset > 0 && m_blockCursor <= m_blocksPerPage / 2)
++ {
++ ProgrammesScroll(-blocksToJump);
++ }
++ else if (m_blockCursor > blocksToJump)
++ {
++ SetBlock(m_blockCursor - blocksToJump);
++ }
++ }
++
++ return handled;
++ }
++
++ break;
++
++ case ACTION_TELETEXT_BLUE:
++ case ACTION_TELETEXT_YELLOW:
++ case ACTION_SCROLL_DOWN: // right horizontal scrolling
++ {
++ int blocksToJump = action.GetID() == ACTION_TELETEXT_BLUE ? m_blocksPerPage/2 : m_blocksPerPage/4;
++
++ m_analogScrollCount += action.GetAmount() * action.GetAmount();
++ bool handled = false;
++
++ while (m_analogScrollCount > 0.4)
++ {
++ handled = true;
++ m_analogScrollCount -= 0.4f;
++
++ if (m_blockOffset + m_blocksPerPage < m_blocks && m_blockCursor >= m_blocksPerPage / 2)
++ {
++ ProgrammesScroll(blocksToJump);
++ }
++ else if (m_blockCursor < m_blocksPerPage - blocksToJump && m_blockOffset + m_blockCursor < m_blocks - blocksToJump)
++ {
++ SetBlock(m_blockCursor + blocksToJump);
++ }
++ }
++
++ return handled;
++ }
++
++ break;
++
++ default:
++
++ if (action.GetID())
++ {
++ return OnClick(action.GetID());
++ }
++ }
++
++ return false;
++}
++
++bool CGUIEPGGridContainer::OnMessage(CGUIMessage& message)
++{
++ if (message.GetControlId() == GetID())
++ {
++ if (message.GetMessage() == GUI_MSG_ITEM_SELECTED)
++ {
++ message.SetParam1(GetSelectedItem());
++ return true;
++ }
++ else if (message.GetMessage() == GUI_MSG_LABEL_BIND && message.GetPointer())
++ {
++ Reset();
++ CFileItemList *items = (CFileItemList *)message.GetPointer();
++
++ /* Create Channel items */
++ int iLastChannelNumber = -1;
++ ItemsPtr itemsPointer;
++ itemsPointer.start = 0;
++ for (int i = 0; i < items->Size(); ++i)
++ {
++ const CEpgInfoTag* tag = items->Get(i)->GetEPGInfoTag();
++ if (!tag || !tag->HasPVRChannel())
++ continue;
++
++ int iCurrentChannelNumber = tag->PVRChannelNumber();
++ if (iCurrentChannelNumber != iLastChannelNumber)
++ {
++ const CPVRChannel *channel = tag->ChannelTag();
++ if (!channel)
++ continue;
++
++ if (i > 0)
++ {
++ itemsPointer.stop = i-1;
++ m_epgItemsPtr.push_back(itemsPointer);
++ itemsPointer.start = i;
++ }
++ iLastChannelNumber = iCurrentChannelNumber;
++ CGUIListItemPtr item(new CFileItem(*channel));
++ m_channelItems.push_back(item);
++ }
++ }
++ if (m_epgItemsPtr.size() > 0)
++ {
++ itemsPointer.stop = items->Size()-1;
++ m_epgItemsPtr.push_back(itemsPointer);
++ }
++
++ /* Create programme items */
++ for (int i = 0; i < items->Size(); i++)
++ m_programmeItems.push_back(items->Get(i));
++
++ ClearGridIndex();
++ m_gridIndex = (struct GridItemsPtr **) calloc(1,m_channelItems.size()*sizeof(struct GridItemsPtr));
++ if (m_gridIndex != NULL)
++ {
++ for (unsigned int i = 0; i < m_channelItems.size(); i++)
++ {
++ m_gridIndex[i] = (struct GridItemsPtr*) calloc(1,MAXBLOCKS*sizeof(struct GridItemsPtr));
++ }
++ }
++
++ UpdateLayout(true); // true to refresh all items
++
++ /* Create Ruler items */
++ CDateTime ruler = m_gridStart;
++ CDateTimeSpan unit(0, 0, m_rulerUnit * MINSPERBLOCK, 0);
++ CGUIListItemPtr rulerItem(new CFileItem(ruler.GetAsLocalizedDate(true, true)));
++ rulerItem->SetProperty("DateLabel", true);
++ m_rulerItems.push_back(rulerItem);
++
++ for (; ruler < m_gridEnd; ruler += unit)
++ {
++ CGUIListItemPtr rulerItem(new CFileItem(ruler.GetAsLocalizedTime("", false)));
++ rulerItem->SetLabel2(ruler.GetAsLocalizedDate(true, true));
++ m_rulerItems.push_back(rulerItem);
++ }
++
++ UpdateItems();
++ //SelectItem(message.GetParam1());
++ return true;
++ }
++ else if (message.GetMessage() == GUI_MSG_REFRESH_LIST)
++ { // update our list contents
++ for (unsigned int i = 0; i < m_channelItems.size(); ++i)
++ m_channelItems[i]->SetInvalid();
++ for (unsigned int i = 0; i < m_programmeItems.size(); ++i)
++ m_programmeItems[i]->SetInvalid();
++ for (unsigned int i = 0; i < m_rulerItems.size(); ++i)
++ m_rulerItems[i]->SetInvalid();
++ }
++ }
++
++ return CGUIControl::OnMessage(message);
++}
++
++void CGUIEPGGridContainer::UpdateItems()
++{
++ CDateTimeSpan blockDuration, gridDuration;
++
++ /* check for invalid start and end time */
++ if (m_gridStart >= m_gridEnd)
++ {
++ CLog::Log(LOGERROR, "CGUIEPGGridContainer - %s - invalid start and end time set", __FUNCTION__);
++ CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), GetParentID()); // message the window
++ SendWindowMessage(msg);
++ return;
++ }
++
++ gridDuration = m_gridEnd - m_gridStart;
++
++ m_blocks = (gridDuration.GetDays()*24*60 + gridDuration.GetHours()*60 + gridDuration.GetMinutes()) / MINSPERBLOCK;
++ if (m_blocks >= MAXBLOCKS)
++ m_blocks = MAXBLOCKS;
++
++ /* if less than one page, can't display grid */
++ if (m_blocks < m_blocksPerPage)
++ {
++ CLog::Log(LOGERROR, "(%s) - Less than one page of data available.", __FUNCTION__);
++ CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), GetParentID()); // message the window
++ SendWindowMessage(msg);
++ return;
++ }
++
++ blockDuration.SetDateTimeSpan(0, 0, MINSPERBLOCK, 0);
++
++ long tick(XbmcThreads::SystemClockMillis());
++
++ for (unsigned int row = 0; row < m_channelItems.size(); ++row)
++ {
++ CDateTime gridCursor = m_gridStart; //reset cursor for new channel
++ unsigned long progIdx = m_epgItemsPtr[row].start;
++ unsigned long lastIdx = m_epgItemsPtr[row].stop;
++ int channelnum = ((CFileItem *)m_programmeItems[progIdx].get())->GetEPGInfoTag()->PVRChannelNumber();
++
++ /** FOR EACH BLOCK **********************************************************************/
++
++ for (int block = 0; block < m_blocks; block++)
++ {
++ while (progIdx <= lastIdx)
++ {
++ CGUIListItemPtr item = m_programmeItems[progIdx];
++ const CEpgInfoTag* tag = ((CFileItem *)item.get())->GetEPGInfoTag();
++ if (tag == NULL)
++ progIdx++;
++
++ if (tag->PVRChannelNumber() != channelnum)
++ break;
++
++ if (m_gridEnd <= tag->StartAsLocalTime())
++ {
++ break;
++ }
++ else if (gridCursor >= tag->EndAsLocalTime())
++ {
++ progIdx++;
++ }
++ else if (gridCursor < tag->EndAsLocalTime())
++ {
++ m_gridIndex[row][block].item = item;
++ break;
++ }
++ else
++ {
++ progIdx++;
++ }
++ }
++
++ gridCursor += blockDuration;
++ }
++
++ /** FOR EACH BLOCK **********************************************************************/
++ int itemSize = 1; // size of the programme in blocks
++ int savedBlock = 0;
++
++ for (int block = 0; block < m_blocks; block++)
++ {
++ if (m_gridIndex[row][block].item != m_gridIndex[row][block+1].item)
++ {
++ if (!m_gridIndex[row][block].item)
++ {
++ CEpgInfoTag broadcast;
++ CFileItemPtr unknown(new CFileItem(broadcast));
++ for (int i = block ; i > block - itemSize; i--)
++ {
++ m_gridIndex[row][i].item = unknown;
++ }
++ }
++
++ CGUIListItemPtr item = m_gridIndex[row][block].item;
++ CFileItem *fileItem = (CFileItem *)item.get();
++
++ m_gridIndex[row][savedBlock].item->SetProperty("GenreType", fileItem->GetEPGInfoTag()->GenreType());
++ if (m_orientation == VERTICAL)
++ {
++ m_gridIndex[row][savedBlock].width = itemSize*m_blockSize;
++ m_gridIndex[row][savedBlock].height = m_channelHeight;
++ }
++ else
++ {
++ m_gridIndex[row][savedBlock].width = m_channelWidth;
++ m_gridIndex[row][savedBlock].height = itemSize*m_blockSize;
++ }
++
++ itemSize = 1;
++ savedBlock = block+1;
++ }
++ else
++ {
++ itemSize++;
++ }
++ }
++ }
++
++ /******************************************* END ******************************************/
++
++ CLog::Log(LOGDEBUG, "%s completed successfully in %u ms", __FUNCTION__, (unsigned int)(XbmcThreads::SystemClockMillis()-tick));
++
++ m_channels = (int)m_epgItemsPtr.size();
++ m_item = GetItem(m_channelCursor);
++ if (m_item)
++ m_blockCursor = GetBlock(m_item->item, m_channelCursor);
++
++ SetInvalid();
++}
++
++void CGUIEPGGridContainer::ChannelScroll(int amount)
++{
++ // increase or decrease the vertical offset
++ int offset = m_channelOffset + amount;
++
++ if (offset > m_channels - m_channelsPerPage)
++ {
++ offset = m_channels - m_channelsPerPage;
++ }
++
++ if (offset < 0) offset = 0;
++
++ ScrollToChannelOffset(offset);
++}
++
++void CGUIEPGGridContainer::ProgrammesScroll(int amount)
++{
++ // increase or decrease the horizontal offset
++ int offset = m_blockOffset + amount;
++
++ if (offset > m_blocks - m_blocksPerPage)
++ {
++ offset = m_blocks - m_blocksPerPage;
++ }
++
++ if (offset < 0) offset = 0;
++
++ ScrollToBlockOffset(offset);
++}
++
++bool CGUIEPGGridContainer::MoveChannel(bool direction)
++{
++ if (direction)
++ {
++ if (m_channelCursor > 0)
++ {
++ SetChannel(m_channelCursor - 1);
++ }
++ else if (m_channelCursor == 0 && m_channelOffset)
++ {
++ ScrollToChannelOffset(m_channelOffset - 1);
++ SetChannel(0);
++ }
++ else if (m_channelWrapAround)
++ {
++ int offset = m_channels - m_channelsPerPage;
++
++ if (offset < 0) offset = 0;
++
++ SetChannel(m_channels - offset - 1);
++
++ ScrollToChannelOffset(offset);
++ }
++ else
++ return false;
++ }
++ else
++ {
++ if (m_channelOffset + m_channelCursor + 1 < m_channels)
++ {
++ if (m_channelCursor + 1 < m_channelsPerPage)
++ {
++ SetChannel(m_channelCursor + 1);
++ }
++ else
++ {
++ ScrollToChannelOffset(m_channelOffset + 1);
++ SetChannel(m_channelsPerPage - 1);
++ }
++ }
++ else if (m_channelWrapAround)
++ {
++ SetChannel(0);
++ ScrollToChannelOffset(0);
++ }
++ else
++ return false;
++ }
++ return true;
++}
++
++bool CGUIEPGGridContainer::MoveProgrammes(bool direction)
++{
++ if (!m_gridIndex || !m_item)
++ return false;
++
++ if (direction)
++ {
++ if (m_channelCursor + m_channelOffset < 0 || m_blockOffset < 0)
++ return false;
++
++ if (m_item->item != m_gridIndex[m_channelCursor + m_channelOffset][m_blockOffset].item)
++ {
++ // this is not first item on page
++ m_item = GetPrevItem(m_channelCursor);
++ m_blockCursor = GetBlock(m_item->item, m_channelCursor);
++ }
++ else if (m_blockCursor <= 0 && m_blockOffset)
++ {
++ // we're at the left edge and offset
++ int itemSize = GetItemSize(m_item);
++ int block = GetRealBlock(m_item->item, m_channelCursor);
++
++ if (block < m_blockOffset) /* current item begins before current offset, keep selected */
++ {
++ if (itemSize > m_blocksPerPage) /* current item is longer than one page, scroll one page left */
++ {
++ m_blockOffset < m_blocksPerPage ? block = 0 : block = m_blockOffset - m_blocksPerPage; // number blocks left < m_blocksPerPAge
++ ScrollToBlockOffset(block);
++ SetBlock(0);
++ }
++ else /* current item is shorter than one page, scroll left to start of item */
++ {
++ ScrollToBlockOffset(block); // -1?
++ SetBlock(0); // align cursor to left edge
++ }
++ }
++ else /* current item starts on this page's edge, select the previous item */
++ {
++ m_item = GetPrevItem(m_channelCursor);
++ itemSize = GetItemSize(m_item);
++
++ if (itemSize > m_blocksPerPage) // previous item is longer than one page, scroll left to last page of item */
++ {
++ ScrollToBlockOffset(m_blockOffset - m_blocksPerPage); // left one whole page
++ //SetBlock(m_blocksPerPage -1 ); // helps navigation by setting cursor to far right edge
++ SetBlock(0); // align cursor to left edge
++ }
++ else /* previous item is shorter than one page, scroll left to start of item */
++ {
++ ScrollToBlockOffset(m_blockOffset - itemSize);
++ SetBlock(0); //should be zero
++ }
++ }
++ }
++ else
++ return false;
++ }
++ else
++ {
++ if (m_item->item != m_gridIndex[m_channelCursor + m_channelOffset][m_blocksPerPage + m_blockOffset - 1].item)
++ {
++ // this is not last item on page
++ m_item = GetNextItem(m_channelCursor);
++ m_blockCursor = GetBlock(m_item->item, m_channelCursor);
++ }
++ else if ((m_blockOffset != m_blocks - m_blocksPerPage) && m_blocks > m_blocksPerPage)
++ {
++ // at right edge, more than one page and not at maximum offset
++ int itemSize = GetItemSize(m_item);
++ int block = GetRealBlock(m_item->item, m_channelCursor);
++
++ if (itemSize > m_blocksPerPage - m_blockCursor) // current item extends into next page, keep selected
++ {
++ if (itemSize > m_blocksPerPage) // current item is longer than one page, scroll one page right
++ {
++ if (m_blockOffset && m_blockOffset + m_blocksPerPage > m_blocks)
++ block = m_blocks - m_blocksPerPage;
++ else
++ block = m_blockOffset + m_blocksPerPage;
++
++ ScrollToBlockOffset(block);
++
++ SetBlock(0);
++ }
++ else // current item is shorter than one page, scroll so end of item sits on end of grid
++ {
++ ScrollToBlockOffset(block + itemSize - m_blocksPerPage);
++ SetBlock(GetBlock(m_item->item, m_channelCursor)); /// change to middle block of item?
++ }
++ }
++ else // current item finishes on this page's edge, select the next item
++ {
++ m_item = GetNextItem(m_channelCursor);
++ itemSize = GetItemSize(m_item);
++
++ if (itemSize > m_blocksPerPage) // next item is longer than one page, scroll to first page of this item
++ {
++ ScrollToBlockOffset(m_blockOffset + m_blocksPerPage);
++ SetBlock(0);
++ }
++ else // next item is shorter than one page, scroll so end of item sits on end of grid
++ {
++ ScrollToBlockOffset(m_blockOffset + itemSize);
++ SetBlock(m_blocksPerPage - itemSize); /// change to middle block of item?
++ }
++ }
++ }
++ else
++ return false;
++ }
++ return true;
++}
++
++void CGUIEPGGridContainer::OnUp()
++{
++ if (m_orientation == VERTICAL)
++ {
++ if (!MoveChannel(true))
++ CGUIControl::OnUp();
++ }
++ else
++ {
++ if (!MoveProgrammes(true))
++ CGUIControl::OnUp();
++ }
++}
++
++void CGUIEPGGridContainer::OnDown()
++{
++ if (m_orientation == VERTICAL)
++ {
++ if (!MoveChannel(false))
++ CGUIControl::OnDown();
++ }
++ else
++ {
++ if (!MoveProgrammes(false))
++ CGUIControl::OnDown();
++ }
++}
++
++void CGUIEPGGridContainer::OnLeft()
++{
++ if (m_orientation == VERTICAL)
++ {
++ if (!MoveProgrammes(true))
++ CGUIControl::OnLeft();
++ }
++ else
++ {
++ if (!MoveChannel(true))
++ CGUIControl::OnLeft();
++ }
++}
++
++void CGUIEPGGridContainer::OnRight()
++{
++ if (m_orientation == VERTICAL)
++ {
++ if (!MoveProgrammes(false))
++ CGUIControl::OnRight();
++ }
++ else
++ {
++ if (!MoveChannel(false))
++ CGUIControl::OnRight();
++ }
++}
++
++void CGUIEPGGridContainer::SetChannel(const CStdString &channel)
++{
++ int iChannelIndex(-1);
++ for (unsigned int iIndex = 0; iIndex < m_channelItems.size(); iIndex++)
++ {
++ CStdString strPath = m_channelItems[iIndex]->GetProperty("path").asString(StringUtils::EmptyString);
++ if (strPath == channel)
++ {
++ iChannelIndex = iIndex;
++ break;
++ }
++ }
++
++ if (iChannelIndex >= 0)
++ ScrollToChannelOffset(iChannelIndex);
++}
++
++void CGUIEPGGridContainer::SetChannel(const CPVRChannel &channel)
++{
++ int iChannelIndex(-1);
++ for (unsigned int iIndex = 0; iIndex < m_channelItems.size(); iIndex++)
++ {
++ int iChannelId = m_channelItems[iIndex]->GetProperty("channelid").asInteger(-1);
++ if (iChannelId == channel.ChannelID())
++ {
++ iChannelIndex = iIndex;
++ break;
++ }
++ }
++
++ if (iChannelIndex >= 0)
++ ScrollToChannelOffset(iChannelIndex);
++}
++
++void CGUIEPGGridContainer::SetChannel(int channel)
++{
++ if (m_blockCursor + m_blockOffset == 0 || m_blockOffset + m_blockCursor + GetItemSize(m_item) == m_blocks)
++ {
++ m_item = GetItem(channel);
++ if (m_item)
++ {
++ m_blockCursor = GetBlock(m_item->item, channel);
++ m_channelCursor = channel;
++ }
++ return;
++ }
++
++ /* basic checks failed, need to correctly identify nearest item */
++ m_item = GetClosestItem(channel);
++ if (m_item)
++ {
++ m_channelCursor = channel;
++ m_blockCursor = GetBlock(m_item->item, m_channelCursor);
++ }
++}
++
++void CGUIEPGGridContainer::SetBlock(int block)
++{
++ m_blockCursor = block;
++ m_item = GetItem(m_channelCursor);
++}
++
++CGUIListItemLayout *CGUIEPGGridContainer::GetFocusedLayout() const
++{
++ CGUIListItemPtr item = GetListItem(0);
++
++ if (item.get()) return item->GetFocusedLayout();
++
++ return NULL;
++}
++
++bool CGUIEPGGridContainer::SelectItemFromPoint(const CPoint &point)
++{
++ /* point has already had origin set to m_posX, m_posY */
++ if (!m_focusedProgrammeLayout || !m_programmeLayout)
++ return false;
++
++ int channel = (int)(point.y / m_channelHeight);
++ int block = (int)(point.x / m_blockSize);
++
++ if (channel > m_channelsPerPage) channel = m_channelsPerPage - 1;
++ if (channel >= m_channels) channel = m_channels - 1;
++ if (channel < 0) channel = 0;
++ if (block > m_blocksPerPage) block = m_blocksPerPage - 1;
++ if (block < 0) block = 0;
++
++ SetChannel(channel);
++ SetBlock(block);
++ return true;
++}
++
++EVENT_RESULT CGUIEPGGridContainer::OnMouseEvent(const CPoint &point, const CMouseEvent &event)
++{
++ switch (event.m_id)
++ {
++ case ACTION_MOUSE_LEFT_CLICK:
++ OnMouseClick(0, point);
++ return EVENT_RESULT_HANDLED;
++ case ACTION_MOUSE_RIGHT_CLICK:
++ OnMouseClick(1, point);
++ return EVENT_RESULT_HANDLED;
++ case ACTION_MOUSE_DOUBLE_CLICK:
++ OnMouseDoubleClick(0, point);
++ return EVENT_RESULT_HANDLED;
++ case ACTION_MOUSE_WHEEL_UP:
++ OnMouseWheel(-1, point);
++ return EVENT_RESULT_HANDLED;
++ case ACTION_MOUSE_WHEEL_DOWN:
++ OnMouseWheel(1, point);
++ return EVENT_RESULT_HANDLED;
++ default:
++ return EVENT_RESULT_UNHANDLED;
++ }
++}
++
++bool CGUIEPGGridContainer::OnMouseOver(const CPoint &point)
++{
++ // select the item under the pointer
++ SelectItemFromPoint(point - CPoint(m_gridPosX, m_posY + m_rulerHeight));
++ return CGUIControl::OnMouseOver(point);
++}
++
++bool CGUIEPGGridContainer::OnMouseClick(int dwButton, const CPoint &point)
++{
++ if (SelectItemFromPoint(point - CPoint(m_gridPosX, m_posY + m_rulerHeight)))
++ { // send click message to window
++ OnClick(ACTION_MOUSE_LEFT_CLICK + dwButton);
++ return true;
++ }
++
++ return false;
++}
++
++bool CGUIEPGGridContainer::OnMouseDoubleClick(int dwButton, const CPoint &point)
++{
++ if (SelectItemFromPoint(point - CPoint(m_gridPosX, m_posY + m_rulerHeight)))
++ { // send double click message to window
++ OnClick(ACTION_MOUSE_DOUBLE_CLICK + dwButton);
++ return true;
++ }
++
++ return false;
++}
++
++bool CGUIEPGGridContainer::OnClick(int actionID)
++{
++ int subItem = 0;
++
++ if (actionID == ACTION_SELECT_ITEM || actionID == ACTION_MOUSE_LEFT_CLICK)
++ {
++ // grab the currently focused subitem (if applicable)
++ CGUIListItemLayout *focusedLayout = GetFocusedLayout();
++
++ if (focusedLayout)
++ subItem = focusedLayout->GetFocusedItem();
++ }
++
++ // Don't know what to do, so send to our parent window.
++ CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID(), actionID, subItem);
++ return SendWindowMessage(msg);
++}
++
++bool CGUIEPGGridContainer::OnMouseWheel(char wheel, const CPoint &point)
++{
++ ///doesn't work while an item is selected?
++ ProgrammesScroll(-wheel);
++ return true;
++}
++
++int CGUIEPGGridContainer::GetSelectedItem() const
++{
++ if (!m_gridIndex || !m_epgItemsPtr.size())
++ return 0;
++
++ CGUIListItemPtr currentItem = m_gridIndex[m_channelCursor + m_channelOffset][m_blockCursor + m_blockOffset].item;
++ if (!currentItem)
++ return 0;
++
++ for (int i = 0; i < (int)m_programmeItems.size(); i++)
++ {
++ if (currentItem == m_programmeItems[i])
++ return i;
++ }
++ return 0;
++}
++
++CGUIListItemPtr CGUIEPGGridContainer::GetListItem(int offset) const
++{
++ if (!m_epgItemsPtr.size())
++ return CGUIListItemPtr();
++
++ return m_item->item;
++}
++
++GridItemsPtr *CGUIEPGGridContainer::GetClosestItem(const int &channel)
++{
++ GridItemsPtr *closest = GetItem(channel);
++
++ if(!closest)
++ return NULL;
++
++ int block = GetBlock(closest->item, channel);
++ int left; // num blocks to start of previous item
++ int right; // num blocks to start of next item
++
++ if (block == m_blockCursor)
++ return closest; // item & m_item start together
++
++ if (block + GetItemSize(closest) == m_blockCursor + GetItemSize(m_item))
++ return closest; // closest item ends when current does
++
++ if (block > m_blockCursor) // item starts after m_item
++ {
++ left = m_blockCursor - GetBlock(closest->item, channel);
++ right = block - m_blockCursor;
++ }
++ else
++ {
++ left = m_blockCursor - block;
++ right = GetBlock(GetNextItem(channel)->item, channel) - m_blockCursor;
++ }
++
++ if (right <= SHORTGAP && right <= left && m_blockCursor + right < m_blocksPerPage)
++ return &m_gridIndex[channel + m_channelOffset][m_blockCursor + right + m_blockOffset];
++
++ return &m_gridIndex[channel + m_channelOffset][m_blockCursor - left + m_blockOffset];
++}
++
++int CGUIEPGGridContainer::GetItemSize(GridItemsPtr *item)
++{
++ if (!item)
++ return (int) m_blockSize; /// stops it crashing
++
++ return (int) ((m_orientation == VERTICAL ? item->width : item->height) / m_blockSize);
++}
++
++int CGUIEPGGridContainer::GetBlock(const CGUIListItemPtr &item, const int &channel)
++{
++ if (!item)
++ return 0;
++
++ return GetRealBlock(item, channel) - m_blockOffset;
++}
++
++int CGUIEPGGridContainer::GetRealBlock(const CGUIListItemPtr &item, const int &channel)
++{
++ int block = 0;
++
++ while (m_gridIndex[channel + m_channelOffset][block].item != item && block < m_blocks)
++ block++;
++
++ return block;
++}
++
++GridItemsPtr *CGUIEPGGridContainer::GetNextItem(const int &channel)
++{
++ int i = m_blockCursor;
++
++ while (m_gridIndex[channel + m_channelOffset][i + m_blockOffset].item == m_gridIndex[channel + m_channelOffset][m_blockCursor + m_blockOffset].item && i < m_blocksPerPage)
++ i++;
++
++ return &m_gridIndex[channel + m_channelOffset][i + m_blockOffset];
++}
++
++GridItemsPtr *CGUIEPGGridContainer::GetPrevItem(const int &channel)
++{
++ int i = m_blockCursor;
++
++ while (m_gridIndex[channel + m_channelOffset][i + m_blockOffset].item == m_gridIndex[channel + m_channelOffset][m_blockCursor + m_blockOffset].item && i > 0)
++ i--;
++
++ return &m_gridIndex[channel + m_channelOffset][i + m_blockOffset];
++
++// return &m_gridIndex[channel + m_channelOffset][m_blockCursor + m_blockOffset - 1];
++}
++
++GridItemsPtr *CGUIEPGGridContainer::GetItem(const int &channel)
++{
++ if ( (channel >= 0) && (channel < m_channels) )
++ return &m_gridIndex[channel + m_channelOffset][m_blockCursor + m_blockOffset];
++ else
++ return NULL;
++}
++
++void CGUIEPGGridContainer::SetFocus(bool bOnOff)
++{
++ if (bOnOff != HasFocus())
++ {
++ SetInvalid();
++ /*m_lastItem.reset();
++ m_lastChannel.reset();*/
++ }
++
++ CGUIControl::SetFocus(bOnOff);
++}
++
++void CGUIEPGGridContainer::DoRender()
++{
++ CGUIControl::DoRender();
++ m_wasReset = false;
++}
++
++void CGUIEPGGridContainer::ScrollToChannelOffset(int offset)
++{
++ float size = m_programmeLayout->Size(VERTICAL);
++ int range = m_channelsPerPage / 4;
++
++ if (range <= 0) range = 1;
++
++ if (offset * size < m_channelScrollOffset && m_channelScrollOffset - offset * size > size * range)
++ { // scrolling up, and we're jumping more than 0.5 of a screen
++ m_channelScrollOffset = (offset + range) * size;
++ }
++
++ if (offset * size > m_channelScrollOffset && offset * size - m_channelScrollOffset > size * range)
++ { // scrolling down, and we're jumping more than 0.5 of a screen
++ m_channelScrollOffset = (offset - range) * size;
++ }
++
++ m_channelScrollSpeed = (offset * size - m_channelScrollOffset) / m_scrollTime;
++
++ m_channelOffset = offset;
++}
++
++void CGUIEPGGridContainer::ScrollToBlockOffset(int offset)
++{
++ float size = m_blockSize;
++ int range = m_blocksPerPage / 1;
++
++ if (range <= 0) range = 1;
++
++ if (offset * size < m_programmeScrollOffset && m_programmeScrollOffset - offset * size > size * range)
++ { // scrolling left, and we're jumping more than 0.5 of a screen
++ m_programmeScrollOffset = (offset + range) * size;
++ }
++
++ if (offset * size > m_programmeScrollOffset && offset * size - m_programmeScrollOffset > size * range)
++ { // scrolling right, and we're jumping more than 0.5 of a screen
++ m_programmeScrollOffset = (offset - range) * size;
++ }
++
++ m_programmeScrollSpeed = (offset * size - m_programmeScrollOffset) / m_scrollTime;
++
++ m_blockOffset = offset;
++}
++
++void CGUIEPGGridContainer::ValidateOffset()
++{
++ if (!m_programmeLayout)
++ return;
++
++ if (m_channelOffset > m_channels - m_channelsPerPage)
++ {
++ m_channelOffset = m_channels - m_channelsPerPage;
++ m_channelScrollOffset = m_channelOffset * m_channelHeight;
++ }
++
++ if (m_channelOffset < 0)
++ {
++ m_channelOffset = 0;
++ m_channelScrollOffset = 0;
++ }
++
++ if (m_blockOffset > m_blocks - m_blocksPerPage)
++ {
++ m_blockOffset = m_blocks - m_blocksPerPage;
++ m_programmeScrollOffset = m_blockOffset * m_blockSize;
++ }
++
++ if (m_blockOffset < 0)
++ {
++ m_blockOffset = 0;
++ m_programmeScrollOffset = 0;
++ }
++}
++
++void CGUIEPGGridContainer::LoadLayout(TiXmlElement *layout)
++{
++ /* layouts for the channel column */
++ TiXmlElement *itemElement = layout->FirstChildElement("channellayout");
++ while (itemElement)
++ { // we have a new item layout
++ CGUIListItemLayout itemLayout;
++ itemLayout.LoadLayout(itemElement, GetParentID(), false);
++ m_channelLayouts.push_back(itemLayout);
++ itemElement = itemElement->NextSiblingElement("channellayout");
++ }
++ itemElement = layout->FirstChildElement("focusedchannellayout");
++ while (itemElement)
++ { // we have a new item layout
++ CGUIListItemLayout itemLayout;
++ itemLayout.LoadLayout(itemElement, GetParentID(), true);
++ m_focusedChannelLayouts.push_back(itemLayout);
++ itemElement = itemElement->NextSiblingElement("focusedchannellayout");
++ }
++
++ /* layouts for the grid items */
++ itemElement = layout->FirstChildElement("focusedlayout");
++ while (itemElement)
++ {
++ CGUIListItemLayout itemLayout;
++ itemLayout.LoadLayout(itemElement, GetParentID(), true);
++ m_focusedProgrammeLayouts.push_back(itemLayout);
++ itemElement = itemElement->NextSiblingElement("focusedlayout");
++ }
++ itemElement = layout->FirstChildElement("itemlayout");
++ while (itemElement)
++ {
++ CGUIListItemLayout itemLayout;
++ itemLayout.LoadLayout(itemElement, GetParentID(), false);
++ m_programmeLayouts.push_back(itemLayout);
++ itemElement = itemElement->NextSiblingElement("itemlayout");
++ }
++
++ /* layout for the timeline above the grid */
++ itemElement = layout->FirstChildElement("rulerlayout");
++ while (itemElement)
++ {
++ CGUIListItemLayout itemLayout;
++ itemLayout.LoadLayout(itemElement, GetParentID(), false);
++ m_rulerLayouts.push_back(itemLayout);
++ itemElement = itemElement->NextSiblingElement("rulerlayout");
++ }
++}
++
++void CGUIEPGGridContainer::UpdateLayout(bool updateAllItems)
++{
++ // if container is invalid, either new data has arrived, or m_blockSize has changed
++ // need to run UpdateItems rather than CalculateLayout?
++ if (updateAllItems)
++ { // free memory of items
++ for (iItems it = m_channelItems.begin(); it != m_channelItems.end(); it++)
++ (*it)->FreeMemory();
++ for (iItems it = m_rulerItems.begin(); it != m_rulerItems.end(); it++)
++ (*it)->FreeMemory();
++ for (iItems it = m_programmeItems.begin(); it != m_programmeItems.end(); it++)
++ (*it)->FreeMemory();
++ }
++
++ // and recalculate the layout
++ CalculateLayout();
++}
++
++CStdString CGUIEPGGridContainer::GetDescription() const
++{
++ CStdString strLabel;
++ int item = GetSelectedItem();
++ if (item >= 0 && item < (int)m_programmeItems.size())
++ {
++ CGUIListItemPtr pItem = m_programmeItems[item];
++ strLabel = pItem->GetLabel();
++ }
++ return strLabel;
++}
++
++void CGUIEPGGridContainer::ClearGridIndex(void)
++{
++ if (m_gridIndex)
++ {
++ for (unsigned int i = 0; i < m_channelItems.size(); i++)
++ {
++ for (int block = 0; block < m_blocks; block++)
++ {
++ if (m_gridIndex[i][block].item)
++ m_gridIndex[i][block].item.get()->ClearProperties();
++ }
++ free(m_gridIndex[i]);
++ }
++ free(m_gridIndex);
++ }
++}
++
++void CGUIEPGGridContainer::Reset()
++{
++ ClearGridIndex();
++
++ m_wasReset = true;
++ m_channelItems.clear();
++ m_programmeItems.clear();
++ m_rulerItems.clear();
++ m_epgItemsPtr.clear();
++
++ m_lastItem = NULL;
++ m_lastChannel = NULL;
++ m_gridIndex = NULL;
++}
++
++void CGUIEPGGridContainer::GoToBegin()
++{
++ ScrollToBlockOffset(0);
++}
++
++void CGUIEPGGridContainer::GoToEnd()
++{
++ ScrollToBlockOffset(m_blocks - m_blocksPerPage);
++}
++
++void CGUIEPGGridContainer::SetStartEnd(CDateTime start, CDateTime end)
++{
++ m_gridStart = CDateTime(start.GetYear(), start.GetMonth(), start.GetDay(), start.GetHour(), start.GetMinute() >= 30 ? 30 : 0, 0);
++ m_gridEnd = CDateTime(end.GetYear(), end.GetMonth(), end.GetDay(), end.GetHour(), end.GetMinute() >= 30 ? 30 : 0, 0);
++
++ CLog::Log(LOGDEBUG, "CGUIEPGGridContainer - %s - start=%s end=%s",
++ __FUNCTION__, m_gridStart.GetAsLocalizedDateTime(false, true).c_str(), m_gridEnd.GetAsLocalizedDateTime(false, true).c_str());
++}
++
++void CGUIEPGGridContainer::CalculateLayout()
++{
++ CGUIListItemLayout *oldFocusedChannelLayout = m_focusedChannelLayout;
++ CGUIListItemLayout *oldChannelLayout = m_channelLayout;
++ CGUIListItemLayout *oldFocusedProgrammeLayout = m_focusedProgrammeLayout;
++ CGUIListItemLayout *oldProgrammeLayout = m_programmeLayout;
++ CGUIListItemLayout *oldRulerLayout = m_rulerLayout;
++ GetCurrentLayouts();
++
++ if (!m_focusedProgrammeLayout || !m_programmeLayout || !m_focusedChannelLayout || !m_channelLayout || !m_rulerLayout)
++ return;
++
++ if (oldChannelLayout == m_channelLayout && oldFocusedChannelLayout == m_focusedChannelLayout &&
++ oldProgrammeLayout == m_programmeLayout && oldFocusedProgrammeLayout == m_focusedProgrammeLayout &&
++ oldRulerLayout == m_rulerLayout)
++ return; // nothing has changed, so don't update stuff
++
++ m_channelHeight = m_channelLayout->Size(VERTICAL);
++ m_channelWidth = m_channelLayout->Size(HORIZONTAL);
++ if (m_orientation == VERTICAL)
++ {
++ m_rulerHeight = m_rulerLayout->Size(VERTICAL);
++ m_gridPosX = m_posX + m_channelWidth;
++ m_gridPosY = m_posY + m_rulerHeight;
++ m_gridWidth = m_width - m_channelWidth;
++ m_gridHeight = m_height - m_rulerHeight;
++ m_blockSize = m_gridWidth / m_blocksPerPage;
++ m_rulerWidth = m_rulerUnit * m_blockSize;
++ m_channelPosX = m_posX;
++ m_channelPosY = m_posY + m_rulerHeight;
++ m_rulerPosX = m_posX + m_channelWidth;
++ m_rulerPosY = m_posY;
++ m_channelsPerPage = (int)(m_gridHeight / m_channelHeight);
++ m_ProgrammesPerPage = (int)(m_gridWidth / m_blockSize) + 1;
++ }
++ else
++ {
++ m_rulerWidth = m_rulerLayout->Size(HORIZONTAL);
++ m_gridPosX = m_posX + m_rulerWidth;
++ m_gridPosY = m_posY + m_channelHeight;
++ m_gridWidth = m_width - m_rulerWidth;
++ m_gridHeight = m_height - m_channelHeight;
++ m_blockSize = m_gridHeight / m_blocksPerPage;
++ m_rulerHeight = m_rulerUnit * m_blockSize;
++ m_channelPosX = m_posX + m_rulerWidth;
++ m_channelPosY = m_posY;
++ m_rulerPosX = m_posX;
++ m_rulerPosY = m_posY + m_channelHeight;
++ m_channelsPerPage = (int)(m_gridWidth / m_channelWidth);
++ m_ProgrammesPerPage = (int)(m_gridHeight / m_blockSize) + 1;
++ }
++
++ // ensure that the scroll offsets are a multiple of our sizes
++ m_channelScrollOffset = m_channelOffset * m_programmeLayout->Size(m_orientation);
++ m_programmeScrollOffset = m_blockOffset * m_blockSize;
++}
++
++void CGUIEPGGridContainer::UpdateScrollOffset()
++{
++ m_channelScrollOffset += m_channelScrollSpeed * (m_renderTime - m_channelScrollLastTime);
++ if ((m_channelScrollSpeed < 0 && m_channelScrollOffset < m_channelOffset * m_programmeLayout->Size(m_orientation)) ||
++ (m_channelScrollSpeed > 0 && m_channelScrollOffset > m_channelOffset * m_programmeLayout->Size(m_orientation)))
++ {
++ m_channelScrollOffset = m_channelOffset * m_programmeLayout->Size(m_orientation);
++ m_channelScrollSpeed = 0;
++ }
++ m_channelScrollLastTime = m_renderTime;
++
++ m_programmeScrollOffset += m_programmeScrollSpeed * (m_renderTime - m_programmeScrollLastTime);
++ if ((m_programmeScrollSpeed < 0 && m_programmeScrollOffset < m_blockOffset * m_blockSize) ||
++ (m_programmeScrollSpeed > 0 && m_programmeScrollOffset > m_blockOffset * m_blockSize))
++ {
++ m_programmeScrollOffset = m_blockOffset * m_blockSize;
++ m_programmeScrollSpeed = 0;
++ }
++ m_programmeScrollLastTime = m_renderTime;
++}
++
++void CGUIEPGGridContainer::GetCurrentLayouts()
++{
++ m_channelLayout = NULL;
++ for (unsigned int i = 0; i < m_channelLayouts.size(); i++)
++ {
++ if (m_channelLayouts[i].CheckCondition())
++ {
++ m_channelLayout = &m_channelLayouts[i];
++ break;
++ }
++ }
++ if (!m_channelLayout && m_channelLayouts.size())
++ m_channelLayout = &m_channelLayouts[0]; // failsafe
++
++ m_focusedChannelLayout = NULL;
++ for (unsigned int i = 0; i < m_focusedChannelLayouts.size(); i++)
++ {
++ if (m_focusedChannelLayouts[i].CheckCondition())
++ {
++ m_focusedChannelLayout = &m_focusedChannelLayouts[i];
++ break;
++ }
++ }
++ if (!m_focusedChannelLayout && m_focusedChannelLayouts.size())
++ m_focusedChannelLayout = &m_focusedChannelLayouts[0]; // failsafe
++
++ m_programmeLayout = NULL;
++ for (unsigned int i = 0; i < m_programmeLayouts.size(); i++)
++ {
++ if (m_programmeLayouts[i].CheckCondition())
++ {
++ m_programmeLayout = &m_programmeLayouts[i];
++ break;
++ }
++ }
++ if (!m_programmeLayout && m_programmeLayouts.size())
++ m_programmeLayout = &m_programmeLayouts[0]; // failsafe
++
++ m_focusedProgrammeLayout = NULL;
++ for (unsigned int i = 0; i < m_focusedProgrammeLayouts.size(); i++)
++ {
++ if (m_focusedProgrammeLayouts[i].CheckCondition())
++ {
++ m_focusedProgrammeLayout = &m_focusedProgrammeLayouts[i];
++ break;
++ }
++ }
++ if (!m_focusedProgrammeLayout && m_focusedProgrammeLayouts.size())
++ m_focusedProgrammeLayout = &m_focusedProgrammeLayouts[0]; // failsafe
++
++ m_rulerLayout = NULL;
++ for (unsigned int i = 0; i < m_rulerLayouts.size(); i++)
++ {
++ if (m_rulerLayouts[i].CheckCondition())
++ {
++ m_rulerLayout = &m_rulerLayouts[i];
++ break;
++ }
++ }
++ if (!m_rulerLayout && m_rulerLayouts.size())
++ m_rulerLayout = &m_rulerLayouts[0]; // failsafe
++}
++
++int CGUIEPGGridContainer::CorrectOffset(int offset, int cursor) const
++{
++ return offset + cursor;
++}
++
++void CGUIEPGGridContainer::SetRenderOffset(const CPoint &offset)
++{
++ m_renderOffset = offset;
++}
++
++void CGUIEPGGridContainer::FreeChannelMemory(int keepStart, int keepEnd)
++{
++ if (keepStart < keepEnd)
++ { // remove before keepStart and after keepEnd
++ for (int i = 0; i < keepStart && i < (int)m_channelItems.size(); ++i)
++ m_channelItems[i]->FreeMemory();
++ for (int i = keepEnd + 1; i < (int)m_channelItems.size(); ++i)
++ m_channelItems[i]->FreeMemory();
++ }
++ else
++ { // wrapping
++ for (int i = keepEnd + 1; i < keepStart && i < (int)m_channelItems.size(); ++i)
++ m_channelItems[i]->FreeMemory();
++ }
++}
++
++void CGUIEPGGridContainer::FreeProgrammeMemory(int keepStart, int keepEnd)
++{
++ if (keepStart < keepEnd)
++ { // remove before keepStart and after keepEnd
++ for (unsigned int i = 0; i < m_epgItemsPtr.size(); i++)
++ {
++ unsigned long progIdx = m_epgItemsPtr[i].start;
++ unsigned long lastIdx = m_epgItemsPtr[i].stop;
++
++ for (unsigned int j = progIdx; j < keepStart+progIdx && j < lastIdx; ++j)
++ m_programmeItems[j]->FreeMemory();
++ for (unsigned int j = keepEnd+progIdx + 1; j < lastIdx; ++j)
++ m_programmeItems[j]->FreeMemory();
++ }
++ }
++ else
++ { // wrapping
++ for (unsigned int i = 0; i < m_epgItemsPtr.size(); i++)
++ {
++ unsigned long progIdx = m_epgItemsPtr[i].start;
++ unsigned long lastIdx = m_epgItemsPtr[i].stop;
++
++ for (unsigned int j = keepEnd+progIdx + 1; j < keepStart+progIdx && j < lastIdx; ++j)
++ m_programmeItems[j]->FreeMemory();
++ }
++ }
++}
++
++void CGUIEPGGridContainer::FreeRulerMemory(int keepStart, int keepEnd)
++{
++ if (keepStart < keepEnd)
++ { // remove before keepStart and after keepEnd
++ for (int i = 1; i < keepStart && i < (int)m_rulerItems.size(); ++i)
++ m_rulerItems[i]->FreeMemory();
++ for (int i = keepEnd + 1; i < (int)m_rulerItems.size(); ++i)
++ m_rulerItems[i]->FreeMemory();
++ }
++ else
++ { // wrapping
++ for (int i = keepEnd + 1; i < keepStart && i < (int)m_rulerItems.size(); ++i)
++ {
++ if (i == 0)
++ continue;
++ m_rulerItems[i]->FreeMemory();
++ }
++ }
++}
++
++void CGUIEPGGridContainer::GetChannelCacheOffsets(int &cacheBefore, int &cacheAfter)
++{
++ if (m_channelScrollSpeed > 0)
++ {
++ cacheBefore = 0;
++ cacheAfter = m_cacheChannelItems;
++ }
++ else if (m_channelScrollSpeed < 0)
++ {
++ cacheBefore = m_cacheChannelItems;
++ cacheAfter = 0;
++ }
++ else
++ {
++ cacheBefore = m_cacheChannelItems / 2;
++ cacheAfter = m_cacheChannelItems / 2;
++ }
++}
++
++void CGUIEPGGridContainer::GetProgrammeCacheOffsets(int &cacheBefore, int &cacheAfter)
++{
++ if (m_programmeScrollSpeed > 0)
++ {
++ cacheBefore = 0;
++ cacheAfter = m_cacheProgrammeItems;
++ }
++ else if (m_programmeScrollSpeed < 0)
++ {
++ cacheBefore = m_cacheProgrammeItems;
++ cacheAfter = 0;
++ }
++ else
++ {
++ cacheBefore = m_cacheProgrammeItems / 2;
++ cacheAfter = m_cacheProgrammeItems / 2;
++ }
++}
++
++void CGUIEPGGridContainer::GetRulerCacheOffsets(int &cacheBefore, int &cacheAfter)
++{
++ if (m_programmeScrollSpeed > 0)
++ {
++ cacheBefore = 0;
++ cacheAfter = m_cacheRulerItems;
++ }
++ else if (m_programmeScrollSpeed < 0)
++ {
++ cacheBefore = m_cacheRulerItems;
++ cacheAfter = 0;
++ }
++ else
++ {
++ cacheBefore = m_cacheRulerItems / 2;
++ cacheAfter = m_cacheRulerItems / 2;
++ }
++}
+diff --git a/xbmc/epg/GUIEPGGridContainer.h b/xbmc/epg/GUIEPGGridContainer.h
+new file mode 100644
+index 0000000..d275bdd
+--- /dev/null
++++ b/xbmc/epg/GUIEPGGridContainer.h
+@@ -0,0 +1,223 @@
++#pragma once
++
++/*
++ * Copyright (C) 2005-2008 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "XBDateTime.h"
++#include "FileItem.h"
++#include "guilib/GUIControl.h"
++#include "guilib/GUIListItemLayout.h"
++
++namespace EPG
++{
++ #define MAXCHANNELS 20
++ #define MAXBLOCKS 2304 //! !!_EIGHT_!! days of 5 minute blocks
++
++ struct GridItemsPtr
++ {
++ CGUIListItemPtr item;
++ float width;
++ float height;
++ };
++
++ class CGUIEPGGridContainer : public CGUIControl
++ {
++ public:
++ CGUIEPGGridContainer(int parentID, int controlID, float posX, float posY, float width, float height,
++ ORIENTATION orientation, int scrollTime, int preloadItems, int minutesPerPage,
++ int rulerUnit);
++ virtual ~CGUIEPGGridContainer(void);
++ virtual CGUIEPGGridContainer *Clone() const { return new CGUIEPGGridContainer(*this); };
++
++ virtual bool OnAction(const CAction &action);
++ virtual void OnDown();
++ virtual void OnUp();
++ virtual void OnLeft();
++ virtual void OnRight();
++ virtual bool OnMouseOver(const CPoint &point);
++ virtual bool OnMouseClick(int dwButton, const CPoint &point);
++ virtual bool OnMouseDoubleClick(int dwButton, const CPoint &point);
++ virtual bool OnMouseWheel(char wheel, const CPoint &point);
++ virtual bool OnMessage(CGUIMessage& message);
++ virtual void SetFocus(bool bOnOff);
++
++ virtual CStdString GetDescription() const;
++ const int GetNumChannels() { return m_channels; };
++ virtual int GetSelectedItem() const;
++ const int GetSelectedChannel() { return m_channelCursor + m_channelOffset; }
++ virtual EVENT_RESULT OnMouseEvent(const CPoint &point, const CMouseEvent &event);
++
++ virtual void Process(unsigned int currentTime, CDirtyRegionList &dirtyregions);
++ virtual void DoRender();
++ virtual void Render();
++ void LoadLayout(TiXmlElement *layout);
++ void LoadContent(TiXmlElement *content);
++
++ virtual bool IsContainer() const { return true; };
++ CGUIListItemPtr GetListItem(int offset) const;
++
++ virtual int CorrectOffset(int offset, int cursor) const;
++
++ /*! \brief Set the offset of the first item in the container from the container's position
++ Useful for lists/panels where the focused item may be larger than the non-focused items and thus
++ normally cut off from the clipping window defined by the container's position + size.
++ \param offset CPoint holding the offset in skin coordinates.
++ */
++ void SetRenderOffset(const CPoint &offset);
++
++ void GoToBegin();
++ void GoToEnd();
++ void SetStartEnd(CDateTime start, CDateTime end);
++ void SetChannel(const PVR::CPVRChannel &channel);
++ void SetChannel(const CStdString &channel);
++
++ protected:
++ bool OnClick(int actionID);
++ bool SelectItemFromPoint(const CPoint &point);
++
++ void UpdateItems();
++
++ void SetChannel(int channel);
++ void SetBlock(int block);
++ void ChannelScroll(int amount);
++ void ProgrammesScroll(int amount);
++ void ValidateOffset();
++ void UpdateLayout(bool refreshAllItems = false);
++ void CalculateLayout();
++ void Reset();
++ void ClearGridIndex(void);
++
++ GridItemsPtr *GetItem(const int &channel);
++ GridItemsPtr *GetNextItem(const int &channel);
++ GridItemsPtr *GetPrevItem(const int &channel);
++ GridItemsPtr *GetClosestItem(const int &channel);
++
++ int GetItemSize(GridItemsPtr *item);
++ int GetBlock(const CGUIListItemPtr &item, const int &channel);
++ int GetRealBlock(const CGUIListItemPtr &item, const int &channel);
++ void MoveToRow(int row);
++ bool MoveChannel(bool direction);
++ bool MoveProgrammes(bool direction);
++
++ CGUIListItemLayout *GetFocusedLayout() const;
++
++ void ScrollToBlockOffset(int offset);
++ void ScrollToChannelOffset(int offset);
++ void UpdateScrollOffset();
++ void RenderChannelItem(float posX, float posY, CGUIListItem *item, bool focused);
++ void RenderProgrammeItem(float posX, float posY, float width, float height, CGUIListItem *item, bool focused);
++ void GetCurrentLayouts();
++
++ CPoint m_renderOffset; ///< \brief render offset of the first item in the list \sa SetRenderOffset
++
++ ORIENTATION m_orientation;
++
++ struct ItemsPtr
++ {
++ long start;
++ long stop;
++ };
++ std::vector< ItemsPtr > m_epgItemsPtr;
++ std::vector< CGUIListItemPtr > m_channelItems;
++ std::vector< CGUIListItemPtr > m_rulerItems;
++ std::vector< CGUIListItemPtr > m_programmeItems;
++ typedef std::vector<CGUIListItemPtr> ::iterator iItems;
++
++ std::vector<CGUIListItemLayout> m_channelLayouts;
++ std::vector<CGUIListItemLayout> m_focusedChannelLayouts;
++ std::vector<CGUIListItemLayout> m_focusedProgrammeLayouts;
++ std::vector<CGUIListItemLayout> m_programmeLayouts;
++ std::vector<CGUIListItemLayout> m_rulerLayouts;
++
++ CGUIListItemLayout *m_channelLayout;
++ CGUIListItemLayout *m_focusedChannelLayout;
++ CGUIListItemLayout *m_programmeLayout;
++ CGUIListItemLayout *m_focusedProgrammeLayout;
++ CGUIListItemLayout *m_rulerLayout;
++
++ bool m_wasReset; // true if we've received a Reset message until we've rendered once. Allows
++ // us to make sure we don't tell the infomanager that we've been moving when
++ // the "movement" was simply due to the list being repopulated (thus cursor position
++ // changing around)
++
++ void FreeChannelMemory(int keepStart, int keepEnd);
++ void FreeProgrammeMemory(int keepStart, int keepEnd);
++ void FreeRulerMemory(int keepStart, int keepEnd);
++
++ void GetChannelCacheOffsets(int &cacheBefore, int &cacheAfter);
++ void GetProgrammeCacheOffsets(int &cacheBefore, int &cacheAfter);
++ void GetRulerCacheOffsets(int &cacheBefore, int &cacheAfter);
++
++ private:
++ int m_rulerUnit; //! number of blocks that makes up one element of the ruler
++ int m_channels;
++ int m_channelsPerPage;
++ int m_ProgrammesPerPage;
++ int m_channelCursor;
++ int m_channelOffset;
++ int m_blocks;
++ int m_blocksPerPage;
++ int m_blockCursor;
++ int m_blockOffset;
++ int m_cacheChannelItems;
++ int m_cacheProgrammeItems;
++ int m_cacheRulerItems;
++
++ float m_rulerPosX; //! X position of first ruler item
++ float m_rulerPosY; //! Y position of first ruler item
++ float m_rulerHeight; //! height of the scrolling timeline above the ruler items
++ float m_rulerWidth; //! width of each element of the ruler
++ float m_channelPosX; //! Y position of first channel row
++ float m_channelPosY; //! Y position of first channel row
++ float m_channelHeight; //! height of each channel row (& every grid item)
++ float m_channelWidth; //! width of the channel item
++ float m_gridPosX; //! X position of first grid item
++ float m_gridPosY; //! Y position of first grid item
++ float m_gridWidth;
++ float m_gridHeight;
++ float m_blockSize; //! a block's width in pixels
++ float m_analogScrollCount;
++
++ CDateTime m_gridStart;
++ CDateTime m_gridEnd;
++
++ struct GridItemsPtr **m_gridIndex;
++ GridItemsPtr *m_item;
++ CGUIListItem *m_lastItem;
++ CGUIListItem *m_lastChannel;
++
++ unsigned int m_renderTime;
++
++ int m_scrollTime;
++ bool m_channelWrapAround;
++ bool m_gridWrapAround; //! only when no more data available should this be true
++
++ int m_programmeScrollLastTime;
++ float m_programmeScrollSpeed;
++ float m_programmeScrollOffset;
++
++ int m_channelScrollLastTime;
++ float m_channelScrollSpeed;
++ float m_channelScrollOffset;
++
++ CStdString m_label;
++ };
++}
+diff --git a/xbmc/epg/Makefile b/xbmc/epg/Makefile
+new file mode 100644
+index 0000000..a52ad61
+--- /dev/null
++++ b/xbmc/epg/Makefile
+@@ -0,0 +1,13 @@
++INCLUDES=-I. -I.. -I../../ -I../linux -I../cores -I../../guilib -I../posix -I../utils
++
++SRCS=EpgInfoTag.cpp \
++ EpgSearchFilter.cpp \
++ Epg.cpp \
++ EpgContainer.cpp \
++ EpgDatabase.cpp \
++ GUIEPGGridContainer.cpp
++
++LIB=epg.a
++
++include ../../Makefile.include
++-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
+diff --git a/xbmc/filesystem/AddonsDirectory.cpp b/xbmc/filesystem/AddonsDirectory.cpp
+index cdac766..ae0c6ec 100644
+--- a/xbmc/filesystem/AddonsDirectory.cpp
++++ b/xbmc/filesystem/AddonsDirectory.cpp
+@@ -31,6 +31,7 @@
+ #include "addons/PluginSource.h"
+ #include "guilib/TextureManager.h"
+ #include "File.h"
++#include "SpecialProtocol.h"
+ #include "utils/URIUtils.h"
+
+ using namespace ADDON;
+@@ -68,7 +69,7 @@ bool CAddonsDirectory::GetDirectory(const CStdString& strPath, CFileItemList &it
+ else if (path.GetHostName().Equals("disabled"))
+ { // grab all disabled addons, including disabled repositories
+ reposAsFolders = false;
+- CAddonMgr::Get().GetAllAddons(addons, false, true);
++ CAddonMgr::Get().GetAllAddons(addons, false, true, false);
+ items.SetProperty("reponame",g_localizeStrings.Get(24039));
+ items.SetLabel(g_localizeStrings.Get(24039));
+ }
+@@ -202,6 +203,7 @@ bool CAddonsDirectory::GetDirectory(const CStdString& strPath, CFileItemList &it
+
+ void CAddonsDirectory::GenerateListing(CURL &path, VECADDONS& addons, CFileItemList &items, bool reposAsFolders)
+ {
++ CStdString xbmcPath = _P("special://xbmc/addons");
+ items.ClearItems();
+ for (unsigned i=0; i < addons.size(); i++)
+ {
+@@ -214,6 +216,9 @@ void CAddonsDirectory::GenerateListing(CURL &path, VECADDONS& addons, CFileItemL
+ AddonPtr addon2;
+ if (CAddonMgr::Get().GetAddon(addon->ID(),addon2))
+ pItem->SetProperty("Addon.Status",g_localizeStrings.Get(305));
++ else if ((addon->Type() == ADDON_PVRDLL) && (CStdString(pItem->GetProperty("Addon.Path").asString()).Left(xbmcPath.size()).Equals(xbmcPath)))
++ pItem->SetProperty("Addon.Status",g_localizeStrings.Get(24023));
++
+ if (!addon->Props().broken.IsEmpty())
+ pItem->SetProperty("Addon.Status",g_localizeStrings.Get(24098));
+ if (addon2 && addon2->Version() < addon->Version())
+diff --git a/xbmc/filesystem/DllLibCMyth.h b/xbmc/filesystem/DllLibCMyth.h
+index 76d1a13..113391c 100644
+--- a/xbmc/filesystem/DllLibCMyth.h
++++ b/xbmc/filesystem/DllLibCMyth.h
+@@ -34,15 +34,21 @@ public:
+ virtual cmyth_conn_t conn_connect_ctrl (char *server, unsigned short port, unsigned buflen, int tcp_rcvbuf)=0;
+ virtual cmyth_conn_t conn_connect_event (char *server, unsigned short port, unsigned buflen, int tcp_rcvbuf)=0;
+ virtual cmyth_file_t conn_connect_file (cmyth_proginfo_t prog, cmyth_conn_t control, unsigned buflen, int tcp_rcvbuf)=0;
+- virtual cmyth_file_t conn_connect_path (char* path, cmyth_conn_t control, unsigned buflen, int tcp_rcvbuf)=0;
++ virtual cmyth_file_t conn_connect_path (char* path, cmyth_conn_t control, unsigned buflen, int tcp_rcvbuf, char* sgToGetFrom)=0;
+ virtual cmyth_recorder_t conn_get_free_recorder (cmyth_conn_t conn)=0;
+ virtual cmyth_recorder_t conn_get_recorder_from_num(cmyth_conn_t conn, int num)=0;
+
++ virtual int conn_get_freespace (cmyth_conn_t control,long long *total, long long *used)=0;
++ virtual int conn_hung (cmyth_conn_t control)=0;
+
+ virtual cmyth_event_t event_get (cmyth_conn_t conn, char * data, int len)=0;
+ virtual int event_select (cmyth_conn_t conn, struct timeval *timeout)=0;
+
+ virtual cmyth_proglist_t proglist_get_all_recorded(cmyth_conn_t control)=0;
++ virtual cmyth_proglist_t proglist_get_all_scheduled(cmyth_conn_t control)=0;
++ virtual cmyth_proglist_t proglist_get_all_pending (cmyth_conn_t control)=0;
++ virtual cmyth_proglist_t proglist_get_conflicting (cmyth_conn_t control)=0;
++
+ virtual int mysql_get_guide(cmyth_database_t db, cmyth_program_t **prog, time_t starttime, time_t endtime) = 0;
+ virtual cmyth_proginfo_t proglist_get_item (cmyth_proglist_t pl, int index)=0;
+ virtual int proglist_get_count (cmyth_proglist_t pl)=0;
+@@ -136,15 +142,21 @@ class DllLibCMyth : public DllDynamic, DllLibCMythInterface
+ DEFINE_METHOD4(cmyth_conn_t, conn_connect_ctrl, (char *p1, unsigned short p2, unsigned p3, int p4))
+ DEFINE_METHOD4(cmyth_conn_t, conn_connect_event, (char *p1, unsigned short p2, unsigned p3, int p4))
+ DEFINE_METHOD4(cmyth_file_t, conn_connect_file, (cmyth_proginfo_t p1, cmyth_conn_t p2, unsigned p3, int p4))
+- DEFINE_METHOD4(cmyth_file_t, conn_connect_path, (char* p1, cmyth_conn_t p2, unsigned p3, int p4))
++ DEFINE_METHOD5(cmyth_file_t, conn_connect_path, (char* p1, cmyth_conn_t p2, unsigned p3, int p4, char* p5))
+
+ DEFINE_METHOD1(cmyth_recorder_t, conn_get_free_recorder, (cmyth_conn_t p1))
+ DEFINE_METHOD2(cmyth_recorder_t, conn_get_recorder_from_num,(cmyth_conn_t p1, int p2))
++ DEFINE_METHOD3(int, conn_get_freespace, (cmyth_conn_t p1, long long *p2, long long *p3))
++ DEFINE_METHOD1(int, conn_hung, (cmyth_conn_t p1))
+
+ DEFINE_METHOD3(cmyth_event_t, event_get, (cmyth_conn_t p1, char * p2, int p3))
+ DEFINE_METHOD2(int, event_select, (cmyth_conn_t p1, struct timeval *p2))
+
+ DEFINE_METHOD1(cmyth_proglist_t, proglist_get_all_recorded, (cmyth_conn_t p1))
++ DEFINE_METHOD1(cmyth_proglist_t, proglist_get_all_scheduled, (cmyth_conn_t p1))
++ DEFINE_METHOD1(cmyth_proglist_t, proglist_get_all_pending, (cmyth_conn_t p1))
++ DEFINE_METHOD1(cmyth_proglist_t, proglist_get_conflicting, (cmyth_conn_t p1))
++
+ DEFINE_METHOD4(int, mysql_get_guide, (cmyth_database_t p1, cmyth_program_t **p2, time_t p3, time_t p4))
+ DEFINE_METHOD2(cmyth_proginfo_t, proglist_get_item, (cmyth_proglist_t p1, int p2))
+ DEFINE_METHOD1(int, proglist_get_count, (cmyth_proglist_t p1))
+@@ -236,10 +248,15 @@ class DllLibCMyth : public DllDynamic, DllLibCMythInterface
+ RESOLVE_METHOD_RENAME(cmyth_conn_connect_path, conn_connect_path)
+ RESOLVE_METHOD_RENAME(cmyth_conn_get_free_recorder, conn_get_free_recorder)
+ RESOLVE_METHOD_RENAME(cmyth_conn_get_recorder_from_num, conn_get_recorder_from_num)
++ RESOLVE_METHOD_RENAME(cmyth_conn_get_freespace, conn_get_freespace)
++ RESOLVE_METHOD_RENAME(cmyth_conn_hung, conn_hung)
+
+ RESOLVE_METHOD_RENAME(cmyth_event_get, event_get)
+ RESOLVE_METHOD_RENAME(cmyth_event_select, event_select)
+ RESOLVE_METHOD_RENAME(cmyth_proglist_get_all_recorded, proglist_get_all_recorded)
++ RESOLVE_METHOD_RENAME(cmyth_proglist_get_all_scheduled, proglist_get_all_scheduled)
++ RESOLVE_METHOD_RENAME(cmyth_proglist_get_all_pending, proglist_get_all_pending)
++ RESOLVE_METHOD_RENAME(cmyth_proglist_get_conflicting, proglist_get_conflicting)
+ RESOLVE_METHOD_RENAME(cmyth_mysql_get_guide, mysql_get_guide)
+ RESOLVE_METHOD_RENAME(cmyth_proglist_get_item, proglist_get_item)
+ RESOLVE_METHOD_RENAME(cmyth_proglist_get_count, proglist_get_count)
+diff --git a/xbmc/filesystem/FactoryDirectory.cpp b/xbmc/filesystem/FactoryDirectory.cpp
+index 6c627af..c0ac1fd 100644
+--- a/xbmc/filesystem/FactoryDirectory.cpp
++++ b/xbmc/filesystem/FactoryDirectory.cpp
+@@ -76,6 +76,9 @@
+ #ifdef HAS_FILESYSTEM_HTSP
+ #include "HTSPDirectory.h"
+ #endif
++#ifdef HAS_PVRCLIENTS
++#include "PVRDirectory.h"
++#endif
+ #include "ZipDirectory.h"
+ #ifdef HAS_FILESYSTEM_RAR
+ #include "RarDirectory.h"
+@@ -187,6 +190,9 @@ IDirectory* CFactoryDirectory::Create(const CStdString& strPath)
+ #ifdef HAS_FILESYSTEM_HTSP
+ if (strProtocol == "htsp") return new CHTSPDirectory();
+ #endif
++#ifdef HAS_PVRCLIENTS
++ if (strProtocol == "pvr") return new CPVRDirectory();
++#endif
+ #ifdef HAS_ZEROCONF
+ if (strProtocol == "zeroconf") return new CZeroconfDirectory();
+ #endif
+diff --git a/xbmc/filesystem/FileFactory.cpp b/xbmc/filesystem/FileFactory.cpp
+index d9e1349..d3ee934 100644
+--- a/xbmc/filesystem/FileFactory.cpp
++++ b/xbmc/filesystem/FileFactory.cpp
+@@ -55,6 +55,9 @@
+ #ifdef HAS_FILESYSTEM_VTP
+ #include "VTPFile.h"
+ #endif
++#ifdef HAS_PVRCLIENTS
++#include "PVRFile.h"
++#endif
+ #include "FileZip.h"
+ #ifdef HAS_FILESYSTEM_RAR
+ #include "FileRar.h"
+@@ -163,6 +166,9 @@ IFile* CFileFactory::CreateLoader(const CURL& url)
+ #ifdef HAS_FILESYSTEM_VTP
+ else if (strProtocol == "vtp") return new CVTPFile();
+ #endif
++#ifdef HAS_PVRCLIENTS
++ else if (strProtocol == "pvr") return new CPVRFile();
++#endif
+ #ifdef HAS_FILESYSTEM_NFS
+ else if (strProtocol == "nfs") return new CFileNFS();
+ #endif
+diff --git a/xbmc/filesystem/ILiveTV.h b/xbmc/filesystem/ILiveTV.h
+index 36ac07d..3b2277b 100644
+--- a/xbmc/filesystem/ILiveTV.h
++++ b/xbmc/filesystem/ILiveTV.h
+@@ -28,8 +28,8 @@ class ILiveTVInterface
+ {
+ public:
+ virtual ~ILiveTVInterface() {}
+- virtual bool NextChannel() = 0;
+- virtual bool PrevChannel() = 0;
++ virtual bool NextChannel(bool preview = false) = 0;
++ virtual bool PrevChannel(bool preview = false) = 0;
+ virtual bool SelectChannel(unsigned int channel) = 0;
+
+ virtual int GetTotalTime() = 0;
+diff --git a/xbmc/filesystem/Makefile.in b/xbmc/filesystem/Makefile.in
+index 467df2b..b04a24c 100644
+--- a/xbmc/filesystem/Makefile.in
++++ b/xbmc/filesystem/Makefile.in
+@@ -64,6 +64,8 @@ SRCS=AddonsDirectory.cpp \
+ PlaylistFileDirectory.cpp \
+ PipesManager.cpp \
+ PluginDirectory.cpp \
++ PVRFile.cpp \
++ PVRDirectory.cpp \
+ RSSDirectory.cpp \
+ RTVDirectory.cpp \
+ SAPDirectory.cpp \
+diff --git a/xbmc/filesystem/MythFile.cpp b/xbmc/filesystem/MythFile.cpp
+index 9711981..4ec1cf1 100644
+--- a/xbmc/filesystem/MythFile.cpp
++++ b/xbmc/filesystem/MythFile.cpp
+@@ -280,8 +280,8 @@ bool CMythFile::SetupFile(const CURL& url)
+ return false;
+
+ m_filename = url.GetFileName().Mid(6);
+-
+- m_file = m_dll->conn_connect_path((char*)m_filename.c_str(), m_control, 16*1024, 4096);
++ CStdString storagegroup;
++ m_file = m_dll->conn_connect_path((char*)m_filename.c_str(), m_control, 16*1024, 4096, (char*)storagegroup.c_str());
+ if(!m_file)
+ {
+ CLog::Log(LOGERROR, "%s - unable to connect to file", __FUNCTION__);
+@@ -632,12 +632,12 @@ bool CMythFile::ChangeChannel(int direction, const CStdString &channel)
+ return true;
+ }
+
+-bool CMythFile::NextChannel()
++bool CMythFile::NextChannel(bool preview)
+ {
+ return ChangeChannel(CHANNEL_DIRECTION_UP, "");
+ }
+
+-bool CMythFile::PrevChannel()
++bool CMythFile::PrevChannel(bool preview)
+ {
+ return ChangeChannel(CHANNEL_DIRECTION_DOWN, "");
+ }
+diff --git a/xbmc/filesystem/MythFile.h b/xbmc/filesystem/MythFile.h
+index 55de1d4..74d7962 100644
+--- a/xbmc/filesystem/MythFile.h
++++ b/xbmc/filesystem/MythFile.h
+@@ -62,8 +62,8 @@ public:
+
+ virtual ILiveTVInterface* GetLiveTV() {return (ILiveTVInterface*)this;}
+
+- virtual bool NextChannel();
+- virtual bool PrevChannel();
++ virtual bool NextChannel(bool preview = false);
++ virtual bool PrevChannel(bool preview = false);
+ virtual bool SelectChannel(unsigned int channel);
+
+ virtual int GetTotalTime();
+diff --git a/xbmc/filesystem/PVRDirectory.cpp b/xbmc/filesystem/PVRDirectory.cpp
+new file mode 100644
+index 0000000..e0c5853
+--- /dev/null
++++ b/xbmc/filesystem/PVRDirectory.cpp
+@@ -0,0 +1,126 @@
++/*
++ * Copyright (C) 2005-2009 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "PVRDirectory.h"
++#include "FileItem.h"
++#include "Util.h"
++#include "URL.h"
++#include "utils/log.h"
++#include "utils/URIUtils.h"
++#include "guilib/LocalizeStrings.h"
++
++#include "pvr/PVRManager.h"
++#include "pvr/channels/PVRChannelGroupsContainer.h"
++#include "pvr/channels/PVRChannelGroup.h"
++#include "pvr/recordings/PVRRecordings.h"
++#include "pvr/timers/PVRTimers.h"
++
++using namespace std;
++using namespace XFILE;
++using namespace PVR;
++
++CPVRDirectory::CPVRDirectory()
++{
++}
++
++CPVRDirectory::~CPVRDirectory()
++{
++}
++
++bool CPVRDirectory::GetDirectory(const CStdString& strPath, CFileItemList &items)
++{
++ CStdString base(strPath);
++ URIUtils::RemoveSlashAtEnd(base);
++
++ CURL url(strPath);
++ CStdString fileName = url.GetFileName();
++ URIUtils::RemoveSlashAtEnd(fileName);
++ CLog::Log(LOGDEBUG, "CPVRDirectory::GetDirectory(%s)", base.c_str());
++ items.SetCacheToDisc(CFileItemList::CACHE_NEVER);
++
++ if (!g_PVRManager.IsStarted())
++ return false;
++
++ if (fileName == "")
++ {
++ CFileItemPtr item;
++
++ item.reset(new CFileItem(base + "/channels/", true));
++ item->SetLabel(g_localizeStrings.Get(19019));
++ item->SetLabelPreformated(true);
++ items.Add(item);
++
++ item.reset(new CFileItem(base + "/recordings/", true));
++ item->SetLabel(g_localizeStrings.Get(19017));
++ item->SetLabelPreformated(true);
++ items.Add(item);
++
++ item.reset(new CFileItem(base + "/timers/", true));
++ item->SetLabel(g_localizeStrings.Get(19040));
++ item->SetLabelPreformated(true);
++ items.Add(item);
++
++ item.reset(new CFileItem(base + "/guide/", true));
++ item->SetLabel(g_localizeStrings.Get(19029));
++ item->SetLabelPreformated(true);
++ items.Add(item);
++
++ // Sort by name only. Labels are preformated.
++ items.AddSortMethod(SORT_METHOD_LABEL, 551 /* Name */, LABEL_MASKS("%L", "", "%L", ""));
++
++ return true;
++ }
++ else if (fileName.Left(10) == "recordings")
++ {
++ return g_PVRRecordings->GetDirectory(strPath, items);
++ }
++ else if (fileName.Left(8) == "channels")
++ {
++ return g_PVRChannelGroups->GetDirectory(strPath, items);
++ }
++ else if (fileName.Left(6) == "timers")
++ {
++ return g_PVRTimers->GetDirectory(strPath, items);
++ }
++
++ return false;
++}
++
++bool CPVRDirectory::SupportsFileOperations(const CStdString& strPath)
++{
++ CURL url(strPath);
++ CStdString filename = url.GetFileName();
++
++ return URIUtils::IsPVRRecording(filename);
++}
++
++bool CPVRDirectory::IsLiveTV(const CStdString& strPath)
++{
++ CURL url(strPath);
++ CStdString filename = url.GetFileName();
++
++ return URIUtils::IsLiveTV(filename);
++}
++
++bool CPVRDirectory::HasRecordings()
++{
++ return g_PVRRecordings->GetNumRecordings() > 0;
++}
+diff --git a/xbmc/filesystem/PVRDirectory.h b/xbmc/filesystem/PVRDirectory.h
+new file mode 100644
+index 0000000..90ce917
+--- /dev/null
++++ b/xbmc/filesystem/PVRDirectory.h
+@@ -0,0 +1,46 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2009 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "IDirectory.h"
++
++class CPVRSession;
++
++namespace XFILE {
++
++class CPVRDirectory
++ : public IDirectory
++{
++public:
++ CPVRDirectory();
++ virtual ~CPVRDirectory();
++
++ virtual bool GetDirectory(const CStdString& strPath, CFileItemList &items);
++ virtual bool IsAllowed(const CStdString &strFile) const { return true; };
++
++ static bool SupportsFileOperations(const CStdString& strPath);
++ static bool IsLiveTV(const CStdString& strPath);
++ static bool HasRecordings();
++
++private:
++};
++
++}
+diff --git a/xbmc/filesystem/PVRFile.cpp b/xbmc/filesystem/PVRFile.cpp
+new file mode 100644
+index 0000000..d36f78a
+--- /dev/null
++++ b/xbmc/filesystem/PVRFile.cpp
+@@ -0,0 +1,324 @@
++/*
++ * Copyright (C) 2005-2009 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "PVRFile.h"
++#include "Util.h"
++#include "cores/dvdplayer/DVDInputStreams/DVDInputStream.h"
++#include "pvr/PVRManager.h"
++#include "pvr/channels/PVRChannelGroupsContainer.h"
++#include "pvr/recordings/PVRRecordings.h"
++#include "pvr/addons/PVRClients.h"
++#include "utils/log.h"
++#include "utils/StringUtils.h"
++
++using namespace std;
++using namespace XFILE;
++using namespace PVR;
++
++CPVRFile::CPVRFile()
++{
++ m_isPlayRecording = false;
++ m_playingItem = -1;
++}
++
++CPVRFile::~CPVRFile()
++{
++}
++
++bool CPVRFile::Open(const CURL& url)
++{
++ Close();
++
++ if (!g_PVRManager.IsStarted())
++ return false;
++
++ CStdString strURL = url.Get();
++
++ if (strURL.Left(18) == "pvr://channels/tv/" || strURL.Left(21) == "pvr://channels/radio/")
++ {
++ const CPVRChannel *tag = g_PVRChannelGroups->GetByPath(strURL);
++ if (tag)
++ {
++ CPVRChannel *newTag = new CPVRChannel(*tag);
++ if (!g_PVRManager.OpenLiveStream(*newTag))
++ {
++ delete newTag;
++ return false;
++ }
++
++ m_isPlayRecording = false;
++ CLog::Log(LOGDEBUG, "PVRFile - %s - playback has started on filename %s", __FUNCTION__, strURL.c_str());
++ }
++ else
++ {
++ CLog::Log(LOGERROR, "PVRFile - %s - channel not found with filename %s", __FUNCTION__, strURL.c_str());
++ return false;
++ }
++ }
++ else if (strURL.Left(17) == "pvr://recordings/")
++ {
++ const CPVRRecording *tag = g_PVRRecordings->GetByPath(strURL);
++ if (tag)
++ {
++ CPVRRecording *newTag = new CPVRRecording(*tag);
++ if (!g_PVRManager.OpenRecordedStream(*newTag))
++ {
++ delete newTag;
++ return false;
++ }
++
++ m_isPlayRecording = true;
++ CLog::Log(LOGDEBUG, "%s - Recording has started on filename %s", __FUNCTION__, strURL.c_str());
++ }
++ else
++ {
++ CLog::Log(LOGERROR, "PVRFile - Recording not found with filename %s", strURL.c_str());
++ return false;
++ }
++ }
++ else
++ {
++ CLog::Log(LOGERROR, "%s - invalid path specified %s", __FUNCTION__, strURL.c_str());
++ return false;
++ }
++
++ return true;
++}
++
++void CPVRFile::Close()
++{
++ g_PVRManager.CloseStream();
++}
++
++unsigned int CPVRFile::Read(void* buffer, int64_t size)
++{
++ return g_PVRManager.IsStarted() ? g_PVRClients->ReadStream((BYTE*)buffer, size) : 0;
++}
++
++int64_t CPVRFile::GetLength()
++{
++ return g_PVRManager.IsStarted() ? g_PVRClients->LengthStream() : 0;
++}
++
++int64_t CPVRFile::Seek(int64_t pos, int whence)
++{
++ return g_PVRManager.IsStarted() ? g_PVRClients->SeekStream(pos, whence) : 0;
++}
++
++int64_t CPVRFile::GetPosition()
++{
++ return g_PVRManager.IsStarted() ? g_PVRClients->GetStreamPosition() : 0;
++}
++
++int CPVRFile::GetTotalTime()
++{
++ return g_PVRManager.GetTotalTime();
++}
++
++int CPVRFile::GetStartTime()
++{
++ return g_PVRManager.GetStartTime();
++}
++
++bool CPVRFile::NextChannel(bool preview/* = false*/)
++{
++ unsigned int newchannel;
++
++ if (m_isPlayRecording)
++ {
++ /* We are inside a recording, skip channelswitch */
++ return true;
++ }
++
++ /* Do channel switch and save new channel number, it is not always
++ * increased by one in a case if next channel is encrypted or we
++ * on the beginning or end of the channel list!
++ */
++ if (g_PVRManager.ChannelUp(&newchannel, preview))
++ {
++ m_playingItem = newchannel;
++ return true;
++ }
++ else
++ {
++ return false;
++ }
++}
++
++bool CPVRFile::PrevChannel(bool preview/* = false*/)
++{
++ unsigned int newchannel;
++
++ if (m_isPlayRecording)
++ {
++ /* We are inside a recording, skip channelswitch */
++ return true;
++ }
++
++ /* Do channel switch and save new channel number, it is not always
++ * increased by one in a case if next channel is encrypted or we
++ * on the beginning or end of the channel list!
++ */
++ if (g_PVRManager.ChannelDown(&newchannel, preview))
++ {
++ m_playingItem = newchannel;
++ return true;
++ }
++ else
++ {
++ return false;
++ }
++}
++
++bool CPVRFile::SelectChannel(unsigned int channel)
++{
++ if (m_isPlayRecording)
++ {
++ /* We are inside a recording, skip channelswitch */
++ /** TODO:
++ ** Add support for cutting keys (functions becomes the numeric keys as integer)
++ **/
++ return true;
++ }
++
++ if (g_PVRManager.ChannelSwitch(channel))
++ {
++ m_playingItem = channel;
++ return true;
++ }
++ else
++ {
++ return false;
++ }
++}
++
++bool CPVRFile::UpdateItem(CFileItem& item)
++{
++ return g_PVRManager.UpdateItem(item);
++}
++
++CStdString CPVRFile::TranslatePVRFilename(const CStdString& pathFile)
++{
++ if (!g_PVRManager.IsStarted())
++ return StringUtils::EmptyString;
++
++ CStdString FileName = pathFile;
++ if (FileName.substr(0, 14) == "pvr://channels")
++ {
++ const CPVRChannel *tag = g_PVRChannelGroups->GetByPath(FileName);
++ if (tag)
++ {
++ CStdString stream = tag->StreamURL();
++ if(!stream.IsEmpty())
++ {
++ if (stream.compare(6, 7, "stream/") == 0)
++ {
++ // pvr://stream
++ // This function was added to retrieve the stream URL for this item
++ // Is is used for the MediaPortal (ffmpeg) PVR addon
++ // see PVRManager.cpp
++ return g_PVRClients->GetStreamURL(*tag);
++ }
++ else
++ {
++ return stream;
++ }
++ }
++ }
++ }
++ return FileName;
++}
++
++bool CPVRFile::CanRecord()
++{
++ if (m_isPlayRecording || !g_PVRManager.IsStarted())
++ return false;
++
++ return g_PVRClients->CanRecordInstantly();
++}
++
++bool CPVRFile::IsRecording()
++{
++ return g_PVRManager.IsStarted() && g_PVRClients->IsRecordingOnPlayingChannel();
++}
++
++bool CPVRFile::Record(bool bOnOff)
++{
++ return g_PVRManager.StartRecordingOnPlayingChannel(bOnOff);
++}
++
++bool CPVRFile::Delete(const CURL& url)
++{
++ if (!g_PVRManager.IsStarted())
++ return false;
++
++ CStdString path(url.GetFileName());
++ if (path.Left(11) == "recordings/" && path[path.size()-1] != '/')
++ {
++ CStdString strURL = url.Get();
++ CPVRRecording *tag = g_PVRRecordings->GetByPath(strURL);
++ if (tag)
++ return tag->Delete();
++ }
++ return false;
++}
++
++bool CPVRFile::Rename(const CURL& url, const CURL& urlnew)
++{
++ if (!g_PVRManager.IsStarted())
++ return false;
++
++ CStdString path(url.GetFileName());
++ CStdString newname(urlnew.GetFileName());
++
++ size_t found = newname.find_last_of("/");
++ if (found != CStdString::npos)
++ newname = newname.substr(found+1);
++
++ if (path.Left(11) == "recordings/" && path[path.size()-1] != '/')
++ {
++ CStdString strURL = url.Get();
++ CPVRRecording *tag = g_PVRRecordings->GetByPath(strURL);
++ if (tag)
++ return tag->Rename(newname);
++ }
++ return false;
++}
++
++bool CPVRFile::Exists(const CURL& url)
++{
++ return g_PVRManager.IsStarted() && g_PVRRecordings->GetByPath(url.Get()) != NULL;
++}
++
++int CPVRFile::IoControl(EIoControl request, void *param)
++{
++ if (request == IOCTRL_SEEK_POSSIBLE)
++ {
++ if (!g_PVRManager.IsStarted())
++ return 0;
++ else if (g_PVRClients->LengthStream() && g_PVRClients->SeekStream(0, SEEK_CUR) >= 0)
++ return 1;
++ else
++ return 0;
++ }
++
++ return -1;
++}
+diff --git a/xbmc/filesystem/PVRFile.h b/xbmc/filesystem/PVRFile.h
+new file mode 100644
+index 0000000..fcfb53f
+--- /dev/null
++++ b/xbmc/filesystem/PVRFile.h
+@@ -0,0 +1,81 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2009 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "IFile.h"
++#include "ILiveTV.h"
++#include "video/VideoInfoTag.h"
++
++
++class CPVRSession;
++
++namespace XFILE {
++
++class CPVRFile
++ : public IFile
++ , ILiveTVInterface
++ , IRecordable
++{
++public:
++ CPVRFile();
++ virtual ~CPVRFile();
++ virtual bool Open(const CURL& url);
++ virtual int64_t Seek(int64_t pos, int whence=SEEK_SET);
++ virtual int64_t GetPosition();
++ virtual int64_t GetLength();
++ virtual int Stat(const CURL& url, struct __stat64* buffer) { return -1; }
++ virtual void Close();
++ virtual unsigned int Read(void* buffer, int64_t size);
++ virtual CStdString GetContent() { return ""; }
++ virtual bool SkipNext() { return true; }
++
++ virtual bool Delete(const CURL& url);
++ virtual bool Rename(const CURL& url, const CURL& urlnew);
++ virtual bool Exists(const CURL& url);
++
++ virtual ILiveTVInterface* GetLiveTV() {return (ILiveTVInterface*)this;}
++
++ virtual bool NextChannel(bool preview = false);
++ virtual bool PrevChannel(bool preview = false);
++ virtual bool SelectChannel(unsigned int channel);
++
++ virtual int GetTotalTime();
++ virtual int GetStartTime();
++ virtual bool UpdateItem(CFileItem& item);
++
++ virtual IRecordable* GetRecordable() {return (IRecordable*)this;}
++
++ virtual bool CanRecord();
++ virtual bool IsRecording();
++ virtual bool Record(bool bOnOff);
++
++ virtual int IoControl(EIoControl request, void *param);
++
++ static CStdString TranslatePVRFilename(const CStdString& pathFile);
++
++protected:
++ bool m_isPlayRecording;
++ int m_playingItem;
++};
++
++}
++
++
+diff --git a/xbmc/filesystem/Slingbox.cpp b/xbmc/filesystem/Slingbox.cpp
+index d7e8763..6fc9005 100644
+--- a/xbmc/filesystem/Slingbox.cpp
++++ b/xbmc/filesystem/Slingbox.cpp
+@@ -212,7 +212,7 @@ bool CSlingboxFile::SkipNext()
+ return m_pSlingbox->IsConnected();
+ }
+
+-bool CSlingboxFile::NextChannel()
++bool CSlingboxFile::NextChannel(bool bPreview /* = false */)
+ {
+ // Prepare variables
+ bool bSuccess = true;
+@@ -298,7 +298,7 @@ bool CSlingboxFile::NextChannel()
+ return bSuccess;
+ }
+
+-bool CSlingboxFile::PrevChannel()
++bool CSlingboxFile::PrevChannel(bool bPreview /* = false */)
+ {
+ // Prepare variables
+ bool bSuccess = true;
+diff --git a/xbmc/filesystem/Slingbox.h b/xbmc/filesystem/Slingbox.h
+index 22ad11f..0deb639 100644
+--- a/xbmc/filesystem/Slingbox.h
++++ b/xbmc/filesystem/Slingbox.h
+@@ -63,8 +63,8 @@ public:
+
+ virtual ILiveTVInterface * GetLiveTV() { return (ILiveTVInterface *)this; }
+
+- virtual bool NextChannel();
+- virtual bool PrevChannel();
++ virtual bool NextChannel(bool bPreview = false); // TODO bPreview is not implemented
++ virtual bool PrevChannel(bool bPreview = false); // TODO bPreview is not implemented
+ virtual bool SelectChannel(unsigned int uiChannel);
+
+ protected:
+diff --git a/xbmc/filesystem/VTPFile.cpp b/xbmc/filesystem/VTPFile.cpp
+index 5a3306d..2d1f467 100644
+--- a/xbmc/filesystem/VTPFile.cpp
++++ b/xbmc/filesystem/VTPFile.cpp
+@@ -140,7 +140,7 @@ int64_t CVTPFile::Seek(int64_t pos, int whence)
+ return -1;
+ }
+
+-bool CVTPFile::NextChannel()
++bool CVTPFile::NextChannel(bool preview/* = false*/)
+ {
+ if(m_session == NULL)
+ return false;
+@@ -166,7 +166,7 @@ bool CVTPFile::NextChannel()
+ return false;
+ }
+
+-bool CVTPFile::PrevChannel()
++bool CVTPFile::PrevChannel(bool preview/* = false*/)
+ {
+ if(m_session == NULL)
+ return false;
+diff --git a/xbmc/filesystem/VTPFile.h b/xbmc/filesystem/VTPFile.h
+index 236e46b..5474336 100644
+--- a/xbmc/filesystem/VTPFile.h
++++ b/xbmc/filesystem/VTPFile.h
+@@ -51,8 +51,8 @@ public:
+
+ virtual ILiveTVInterface* GetLiveTV() {return (ILiveTVInterface*)this;}
+
+- virtual bool NextChannel();
+- virtual bool PrevChannel();
++ virtual bool NextChannel(bool preview = false);
++ virtual bool PrevChannel(bool preview = false);
+ virtual bool SelectChannel(unsigned int channel);
+
+ virtual int GetTotalTime() { return 0; }
+diff --git a/xbmc/guilib/GUIControl.h b/xbmc/guilib/GUIControl.h
+index e18a04e..cef08d8 100644
+--- a/xbmc/guilib/GUIControl.h
++++ b/xbmc/guilib/GUIControl.h
+@@ -270,6 +270,7 @@ public:
+ GUICONTAINER_LIST,
+ GUICONTAINER_WRAPLIST,
+ GUICONTAINER_FIXEDLIST,
++ GUICONTAINER_EPGGRID,
+ GUICONTAINER_PANEL
+ };
+ GUICONTROLTYPES GetControlType() const { return ControlType; }
+diff --git a/xbmc/guilib/GUIControlFactory.cpp b/xbmc/guilib/GUIControlFactory.cpp
+index 7637211..96c83ae 100644
+--- a/xbmc/guilib/GUIControlFactory.cpp
++++ b/xbmc/guilib/GUIControlFactory.cpp
+@@ -49,6 +49,7 @@
+ #include "GUIListContainer.h"
+ #include "GUIFixedListContainer.h"
+ #include "GUIWrappingListContainer.h"
++#include "epg/GUIEPGGridContainer.h"
+ #include "GUIPanelContainer.h"
+ #include "GUIMultiSelectText.h"
+ #include "GUIListLabel.h"
+@@ -64,6 +65,7 @@
+ #include "GUIAction.h"
+
+ using namespace std;
++using namespace EPG;
+
+ typedef struct
+ {
+@@ -106,6 +108,7 @@ static const ControlMapping controls[] =
+ {"list", CGUIControl::GUICONTAINER_LIST},
+ {"wraplist", CGUIControl::GUICONTAINER_WRAPLIST},
+ {"fixedlist", CGUIControl::GUICONTAINER_FIXEDLIST},
++ {"epggrid", CGUIControl::GUICONTAINER_EPGGRID},
+ {"panel", CGUIControl::GUICONTAINER_PANEL}};
+
+ CGUIControl::GUICONTROLTYPES CGUIControlFactory::TranslateControlType(const CStdString &type)
+@@ -672,6 +675,8 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
+
+ int focusPosition = 0;
+ int scrollTime = 200;
++ int timeBlocks = 36;
++ int rulerUnit = 12;
+ bool useControlCoords = false;
+ bool renderFocusedLast = false;
+
+@@ -888,6 +893,8 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
+ GetAspectRatio(pControlNode, "aspectratio", aspect);
+ XMLUtils::GetBoolean(pControlNode, "scroll", bScrollLabel);
+ XMLUtils::GetBoolean(pControlNode,"pulseonselect", bPulse);
++ XMLUtils::GetInt(pControlNode, "timeblocks", timeBlocks);
++ XMLUtils::GetInt(pControlNode, "rulerunit", rulerUnit);
+
+ GetInfoTexture(pControlNode, "imagepath", texture, texturePath, parentID);
+
+@@ -1185,6 +1192,7 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
+ parentID, id, posX, posY, width, height,
+ textureBackground, textureLeft, textureMid, textureRight,
+ textureOverlay, bReveal);
++
+ ((CGUIProgressControl *)control)->SetInfo(singleInfo);
+ }
+ else if (type == CGUIControl::GUICONTROL_IMAGE)
+@@ -1236,6 +1244,12 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
+ ((CGUIWrappingListContainer *)control)->SetPageControl(pageControl);
+ ((CGUIWrappingListContainer *)control)->SetRenderOffset(offset);
+ }
++ else if (type == CGUIControl::GUICONTAINER_EPGGRID)
++ {
++ control = new CGUIEPGGridContainer(parentID, id, posX, posY, width, height, orientation, scrollTime, preloadItems, timeBlocks, rulerUnit);
++ ((CGUIEPGGridContainer *)control)->LoadLayout(pControlNode);
++ ((CGUIEPGGridContainer *)control)->SetRenderOffset(offset);
++ }
+ else if (type == CGUIControl::GUICONTAINER_FIXEDLIST)
+ {
+ CScroller scroller;
+diff --git a/xbmc/guilib/GUIDialog.cpp b/xbmc/guilib/GUIDialog.cpp
+index 8e9047d..0ae2763 100644
+--- a/xbmc/guilib/GUIDialog.cpp
++++ b/xbmc/guilib/GUIDialog.cpp
+@@ -36,6 +36,7 @@ CGUIDialog::CGUIDialog(int id, const CStdString &xmlFile)
+ m_renderOrder = 1;
+ m_autoClosing = false;
+ m_enableSound = true;
++ m_bAutoClosed = false;
+ }
+
+ CGUIDialog::~CGUIDialog(void)
+@@ -227,7 +228,10 @@ void CGUIDialog::Show()
+ void CGUIDialog::FrameMove()
+ {
+ if (m_autoClosing && m_showStartTime + m_showDuration < CTimeUtils::GetFrameTime() && !m_closing)
++ {
++ m_bAutoClosed = true;
+ Close();
++ }
+ CGUIWindow::FrameMove();
+ }
+
+@@ -249,8 +253,11 @@ void CGUIDialog::SetAutoClose(unsigned int timeoutMs)
+ {
+ m_autoClosing = true;
+ m_showDuration = timeoutMs;
+- if (m_active)
+- m_showStartTime = CTimeUtils::GetFrameTime();
++ ResetAutoClose();
+ }
+
+-
++void CGUIDialog::ResetAutoClose(void)
++{
++ if (m_autoClosing && m_active)
++ m_showStartTime = CTimeUtils::GetFrameTime();
++}
+diff --git a/xbmc/guilib/GUIDialog.h b/xbmc/guilib/GUIDialog.h
+index e79ad6b..b38ef40 100644
+--- a/xbmc/guilib/GUIDialog.h
++++ b/xbmc/guilib/GUIDialog.h
+@@ -56,6 +56,8 @@ public:
+ virtual bool IsModalDialog() const { return m_bModal; };
+
+ void SetAutoClose(unsigned int timeoutMs);
++ void ResetAutoClose(void);
++ bool IsAutoClosed(void) const { return m_bAutoClosed; };
+ void SetSound(bool OnOff) { m_enableSound = OnOff; };
+ virtual bool IsSoundEnabled() const { return m_enableSound; };
+
+@@ -74,4 +76,5 @@ protected:
+ bool m_enableSound;
+ unsigned int m_showStartTime;
+ unsigned int m_showDuration;
++ bool m_bAutoClosed;
+ };
+diff --git a/xbmc/guilib/GUIEditControl.cpp b/xbmc/guilib/GUIEditControl.cpp
+index 43dbd6c..fdf6fa9 100644
+--- a/xbmc/guilib/GUIEditControl.cpp
++++ b/xbmc/guilib/GUIEditControl.cpp
+@@ -256,6 +256,20 @@ void CGUIEditControl::OnClick()
+ case INPUT_TYPE_SECONDS:
+ textChanged = CGUIDialogNumeric::ShowAndGetSeconds(utf8, g_localizeStrings.Get(21420));
+ break;
++ case INPUT_TYPE_TIME:
++ {
++ CDateTime dateTime;
++ dateTime.SetFromDBTime(utf8);
++ SYSTEMTIME time;
++ dateTime.GetAsSystemTime(time);
++ if (CGUIDialogNumeric::ShowAndGetTime(time, heading > 0 ? heading : g_localizeStrings.Get(21420)))
++ {
++ dateTime = CDateTime(time);
++ utf8 = dateTime.GetAsLocalizedTime("", false);
++ textChanged = true;
++ }
++ break;
++ }
+ case INPUT_TYPE_DATE:
+ {
+ CDateTime dateTime;
+@@ -264,7 +278,7 @@ void CGUIEditControl::OnClick()
+ dateTime = CDateTime(2000, 1, 1, 0, 0, 0);
+ SYSTEMTIME date;
+ dateTime.GetAsSystemTime(date);
+- if (CGUIDialogNumeric::ShowAndGetDate(date, g_localizeStrings.Get(21420)))
++ if (CGUIDialogNumeric::ShowAndGetDate(date, heading > 0 ? heading : g_localizeStrings.Get(21420)))
+ {
+ dateTime = CDateTime(date);
+ utf8 = dateTime.GetAsDBDate();
+diff --git a/xbmc/guilib/GUIEditControl.h b/xbmc/guilib/GUIEditControl.h
+index 90d3395..d50fdf7 100644
+--- a/xbmc/guilib/GUIEditControl.h
++++ b/xbmc/guilib/GUIEditControl.h
+@@ -44,6 +44,7 @@ public:
+ INPUT_TYPE_TEXT = 0,
+ INPUT_TYPE_NUMBER,
+ INPUT_TYPE_SECONDS,
++ INPUT_TYPE_TIME,
+ INPUT_TYPE_DATE,
+ INPUT_TYPE_IPADDRESS,
+ INPUT_TYPE_PASSWORD,
+diff --git a/xbmc/guilib/GUILabelControl.cpp b/xbmc/guilib/GUILabelControl.cpp
+index f78c8a9..75129c3 100644
+--- a/xbmc/guilib/GUILabelControl.cpp
++++ b/xbmc/guilib/GUILabelControl.cpp
+@@ -175,6 +175,13 @@ float CGUILabelControl::GetWidth() const
+ return m_width;
+ }
+
++void CGUILabelControl::SetWidth(float width)
++{
++ m_width = width;
++ m_label.SetMaxRect(m_posX, m_posY, m_width, m_height);
++ CGUIControl::SetWidth(m_width);
++}
++
+ bool CGUILabelControl::OnMessage(CGUIMessage& message)
+ {
+ if ( message.GetControlId() == GetID() )
+diff --git a/xbmc/guilib/GUILabelControl.h b/xbmc/guilib/GUILabelControl.h
+index cd0222b..29f04b2 100644
+--- a/xbmc/guilib/GUILabelControl.h
++++ b/xbmc/guilib/GUILabelControl.h
+@@ -51,6 +51,7 @@ public:
+ virtual bool OnMessage(CGUIMessage& message);
+ virtual CStdString GetDescription() const;
+ virtual float GetWidth() const;
++ virtual void SetWidth(float width);
+ virtual CRect CalcRenderRegion() const;
+
+ const CLabelInfo& GetLabelInfo() const { return m_label.GetLabelInfo(); };
+diff --git a/xbmc/guilib/GUIListGroup.cpp b/xbmc/guilib/GUIListGroup.cpp
+index cc23bbe..88e9ea7 100644
+--- a/xbmc/guilib/GUIListGroup.cpp
++++ b/xbmc/guilib/GUIListGroup.cpp
+@@ -118,6 +118,50 @@ void CGUIListGroup::UpdateInfo(const CGUIListItem *item)
+ }
+ }
+
++void CGUIListGroup::EnlargeWidth(float difference)
++{
++ // Alters the width of the controls that have an ID of 1
++ for (iControls it = m_children.begin(); it != m_children.end(); it++)
++ {
++ CGUIControl *child = *it;
++ if (child->GetID() >= 1 && child->GetID() <= 14)
++ {
++ if (child->GetID() == 1) // label
++ {
++ child->SetWidth(child->GetWidth() + difference - 10);
++ child->SetVisible(child->GetWidth() > 10); ///
++ }
++ else
++ {
++ child->SetWidth(child->GetWidth() + difference);
++ }
++ }
++ }
++ SetInvalid();
++}
++
++void CGUIListGroup::EnlargeHeight(float difference)
++{
++ // Alters the width of the controls that have an ID of 1
++ for (iControls it = m_children.begin(); it != m_children.end(); it++)
++ {
++ CGUIControl *child = *it;
++ if (child->GetID() >= 1 && child->GetID() <= 14)
++ {
++ if (child->GetID() == 1) // label
++ {
++ child->SetHeight(child->GetHeight() + difference);
++ child->SetVisible(child->GetHeight() > 10); ///
++ }
++ else
++ {
++ child->SetHeight(child->GetHeight() + difference);
++ }
++ }
++ }
++ SetInvalid();
++}
++
+ void CGUIListGroup::SetInvalid()
+ {
+ if (!m_bInvalidated)
+diff --git a/xbmc/guilib/GUIListGroup.h b/xbmc/guilib/GUIListGroup.h
+index e98d799..deb0db7 100644
+--- a/xbmc/guilib/GUIListGroup.h
++++ b/xbmc/guilib/GUIListGroup.h
+@@ -48,6 +48,8 @@ public:
+ virtual void UpdateInfo(const CGUIListItem *item);
+ virtual void SetInvalid();
+
++ void EnlargeWidth(float difference);
++ void EnlargeHeight(float difference);
+ void SetFocusedItem(unsigned int subfocus);
+ unsigned int GetFocusedItem() const;
+ bool MoveLeft();
+diff --git a/xbmc/guilib/GUIListItemLayout.cpp b/xbmc/guilib/GUIListItemLayout.cpp
+index 2f9d725..dfddbfd 100644
+--- a/xbmc/guilib/GUIListItemLayout.cpp
++++ b/xbmc/guilib/GUIListItemLayout.cpp
+@@ -106,6 +106,20 @@ unsigned int CGUIListItemLayout::GetFocusedItem() const
+ return m_group.GetFocusedItem();
+ }
+
++void CGUIListItemLayout::SetWidth(float width)
++{
++ m_group.EnlargeWidth(width - m_width);
++ m_width = width;
++ SetInvalid();
++}
++
++void CGUIListItemLayout::SetHeight(float height)
++{
++ m_group.EnlargeHeight(height - m_height);
++ m_height = height;
++ SetInvalid();
++}
++
+ void CGUIListItemLayout::SelectItemFromPoint(const CPoint &point)
+ {
+ m_group.SelectItemFromPoint(point);
+diff --git a/xbmc/guilib/GUIListItemLayout.h b/xbmc/guilib/GUIListItemLayout.h
+index b571dd6..f36d8d8 100644
+--- a/xbmc/guilib/GUIListItemLayout.h
++++ b/xbmc/guilib/GUIListItemLayout.h
+@@ -50,6 +50,8 @@ public:
+ void CreateListControlLayouts(float width, float height, bool focused, const CLabelInfo &labelInfo, const CLabelInfo &labelInfo2, const CTextureInfo &texture, const CTextureInfo &textureFocus, float texHeight, float iconWidth, float iconHeight, const CStdString &nofocusCondition, const CStdString &focusCondition);
+ //#endif
+
++ void SetWidth(float width);
++ void SetHeight(float height);
+ void SelectItemFromPoint(const CPoint &point);
+ bool MoveLeft();
+ bool MoveRight();
+diff --git a/xbmc/guilib/GUIListLabel.cpp b/xbmc/guilib/GUIListLabel.cpp
+index 794ceb5..255807b 100644
+--- a/xbmc/guilib/GUIListLabel.cpp
++++ b/xbmc/guilib/GUIListLabel.cpp
+@@ -105,6 +105,18 @@ void CGUIListLabel::SetInvalid()
+ CGUIControl::SetInvalid();
+ }
+
++void CGUIListLabel::SetWidth(float width)
++{
++ m_width = width;
++ if (m_label.GetLabelInfo().align & XBFONT_RIGHT)
++ m_label.SetMaxRect(m_posX - m_width, m_posY, m_width, m_height);
++ else if (m_label.GetLabelInfo().align & XBFONT_CENTER_X)
++ m_label.SetMaxRect(m_posX - m_width*0.5f, m_posY, m_width, m_height);
++ else
++ m_label.SetMaxRect(m_posX, m_posY, m_posX + m_width, m_posY + m_height);
++ CGUIControl::SetWidth(m_width);
++}
++
+ void CGUIListLabel::SetLabel(const CStdString &label)
+ {
+ m_label.SetText(label);
+diff --git a/xbmc/guilib/GUIListLabel.h b/xbmc/guilib/GUIListLabel.h
+index 43812b0..bde065d 100644
+--- a/xbmc/guilib/GUIListLabel.h
++++ b/xbmc/guilib/GUIListLabel.h
+@@ -47,6 +47,7 @@ public:
+ virtual void UpdateInfo(const CGUIListItem *item = NULL);
+ virtual void SetFocus(bool focus);
+ virtual void SetInvalid();
++ virtual void SetWidth(float width);
+
+ void SetLabel(const CStdString &label);
+ void SetSelected(bool selected);
+diff --git a/xbmc/guilib/GUIProgressControl.cpp b/xbmc/guilib/GUIProgressControl.cpp
+index c9ade8d..70e1f7f 100644
+--- a/xbmc/guilib/GUIProgressControl.cpp
++++ b/xbmc/guilib/GUIProgressControl.cpp
+@@ -21,6 +21,9 @@
+
+ #include "GUIProgressControl.h"
+ #include "GUIInfoManager.h"
++#include "GUIListItem.h"
++#include "GUIWindowManager.h"
++#include "FileItem.h"
+
+ CGUIProgressControl::CGUIProgressControl(int parentID, int controlID,
+ float posX, float posY, float width,
+@@ -41,6 +44,7 @@ CGUIProgressControl::CGUIProgressControl(int parentID, int controlID,
+ m_iInfoCode = 0;
+ ControlType = GUICONTROL_PROGRESS;
+ m_bReveal = reveal;
++ m_bChanged = false;
+ }
+
+ CGUIProgressControl::~CGUIProgressControl(void)
+diff --git a/xbmc/guilib/GUIProgressControl.h b/xbmc/guilib/GUIProgressControl.h
+index 310879a..083dcaa 100644
+--- a/xbmc/guilib/GUIProgressControl.h
++++ b/xbmc/guilib/GUIProgressControl.h
+@@ -77,5 +77,6 @@ protected:
+ int m_iInfoCode;
+ float m_fPercent;
+ bool m_bReveal;
++ bool m_bChanged;
+ };
+ #endif
+diff --git a/xbmc/guilib/GUIWindow.cpp b/xbmc/guilib/GUIWindow.cpp
+index c3dad9a..9b375b4 100644
+--- a/xbmc/guilib/GUIWindow.cpp
++++ b/xbmc/guilib/GUIWindow.cpp
+@@ -359,13 +359,13 @@ void CGUIWindow::Close_Internal(bool forceClose /*= false*/, int nextWindowID /*
+ OnMessage(msg);
+ }
+
+-void CGUIWindow::Close(bool forceClose /*= false*/, int nextWindowID /*= 0*/, bool enableSound /*= true*/)
++void CGUIWindow::Close(bool forceClose /*= false*/, int nextWindowID /*= 0*/, bool enableSound /*= true*/, bool bWait /* = true */)
+ {
+ if (!g_application.IsCurrentThread())
+ {
+ // make sure graphics lock is not held
+ CSingleExit leaveIt(g_graphicsContext);
+- g_application.getApplicationMessenger().Close(this, forceClose, true, nextWindowID, enableSound);
++ g_application.getApplicationMessenger().Close(this, forceClose, bWait, nextWindowID, enableSound);
+ }
+ else
+ Close_Internal(forceClose, nextWindowID, enableSound);
+diff --git a/xbmc/guilib/GUIWindow.h b/xbmc/guilib/GUIWindow.h
+index a86cef5..fa087c1 100644
+--- a/xbmc/guilib/GUIWindow.h
++++ b/xbmc/guilib/GUIWindow.h
+@@ -106,7 +106,7 @@ public:
+ */
+ virtual void FrameMove() {};
+
+- void Close(bool forceClose = false, int nextWindowID = 0, bool enableSound = true);
++ void Close(bool forceClose = false, int nextWindowID = 0, bool enableSound = true, bool bWait = true);
+
+ // OnAction() is called by our window manager. We should process any messages
+ // that should be handled at the window level in the derived classes, and any
+diff --git a/xbmc/guilib/Key.h b/xbmc/guilib/Key.h
+index 3198084..6d0baa2 100644
+--- a/xbmc/guilib/Key.h
++++ b/xbmc/guilib/Key.h
+@@ -265,6 +265,8 @@
+ #define ACTION_AUDIO_DELAY 161
+ #define ACTION_SUBTITLE_DELAY 162
+
++#define ACTION_RECORD 170
++
+ #define ACTION_PASTE 180
+ #define ACTION_NEXT_CONTROL 181
+ #define ACTION_PREV_CONTROL 182
+@@ -326,14 +328,14 @@
+ #define WINDOW_SETTINGS_MYVIDEOS 10017
+ #define WINDOW_SETTINGS_NETWORK 10018
+ #define WINDOW_SETTINGS_APPEARANCE 10019
+-
+-#define WINDOW_SCRIPTS 10020 // virtual window for backward compatibility
++#define WINDOW_SETTINGS_MYPVR 10020
+
+ #define WINDOW_VIDEO_FILES 10024
+ #define WINDOW_VIDEO_NAV 10025
+ #define WINDOW_VIDEO_PLAYLIST 10028
+
+ #define WINDOW_LOGIN_SCREEN 10029
++#define WINDOW_SCRIPTS 10030
+ #define WINDOW_SETTINGS_PROFILES 10034
+
+ #define WINDOW_ADDON_BROWSER 10040
+@@ -380,17 +382,32 @@
+ #define WINDOW_DIALOG_SLIDER 10145
+ #define WINDOW_DIALOG_ADDON_INFO 10146
+ #define WINDOW_DIALOG_TEXT_VIEWER 10147
+-#define WINDOW_DIALOG_PLAY_EJECT 10148
+-#define WINDOW_DIALOG_PERIPHERAL_MANAGER 10149
+-#define WINDOW_DIALOG_PERIPHERAL_SETTINGS 10150
++#define WINDOW_DIALOG_EXT_PROGRESS 10148
++#define WINDOW_DIALOG_PLAY_EJECT 10149
++#define WINDOW_DIALOG_PERIPHERAL_MANAGER 10150
++#define WINDOW_DIALOG_PERIPHERAL_SETTINGS 10151
+
+ #define WINDOW_MUSIC_PLAYLIST 10500
+ #define WINDOW_MUSIC_FILES 10501
+ #define WINDOW_MUSIC_NAV 10502
+ #define WINDOW_MUSIC_PLAYLIST_EDITOR 10503
+
+-#define WINDOW_DIALOG_OSD_TELETEXT 10600
+-
++// PVR related Window and Dialog ID's
++#define WINDOW_PVR 10600
++#define WINDOW_DIALOG_PVR_GUIDE_INFO 10601
++#define WINDOW_DIALOG_PVR_RECORDING_INFO 10602
++#define WINDOW_DIALOG_PVR_TIMER_SETTING 10603
++#define WINDOW_DIALOG_PVR_GROUP_MANAGER 10604
++#define WINDOW_DIALOG_PVR_CHANNEL_MANAGER 10605
++#define WINDOW_DIALOG_PVR_GUIDE_SEARCH 10606
++#define WINDOW_DIALOG_PVR_CHANNEL_SCAN 10607
++#define WINDOW_DIALOG_PVR_UPDATE_PROGRESS 10608
++#define WINDOW_DIALOG_PVR_OSD_CHANNELS 10609
++#define WINDOW_DIALOG_PVR_OSD_GUIDE 10610
++#define WINDOW_DIALOG_PVR_OSD_DIRECTOR 10611
++#define WINDOW_DIALOG_PVR_OSD_CUTTER 10612
++#define WINDOW_DIALOG_OSD_TELETEXT 10613
++// PVR_WINDOW VIEWS = 10694-10699
+ //#define WINDOW_VIRTUAL_KEYBOARD 11000
+ #define WINDOW_DIALOG_SELECT 12000
+ #define WINDOW_DIALOG_MUSIC_INFO 12001
+@@ -418,6 +435,11 @@
+ #define WINDOW_PYTHON_START 13000
+ #define WINDOW_PYTHON_END 13099
+
++// WINDOW_ID's from 14000 to 14099 reserved for Addons
++
++#define WINDOW_ADDON_START 14000
++#define WINDOW_ADDON_END 14099
++
+ #define ICON_TYPE_NONE 101
+ #define ICON_TYPE_PROGRAMS 102
+ #define ICON_TYPE_MUSIC 103
+diff --git a/xbmc/input/ButtonTranslator.cpp b/xbmc/input/ButtonTranslator.cpp
+index 0e76a1c..efd2ec6 100644
+--- a/xbmc/input/ButtonTranslator.cpp
++++ b/xbmc/input/ButtonTranslator.cpp
+@@ -226,6 +226,23 @@ static const ActionMapping windows[] =
+ {"music" , WINDOW_MUSIC},
+ {"video" , WINDOW_VIDEOS},
+ {"videos" , WINDOW_VIDEO_NAV},
++ {"tv" , WINDOW_PVR}, // backward compat
++ {"pvr" , WINDOW_PVR},
++
++ {"pvrguideinfo" , WINDOW_DIALOG_PVR_GUIDE_INFO},
++ {"pvrrecordinginfo" , WINDOW_DIALOG_PVR_RECORDING_INFO},
++ {"pvrtimersetting" , WINDOW_DIALOG_PVR_TIMER_SETTING},
++ {"pvrgroupmanager" , WINDOW_DIALOG_PVR_GROUP_MANAGER},
++ {"pvrchannelmanager" , WINDOW_DIALOG_PVR_CHANNEL_MANAGER},
++ {"pvrguidesearch" , WINDOW_DIALOG_PVR_GUIDE_SEARCH},
++ {"pvrchannelscan" , WINDOW_DIALOG_PVR_CHANNEL_SCAN},
++ {"pvrupdateprogress" , WINDOW_DIALOG_PVR_UPDATE_PROGRESS},
++ {"pvrosdchannels" , WINDOW_DIALOG_PVR_OSD_CHANNELS},
++ {"pvrosdguide" , WINDOW_DIALOG_PVR_OSD_GUIDE},
++ {"pvrosddirector" , WINDOW_DIALOG_PVR_OSD_DIRECTOR},
++ {"pvrosdcutter" , WINDOW_DIALOG_PVR_OSD_CUTTER},
++ {"pvrosdteletext" , WINDOW_DIALOG_OSD_TELETEXT},
++
+ {"systeminfo" , WINDOW_SYSTEM_INFORMATION},
+ {"testpattern" , WINDOW_TEST_PATTERN},
+ {"screencalibration" , WINDOW_SCREEN_CALIBRATION},
+@@ -238,6 +255,8 @@ static const ActionMapping windows[] =
+ {"videossettings" , WINDOW_SETTINGS_MYVIDEOS},
+ {"networksettings" , WINDOW_SETTINGS_NETWORK},
+ {"appearancesettings" , WINDOW_SETTINGS_APPEARANCE},
++ {"pvrsettings" , WINDOW_SETTINGS_MYPVR},
++ {"tvsettings" , WINDOW_SETTINGS_MYPVR}, // backward compat
+ {"scripts" , WINDOW_PROGRAMS}, // backward compat
+ {"videofiles" , WINDOW_VIDEO_FILES},
+ {"videolibrary" , WINDOW_VIDEO_NAV},
+@@ -250,6 +269,10 @@ static const ActionMapping windows[] =
+ {"virtualkeyboard" , WINDOW_DIALOG_KEYBOARD},
+ {"volumebar" , WINDOW_DIALOG_VOLUME_BAR},
+ {"submenu" , WINDOW_DIALOG_SUB_MENU},
++ {"pvrosdchannels" , WINDOW_DIALOG_PVR_OSD_CHANNELS},
++ {"pvrosdguide" , WINDOW_DIALOG_PVR_OSD_GUIDE},
++ {"pvrosddirector" , WINDOW_DIALOG_PVR_OSD_DIRECTOR},
++ {"pvrosdcutter" , WINDOW_DIALOG_PVR_OSD_CUTTER},
+ {"favourites" , WINDOW_DIALOG_FAVOURITES},
+ {"contextmenu" , WINDOW_DIALOG_CONTEXT_MENU},
+ {"infodialog" , WINDOW_DIALOG_KAI_TOAST},
+@@ -1133,13 +1156,17 @@ uint32_t CButtonTranslator::TranslateRemoteString(const char *szButton)
+ else if (strButton.Equals("pageminus")) buttonCode = XINPUT_IR_REMOTE_CHANNEL_MINUS;
+ else if (strButton.Equals("mute")) buttonCode = XINPUT_IR_REMOTE_MUTE;
+ else if (strButton.Equals("recordedtv")) buttonCode = XINPUT_IR_REMOTE_RECORDED_TV;
+- else if (strButton.Equals("guide")) buttonCode = XINPUT_IR_REMOTE_TITLE; // same as title
++ else if (strButton.Equals("guide")) buttonCode = XINPUT_IR_REMOTE_GUIDE;
+ else if (strButton.Equals("livetv")) buttonCode = XINPUT_IR_REMOTE_LIVE_TV;
++ else if (strButton.Equals("liveradio")) buttonCode = XINPUT_IR_REMOTE_LIVE_RADIO;
++ else if (strButton.Equals("epgsearch")) buttonCode = XINPUT_IR_REMOTE_EPG_SEARCH;
+ else if (strButton.Equals("star")) buttonCode = XINPUT_IR_REMOTE_STAR;
+ else if (strButton.Equals("hash")) buttonCode = XINPUT_IR_REMOTE_HASH;
+ else if (strButton.Equals("clear")) buttonCode = XINPUT_IR_REMOTE_CLEAR;
+ else if (strButton.Equals("enter")) buttonCode = XINPUT_IR_REMOTE_ENTER;
+ else if (strButton.Equals("xbox")) buttonCode = XINPUT_IR_REMOTE_DISPLAY; // same as display
++ else if (strButton.Equals("playlist")) buttonCode = XINPUT_IR_REMOTE_PLAYLIST;
++ else if (strButton.Equals("guide")) buttonCode = XINPUT_IR_REMOTE_GUIDE;
+ else if (strButton.Equals("teletext")) buttonCode = XINPUT_IR_REMOTE_TELETEXT;
+ else if (strButton.Equals("red")) buttonCode = XINPUT_IR_REMOTE_RED;
+ else if (strButton.Equals("green")) buttonCode = XINPUT_IR_REMOTE_GREEN;
+diff --git a/xbmc/input/XBIRRemote.h b/xbmc/input/XBIRRemote.h
+index bc74373..134d694 100644
+--- a/xbmc/input/XBIRRemote.h
++++ b/xbmc/input/XBIRRemote.h
+@@ -88,6 +88,11 @@
+ #define XINPUT_IR_REMOTE_GREEN 252
+ #define XINPUT_IR_REMOTE_YELLOW 253
+ #define XINPUT_IR_REMOTE_BLUE 254
++#define XINPUT_IR_REMOTE_PLAYLIST 255
++#define XINPUT_IR_REMOTE_GUIDE 256
++
++#define XINPUT_IR_REMOTE_LIVE_RADIO 248
++#define XINPUT_IR_REMOTE_EPG_SEARCH 246
+
+ typedef struct _XINPUT_IR_REMOTE
+ {
+diff --git a/xbmc/interfaces/Builtins.cpp b/xbmc/interfaces/Builtins.cpp
+index 588be8d..6f4cec8 100644
+--- a/xbmc/interfaces/Builtins.cpp
++++ b/xbmc/interfaces/Builtins.cpp
+@@ -111,11 +111,14 @@ const BUILT_IN commands[] = {
+ { "Quit", false, "Quit XBMC" },
+ { "Hibernate", false, "Hibernates the system" },
+ { "Suspend", false, "Suspends the system" },
++ { "InhibitIdleShutdown", false, "Inhibit idle shutdown" },
++ { "AllowIdleShutdown", false, "Allow idle shutdown" },
+ { "RestartApp", false, "Restart XBMC" },
+ { "Minimize", false, "Minimize XBMC" },
+ { "Reset", false, "Reset the xbox (warm reboot)" },
+ { "Mastermode", false, "Control master mode" },
+ { "ActivateWindow", true, "Activate the specified window" },
++ { "ActivateWindowAndFocus", true, "Activate the specified window and sets focus to the specified id" },
+ { "ReplaceWindow", true, "Replaces the current window with the new one" },
+ { "TakeScreenshot", false, "Takes a Screenshot" },
+ { "RunScript", true, "Run the specified script" },
+@@ -271,6 +274,11 @@ int CBuiltins::Execute(const CStdString& execString)
+ {
+ g_application.getApplicationMessenger().Quit();
+ }
++ else if (execute.Equals("inhibitidleshutdown"))
++ {
++ bool inhibit = (params.size() == 1 && params[0].Equals("true"));
++ g_application.getApplicationMessenger().InhibitIdleShutdown(inhibit);
++ }
+ else if (execute.Equals("minimize"))
+ {
+ g_application.getApplicationMessenger().Minimize();
+@@ -350,6 +358,39 @@ int CBuiltins::Execute(const CStdString& execString)
+ CGUIMessage msg(GUI_MSG_SETFOCUS, g_windowManager.GetFocusedWindow(), controlID, subItem);
+ g_windowManager.SendMessage(msg);
+ }
++ else if ((execute.Equals("activatewindowandfocus")) && params.size())
++ {
++ CStdString strWindow = params[0];
++
++ // confirm the window destination is valid prior to switching
++ int iWindow = CButtonTranslator::TranslateWindow(strWindow);
++ if (iWindow != WINDOW_INVALID)
++ {
++ // disable the screensaver
++ g_application.WakeUpScreenSaverAndDPMS();
++#if defined(__APPLE__) && defined(__arm__)
++ if (params[0].Equals("shutdownmenu"))
++ CBuiltins::Execute("Quit");
++#endif
++ vector<CStdString> dummy;
++ g_windowManager.ActivateWindow(iWindow, dummy, !execute.Equals("activatewindow"));
++
++ unsigned int iPtr = 1;
++ while (params.size() > iPtr + 1)
++ {
++ CGUIMessage msg(GUI_MSG_SETFOCUS, g_windowManager.GetFocusedWindow(),
++ atol(params[iPtr].c_str()),
++ (params.size() >= iPtr + 2) ? atol(params[iPtr + 1].c_str())+1 : 0);
++ g_windowManager.SendMessage(msg);
++ iPtr += 2;
++ }
++ }
++ else
++ {
++ CLog::Log(LOGERROR, "ActivateWindowAndFocus called with invalid destination window: %s", strWindow.c_str());
++ return false;
++ }
++ }
+ #ifdef HAS_PYTHON
+ else if (execute.Equals("runscript") && params.size())
+ {
+diff --git a/xbmc/interfaces/json-rpc/JSONServiceDescription.cpp b/xbmc/interfaces/json-rpc/JSONServiceDescription.cpp
+index b38806f..4cf67a4 100644
+--- a/xbmc/interfaces/json-rpc/JSONServiceDescription.cpp
++++ b/xbmc/interfaces/json-rpc/JSONServiceDescription.cpp
+@@ -34,6 +34,7 @@
+ #include "InputOperations.h"
+ #include "XBMCOperations.h"
+ #include "ApplicationOperations.h"
++#include "PVROperations.h"
+
+ using namespace std;
+ using namespace JSONRPC;
+@@ -189,7 +190,18 @@ JsonRpcMethodMap CJSONServiceDescription::m_methodMaps[] = {
+
+ // XBMC operations
+ { "XBMC.GetInfoLabels", CXBMCOperations::GetInfoLabels },
+- { "XBMC.GetInfoBooleans", CXBMCOperations::GetInfoBooleans }
++ { "XBMC.GetInfoBooleans", CXBMCOperations::GetInfoBooleans },
++
++// PVR operations
++ { "PVR.ChannelSwitch", CPVROperations::ChannelSwitch },
++ { "PVR.ChannelUp", CPVROperations::ChannelUp },
++ { "PVR.ChannelDown", CPVROperations::ChannelDown },
++ { "PVR.RecordCurrentChannel", CPVROperations::RecordCurrentChannel },
++ { "PVR.ScheduleRecording", CPVROperations::ScheduleRecording },
++ { "PVR.IsAvailable", CPVROperations::IsAvailable },
++ { "PVR.IsScanningChannels", CPVROperations::IsScanningChannels },
++ { "PVR.IsRecording", CPVROperations::IsRecording },
++ { "PVR.ScanChannels", CPVROperations::ScanChannels }
+ };
+
+ bool CJSONServiceDescription::prepareDescription(std::string &description, CVariant &descriptionObject, std::string &name)
+diff --git a/xbmc/interfaces/json-rpc/JSONUtils.h b/xbmc/interfaces/json-rpc/JSONUtils.h
+index 984f576..142844e 100644
+--- a/xbmc/interfaces/json-rpc/JSONUtils.h
++++ b/xbmc/interfaces/json-rpc/JSONUtils.h
+@@ -67,19 +67,20 @@ namespace JSONRPC
+ */
+ enum OperationPermission
+ {
+- ReadData = 0x1,
+- ControlPlayback = 0x2,
+- ControlNotify = 0x4,
+- ControlPower = 0x8,
+- UpdateData = 0x10,
+- RemoveData = 0x20,
+- Navigate = 0x40,
+- WriteFile = 0x80
++ ReadData = 0x1,
++ ControlPlayback = 0x2,
++ ControlNotify = 0x4,
++ ControlPower = 0x8,
++ UpdateData = 0x10,
++ RemoveData = 0x20,
++ Navigate = 0x40,
++ WriteFile = 0x80,
++ ControlPVR = 0x300
+ };
+
+- static const int OPERATION_PERMISSION_ALL = (ReadData | ControlPlayback | ControlNotify | ControlPower | UpdateData | RemoveData | Navigate | WriteFile);
++ static const int OPERATION_PERMISSION_ALL = (ReadData | ControlPlayback | ControlNotify | ControlPower | UpdateData | RemoveData | Navigate | WriteFile | ControlPVR);
+
+- static const int OPERATION_PERMISSION_NOTIFICATION = (ControlPlayback | ControlNotify | ControlPower | UpdateData | RemoveData | Navigate | WriteFile);
++ static const int OPERATION_PERMISSION_NOTIFICATION = (ControlPlayback | ControlNotify | ControlPower | UpdateData | RemoveData | Navigate | WriteFile | ControlPVR);
+
+ /*!
+ \brief Possible value types of a parameter or return type
+@@ -219,6 +220,8 @@ namespace JSONRPC
+ return "Navigate";
+ case WriteFile:
+ return "WriteFile";
++ case ControlPVR:
++ return "ControlPVR";
+ default:
+ return "Unknown";
+ }
+@@ -246,7 +249,8 @@ namespace JSONRPC
+ return Navigate;
+ if (permission.compare("WriteFile") == 0)
+ return WriteFile;
+-
++ if (permission.compare("ControlPVR") == 0)
++ return ControlPVR;
+ return ReadData;
+ }
+
+diff --git a/xbmc/interfaces/json-rpc/Makefile b/xbmc/interfaces/json-rpc/Makefile
+index 648ec24..f281b8f 100644
+--- a/xbmc/interfaces/json-rpc/Makefile
++++ b/xbmc/interfaces/json-rpc/Makefile
+@@ -10,6 +10,7 @@ SRCS=ApplicationOperations.cpp \
+ InputOperations.cpp \
+ VideoLibrary.cpp \
+ XBMCOperations.cpp \
++ PVROperations.cpp \
+
+ LIB=json-rpc.a
+
+diff --git a/xbmc/interfaces/json-rpc/PVROperations.cpp b/xbmc/interfaces/json-rpc/PVROperations.cpp
+new file mode 100644
+index 0000000..c57b6b0
+--- /dev/null
++++ b/xbmc/interfaces/json-rpc/PVROperations.cpp
+@@ -0,0 +1,207 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "PVROperations.h"
++#include "Application.h"
++#include "utils/log.h"
++
++#include "pvr/PVRManager.h"
++#include "pvr/channels/PVRChannelGroupsContainer.h"
++#include "pvr/channels/PVRChannel.h"
++#include "pvr/timers/PVRTimers.h"
++#include "pvr/timers/PVRTimerInfoTag.h"
++#include "epg/EpgInfoTag.h"
++#include "epg/EpgContainer.h"
++
++using namespace JSONRPC;
++using namespace PVR;
++using namespace EPG;
++
++JSON_STATUS CPVROperations::ChannelSwitch(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
++{
++ if (!g_PVRManager.IsStarted())
++ {
++ CLog::Log(LOGDEBUG, "JSONRPC: PVR not started");
++ return FailedToExecute;
++ }
++
++ int iChannelId = (int)parameterObject["channelid"].asInteger();
++ if (iChannelId <= 0)
++ return InvalidParams;
++
++ CLog::Log(LOGDEBUG, "JSONRPC: switch to channel '%d'", iChannelId);
++
++ const CPVRChannel *channel = g_PVRChannelGroups->GetByChannelIDFromAll(iChannelId);
++ if (channel == NULL)
++ return InternalError;
++
++ CPVRChannel currentChannel;
++ if (g_PVRManager.GetCurrentChannel(currentChannel) && currentChannel.IsRadio() == channel->IsRadio())
++ g_application.getApplicationMessenger().SendAction(CAction(ACTION_CHANNEL_SWITCH, (float)channel->ChannelNumber()));
++ else
++ g_application.getApplicationMessenger().MediaPlay(CFileItem(*channel));
++ return ACK;
++}
++
++JSON_STATUS CPVROperations::ChannelUp(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
++{
++ if (!g_PVRManager.IsStarted())
++ {
++ CLog::Log(LOGDEBUG, "JSONRPC: PVR not started");
++ return FailedToExecute;
++ }
++
++ CLog::Log(LOGDEBUG, "JSONRPC: channel up");
++ g_application.getApplicationMessenger().SendAction(CAction(ACTION_NEXT_ITEM));
++ return ACK;
++}
++
++JSON_STATUS CPVROperations::ChannelDown(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
++{
++ if (!g_PVRManager.IsStarted())
++ {
++ CLog::Log(LOGDEBUG, "JSONRPC: PVR not started");
++ return FailedToExecute;
++ }
++
++ CLog::Log(LOGDEBUG, "JSONRPC: channel down");
++ g_application.getApplicationMessenger().SendAction(CAction(ACTION_PREV_ITEM));
++ return ACK;
++}
++
++JSON_STATUS CPVROperations::RecordCurrentChannel(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
++{
++ if (!g_PVRManager.IsStarted())
++ {
++ CLog::Log(LOGDEBUG, "JSONRPC: PVR not started");
++ return FailedToExecute;
++ }
++
++ CPVRChannel currentChannel;
++ if (g_PVRManager.GetCurrentChannel(currentChannel))
++ {
++ bool bOnOff = !currentChannel.IsRecording();
++ if (g_PVRManager.StartRecordingOnPlayingChannel(bOnOff))
++ {
++ CLog::Log(LOGDEBUG, "JSONRPC: set recording on currently playing channel to '%s'", bOnOff ? "on" : "off" );
++ return ACK;
++ }
++ else
++ {
++ CLog::Log(LOGERROR, "JSONRPC: unable to set recording to '%s'", bOnOff ? "on" : "off" );
++ return InternalError;
++ }
++ }
++ else
++ {
++ CLog::Log(LOGERROR, "JSONRPC: failed to start recording - no channel is playing");
++ return FailedToExecute;
++ }
++}
++
++JSON_STATUS CPVROperations::IsAvailable(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
++{
++ result = g_PVRManager.IsStarted();
++
++ return OK;
++}
++
++JSON_STATUS CPVROperations::IsScanningChannels(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
++{
++ if (!g_PVRManager.IsStarted())
++ {
++ CLog::Log(LOGDEBUG, "JSONRPC: PVR not started");
++ return FailedToExecute;
++ }
++
++ result = g_PVRManager.IsRunningChannelScan();
++
++ return OK;
++}
++
++JSON_STATUS CPVROperations::IsRecording(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
++{
++ if (!g_PVRManager.IsStarted())
++ {
++ CLog::Log(LOGDEBUG, "JSONRPC: PVR not started");
++ return FailedToExecute;
++ }
++
++ result = g_PVRManager.IsRecording();
++
++ return OK;
++}
++
++JSON_STATUS CPVROperations::ScanChannels(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
++{
++ if (!g_PVRManager.IsStarted())
++ {
++ CLog::Log(LOGDEBUG, "JSONRPC: PVR not started");
++ return FailedToExecute;
++ }
++
++ if (!g_PVRManager.IsRunningChannelScan())
++ g_PVRManager.StartChannelScan();
++
++ return ACK;
++}
++
++JSON_STATUS CPVROperations::ScheduleRecording(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
++{
++ if (!g_PVRManager.IsStarted())
++ {
++ CLog::Log(LOGDEBUG, "JSONRPC: PVR not started");
++ return FailedToExecute;
++ }
++
++ int iEpgId = (int)parameterObject["epgid"].asInteger();
++ int iUniqueId = (int)parameterObject["uniqueid"].asInteger();
++ int iStartTime = (int)parameterObject["starttime"].asInteger();
++
++ if (iEpgId > 0 && iUniqueId > 0 && iStartTime > 0)
++ {
++ CDateTime startTime(iStartTime);
++ CEpgInfoTag *tag = g_EpgContainer.GetById(iEpgId)->GetTag(iUniqueId, startTime);
++
++ if (tag && tag->HasPVRChannel())
++ {
++ CLog::Log(LOGDEBUG, "JSONRPC: schedule recording - channel: '%s' start: '%s' end: '%s'", tag->PVRChannelName().c_str(), tag->StartAsLocalTime().GetAsLocalizedDateTime(false, false).c_str(), tag->EndAsLocalTime().GetAsLocalizedDateTime(false, false).c_str());
++
++ CPVRTimerInfoTag *newTimer = CPVRTimerInfoTag::CreateFromEpg(*tag);
++ bool bCreated = (newTimer != NULL);
++ bool bAdded = false;
++
++ if (bCreated)
++ {
++ CLog::Log(LOGDEBUG, "JSONRPC: recording scheduled");
++ bAdded = CPVRTimers::AddTimer(*newTimer);
++ }
++ else
++ {
++ CLog::Log(LOGERROR, "JSONRPC: failed to schedule recording");
++ }
++ delete newTimer;
++ return bAdded ? ACK : InternalError;
++ }
++ }
++
++ return InvalidParams;
++}
+diff --git a/xbmc/interfaces/json-rpc/PVROperations.h b/xbmc/interfaces/json-rpc/PVROperations.h
+new file mode 100644
+index 0000000..1ed4e4b
+--- /dev/null
++++ b/xbmc/interfaces/json-rpc/PVROperations.h
+@@ -0,0 +1,41 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "utils/StdString.h"
++#include "JSONRPC.h"
++
++namespace JSONRPC
++{
++ class CPVROperations
++ {
++ public:
++ static JSON_STATUS ChannelSwitch(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result);
++ static JSON_STATUS ChannelUp(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result);
++ static JSON_STATUS ChannelDown(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result);
++ static JSON_STATUS RecordCurrentChannel(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result);
++ static JSON_STATUS ScheduleRecording(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result);
++ static JSON_STATUS IsAvailable(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result);
++ static JSON_STATUS IsScanningChannels(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result);
++ static JSON_STATUS IsRecording(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result);
++ static JSON_STATUS ScanChannels(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result);
++ };
++}
+diff --git a/xbmc/interfaces/json-rpc/ServiceDescription.h b/xbmc/interfaces/json-rpc/ServiceDescription.h
+index 1d68689..4af3d04 100644
+--- a/xbmc/interfaces/json-rpc/ServiceDescription.h
++++ b/xbmc/interfaces/json-rpc/ServiceDescription.h
+@@ -819,7 +819,8 @@ namespace JSONRPC
+ "\"UpdateData\": { \"type\": \"boolean\", \"required\": true },"
+ "\"RemoveData\": { \"type\": \"boolean\", \"required\": true },"
+ "\"Navigate\": { \"type\": \"boolean\", \"required\": true },"
+- "\"WriteFile\": { \"type\": \"boolean\", \"required\": true }"
++ "\"WriteFile\": { \"type\": \"boolean\", \"required\": true },"
++ "\"ControlPVR\": { \"type\": \"boolean\", \"required\": true }"
+ "}"
+ "}"
+ "}",
+@@ -2044,6 +2045,84 @@ namespace JSONRPC
+ "\"type\": \"object\","
+ "\"description\": \"List of key-value pairs of the retrieved info booleans\""
+ "}"
++ "}",
++ "\"PVR.ChannelSwitch\": {"
++ "\"type\": \"method\","
++ "\"description\": \"Switch channel to given channelid\","
++ "\"transport\": \"Response\","
++ "\"permission\": \"ControlPlayback\","
++ "\"params\": ["
++ "{ \"name\": \"channelid\", \"type\": \"integer\", \"minimum\": 0, \"required\": true }"
++ "],"
++ "\"returns\": \"string\""
++ "}",
++ "\"PVR.ChannelUp\": {"
++ "\"type\": \"method\","
++ "\"description\": \"Switch channel up\","
++ "\"transport\": \"Response\","
++ "\"permission\": \"ControlPlayback\","
++ "\"params\": [],"
++ "\"returns\": \"string\""
++ "}",
++ "\"PVR.ChannelDown\": {"
++ "\"type\": \"method\","
++ "\"description\": \"Switch channel down\","
++ "\"transport\": \"Response\","
++ "\"permission\": \"ControlPlayback\","
++ "\"params\": [],"
++ "\"returns\": \"string\""
++ "}",
++ "\"PVR.RecordCurrentChannel\": {"
++ "\"type\": \"method\","
++ "\"description\": \"Toggles Recording of currently playing channel\","
++ "\"transport\": \"Response\","
++ "\"permission\": \"ControlPVR\","
++ "\"params\": [],"
++ "\"returns\": \"string\""
++ "}",
++ "\"PVR.ScheduleRecording\": {"
++ "\"type\": \"method\","
++ "\"description\": \"Schedule recording\","
++ "\"transport\": \"Response\","
++ "\"permission\": \"ControlPVR\","
++ "\"params\": ["
++ "{ \"name\": \"epgid\", \"type\": \"integer\", \"minimum\": 0, \"required\": true },"
++ "{ \"name\": \"uniqueid\", \"type\": \"integer\", \"minimum\": 0, \"required\": true },"
++ "{ \"name\": \"starttime\", \"type\": \"integer\", \"minimum\": 0, \"required\": true }"
++ "],"
++ "\"returns\": \"string\""
++ "}",
++ "\"PVR.IsAvailable\": {"
++ "\"type\": \"method\","
++ "\"description\": \"Checks whether PVR is available or not\","
++ "\"transport\": \"Response\","
++ "\"permission\": \"ReadData\","
++ "\"params\": [],"
++ "\"returns\": \"boolean\""
++ "}",
++ "\"PVR.IsScanningChannels\": {"
++ "\"type\": \"method\","
++ "\"description\": \"Checks whether PVR is scanning channels\","
++ "\"transport\": \"Response\","
++ "\"permission\": \"ReadData\","
++ "\"params\": [],"
++ "\"returns\": \"boolean\""
++ "}",
++ "\"PVR.IsRecording\": {"
++ "\"type\": \"method\","
++ "\"description\": \"Checks whether PVR is recording\","
++ "\"transport\": \"Response\","
++ "\"permission\": \"ReadData\","
++ "\"params\": [],"
++ "\"returns\": \"boolean\""
++ "}",
++ "\"PVR.ScanChannels\": {"
++ "\"type\": \"method\","
++ "\"description\": \"Starts a channel scan\","
++ "\"transport\": \"Response\","
++ "\"permission\": \"ControlPVR\","
++ "\"params\": [],"
++ "\"returns\": \"string\""
+ "}"
+ };
+
+diff --git a/xbmc/interfaces/json-rpc/methods.json b/xbmc/interfaces/json-rpc/methods.json
+index 5e2a5a6..7c8a947 100644
+--- a/xbmc/interfaces/json-rpc/methods.json
++++ b/xbmc/interfaces/json-rpc/methods.json
+@@ -42,7 +42,8 @@
+ "UpdateData": { "type": "boolean", "required": true },
+ "RemoveData": { "type": "boolean", "required": true },
+ "Navigate": { "type": "boolean", "required": true },
+- "WriteFile": { "type": "boolean", "required": true }
++ "WriteFile": { "type": "boolean", "required": true },
++ "ControlPVR": { "type": "boolean", "required": true }
+ }
+ }
+ },
+@@ -1267,5 +1268,83 @@
+ "type": "object",
+ "description": "List of key-value pairs of the retrieved info booleans"
+ }
++ },
++ "PVR.ChannelSwitch": {
++ "type": "method",
++ "description": "Switch channel to given channelid",
++ "transport": "Response",
++ "permission": "ControlPlayback",
++ "params": [
++ { "name": "channelid", "type": "integer", "minimum": 0, "required": true }
++ ],
++ "returns": "string"
++ },
++ "PVR.ChannelUp": {
++ "type": "method",
++ "description": "Switch channel up",
++ "transport": "Response",
++ "permission": "ControlPlayback",
++ "params": [],
++ "returns": "string"
++ },
++ "PVR.ChannelDown": {
++ "type": "method",
++ "description": "Switch channel down",
++ "transport": "Response",
++ "permission": "ControlPlayback",
++ "params": [],
++ "returns": "string"
++ },
++ "PVR.RecordCurrentChannel": {
++ "type": "method",
++ "description": "Toggles Recording of currently playing channel",
++ "transport": "Response",
++ "permission": "ControlPVR",
++ "params": [],
++ "returns": "string"
++ },
++ "PVR.ScheduleRecording": {
++ "type": "method",
++ "description": "Schedule recording",
++ "transport": "Response",
++ "permission": "ControlPVR",
++ "params": [
++ { "name": "epgid", "type": "integer", "minimum": 0, "required": true },
++ { "name": "uniqueid", "type": "integer", "minimum": 0, "required": true },
++ { "name": "starttime", "type": "integer", "minimum": 0, "required": true}
++ ],
++ "returns": "string"
++ },
++ "PVR.IsAvailable": {
++ "type": "method",
++ "description": "Checks whether PVR is available or not",
++ "transport": "Response",
++ "permission": "ReadData",
++ "params": [],
++ "returns": "boolean"
++ },
++ "PVR.IsScanningChannels": {
++ "type": "method",
++ "description": "Checks whether PVR is scanning channels",
++ "transport": "Response",
++ "permission": "ReadData",
++ "params": [],
++ "returns": "boolean"
++ },
++ "PVR.IsRecording": {
++ "type": "method",
++ "description": "Checks whether PVR is recording",
++ "transport": "Response",
++ "permission": "ReadData",
++ "params": [],
++ "returns": "boolean"
++ },
++ "PVR.ScanChannels": {
++ "type": "method",
++ "description": "Starts a channel scan",
++ "transport": "Response",
++ "permission": "ControlPVR",
++ "params": [],
++ "returns": "string"
+ }
+ }
+\ No newline at end of file
+diff --git a/xbmc/interfaces/python/xbmcmodule/xbmcmodule.cpp b/xbmc/interfaces/python/xbmcmodule/xbmcmodule.cpp
+index ced6bc9..974993a 100644
+--- a/xbmc/interfaces/python/xbmcmodule/xbmcmodule.cpp
++++ b/xbmc/interfaces/python/xbmcmodule/xbmcmodule.cpp
+@@ -53,6 +53,9 @@
+ #include "utils/FileUtils.h"
+ #include "pythreadstate.h"
+ #include "utils/log.h"
++#include "utils/Weather.h"
++#include "guilib/GUIFontManager.h"
++#include "filesystem/Directory.h"
+ #include "pyrendercapture.h"
+
+ // include for constants
+@@ -966,6 +969,56 @@ namespace PYXBMC
+ return Py_BuildValue((char*)"ss",strSize.c_str(), strHash.c_str());
+ }
+
++ // getcleanmovietitle function
++ PyDoc_STRVAR(setLanguage__doc__,
++ "setLanguage(language)\n"
++ "\n"
++ "language : string or unicode - Language string\n"
++ "\n"
++ "example:\n"
++ " xbmc.setLanguage('English')");
++
++ PyObject* XBMC_SetLanguage(PyObject *self, PyObject *args)
++ {
++ char *cLine = NULL;
++ if (!PyArg_ParseTuple(args, (char*)"s", &cLine)) return NULL;
++ CStdString strLanguage = cLine;
++ CFileItemList items;
++ CDirectory::GetDirectory("special://xbmc/language/", items);
++ for (int i = 0; i < items.Size(); ++i)
++ {
++ CFileItemPtr pItem = items[i];
++ if (pItem->m_bIsFolder && (strcmpi(pItem->GetLabel().c_str(), strLanguage.c_str()) == 0))
++ {
++ CStdString strLangInfoPath;
++ strLangInfoPath.Format("special://xbmc/language/%s/langinfo.xml", strLanguage.c_str());
++ g_langInfo.Load(strLangInfoPath);
++
++ if (g_langInfo.ForceUnicodeFont() && !g_fontManager.IsFontSetUnicode())
++ {
++ CLog::Log(LOGINFO, "Language needs a ttf font, loading first ttf font available");
++ CStdString strFontSet;
++ if (g_fontManager.GetFirstFontSetUnicode(strFontSet))
++ strLanguage = strFontSet;
++ else
++ CLog::Log(LOGERROR, "No ttf font found but needed: %s", strFontSet.c_str());
++ }
++ g_guiSettings.SetString("locale.language", strLanguage);
++
++ g_charsetConverter.reset();
++
++ CStdString strLanguagePath;
++ strLanguagePath.Format("special://xbmc/language/%s/strings.xml", strLanguage.c_str());
++ g_localizeStrings.Load(strLanguagePath);
++ g_weatherManager.Refresh();
++
++ g_application.getApplicationMessenger().ExecBuiltIn("ReloadSkin");
++ }
++ }
++ Py_INCREF(Py_None);
++ return Py_None;
++ }
++
+ // define c functions to be used in python here
+ PyMethodDef xbmcMethods[] = {
+ {(char*)"output", (PyCFunction)XBMC_Output, METH_VARARGS|METH_KEYWORDS, output__doc__},
+@@ -1013,6 +1066,8 @@ namespace PYXBMC
+ {(char*)"skinHasImage", (PyCFunction)XBMC_SkinHasImage, METH_VARARGS|METH_KEYWORDS, skinHasImage__doc__},
+ {(char*)"subHashAndFileSize", (PyCFunction)XBMC_subHashAndFileSize, METH_VARARGS, subHashAndFileSize__doc__},
+
++ {(char*)"setLanguage", (PyCFunction)XBMC_SetLanguage, METH_VARARGS, setLanguage__doc__},
++
+ {NULL, NULL, 0, NULL}
+ };
+
+diff --git a/xbmc/pvr/Makefile b/xbmc/pvr/Makefile
+new file mode 100644
+index 0000000..3b28f9b
+--- /dev/null
++++ b/xbmc/pvr/Makefile
+@@ -0,0 +1,8 @@
++SRCS=PVRGUIInfo.cpp \
++ PVRManager.cpp \
++ PVRDatabase.cpp
++
++LIB=pvr.a
++
++include ../../Makefile.include
++-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
+diff --git a/xbmc/pvr/PVRDatabase.cpp b/xbmc/pvr/PVRDatabase.cpp
+new file mode 100644
+index 0000000..8aadb04
+--- /dev/null
++++ b/xbmc/pvr/PVRDatabase.cpp
+@@ -0,0 +1,935 @@
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "PVRDatabase.h"
++#include "dbwrappers/dataset.h"
++#include "settings/AdvancedSettings.h"
++#include "settings/VideoSettings.h"
++#include "utils/log.h"
++
++#include "PVRManager.h"
++#include "channels/PVRChannelGroupsContainer.h"
++#include "addons/PVRClient.h"
++
++using namespace std;
++using namespace dbiplus;
++using namespace PVR;
++using namespace ADDON;
++
++bool CPVRDatabase::Open()
++{
++ return CDatabase::Open(g_advancedSettings.m_databaseTV);
++}
++
++bool CPVRDatabase::CreateTables()
++{
++ bool bReturn(false);
++
++ try
++ {
++ if (!CDatabase::CreateTables())
++ return false;
++
++ BeginTransaction();
++ CLog::Log(LOGINFO, "PVR - %s - creating tables", __FUNCTION__);
++
++ CLog::Log(LOGDEBUG, "PVR - %s - creating table 'clients'", __FUNCTION__);
++ m_pDS->exec(
++ "CREATE TABLE clients ("
++ "idClient integer primary key, "
++ "sName varchar(64), "
++ "sUid varchar(32)"
++ ")"
++ );
++
++ CLog::Log(LOGDEBUG, "PVR - %s - creating table 'channels'", __FUNCTION__);
++ m_pDS->exec(
++ "CREATE TABLE channels ("
++ "idChannel integer primary key, "
++ "iUniqueId integer, "
++ "bIsRadio bool, "
++ "bIsHidden bool, "
++ "bIsUserSetIcon bool, "
++ "sIconPath varchar(255), "
++ "sChannelName varchar(64), "
++ "bIsVirtual bool, "
++ "bEPGEnabled bool, "
++ "sEPGScraper varchar(32), "
++ "iLastWatched integer,"
++
++ // TODO use mapping table
++ "iClientId integer, "
++ "iClientChannelNumber integer, "
++ "sInputFormat varchar(32), "
++ "sStreamURL varchar(255), "
++ "iEncryptionSystem integer, "
++
++ "idEpg integer"
++ ")"
++ );
++ m_pDS->exec("CREATE UNIQUE INDEX idx_channels_iClientId_iUniqueId on channels(iClientId, iUniqueId);");
++
++ // TODO use a mapping table so multiple backends per channel can be implemented
++ // CLog::Log(LOGDEBUG, "PVR - %s - creating table 'map_channels_clients'", __FUNCTION__);
++ // m_pDS->exec(
++ // "CREATE TABLE map_channels_clients ("
++ // "idChannel integer primary key, "
++ // "idClient integer, "
++ // "iClientChannelNumber integer,"
++ // "sInputFormat string,"
++ // "sStreamURL string,"
++ // "iEncryptionSystem integer"
++ // ");"
++ // );
++ // m_pDS->exec("CREATE UNIQUE INDEX idx_idChannel_idClient on map_channels_clients(idChannel, idClient);");
++
++ CLog::Log(LOGDEBUG, "PVR - %s - creating table 'channelgroups'", __FUNCTION__);
++ m_pDS->exec(
++ "CREATE TABLE channelgroups ("
++ "idGroup integer primary key,"
++ "bIsRadio bool, "
++ "iGroupType integer, "
++ "sName varchar(64)"
++ ")"
++ );
++ m_pDS->exec("CREATE INDEX idx_channelgroups_bIsRadio on channelgroups(bIsRadio);");
++
++ CLog::Log(LOGDEBUG, "PVR - %s - creating table 'map_channelgroups_channels'", __FUNCTION__);
++ m_pDS->exec(
++ "CREATE TABLE map_channelgroups_channels ("
++ "idChannel integer, "
++ "idGroup integer, "
++ "iChannelNumber integer"
++ ")"
++ );
++ m_pDS->exec("CREATE UNIQUE INDEX idx_idGroup_idChannel on map_channelgroups_channels(idGroup, idChannel);");
++
++ CLog::Log(LOGDEBUG, "PVR - %s - creating table 'channelsettings'", __FUNCTION__);
++ m_pDS->exec(
++ "CREATE TABLE channelsettings ("
++ "idChannel integer primary key, "
++ "iInterlaceMethod integer, "
++ "iViewMode integer, "
++ "fCustomZoomAmount float, "
++ "fPixelRatio float, "
++ "iAudioStream integer, "
++ "iSubtitleStream integer,"
++ "fSubtitleDelay float, "
++ "bSubtitles bool, "
++ "fBrightness float, "
++ "fContrast float, "
++ "fGamma float,"
++ "fVolumeAmplification float, "
++ "fAudioDelay float, "
++ "bOutputToAllSpeakers bool, "
++ "bCrop bool, "
++ "iCropLeft integer, "
++ "iCropRight integer, "
++ "iCropTop integer, "
++ "iCropBottom integer, "
++ "fSharpness float, "
++ "fNoiseReduction float, "
++ "fCustomVerticalShift float, "
++ "bCustomNonLinStretch bool, "
++ "bPostProcess bool, "
++ "iScalingMethod integer, "
++ "iDeinterlaceMode integer "
++ ")"
++ );
++
++ CommitTransaction();
++ bReturn = true;
++ }
++ catch (...)
++ {
++ CLog::Log(LOGERROR, "PVR - %s - unable to create PVR database tables (error %i)", __FUNCTION__, (int)GetLastError());
++ RollbackTransaction();
++ bReturn = false;
++ }
++
++ if (bReturn)
++ {
++ // disable all PVR add-on when started the first time
++ ADDON::VECADDONS addons;
++ if ((bReturn = CAddonMgr::Get().GetAddons(ADDON_PVRDLL, addons, true, false)) == false)
++ CLog::Log(LOGERROR, "PVR - %s - failed to get add-ons from the add-on manager", __FUNCTION__);
++ else
++ {
++ CAddonDatabase database;
++ database.Open();
++ for (IVECADDONS it = addons.begin(); it != addons.end(); it++)
++ database.DisableAddon(it->get()->ID());
++ database.Close();
++ }
++ }
++
++ return bReturn;
++}
++
++bool CPVRDatabase::UpdateOldVersion(int iVersion)
++{
++ bool bReturn = true;
++
++ BeginTransaction();
++
++ try
++ {
++ if (iVersion < 11)
++ {
++ CLog::Log(LOGERROR, "PVR - %s - updating from table versions < 11 not supported. please delete '%s'",
++ __FUNCTION__, GetBaseDBName());
++ bReturn = false;
++ }
++ else
++ {
++ if (iVersion < 12)
++ m_pDS->exec("DROP VIEW vw_last_watched;");
++
++ if (iVersion < 13)
++ m_pDS->exec("ALTER TABLE channels ADD idEpg integer;");
++
++ if (iVersion < 14)
++ m_pDS->exec("ALTER TABLE channelsettings ADD fCustomVerticalShift float;");
++
++ if (iVersion < 15)
++ {
++ m_pDS->exec("ALTER TABLE channelsettings ADD bCustomNonLinStretch bool;");
++ m_pDS->exec("ALTER TABLE channelsettings ADD bPostProcess bool;");
++ m_pDS->exec("ALTER TABLE channelsettings ADD iScalingMethod integer;");
++ }
++ if (iVersion < 16)
++ {
++ /* sqlite apparently can't delete columns from an existing table, so just leave the extra column alone */
++ }
++ if (iVersion < 17)
++ {
++ m_pDS->exec("ALTER TABLE channelsettings ADD iDeinterlaceMode integer");
++ m_pDS->exec("UPDATE channelsettings SET iDeinterlaceMode = 2 WHERE iInterlaceMethod NOT IN (0,1)"); // anything other than none: method auto => mode force
++ m_pDS->exec("UPDATE channelsettings SET iDeinterlaceMode = 1 WHERE iInterlaceMethod = 1"); // method auto => mode auto
++ m_pDS->exec("UPDATE channelsettings SET iDeinterlaceMode = 0, iInterlaceMethod = 1 WHERE iInterlaceMethod = 0"); // method none => mode off, method auto
++ }
++ if (iVersion < 18)
++ {
++ m_pDS->exec("DROP INDEX idx_channels_iClientId;");
++ m_pDS->exec("DROP INDEX idx_channels_iLastWatched;");
++ m_pDS->exec("DROP INDEX idx_channels_bIsRadio;");
++ m_pDS->exec("DROP INDEX idx_channels_bIsHidden;");
++ m_pDS->exec("DROP INDEX idx_idChannel_idGroup;");
++ m_pDS->exec("DROP INDEX idx_idGroup_iChannelNumber;");
++ m_pDS->exec("CREATE UNIQUE INDEX idx_channels_iClientId_iUniqueId on channels(iClientId, iUniqueId);");
++ m_pDS->exec("CREATE UNIQUE INDEX idx_idGroup_idChannel on map_channelgroups_channels(idGroup, idChannel);");
++ }
++ if (iVersion < 19)
++ {
++ // bit of a hack, but we need to keep the version/contents of the non-pvr databases the same to allow clean upgrades
++ ADDON::VECADDONS addons;
++ if ((bReturn = CAddonMgr::Get().GetAddons(ADDON_PVRDLL, addons, true, false)) == false)
++ CLog::Log(LOGERROR, "PVR - %s - failed to get add-ons from the add-on manager", __FUNCTION__);
++ else
++ {
++ CAddonDatabase database;
++ database.Open();
++ for (IVECADDONS it = addons.begin(); it != addons.end(); it++)
++ {
++ if (!database.IsSystemPVRAddonEnabled(it->get()->ID()))
++ database.DisableAddon(it->get()->ID());
++ }
++ database.Close();
++ }
++ }
++ if (iVersion < 20)
++ m_pDS->exec("ALTER TABLE channels ADD bIsUserSetIcon bool");
++
++ if (iVersion < 21)
++ m_pDS->exec("ALTER TABLE channelgroups ADD iGroupType integer");
++ }
++ }
++ catch (...)
++ {
++ CLog::Log(LOGERROR, "PVR - %s - error attempting to update the database version!", __FUNCTION__);
++ bReturn = false;
++ }
++
++ if (bReturn)
++ CommitTransaction();
++ else
++ RollbackTransaction();
++
++ return bReturn;
++}
++
++int CPVRDatabase::GetLastChannelId(void)
++{
++ int iReturn(0);
++
++ CStdString strQuery = FormatSQL("SELECT MAX(idChannel) as iMaxChannel FROM channels");
++ if (ResultQuery(strQuery))
++ {
++ try
++ {
++ if (!m_pDS->eof())
++ iReturn = m_pDS->fv("iMaxChannel").get_asInt();
++ }
++ catch (...) {}
++ }
++
++ return iReturn;
++}
++
++/********** Channel methods **********/
++
++bool CPVRDatabase::DeleteChannels(void)
++{
++ CLog::Log(LOGDEBUG, "PVR - %s - deleting all channels from the database", __FUNCTION__);
++ return DeleteValues("channels");
++}
++
++bool CPVRDatabase::DeleteClientChannels(const CPVRClient &client)
++{
++ /* invalid client Id */
++ if (client.GetID() <= 0)
++ {
++ CLog::Log(LOGERROR, "PVR - %s - invalid client id: %i", __FUNCTION__, client.GetID());
++ return false;
++ }
++
++ CLog::Log(LOGDEBUG, "PVR - %s - deleting all channels from client '%i' from the database", __FUNCTION__, client.GetID());
++ CStdString strWhereClause = FormatSQL("iClientId = %u", client.GetID());
++ return DeleteValues("channels", strWhereClause);
++}
++
++bool CPVRDatabase::Delete(const CPVRChannel &channel)
++{
++ /* invalid channel */
++ if (channel.ChannelID() <= 0)
++ return false;
++
++ CLog::Log(LOGDEBUG, "PVR - %s - deleting channel '%s' from the database", __FUNCTION__, channel.ChannelName().c_str());
++ CStdString strWhereClause = FormatSQL("idChannel = %u", channel.ChannelID());
++ return DeleteValues("channels", strWhereClause);
++}
++
++int CPVRDatabase::Get(CPVRChannelGroupInternal &results)
++{
++ int iReturn(0);
++
++ CStdString strQuery = FormatSQL("SELECT channels.idChannel, channels.iUniqueId, channels.bIsRadio, channels.bIsHidden, channels.bIsUserSetIcon, "
++ "channels.sIconPath, channels.sChannelName, channels.bIsVirtual, channels.bEPGEnabled, channels.sEPGScraper, channels.iLastWatched, channels.iClientId, "
++ "channels.iClientChannelNumber, channels.sInputFormat, channels.sInputFormat, channels.sStreamURL, channels.iEncryptionSystem, map_channelgroups_channels.iChannelNumber, channels.idEpg "
++ "FROM map_channelgroups_channels "
++ "LEFT JOIN channels ON channels.idChannel = map_channelgroups_channels.idChannel "
++ "WHERE map_channelgroups_channels.idGroup = %u", results.IsRadio() ? XBMC_INTERNAL_GROUP_RADIO : XBMC_INTERNAL_GROUP_TV);
++ if (ResultQuery(strQuery))
++ {
++ try
++ {
++ while (!m_pDS->eof())
++ {
++ CPVRChannel *channel = new CPVRChannel();
++
++ channel->m_iChannelId = m_pDS->fv("idChannel").get_asInt();
++ channel->m_iUniqueId = m_pDS->fv("iUniqueId").get_asInt();
++ channel->m_bIsRadio = m_pDS->fv("bIsRadio").get_asBool();
++ channel->m_bIsHidden = m_pDS->fv("bIsHidden").get_asBool();
++ channel->m_bIsUserSetIcon = m_pDS->fv("bIsUserSetIcon").get_asBool();
++ channel->m_strIconPath = m_pDS->fv("sIconPath").get_asString();
++ channel->m_strChannelName = m_pDS->fv("sChannelName").get_asString();
++ channel->m_bIsVirtual = m_pDS->fv("bIsVirtual").get_asBool();
++ channel->m_bEPGEnabled = m_pDS->fv("bEPGEnabled").get_asBool();
++ channel->m_strEPGScraper = m_pDS->fv("sEPGScraper").get_asString();
++ channel->m_iLastWatched = (time_t) m_pDS->fv("iLastWatched").get_asInt();
++ channel->m_iClientId = m_pDS->fv("iClientId").get_asInt();
++ channel->m_iClientChannelNumber = m_pDS->fv("iClientChannelNumber").get_asInt();
++ channel->m_strInputFormat = m_pDS->fv("sInputFormat").get_asString();
++ channel->m_strStreamURL = m_pDS->fv("sStreamURL").get_asString();
++ channel->m_iClientEncryptionSystem = m_pDS->fv("iEncryptionSystem").get_asInt();
++ channel->m_iEpgId = m_pDS->fv("idEpg").get_asInt();
++
++ CLog::Log(LOGDEBUG, "PVR - %s - channel '%s' loaded from the database", __FUNCTION__, channel->m_strChannelName.c_str());
++ PVRChannelGroupMember newMember = { channel, m_pDS->fv("iChannelNumber").get_asInt() };
++ results.push_back(newMember);
++
++ m_pDS->next();
++ ++iReturn;
++ }
++ m_pDS->close();
++ }
++ catch (...)
++ {
++ CLog::Log(LOGERROR, "PVR - %s - couldn't load channels from the database", __FUNCTION__);
++ }
++ }
++ else
++ {
++ CLog::Log(LOGERROR, "PVR - %s - query failed", __FUNCTION__);
++ }
++
++ m_pDS->close();
++ return iReturn;
++}
++
++bool CPVRDatabase::DeleteChannelSettings()
++{
++ CLog::Log(LOGDEBUG, "PVR - %s - deleting all channel settings from the database", __FUNCTION__);
++ return DeleteValues("channelsettings");
++}
++
++bool CPVRDatabase::DeleteChannelSettings(const CPVRChannel &channel)
++{
++ bool bReturn(false);
++
++ /* invalid channel */
++ if (channel.ChannelID() <= 0)
++ {
++ CLog::Log(LOGERROR, "PVR - %s - invalid channel id: %i", __FUNCTION__, channel.ChannelID());
++ return bReturn;
++ }
++
++ CStdString strWhereClause = FormatSQL("idChannel = %u", channel.ChannelID());
++ return DeleteValues("channelsettings", strWhereClause);
++}
++
++bool CPVRDatabase::GetChannelSettings(const CPVRChannel &channel, CVideoSettings &settings)
++{
++ bool bReturn(false);
++
++ /* invalid channel */
++ if (channel.ChannelID() <= 0)
++ {
++ CLog::Log(LOGERROR, "PVR - %s - invalid channel id: %i", __FUNCTION__, channel.ChannelID());
++ return bReturn;
++ }
++
++ CStdString strQuery = FormatSQL("SELECT * FROM channelsettings WHERE idChannel = %u;", channel.ChannelID());
++
++ if (ResultQuery(strQuery))
++ {
++ try
++ {
++ if (m_pDS->num_rows() > 0)
++ {
++ settings.m_AudioDelay = m_pDS->fv("fAudioDelay").get_asFloat();
++ settings.m_AudioStream = m_pDS->fv("iAudioStream").get_asInt();
++ settings.m_Brightness = m_pDS->fv("fBrightness").get_asFloat();
++ settings.m_Contrast = m_pDS->fv("fContrast").get_asFloat();
++ settings.m_CustomPixelRatio = m_pDS->fv("fPixelRatio").get_asFloat();
++ settings.m_CustomNonLinStretch = m_pDS->fv("bCustomNonLinStretch").get_asBool();
++ settings.m_NoiseReduction = m_pDS->fv("fNoiseReduction").get_asFloat();
++ settings.m_PostProcess = m_pDS->fv("bPostProcess").get_asBool();
++ settings.m_Sharpness = m_pDS->fv("fSharpness").get_asFloat();
++ settings.m_CustomZoomAmount = m_pDS->fv("fCustomZoomAmount").get_asFloat();
++ settings.m_CustomVerticalShift = m_pDS->fv("fCustomVerticalShift").get_asFloat();
++ settings.m_Gamma = m_pDS->fv("fGamma").get_asFloat();
++ settings.m_SubtitleDelay = m_pDS->fv("fSubtitleDelay").get_asFloat();
++ settings.m_SubtitleOn = m_pDS->fv("bSubtitles").get_asBool();
++ settings.m_SubtitleStream = m_pDS->fv("iSubtitleStream").get_asInt();
++ settings.m_ViewMode = m_pDS->fv("iViewMode").get_asInt();
++ settings.m_Crop = m_pDS->fv("bCrop").get_asBool();
++ settings.m_CropLeft = m_pDS->fv("iCropLeft").get_asInt();
++ settings.m_CropRight = m_pDS->fv("iCropRight").get_asInt();
++ settings.m_CropTop = m_pDS->fv("iCropTop").get_asInt();
++ settings.m_CropBottom = m_pDS->fv("iCropBottom").get_asInt();
++ settings.m_InterlaceMethod = (EINTERLACEMETHOD)m_pDS->fv("iInterlaceMethod").get_asInt();
++ settings.m_DeinterlaceMode = (EDEINTERLACEMODE)m_pDS->fv("iDeinterlaceMode").get_asInt();
++ settings.m_VolumeAmplification = m_pDS->fv("fVolumeAmplification").get_asFloat();
++ settings.m_OutputToAllSpeakers = m_pDS->fv("bOutputToAllSpeakers").get_asBool();
++ settings.m_ScalingMethod = (ESCALINGMETHOD)m_pDS->fv("iScalingMethod").get_asInt();
++
++ bReturn = true;
++ }
++
++ m_pDS->close();
++ }
++ catch(...)
++ {
++ CLog::Log(LOGERROR, "PVR - %s - failed to get channel settings for channel '%s'", __FUNCTION__, channel.ChannelName().c_str());
++ }
++ }
++ else
++ {
++ CLog::Log(LOGERROR, "PVR - %s - query failed", __FUNCTION__);
++ }
++
++ return bReturn;
++}
++
++bool CPVRDatabase::PersistChannelSettings(const CPVRChannel &channel, const CVideoSettings &settings)
++{
++ /* invalid channel */
++ if (channel.ChannelID() <= 0)
++ {
++ CLog::Log(LOGERROR, "PVR - %s - invalid channel id: %i", __FUNCTION__, channel.ChannelID());
++ return false;
++ }
++
++ CStdString strQuery = FormatSQL(
++ "REPLACE INTO channelsettings "
++ "(idChannel, iInterlaceMethod, iViewMode, fCustomZoomAmount, fPixelRatio, iAudioStream, iSubtitleStream, fSubtitleDelay, "
++ "bSubtitles, fBrightness, fContrast, fGamma, fVolumeAmplification, fAudioDelay, bOutputToAllSpeakers, bCrop, iCropLeft, "
++ "iCropRight, iCropTop, iCropBottom, fSharpness, fNoiseReduction, fCustomVerticalShift, bCustomNonLinStretch, bPostProcess, iScalingMethod, iDeinterlaceMode) VALUES "
++ "(%i, %i, %i, %f, %f, %i, %i, %f, %i, %f, %f, %f, %f, %f, %i, %i, %i, %i, %i, %i, %f, %f, %f, %i, %i, %i, %i);",
++ channel.ChannelID(), settings.m_InterlaceMethod, settings.m_ViewMode, settings.m_CustomZoomAmount, settings.m_CustomPixelRatio,
++ settings.m_AudioStream, settings.m_SubtitleStream, settings.m_SubtitleDelay, settings.m_SubtitleOn ? 1 :0,
++ settings.m_Brightness, settings.m_Contrast, settings.m_Gamma, settings.m_VolumeAmplification, settings.m_AudioDelay,
++ settings.m_OutputToAllSpeakers ? 1 : 0, settings.m_Crop ? 1 : 0, settings.m_CropLeft, settings.m_CropRight, settings.m_CropTop,
++ settings.m_CropBottom, settings.m_Sharpness, settings.m_NoiseReduction, settings.m_CustomVerticalShift,
++ settings.m_CustomNonLinStretch ? 1 : 0, settings.m_PostProcess ? 1 : 0, settings.m_ScalingMethod, settings.m_DeinterlaceMode);
++
++ return ExecuteQuery(strQuery);
++}
++
++/********** Channel group methods **********/
++
++bool CPVRDatabase::RemoveChannelsFromGroup(const CPVRChannelGroup &group)
++{
++ CStdString strWhereClause = FormatSQL("idGroup = %u", group.GroupID());
++ return DeleteValues("map_channelgroups_channels", strWhereClause);
++}
++
++bool CPVRDatabase::GetCurrentGroupMembers(const CPVRChannelGroup &group, vector<int> &members)
++{
++ bool bReturn(false);
++ /* invalid group id */
++ if (group.GroupID() <= 0)
++ {
++ CLog::Log(LOGERROR, "PVR - %s - invalid group id: %d", __FUNCTION__, group.GroupID());
++ return false;
++ }
++
++ CStdString strCurrentMembersQuery = FormatSQL("SELECT idChannel FROM map_channelgroups_channels WHERE idGroup = %u", group.GroupID());
++ if (ResultQuery(strCurrentMembersQuery))
++ {
++ try
++ {
++ while (!m_pDS->eof())
++ {
++ members.push_back(m_pDS->fv("idChannel").get_asInt());
++ m_pDS->next();
++ }
++ m_pDS->close();
++ bReturn = true;
++ }
++ catch (...)
++ {
++ CLog::Log(LOGERROR, "PVR - %s - couldn't load channels from the database", __FUNCTION__);
++ }
++ }
++ else
++ {
++ CLog::Log(LOGERROR, "PVR - %s - query failed", __FUNCTION__);
++ }
++
++ return bReturn;
++}
++
++bool CPVRDatabase::DeleteChannelsFromGroup(const CPVRChannelGroup &group)
++{
++ /* invalid group id */
++ if (group.GroupID() <= 0)
++ {
++ CLog::Log(LOGERROR, "PVR - %s - invalid group id: %d", __FUNCTION__, group.GroupID());
++ return false;
++ }
++
++ CStdString strWhereClause;
++ strWhereClause = FormatSQL("idGroup = %u", group.GroupID());
++ return DeleteValues("map_channelgroups_channels", strWhereClause);
++}
++
++bool CPVRDatabase::DeleteChannelsFromGroup(const CPVRChannelGroup &group, const vector<int> &channelsToDelete)
++{
++ bool bDelete(true);
++ unsigned int iDeletedChannels(0);
++ /* invalid group id */
++ if (group.GroupID() <= 0)
++ {
++ CLog::Log(LOGERROR, "PVR - %s - invalid group id: %d", __FUNCTION__, group.GroupID());
++ return false;
++ }
++
++ while (iDeletedChannels < channelsToDelete.size())
++ {
++ CStdString strChannelsToDelete;
++ CStdString strWhereClause;
++
++ for (unsigned int iChannelPtr = 0; iChannelPtr + iDeletedChannels < channelsToDelete.size() && iChannelPtr < 50; iChannelPtr++)
++ strChannelsToDelete.AppendFormat(", %d", channelsToDelete.at(iDeletedChannels + iChannelPtr));
++
++ if (!strChannelsToDelete.IsEmpty())
++ {
++ strChannelsToDelete = strChannelsToDelete.Right(strChannelsToDelete.length() - 2);
++ strWhereClause = FormatSQL("idGroup = %u AND idChannel IN (%s)", group.GroupID(), strChannelsToDelete.c_str());
++ bDelete = DeleteValues("map_channelgroups_channels", strWhereClause) && bDelete;
++ }
++
++ iDeletedChannels += 50;
++ }
++
++ return bDelete;
++}
++
++bool CPVRDatabase::RemoveStaleChannelsFromGroup(const CPVRChannelGroup &group)
++{
++ bool bDelete(true);
++ /* invalid group id */
++ if (group.GroupID() <= 0)
++ {
++ CLog::Log(LOGERROR, "PVR - %s - invalid group id: %d", __FUNCTION__, group.GroupID());
++ return false;
++ }
++
++ if (!group.IsInternalGroup())
++ {
++ /* First remove channels that don't exist in the main channels table */
++ CStdString strWhereClause = FormatSQL("idChannel IN (SELECT map_channelgroups_channels.idChannel FROM map_channelgroups_channels LEFT JOIN channels on map_channelgroups_channels.idChannel = channels.idChannel WHERE channels.idChannel IS NULL)");
++ bDelete = DeleteValues("map_channelgroups_channels", strWhereClause);
++ }
++
++ if (group.size() > 0)
++ {
++ vector<int> currentMembers;
++ if (GetCurrentGroupMembers(group, currentMembers))
++ {
++ vector<int> channelsToDelete;
++ for (unsigned int iChannelPtr = 0; iChannelPtr < currentMembers.size(); iChannelPtr++)
++ {
++ if (!group.IsGroupMember(currentMembers.at(iChannelPtr)))
++ channelsToDelete.push_back(currentMembers.at(iChannelPtr));
++ }
++
++ bDelete = DeleteChannelsFromGroup(group, channelsToDelete) && bDelete;
++ }
++ }
++ else
++ {
++ CStdString strWhereClause = FormatSQL("idGroup = %u", group.GroupID());
++ bDelete = DeleteValues("map_channelgroups_channels", strWhereClause) && bDelete;
++ }
++
++ return bDelete;
++}
++
++bool CPVRDatabase::DeleteChannelGroups(void)
++{
++ CLog::Log(LOGDEBUG, "PVR - %s - deleting all channel groups from the database", __FUNCTION__);
++
++ return DeleteValues("channelgroups") &&
++ DeleteValues("map_channelgroups_channels");
++}
++
++bool CPVRDatabase::Delete(const CPVRChannelGroup &group)
++{
++ /* invalid group id */
++ if (group.GroupID() <= 0)
++ {
++ CLog::Log(LOGERROR, "PVR - %s - invalid group id: %d", __FUNCTION__, group.GroupID());
++ return false;
++ }
++
++ CStdString strWhereClause = FormatSQL("idGroup = %u AND bIsRadio = %u", group.GroupID(), group.IsRadio());
++ return RemoveChannelsFromGroup(group) &&
++ DeleteValues("channelgroups", strWhereClause);
++}
++
++bool CPVRDatabase::Get(CPVRChannelGroups &results)
++{
++ bool bReturn = false;
++ CStdString strQuery = FormatSQL("SELECT * from channelgroups WHERE bIsRadio = %u;", results.IsRadio());
++
++ if (ResultQuery(strQuery))
++ {
++ try
++ {
++ while (!m_pDS->eof())
++ {
++ CPVRChannelGroup data(m_pDS->fv("bIsRadio").get_asBool());
++
++ data.SetGroupID(m_pDS->fv("idGroup").get_asInt());
++ data.SetGroupName(m_pDS->fv("sName").get_asString());
++ data.SetGroupType(m_pDS->fv("iGroupType").get_asInt());
++
++ results.Update(data);
++
++ CLog::Log(LOGDEBUG, "PVR - %s - group '%s' loaded from the database", __FUNCTION__, data.GroupName().c_str());
++ m_pDS->next();
++ }
++ m_pDS->close();
++ bReturn = true;
++ }
++ catch (...)
++ {
++ CLog::Log(LOGERROR, "%s - couldn't load channels from the database", __FUNCTION__);
++ }
++ }
++
++ return bReturn;
++}
++
++int CPVRDatabase::Get(CPVRChannelGroup &group)
++{
++ int iReturn = -1;
++
++ /* invalid group id */
++ if (group.GroupID() < 0)
++ {
++ CLog::Log(LOGERROR, "PVR - %s - invalid group id: %d", __FUNCTION__, group.GroupID());
++ return -1;
++ }
++
++ CStdString strQuery = FormatSQL("SELECT idChannel, iChannelNumber FROM map_channelgroups_channels WHERE idGroup = %u ORDER BY iChannelNumber", group.GroupID());
++ if (ResultQuery(strQuery))
++ {
++ iReturn = 0;
++
++ try
++ {
++ while (!m_pDS->eof())
++ {
++ int iChannelId = m_pDS->fv("idChannel").get_asInt();
++ int iChannelNumber = m_pDS->fv("iChannelNumber").get_asInt();
++ CPVRChannel *channel = (CPVRChannel *) g_PVRChannelGroups->GetByChannelIDFromAll(iChannelId);
++
++ if (channel && group.AddToGroup(*channel, iChannelNumber))
++ ++iReturn;
++
++ m_pDS->next();
++ }
++ m_pDS->close();
++ }
++ catch(...)
++ {
++ CLog::Log(LOGERROR, "PVR - %s - failed to get channels", __FUNCTION__);
++ }
++ }
++
++ return iReturn;
++}
++
++bool CPVRDatabase::PersistChannels(CPVRChannelGroup &group)
++{
++ bool bReturn(true);
++ int iLastChannel(0);
++
++ /* we can only safely get this from a local db */
++ if (m_sqlite)
++ iLastChannel = GetLastChannelId();
++
++ for (unsigned int iChannelPtr = 0; iChannelPtr < group.size(); iChannelPtr++)
++ {
++ PVRChannelGroupMember member = group.at(iChannelPtr);
++ if (member.channel->IsChanged() || member.channel->IsNew())
++ {
++ if (m_sqlite && member.channel->IsNew())
++ member.channel->SetChannelID(++iLastChannel, false);
++ bReturn &= Persist(*member.channel, m_sqlite || !member.channel->IsNew());
++ }
++ }
++ return CommitInsertQueries();
++}
++
++bool CPVRDatabase::PersistGroupMembers(CPVRChannelGroup &group)
++{
++ bool bReturn = true;
++ bool bRemoveChannels = true;
++ CStdString strQuery;
++ CSingleLock lock(group.m_critSection);
++
++ if (group.size() > 0)
++ {
++ for (unsigned int iChannelPtr = 0; iChannelPtr < group.size(); iChannelPtr++)
++ {
++ PVRChannelGroupMember member = group.at(iChannelPtr);
++
++ CStdString strWhereClause = FormatSQL("idChannel = %u AND idGroup = %u AND iChannelNumber = %u",
++ member.channel->ChannelID(), group.GroupID(), member.iChannelNumber);
++
++ CStdString strValue = GetSingleValue("map_channelgroups_channels", "idChannel", strWhereClause);
++ if (strValue.IsEmpty())
++ {
++ strQuery = FormatSQL("REPLACE INTO map_channelgroups_channels ("
++ "idGroup, idChannel, iChannelNumber) "
++ "VALUES (%i, %i, %i);",
++ group.GroupID(), member.channel->ChannelID(), member.iChannelNumber);
++ QueueInsertQuery(strQuery);
++ }
++ }
++ lock.Leave();
++
++ bReturn = CommitInsertQueries();
++ bRemoveChannels = RemoveStaleChannelsFromGroup(group);
++ }
++
++ return bReturn && bRemoveChannels;
++}
++
++/********** Client methods **********/
++
++bool CPVRDatabase::DeleteClients()
++{
++ CLog::Log(LOGDEBUG, "PVR - %s - deleting all clients from the database", __FUNCTION__);
++
++ return DeleteValues("clients");
++ //TODO && DeleteValues("map_channels_clients");
++}
++
++bool CPVRDatabase::Delete(const CPVRClient &client)
++{
++ /* invalid client uid */
++ if (client.ID().IsEmpty())
++ {
++ CLog::Log(LOGERROR, "PVR - %s - invalid client uid", __FUNCTION__);
++ return false;
++ }
++
++ CStdString strWhereClause = FormatSQL("sUid = '%s'", client.ID().c_str());
++ return DeleteValues("clients", strWhereClause);
++}
++
++int CPVRDatabase::GetClientId(const CStdString &strClientUid)
++{
++ CStdString strWhereClause = FormatSQL("sUid = '%s'", strClientUid.c_str());
++ CStdString strValue = GetSingleValue("clients", "idClient", strWhereClause);
++
++ if (strValue.IsEmpty())
++ return -1;
++
++ return atol(strValue.c_str());
++}
++
++bool CPVRDatabase::Persist(CPVRChannelGroup &group)
++{
++ bool bReturn(false);
++ if (group.GroupName().IsEmpty())
++ {
++ CLog::Log(LOGERROR, "%s - empty group name", __FUNCTION__);
++ return bReturn;
++ }
++
++ CStdString strQuery;
++ bReturn = true;
++ {
++ CSingleLock lock(group.m_critSection);
++
++ /* insert a new entry when this is a new group, or replace the existing one otherwise */
++ if (group.GroupID() <= 0)
++ strQuery = FormatSQL("INSERT INTO channelgroups (bIsRadio, iGroupType, sName) VALUES (%i, %i, '%s')",
++ (group.IsRadio() ? 1 :0), group.GroupType(), group.GroupName().c_str());
++ else
++ strQuery = FormatSQL("REPLACE INTO channelgroups (idGroup, bIsRadio, iGroupType, sName) VALUES (%i, %i, %i, '%s')",
++ group.GroupID(), (group.IsRadio() ? 1 :0), group.GroupType(), group.GroupName().c_str());
++
++ bReturn = ExecuteQuery(strQuery);
++
++ /* set the group id if it was <= 0 */
++ if (bReturn && group.GroupID() <= 0)
++ group.m_iGroupId = (int) m_pDS->lastinsertid();
++ }
++
++ /* only persist the channel data for the internal groups */
++ if (group.IsInternalGroup())
++ bReturn &= PersistChannels(group);
++
++ /* persist the group member entries */
++ if (bReturn)
++ bReturn = PersistGroupMembers(group);
++
++ return bReturn;
++}
++
++int CPVRDatabase::Persist(const AddonPtr client)
++{
++ int iReturn(-1);
++
++ /* invalid client uid or name */
++ if (client->Name().IsEmpty() || client->ID().IsEmpty())
++ {
++ CLog::Log(LOGERROR, "PVR - %s - invalid client uid or name", __FUNCTION__);
++ return iReturn;
++ }
++
++ /* only add this client if it's not already in the database */
++ iReturn = GetClientId(client->ID());
++ if (iReturn <= 0)
++ {
++ CStdString strQuery = FormatSQL("INSERT INTO clients (sName, sUid) VALUES ('%s', '%s');",
++ client->Name().c_str(), client->ID().c_str());
++
++ if (ExecuteQuery(strQuery))
++ iReturn = (int) m_pDS->lastinsertid();
++ }
++
++ return iReturn;
++}
++
++bool CPVRDatabase::Persist(CPVRChannel &channel, bool bQueueWrite /* = false */)
++{
++ bool bReturn(false);
++
++ /* invalid channel */
++ if (channel.UniqueID() <= 0)
++ {
++ CLog::Log(LOGERROR, "PVR - %s - invalid channel uid: %d", __FUNCTION__, channel.UniqueID());
++ return bReturn;
++ }
++
++ CStdString strQuery;
++ if (channel.ChannelID() <= 0)
++ {
++ /* new channel */
++ strQuery = FormatSQL("INSERT INTO channels ("
++ "iUniqueId, bIsRadio, bIsHidden, bIsUserSetIcon, "
++ "sIconPath, sChannelName, bIsVirtual, bEPGEnabled, sEPGScraper, iLastWatched, iClientId, "
++ "iClientChannelNumber, sInputFormat, sStreamURL, iEncryptionSystem, idEpg) "
++ "VALUES (%i, %i, %i, %i, '%s', '%s', %i, %i, '%s', %u, %i, %i, '%s', '%s', %i, %i)",
++ channel.UniqueID(), (channel.IsRadio() ? 1 :0), (channel.IsHidden() ? 1 : 0), (channel.IsUserSetIcon() ? 1 : 0),
++ channel.IconPath().c_str(), channel.ChannelName().c_str(), (channel.IsVirtual() ? 1 : 0), (channel.EPGEnabled() ? 1 : 0), channel.EPGScraper().c_str(), channel.LastWatched(), channel.ClientID(),
++ channel.ClientChannelNumber(), channel.InputFormat().c_str(), channel.StreamURL().c_str(), channel.EncryptionSystem(),
++ channel.EpgID());
++ }
++ else
++ {
++ /* update channel */
++ strQuery = FormatSQL("REPLACE INTO channels ("
++ "iUniqueId, bIsRadio, bIsHidden, bIsUserSetIcon, "
++ "sIconPath, sChannelName, bIsVirtual, bEPGEnabled, sEPGScraper, iLastWatched, iClientId, "
++ "iClientChannelNumber, sInputFormat, sStreamURL, iEncryptionSystem, idChannel, idEpg) "
++ "VALUES (%i, %i, %i, %i, '%s', '%s', %i, %i, '%s', %u, %i, %i, '%s', '%s', %i, %i, %i)",
++ channel.UniqueID(), (channel.IsRadio() ? 1 :0), (channel.IsHidden() ? 1 : 0), (channel.IsUserSetIcon() ? 1 : 0),
++ channel.IconPath().c_str(), channel.ChannelName().c_str(), (channel.IsVirtual() ? 1 : 0), (channel.EPGEnabled() ? 1 : 0), channel.EPGScraper().c_str(), channel.LastWatched(), channel.ClientID(),
++ channel.ClientChannelNumber(), channel.InputFormat().c_str(), channel.StreamURL().c_str(), channel.EncryptionSystem(), channel.ChannelID(),
++ channel.EpgID());
++ }
++
++ if (bQueueWrite)
++ {
++ QueueInsertQuery(strQuery);
++ bReturn = true;
++ }
++ else if (ExecuteQuery(strQuery))
++ {
++ CSingleLock lock(channel.m_critSection);
++ if (channel.m_iChannelId <= 0)
++ channel.m_iChannelId = (int)m_pDS->lastinsertid();
++ bReturn = true;
++ }
++
++ return bReturn;
++}
+diff --git a/xbmc/pvr/PVRDatabase.h b/xbmc/pvr/PVRDatabase.h
+new file mode 100644
+index 0000000..6e4a8a6
+--- /dev/null
++++ b/xbmc/pvr/PVRDatabase.h
+@@ -0,0 +1,258 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "addons/Addon.h"
++#include "addons/AddonDll.h"
++#include "addons/DllPVRClient.h"
++#include "PVRManager.h"
++#include "dbwrappers/Database.h"
++#include "XBDateTime.h"
++#include "utils/log.h"
++
++class CVideoSettings;
++
++namespace PVR
++{
++ class CPVRChannelGroup;
++ class CPVRChannelGroupInternal;
++ class CPVRChannelsContainer;
++ class CPVRChannel;
++ class CPVRChannelGroups;
++ class CPVRClient;
++
++ /** The PVR database */
++
++ class CPVRDatabase : public CDatabase
++ {
++ public:
++ /*!
++ * @brief Create a new instance of the PVR database.
++ */
++ CPVRDatabase(void) {};
++ virtual ~CPVRDatabase(void) {};
++
++ /*!
++ * @brief Open the database.
++ * @return True if it was opened successfully, false otherwise.
++ */
++ virtual bool Open();
++
++ /*!
++ * @brief Get the minimal database version that is required to operate correctly.
++ * @return The minimal database version.
++ */
++ virtual int GetMinVersion() const { return 21; };
++
++ /*!
++ * @brief Get the default sqlite database filename.
++ * @return The default filename.
++ */
++ const char *GetBaseDBName() const { return "TV"; };
++
++ /*! @name Channel methods */
++ //@{
++
++ /*!
++ * @brief Remove all channels from the database.
++ * @return True if all channels were removed, false otherwise.
++ */
++ bool DeleteChannels(void);
++
++ /*!
++ * @brief Remove all channels from a client from the database.
++ * @param client The client to delete the channels for.
++ * @return True if the channels were deleted, false otherwise.
++ */
++ bool DeleteClientChannels(const CPVRClient &client);
++
++ /*!
++ * @brief Add or update a channel entry in the database
++ * @param channel The channel to persist.
++ * @param bQueueWrite If true, don't write immediately
++ * @return True when persisted or queued, false otherwise.
++ */
++ bool Persist(CPVRChannel &channel, bool bQueueWrite = false);
++
++ /*!
++ * @brief Remove a channel entry from the database
++ * @param channel The channel to remove.
++ * @return True if the channel was removed, false otherwise.
++ */
++ bool Delete(const CPVRChannel &channel);
++
++ /*!
++ * @brief Get the list of channels from the database
++ * @param results The channel group to store the results in.
++ * @return The amount of channels that were added.
++ */
++ int Get(CPVRChannelGroupInternal &results);
++
++ //@}
++
++ /*! @name Channel settings methods */
++ //@{
++
++ /*!
++ * @brief Remove all channel settings from the database.
++ * @return True if all channels were removed successfully, false if not.
++ */
++ bool DeleteChannelSettings();
++
++ /*!
++ * @brief Remove channel settings from the database.
++ * @return True if channel were removed successfully, false if not.
++ */
++ bool DeleteChannelSettings(const CPVRChannel &channel);
++
++ /*!
++ * @brief Get the channel settings from the database.
++ * @param channel The channel to get the settings for.
++ * @param settings Store the settings in here.
++ * @return True if the settings were fetched successfully, false if not.
++ */
++ bool GetChannelSettings(const CPVRChannel &channel, CVideoSettings &settings);
++
++ /*!
++ * @brief Store channel settings in the database.
++ * @param channel The channel to store the settings for.
++ * @param settings The settings to store.
++ * @return True if the settings were stored successfully, false if not.
++ */
++ bool PersistChannelSettings(const CPVRChannel &channel, const CVideoSettings &settings);
++
++ //@}
++
++ /*! @name Channel group methods */
++ //@{
++
++ /*!
++ * @brief Remove all channel groups from the database
++ * @return True if all channel groups were removed.
++ */
++ bool DeleteChannelGroups(void);
++
++ /*!
++ * @brief Delete a channel group from the database.
++ * @param group The group to delete.
++ * @return True if the group was deleted successfully, false otherwise.
++ */
++ bool Delete(const CPVRChannelGroup &group);
++
++ /*!
++ * @brief Get the channel groups.
++ * @param results The container to store the results in.
++ * @return True if the list was fetched successfully, false otherwise.
++ */
++ bool Get(CPVRChannelGroups &results);
++
++ /*!
++ * @brief Add the group members to a group.
++ * @param group The group to get the channels for.
++ * @return The amount of channels that were added.
++ */
++ int Get(CPVRChannelGroup &group);
++
++ /*!
++ * @brief Add or update a channel group entry in the database.
++ * @param group The group to persist.
++ * @return True if the group was persisted successfully, false otherwise.
++ */
++ bool Persist(CPVRChannelGroup &group);
++
++ //@}
++
++ /*! @name Client methods */
++ //@{
++ /*!
++ * @brief Remove all client information from the database.
++ * @return True if all clients were removed successfully.
++ */
++ bool DeleteClients();
++
++ /*!
++ * @brief Add a client to the database if it's not already in there.
++ * @param strClientName The name of the client.
++ * @param strGuid The unique ID of the client.
++ * @return The database ID of the client.
++ */
++ int Persist(const ADDON::AddonPtr addon);
++
++ /*!
++ * @brief Remove a client from the database
++ * @param strGuid The unique ID of the client.
++ * @return True if the client was removed successfully, false otherwise.
++ */
++ bool Delete(const CPVRClient &client);
++
++ //@}
++
++ private:
++ /*!
++ * @brief Create the PVR database tables.
++ * @return True if the tables were created successfully, false otherwise.
++ */
++ bool CreateTables();
++
++ bool DeleteChannelsFromGroup(const CPVRChannelGroup &group);
++ bool DeleteChannelsFromGroup(const CPVRChannelGroup &group, const std::vector<int> &channelsToDelete);
++
++ /*!
++ * @brief Get the database ID of a client.
++ * @param strClientUid The unique ID of the client.
++ * @return The database ID of the client or -1 if it wasn't found.
++ */
++ int GetClientId(const CStdString &strClientUid);
++
++ bool GetCurrentGroupMembers(const CPVRChannelGroup &group, std::vector<int> &members);
++ int GetLastChannelId(void);
++ bool RemoveStaleChannelsFromGroup(const CPVRChannelGroup &group);
++
++ /*!
++ * @brief Update an old version of the database.
++ * @param version The version to update the database from.
++ * @return True if it was updated successfully, false otherwise.
++ */
++ bool UpdateOldVersion(int version);
++
++ bool PersistGroupMembers(CPVRChannelGroup &group);
++
++ bool PersistChannels(CPVRChannelGroup &group);
++
++ bool RemoveChannelsFromGroup(const CPVRChannelGroup &group);
++ };
++
++ /*!
++ * @brief Try to open the PVR database.
++ * @return The opened database or NULL if the database failed to open.
++ */
++ inline CPVRDatabase *GetPVRDatabase(void)
++ {
++ CPVRDatabase *database = g_PVRManager.GetTVDatabase();
++ if (!database || !database->IsOpen())
++ {
++ CLog::Log(LOGERROR, "PVR - failed to open the database");
++ database = NULL;
++ }
++
++ return database;
++ }
++}
+diff --git a/xbmc/pvr/PVRGUIInfo.cpp b/xbmc/pvr/PVRGUIInfo.cpp
+new file mode 100644
+index 0000000..10bb6f1
+--- /dev/null
++++ b/xbmc/pvr/PVRGUIInfo.cpp
+@@ -0,0 +1,864 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "Application.h"
++#include "PVRGUIInfo.h"
++#include "guilib/LocalizeStrings.h"
++#include "utils/StringUtils.h"
++#include "GUIInfoManager.h"
++#include "Util.h"
++#include "threads/SingleLock.h"
++#include "PVRManager.h"
++#include "pvr/addons/PVRClients.h"
++#include "pvr/timers/PVRTimers.h"
++#include "pvr/recordings/PVRRecordings.h"
++#include "pvr/channels/PVRChannel.h"
++#include "epg/EpgInfoTag.h"
++#include "settings/AdvancedSettings.h"
++
++using namespace PVR;
++using namespace EPG;
++using namespace std;
++
++CPVRGUIInfo::CPVRGUIInfo(void) :
++ CThread("PVR GUI info updater"),
++ m_playingEpgTag(NULL)
++{
++ ResetProperties();
++}
++
++CPVRGUIInfo::~CPVRGUIInfo(void)
++{
++ Stop();
++}
++
++void CPVRGUIInfo::ResetProperties(void)
++{
++ CSingleLock lock(m_critSection);
++ m_strActiveTimerTitle = StringUtils::EmptyString;
++ m_strActiveTimerChannelName = StringUtils::EmptyString;
++ m_strActiveTimerChannelIcon = StringUtils::EmptyString;
++ m_strActiveTimerTime = StringUtils::EmptyString;
++ m_strNextTimerInfo = StringUtils::EmptyString;
++ m_strNextRecordingTitle = StringUtils::EmptyString;
++ m_strNextRecordingChannelName = StringUtils::EmptyString;
++ m_strNextRecordingChannelIcon = StringUtils::EmptyString;
++ m_strNextRecordingTime = StringUtils::EmptyString;
++ m_iTimerAmount = 0;
++ m_bHasRecordings = false;
++ m_iRecordingTimerAmount = 0;
++ m_iActiveClients = 0;
++ m_strPlayingClientName = StringUtils::EmptyString;
++ m_strBackendName = StringUtils::EmptyString;
++ m_strBackendVersion = StringUtils::EmptyString;
++ m_strBackendHost = StringUtils::EmptyString;
++ m_strBackendDiskspace = StringUtils::EmptyString;
++ m_strBackendTimers = StringUtils::EmptyString;
++ m_strBackendRecordings = StringUtils::EmptyString;
++ m_strBackendChannels = StringUtils::EmptyString;
++ m_strTotalDiskspace = StringUtils::EmptyString;
++ m_iAddonInfoToggleStart = 0;
++ m_iAddonInfoToggleCurrent = 0;
++ m_iTimerInfoToggleStart = 0;
++ m_iTimerInfoToggleCurrent = 0;
++ m_iToggleShowInfo = 0;
++ m_iDuration = 0;
++ m_bHasNonRecordingTimers = false;
++ m_bIsPlayingTV = false;
++ m_bIsPlayingRadio = false;
++ m_bIsPlayingRecording = false;
++ m_bIsPlayingEncryptedStream = false;
++
++ ResetPlayingTag();
++ ClearQualityInfo(m_qualityInfo);
++}
++
++void CPVRGUIInfo::ClearQualityInfo(PVR_SIGNAL_STATUS &qualityInfo)
++{
++ strncpy(qualityInfo.strAdapterName, g_localizeStrings.Get(13106).c_str(), 1024);
++ strncpy(qualityInfo.strAdapterStatus, g_localizeStrings.Get(13106).c_str(), 1024);
++ qualityInfo.iSNR = 0;
++ qualityInfo.iSignal = 0;
++ qualityInfo.iSNR = 0;
++ qualityInfo.iUNC = 0;
++ qualityInfo.dVideoBitrate = 0;
++ qualityInfo.dAudioBitrate = 0;
++ qualityInfo.dDolbyBitrate = 0;
++}
++
++void CPVRGUIInfo::Start(void)
++{
++ ResetProperties();
++ Create();
++ SetPriority(-1);
++}
++
++void CPVRGUIInfo::Stop(void)
++{
++ StopThread();
++ if (g_PVRTimers)
++ g_PVRTimers->UnregisterObserver(this);
++}
++
++void CPVRGUIInfo::Notify(const Observable &obs, const CStdString& msg)
++{
++ if (msg.Equals("timers") || msg.Equals("timers-reset"))
++ UpdateTimersCache();
++}
++
++void CPVRGUIInfo::ShowPlayerInfo(int iTimeout)
++{
++ CSingleLock lock(m_critSection);
++
++ if (iTimeout > 0)
++ m_iToggleShowInfo = (int) XbmcThreads::SystemClockMillis() + iTimeout * 1000;
++
++ g_infoManager.SetShowInfo(true);
++}
++
++void CPVRGUIInfo::ToggleShowInfo(void)
++{
++ CSingleLock lock(m_critSection);
++
++ if (m_iToggleShowInfo > 0 && m_iToggleShowInfo < (unsigned int) XbmcThreads::SystemClockMillis())
++ {
++ m_iToggleShowInfo = 0;
++ g_infoManager.SetShowInfo(false);
++ }
++}
++
++bool CPVRGUIInfo::AddonInfoToggle(void)
++{
++ CSingleLock lock(m_critSection);
++ if (m_iAddonInfoToggleStart == 0)
++ {
++ m_iAddonInfoToggleStart = XbmcThreads::SystemClockMillis();
++ m_iAddonInfoToggleCurrent = 0;
++ return true;
++ }
++
++ if ((int) (XbmcThreads::SystemClockMillis() - m_iAddonInfoToggleStart) > g_advancedSettings.m_iPVRInfoToggleInterval)
++ {
++ unsigned int iPrevious = m_iAddonInfoToggleCurrent;
++ if (((int) ++m_iAddonInfoToggleCurrent) > m_iActiveClients - 1)
++ m_iAddonInfoToggleCurrent = 0;
++
++ return m_iAddonInfoToggleCurrent != iPrevious;
++ }
++
++ return false;
++}
++
++bool CPVRGUIInfo::TimerInfoToggle(void)
++{
++ CSingleLock lock(m_critSection);
++ if (m_iTimerInfoToggleStart == 0)
++ {
++ m_iTimerInfoToggleStart = XbmcThreads::SystemClockMillis();
++ m_iTimerInfoToggleCurrent = 0;
++ return true;
++ }
++
++ if ((int) (XbmcThreads::SystemClockMillis() - m_iTimerInfoToggleStart) > g_advancedSettings.m_iPVRInfoToggleInterval)
++ {
++ unsigned int iPrevious = m_iTimerInfoToggleCurrent;
++ unsigned int iBoundary = m_iRecordingTimerAmount > 0 ? m_iRecordingTimerAmount : m_iTimerAmount;
++ if (++m_iTimerInfoToggleCurrent > iBoundary - 1)
++ m_iTimerInfoToggleCurrent = 0;
++
++ return m_iTimerInfoToggleCurrent != iPrevious;
++ }
++
++ return false;
++}
++
++void CPVRGUIInfo::Process(void)
++{
++ unsigned int mLoop(0);
++
++ /* updated on request */
++ g_PVRTimers->RegisterObserver(this);
++ UpdateTimersCache();
++
++ while (!g_application.m_bStop && !m_bStop)
++ {
++ if (!m_bStop)
++ ToggleShowInfo();
++ Sleep(0);
++
++ if (!m_bStop)
++ UpdateQualityData();
++ Sleep(0);
++
++ if (!m_bStop)
++ UpdateMisc();
++ Sleep(0);
++
++ if (!m_bStop)
++ UpdatePlayingTag();
++ Sleep(0);
++
++ if (!m_bStop)
++ UpdateTimersToggle();
++ Sleep(0);
++
++ if (!m_bStop)
++ UpdateNextTimer();
++ Sleep(0);
++
++ if (!m_bStop && mLoop % 10 == 0)
++ UpdateBackendCache(); /* updated every 10 iterations */
++
++ if (++mLoop == 1000)
++ mLoop = 0;
++
++ if (!m_bStop)
++ Sleep(1000);
++ }
++
++ if (!m_bStop)
++ ResetPlayingTag();
++}
++
++void CPVRGUIInfo::UpdateQualityData(void)
++{
++ PVR_SIGNAL_STATUS qualityInfo;
++ ClearQualityInfo(qualityInfo);
++ g_PVRClients->GetQualityData(&qualityInfo);
++
++ CSingleLock lock(m_critSection);
++ m_qualityInfo.dAudioBitrate = qualityInfo.dAudioBitrate;
++ m_qualityInfo.dDolbyBitrate = qualityInfo.dDolbyBitrate;
++ m_qualityInfo.dVideoBitrate = qualityInfo.dVideoBitrate;
++ m_qualityInfo.iBER = qualityInfo.iBER;
++ m_qualityInfo.iSNR = qualityInfo.iSNR;
++ m_qualityInfo.iSignal = qualityInfo.iSignal;
++ m_qualityInfo.iUNC = qualityInfo.iUNC;
++ strncpy(m_qualityInfo.strAdapterName, qualityInfo.strAdapterName, 1024);
++ strncpy(m_qualityInfo.strAdapterStatus, qualityInfo.strAdapterStatus, 1024);
++}
++
++void CPVRGUIInfo::UpdateMisc(void)
++{
++ bool bStarted = g_PVRManager.IsStarted();
++ CStdString strPlayingClientName = bStarted ? g_PVRClients->GetPlayingClientName() : StringUtils::EmptyString;
++ bool bHasRecordings = bStarted && g_PVRRecordings->GetNumRecordings() > 0;
++ bool bIsPlayingTV = bStarted && g_PVRClients->IsPlayingTV();
++ bool bIsPlayingRadio = bStarted && g_PVRClients->IsPlayingRadio();
++ bool bIsPlayingRecording = bStarted && g_PVRClients->IsPlayingRecording();
++ bool bIsPlayingEncryptedStream = bStarted && g_PVRClients->IsEncrypted();
++ /* safe to fetch these unlocked, since they're updated from the same thread as this one */
++ bool bHasNonRecordingTimers = bStarted && m_iTimerAmount - m_iRecordingTimerAmount > 0;
++
++ CSingleLock lock(m_critSection);
++ m_strPlayingClientName = strPlayingClientName;
++ m_bHasRecordings = bHasRecordings;
++ m_bHasNonRecordingTimers = bHasNonRecordingTimers;
++ m_bIsPlayingTV = bIsPlayingTV;
++ m_bIsPlayingRadio = bIsPlayingRadio;
++ m_bIsPlayingRecording = bIsPlayingRecording;
++ m_bIsPlayingEncryptedStream = bIsPlayingEncryptedStream;
++}
++
++bool CPVRGUIInfo::TranslateCharInfo(DWORD dwInfo, CStdString &strValue) const
++{
++ bool bReturn(true);
++ CSingleLock lock(m_critSection);
++
++ switch(dwInfo)
++ {
++ case PVR_NOW_RECORDING_TITLE:
++ CharInfoActiveTimerTitle(strValue);
++ break;
++ case PVR_NOW_RECORDING_CHANNEL:
++ CharInfoActiveTimerChannelName(strValue);
++ break;
++ case PVR_NOW_RECORDING_CHAN_ICO:
++ CharInfoActiveTimerChannelIcon(strValue);
++ break;
++ case PVR_NOW_RECORDING_DATETIME:
++ CharInfoActiveTimerDateTime(strValue);
++ break;
++ case PVR_NEXT_RECORDING_TITLE:
++ CharInfoNextTimerTitle(strValue);
++ break;
++ case PVR_NEXT_RECORDING_CHANNEL:
++ CharInfoNextTimerChannelName(strValue);
++ break;
++ case PVR_NEXT_RECORDING_CHAN_ICO:
++ CharInfoNextTimerChannelIcon(strValue);
++ break;
++ case PVR_NEXT_RECORDING_DATETIME:
++ CharInfoNextTimerDateTime(strValue);
++ break;
++ case PVR_PLAYING_DURATION:
++ CharInfoPlayingDuration(strValue);
++ break;
++ case PVR_PLAYING_TIME:
++ CharInfoPlayingTime(strValue);
++ break;
++ case PVR_NEXT_TIMER:
++ CharInfoNextTimer(strValue);
++ break;
++ case PVR_ACTUAL_STREAM_VIDEO_BR:
++ CharInfoVideoBR(strValue);
++ break;
++ case PVR_ACTUAL_STREAM_AUDIO_BR:
++ CharInfoAudioBR(strValue);
++ break;
++ case PVR_ACTUAL_STREAM_DOLBY_BR:
++ CharInfoDolbyBR(strValue);
++ break;
++ case PVR_ACTUAL_STREAM_SIG:
++ CharInfoSignal(strValue);
++ break;
++ case PVR_ACTUAL_STREAM_SNR:
++ CharInfoSNR(strValue);
++ break;
++ case PVR_ACTUAL_STREAM_BER:
++ CharInfoBER(strValue);
++ break;
++ case PVR_ACTUAL_STREAM_UNC:
++ CharInfoUNC(strValue);
++ break;
++ case PVR_ACTUAL_STREAM_CLIENT:
++ CharInfoPlayingClientName(strValue);
++ break;
++ case PVR_ACTUAL_STREAM_DEVICE:
++ CharInfoFrontendName(strValue);
++ break;
++ case PVR_ACTUAL_STREAM_STATUS:
++ CharInfoFrontendStatus(strValue);
++ break;
++ case PVR_ACTUAL_STREAM_CRYPTION:
++ CharInfoEncryption(strValue);
++ break;
++ case PVR_BACKEND_NAME:
++ CharInfoBackendName(strValue);
++ break;
++ case PVR_BACKEND_VERSION:
++ CharInfoBackendVersion(strValue);
++ break;
++ case PVR_BACKEND_HOST:
++ CharInfoBackendHost(strValue);
++ break;
++ case PVR_BACKEND_DISKSPACE:
++ CharInfoBackendDiskspace(strValue);
++ break;
++ case PVR_BACKEND_CHANNELS:
++ CharInfoBackendChannels(strValue);
++ break;
++ case PVR_BACKEND_TIMERS:
++ CharInfoBackendTimers(strValue);
++ break;
++ case PVR_BACKEND_RECORDINGS:
++ CharInfoBackendRecordings(strValue);
++ break;
++ case PVR_BACKEND_NUMBER:
++ CharInfoBackendNumber(strValue);
++ break;
++ case PVR_TOTAL_DISKSPACE:
++ CharInfoTotalDiskSpace(strValue);
++ break;
++ default:
++ strValue = StringUtils::EmptyString;
++ bReturn = false;
++ break;
++ }
++
++ return bReturn;
++}
++
++bool CPVRGUIInfo::TranslateBoolInfo(DWORD dwInfo) const
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ switch (dwInfo)
++ {
++ case PVR_IS_RECORDING:
++ bReturn = m_iRecordingTimerAmount > 0;
++ break;
++ case PVR_HAS_TIMER:
++ bReturn = m_iTimerAmount > 0;
++ break;
++ case PVR_HAS_NONRECORDING_TIMER:
++ bReturn = m_bHasNonRecordingTimers;
++ break;
++ case PVR_IS_PLAYING_TV:
++ bReturn = m_bIsPlayingTV;
++ break;
++ case PVR_IS_PLAYING_RADIO:
++ bReturn = m_bIsPlayingRadio;
++ break;
++ case PVR_IS_PLAYING_RECORDING:
++ bReturn = m_bIsPlayingRecording;
++ break;
++ case PVR_ACTUAL_STREAM_ENCRYPTED:
++ bReturn = m_bIsPlayingEncryptedStream;
++ break;
++ default:
++ break;
++ }
++
++ return bReturn;
++}
++
++int CPVRGUIInfo::TranslateIntInfo(DWORD dwInfo) const
++{
++ int iReturn(0);
++ CSingleLock lock(m_critSection);
++
++ if (dwInfo == PVR_PLAYING_PROGRESS)
++ iReturn = (int) ((float) GetStartTime() / m_iDuration * 100);
++ else if (dwInfo == PVR_ACTUAL_STREAM_SIG_PROGR)
++ iReturn = (int) ((float) m_qualityInfo.iSignal / 0xFFFF * 100);
++ else if (dwInfo == PVR_ACTUAL_STREAM_SNR_PROGR)
++ iReturn = (int) ((float) m_qualityInfo.iSNR / 0xFFFF * 100);
++
++ return iReturn;
++}
++
++void CPVRGUIInfo::CharInfoActiveTimerTitle(CStdString &strValue) const
++{
++ strValue.Format("%s", m_strActiveTimerTitle);
++}
++
++void CPVRGUIInfo::CharInfoActiveTimerChannelName(CStdString &strValue) const
++{
++ strValue.Format("%s", m_strActiveTimerChannelName);
++}
++
++void CPVRGUIInfo::CharInfoActiveTimerChannelIcon(CStdString &strValue) const
++{
++ strValue.Format("%s", m_strActiveTimerChannelIcon);
++}
++
++void CPVRGUIInfo::CharInfoActiveTimerDateTime(CStdString &strValue) const
++{
++ strValue.Format("%s", m_strActiveTimerTime);
++}
++
++void CPVRGUIInfo::CharInfoNextTimerTitle(CStdString &strValue) const
++{
++ strValue.Format("%s", m_strNextRecordingTitle);
++}
++
++void CPVRGUIInfo::CharInfoNextTimerChannelName(CStdString &strValue) const
++{
++ strValue.Format("%s", m_strNextRecordingChannelName);
++}
++
++void CPVRGUIInfo::CharInfoNextTimerChannelIcon(CStdString &strValue) const
++{
++ strValue.Format("%s", m_strNextRecordingChannelIcon);
++}
++
++void CPVRGUIInfo::CharInfoNextTimerDateTime(CStdString &strValue) const
++{
++ strValue.Format("%s", m_strNextRecordingTime);
++}
++
++void CPVRGUIInfo::CharInfoPlayingDuration(CStdString &strValue) const
++{
++ strValue.Format("%s", StringUtils::SecondsToTimeString(m_iDuration / 1000, TIME_FORMAT_GUESS));
++}
++
++void CPVRGUIInfo::CharInfoPlayingTime(CStdString &strValue) const
++{
++ strValue.Format("%s", StringUtils::SecondsToTimeString(GetStartTime()/1000, TIME_FORMAT_GUESS));
++}
++
++void CPVRGUIInfo::CharInfoNextTimer(CStdString &strValue) const
++{
++ strValue.Format("%s", m_strNextTimerInfo);
++}
++
++void CPVRGUIInfo::CharInfoBackendNumber(CStdString &strValue) const
++{
++ if (m_iActiveClients > 0)
++ strValue.Format("%u %s %u", m_iAddonInfoToggleCurrent+1, g_localizeStrings.Get(20163), m_iActiveClients);
++ else
++ strValue.Format("%s", g_localizeStrings.Get(14023));
++}
++
++void CPVRGUIInfo::CharInfoTotalDiskSpace(CStdString &strValue) const
++{
++ strValue.Format("%s", m_strTotalDiskspace);
++}
++
++void CPVRGUIInfo::CharInfoVideoBR(CStdString &strValue) const
++{
++ strValue.Format("%.2f Mbit/s", m_qualityInfo.dVideoBitrate);
++}
++
++void CPVRGUIInfo::CharInfoAudioBR(CStdString &strValue) const
++{
++ strValue.Format("%.0f kbit/s", m_qualityInfo.dAudioBitrate);
++}
++
++void CPVRGUIInfo::CharInfoDolbyBR(CStdString &strValue) const
++{
++ strValue.Format("%.0f kbit/s", m_qualityInfo.dDolbyBitrate);
++}
++
++void CPVRGUIInfo::CharInfoSignal(CStdString &strValue) const
++{
++ strValue.Format("%d %%", m_qualityInfo.iSignal / 655);
++}
++
++void CPVRGUIInfo::CharInfoSNR(CStdString &strValue) const
++{
++ strValue.Format("%d %%", m_qualityInfo.iSNR / 655);
++}
++
++void CPVRGUIInfo::CharInfoBER(CStdString &strValue) const
++{
++ strValue.Format("%08X", m_qualityInfo.iBER);
++}
++
++void CPVRGUIInfo::CharInfoUNC(CStdString &strValue) const
++{
++ strValue.Format("%08X", m_qualityInfo.iUNC);
++}
++
++void CPVRGUIInfo::CharInfoFrontendName(CStdString &strValue) const
++{
++ if (!strcmp(m_qualityInfo.strAdapterName, StringUtils::EmptyString))
++ strValue.Format("%s", g_localizeStrings.Get(13205));
++ else
++ strValue.Format("%s", m_qualityInfo.strAdapterName);
++}
++
++void CPVRGUIInfo::CharInfoFrontendStatus(CStdString &strValue) const
++{
++ if (!strcmp(m_qualityInfo.strAdapterStatus, StringUtils::EmptyString))
++ strValue.Format("%s", g_localizeStrings.Get(13205));
++ else
++ strValue.Format("%s", m_qualityInfo.strAdapterStatus);
++}
++
++void CPVRGUIInfo::CharInfoBackendName(CStdString &strValue) const
++{
++ if (m_strBackendName.IsEmpty())
++ strValue.Format("%s", g_localizeStrings.Get(13205));
++ else
++ strValue.Format("%s", m_strBackendName);
++}
++
++void CPVRGUIInfo::CharInfoBackendVersion(CStdString &strValue) const
++{
++ if (m_strBackendVersion.IsEmpty())
++ strValue.Format("%s", g_localizeStrings.Get(13205));
++ else
++ strValue.Format("%s", m_strBackendVersion);
++}
++
++void CPVRGUIInfo::CharInfoBackendHost(CStdString &strValue) const
++{
++ if (m_strBackendHost.IsEmpty())
++ strValue.Format("%s", g_localizeStrings.Get(13205));
++ else
++ strValue.Format("%s", m_strBackendHost);
++}
++
++void CPVRGUIInfo::CharInfoBackendDiskspace(CStdString &strValue) const
++{
++ if (m_strBackendDiskspace.IsEmpty())
++ strValue.Format("%s", g_localizeStrings.Get(13205));
++ else
++ strValue.Format("%s", m_strBackendDiskspace);
++}
++
++void CPVRGUIInfo::CharInfoBackendChannels(CStdString &strValue) const
++{
++ if (m_strBackendChannels.IsEmpty())
++ strValue.Format("%s", g_localizeStrings.Get(13205));
++ else
++ strValue.Format("%s", m_strBackendChannels);
++}
++
++void CPVRGUIInfo::CharInfoBackendTimers(CStdString &strValue) const
++{
++ if (m_strBackendTimers.IsEmpty())
++ strValue.Format("%s", g_localizeStrings.Get(13205));
++ else
++ strValue.Format("%s", m_strBackendTimers);
++}
++
++void CPVRGUIInfo::CharInfoBackendRecordings(CStdString &strValue) const
++{
++ if (m_strBackendRecordings.IsEmpty())
++ strValue.Format("%s", g_localizeStrings.Get(13205));
++ else
++ strValue.Format("%s", m_strBackendRecordings);
++}
++
++void CPVRGUIInfo::CharInfoPlayingClientName(CStdString &strValue) const
++{
++ if (m_strPlayingClientName.IsEmpty())
++ strValue.Format("%s", g_localizeStrings.Get(13205));
++ else
++ strValue.Format("%s", m_strPlayingClientName);
++}
++
++void CPVRGUIInfo::CharInfoEncryption(CStdString &strValue) const
++{
++ CPVRChannel channel;
++ if (g_PVRClients->GetPlayingChannel(channel))
++ strValue.Format("%s", channel.EncryptionName());
++ else
++ strValue = StringUtils::EmptyString;
++}
++
++void CPVRGUIInfo::UpdateBackendCache(void)
++{
++ CStdString strBackendName;
++ CStdString strBackendVersion;
++ CStdString strBackendHost;
++ CStdString strBackendDiskspace;
++ CStdString strBackendTimers;
++ CStdString strBackendRecordings;
++ CStdString strBackendChannels;
++ int iActiveClients(0);
++
++ if (!AddonInfoToggle())
++ return;
++
++ CPVRClients *clients = g_PVRClients;
++ CLIENTMAP activeClients;
++ iActiveClients = clients->GetConnectedClients(&activeClients);
++ if (iActiveClients > 0)
++ {
++ CLIENTMAPITR activeClient = activeClients.begin();
++ /* safe to read unlocked */
++ for (unsigned int i = 0; i < m_iAddonInfoToggleCurrent; i++)
++ activeClient++;
++
++ long long kBTotal = 0;
++ long long kBUsed = 0;
++
++ if (activeClient->second->GetDriveSpace(&kBTotal, &kBUsed) == PVR_ERROR_NO_ERROR)
++ {
++ kBTotal /= 1024; // Convert to MBytes
++ kBUsed /= 1024; // Convert to MBytes
++ strBackendDiskspace.Format("%s %.1f GByte - %s: %.1f GByte",
++ g_localizeStrings.Get(20161), (float) kBTotal / 1024, g_localizeStrings.Get(20162), (float) kBUsed / 1024);
++ }
++ else
++ {
++ strBackendDiskspace = g_localizeStrings.Get(19055);
++ }
++
++ int NumChannels = activeClient->second->GetChannelsAmount();
++ if (NumChannels >= 0)
++ strBackendChannels.Format("%i", NumChannels);
++ else
++ strBackendChannels = g_localizeStrings.Get(161);
++
++ int NumTimers = activeClient->second->GetTimersAmount();
++ if (NumTimers >= 0)
++ strBackendTimers.Format("%i", NumTimers);
++ else
++ strBackendTimers = g_localizeStrings.Get(161);
++
++ int NumRecordings = activeClient->second->GetRecordingsAmount();
++ if (NumRecordings >= 0)
++ strBackendRecordings.Format("%i", NumRecordings);
++ else
++ strBackendRecordings = g_localizeStrings.Get(161);
++
++ strBackendName = activeClient->second->GetBackendName();
++ strBackendVersion = activeClient->second->GetBackendVersion();
++ strBackendHost = activeClient->second->GetConnectionString();
++ }
++
++ CSingleLock lock(m_critSection);
++ m_strBackendName = strBackendName;
++ m_strBackendVersion = strBackendVersion;
++ m_strBackendHost = strBackendHost;
++ m_strBackendDiskspace = strBackendDiskspace;
++ m_strBackendTimers = strBackendTimers;
++ m_strBackendRecordings = strBackendRecordings;
++ m_strBackendChannels = strBackendChannels;
++ m_iActiveClients = iActiveClients;
++}
++
++void CPVRGUIInfo::UpdateTimersCache(void)
++{
++ int iTimerAmount = g_PVRTimers->GetNumActiveTimers();
++ int iRecordingTimerAmount = g_PVRTimers->GetNumActiveRecordings();
++
++ {
++ CSingleLock lock(m_critSection);
++ m_iTimerAmount = iTimerAmount;
++ m_iRecordingTimerAmount = iRecordingTimerAmount;
++ m_iTimerInfoToggleStart = 0;
++ }
++
++ UpdateTimersToggle();
++}
++
++void CPVRGUIInfo::UpdateNextTimer(void)
++{
++ CStdString strNextRecordingTitle;
++ CStdString strNextRecordingChannelName;
++ CStdString strNextRecordingChannelIcon;
++ CStdString strNextRecordingTime;
++ CStdString strNextTimerInfo;
++
++ CPVRTimerInfoTag tag;
++ if (g_PVRTimers->GetNextActiveTimer(&tag))
++ {
++ strNextRecordingTitle.Format("%s", tag.m_strTitle);
++ strNextRecordingChannelName.Format("%s", tag.ChannelName());
++ strNextRecordingChannelIcon.Format("%s", tag.ChannelIcon());
++ strNextRecordingTime.Format("%s", tag.StartAsLocalTime().GetAsLocalizedDateTime(false, false));
++
++ strNextTimerInfo.Format("%s %s %s %s",
++ g_localizeStrings.Get(19106),
++ tag.StartAsLocalTime().GetAsLocalizedDate(true),
++ g_localizeStrings.Get(19107),
++ tag.StartAsLocalTime().GetAsLocalizedTime("HH:mm", false));
++ }
++
++ CSingleLock lock(m_critSection);
++ m_strNextRecordingTitle = strNextRecordingTitle;
++ m_strNextRecordingChannelName = strNextRecordingChannelName;
++ m_strNextRecordingChannelIcon = strNextRecordingChannelIcon;
++ m_strNextRecordingTime = strNextRecordingTime;
++ m_strNextTimerInfo = strNextTimerInfo;
++}
++
++void CPVRGUIInfo::UpdateTimersToggle(void)
++{
++ if (!TimerInfoToggle())
++ return;
++
++ CStdString strActiveTimerTitle;
++ CStdString strActiveTimerChannelName;
++ CStdString strActiveTimerChannelIcon;
++ CStdString strActiveTimerTime;
++
++ /* safe to fetch these unlocked, since they're updated from the same thread as this one */
++ if (m_iRecordingTimerAmount > 0)
++ {
++ vector<CPVRTimerInfoTag *> activeTags;
++ g_PVRTimers->GetActiveRecordings(&activeTags);
++ if (activeTags.at(m_iTimerInfoToggleCurrent) != 0)
++ {
++ strActiveTimerTitle.Format("%s", activeTags.at(m_iTimerInfoToggleCurrent)->m_strTitle);
++ strActiveTimerChannelName.Format("%s", activeTags.at(m_iTimerInfoToggleCurrent)->ChannelName());
++ strActiveTimerChannelIcon.Format("%s", activeTags.at(m_iTimerInfoToggleCurrent)->ChannelIcon());
++ strActiveTimerTime.Format("%s", activeTags.at(m_iTimerInfoToggleCurrent)->StartAsLocalTime().GetAsLocalizedDateTime(false, false));
++ }
++ }
++
++ CSingleLock lock(m_critSection);
++ m_strActiveTimerTitle = strActiveTimerTitle;
++ m_strActiveTimerChannelName = strActiveTimerChannelName;
++ m_strActiveTimerChannelIcon = strActiveTimerChannelIcon;
++ m_strActiveTimerTime = strActiveTimerTime;
++}
++
++int CPVRGUIInfo::GetDuration(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_iDuration;
++}
++
++int CPVRGUIInfo::GetStartTime(void) const
++{
++ CSingleLock lock(m_critSection);
++ if (m_playingEpgTag)
++ {
++ /* Calculate here the position we have of the running live TV event.
++ * "position in ms" = ("current local time" - "event start local time") * 1000
++ */
++ CDateTime current = CDateTime::GetCurrentDateTime();
++ CDateTime start = m_playingEpgTag->StartAsLocalTime();
++ CDateTimeSpan time = current > start ? current - start : CDateTimeSpan(0, 0, 0, 0);
++ return (time.GetDays() * 60 * 60 * 24
++ + time.GetHours() * 60 * 60
++ + time.GetMinutes() * 60
++ + time.GetSeconds()) * 1000;
++ }
++ else
++ {
++ return 0;
++ }
++}
++
++void CPVRGUIInfo::ResetPlayingTag(void)
++{
++ CSingleLock lock(m_critSection);
++ if (m_playingEpgTag)
++ delete m_playingEpgTag;
++ m_playingEpgTag = NULL;
++ m_iDuration = 0;
++}
++
++bool CPVRGUIInfo::GetPlayingTag(CEpgInfoTag &tag) const
++{
++ bool bReturn(false);
++
++ CSingleLock lock(m_critSection);
++ if (m_playingEpgTag)
++ {
++ tag = *m_playingEpgTag;
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++void CPVRGUIInfo::UpdatePlayingTag(void)
++{
++ CPVRChannel currentChannel;
++ CPVRRecording recording;
++ if (g_PVRManager.GetCurrentChannel(currentChannel))
++ {
++ CEpgInfoTag epgTag;
++ bool bHasEpgTag = GetPlayingTag(epgTag);
++ const CPVRChannel *channel = bHasEpgTag ? epgTag.ChannelTag() : NULL;
++
++ if (!bHasEpgTag || !epgTag.IsActive() ||
++ !channel || *channel != currentChannel)
++ {
++ CEpgInfoTag newTag;
++ {
++ CSingleLock lock(m_critSection);
++ ResetPlayingTag();
++ if (currentChannel.GetEPGNow(newTag))
++ {
++ m_playingEpgTag = new CEpgInfoTag(newTag);
++ m_iDuration = m_playingEpgTag->GetDuration() * 1000;
++ }
++ }
++ g_PVRManager.UpdateCurrentFile();
++ }
++ }
++ else if (g_PVRClients->GetPlayingRecording(recording))
++ {
++ ResetPlayingTag();
++ m_iDuration = recording.GetDuration() * 1000;
++ }
++}
+diff --git a/xbmc/pvr/PVRGUIInfo.h b/xbmc/pvr/PVRGUIInfo.h
+new file mode 100644
+index 0000000..d1af759
+--- /dev/null
++++ b/xbmc/pvr/PVRGUIInfo.h
+@@ -0,0 +1,172 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "threads/CriticalSection.h"
++#include "utils/Observer.h"
++#include "threads/Thread.h"
++#include "addons/include/xbmc_pvr_types.h"
++
++namespace EPG
++{
++ class CEpgInfoTag;
++}
++
++namespace PVR
++{
++ class CPVRTimerInfoTag;
++ class CPVRRecording;
++
++ class CPVRGUIInfo : private CThread,
++ private Observer
++ {
++ public:
++ CPVRGUIInfo(void);
++ virtual ~CPVRGUIInfo(void);
++
++ void Start(void);
++ void Stop(void);
++
++ void Notify(const Observable &obs, const CStdString& msg);
++
++ bool TranslateBoolInfo(DWORD dwInfo) const;
++ bool TranslateCharInfo(DWORD dwInfo, CStdString &strValue) const;
++ int TranslateIntInfo(DWORD dwInfo) const;
++
++ /*!
++ * @brief Get the total duration of the currently playing LiveTV item.
++ * @return The total duration in milliseconds or NULL if no channel is playing.
++ */
++ int GetDuration(void) const;
++
++ /*!
++ * @brief Get the current position in milliseconds since the start of a LiveTV item.
++ * @return The position in milliseconds or NULL if no channel is playing.
++ */
++ int GetStartTime(void) const;
++
++ /*!
++ * @brief Show the player info.
++ * @param iTimeout Hide the player info after iTimeout seconds.
++ * @todo not really the right place for this :-)
++ */
++ void ShowPlayerInfo(int iTimeout);
++
++ /*!
++ * @brief Clear the playing EPG tag.
++ */
++ void ResetPlayingTag(void);
++
++ bool GetPlayingTag(EPG::CEpgInfoTag &tag) const;
++
++ private:
++ void ResetProperties(void);
++ void ClearQualityInfo(PVR_SIGNAL_STATUS &qualityInfo);
++ void Process(void);
++
++ void UpdatePlayingTag(void);
++ void UpdateTimersCache(void);
++ void UpdateBackendCache(void);
++ void UpdateQualityData(void);
++ void UpdateMisc(void);
++ void UpdateNextTimer(void);
++
++ bool AddonInfoToggle(void);
++ bool TimerInfoToggle(void);
++ void UpdateTimersToggle(void);
++ void ToggleShowInfo(void);
++
++ void CharInfoActiveTimerTitle(CStdString &strValue) const;
++ void CharInfoActiveTimerChannelName(CStdString &strValue) const;
++ void CharInfoActiveTimerChannelIcon(CStdString &strValue) const;
++ void CharInfoActiveTimerDateTime(CStdString &strValue) const;
++ void CharInfoNextTimerTitle(CStdString &strValue) const;
++ void CharInfoNextTimerChannelName(CStdString &strValue) const;
++ void CharInfoNextTimerChannelIcon(CStdString &strValue) const;
++ void CharInfoNextTimerDateTime(CStdString &strValue) const;
++ void CharInfoPlayingDuration(CStdString &strValue) const;
++ void CharInfoPlayingTime(CStdString &strValue) const;
++ void CharInfoNextTimer(CStdString &strValue) const;
++ void CharInfoBackendNumber(CStdString &strValue) const;
++ void CharInfoTotalDiskSpace(CStdString &strValue) const;
++ void CharInfoVideoBR(CStdString &strValue) const;
++ void CharInfoAudioBR(CStdString &strValue) const;
++ void CharInfoDolbyBR(CStdString &strValue) const;
++ void CharInfoSignal(CStdString &strValue) const;
++ void CharInfoSNR(CStdString &strValue) const;
++ void CharInfoBER(CStdString &strValue) const;
++ void CharInfoUNC(CStdString &strValue) const;
++ void CharInfoFrontendName(CStdString &strValue) const;
++ void CharInfoFrontendStatus(CStdString &strValue) const;
++ void CharInfoBackendName(CStdString &strValue) const;
++ void CharInfoBackendVersion(CStdString &strValue) const;
++ void CharInfoBackendHost(CStdString &strValue) const;
++ void CharInfoBackendDiskspace(CStdString &strValue) const;
++ void CharInfoBackendChannels(CStdString &strValue) const;
++ void CharInfoBackendTimers(CStdString &strValue) const;
++ void CharInfoBackendRecordings(CStdString &strValue) const;
++ void CharInfoPlayingClientName(CStdString &strValue) const;
++ void CharInfoEncryption(CStdString &strValue) const;
++
++ /** @name GUIInfoManager data */
++ //@{
++ CStdString m_strActiveTimerTitle;
++ CStdString m_strActiveTimerChannelName;
++ CStdString m_strActiveTimerChannelIcon;
++ CStdString m_strActiveTimerTime;
++ CStdString m_strNextTimerInfo;
++ CStdString m_strNextRecordingTitle;
++ CStdString m_strNextRecordingChannelName;
++ CStdString m_strNextRecordingChannelIcon;
++ CStdString m_strNextRecordingTime;
++ bool m_bHasRecordings;
++ unsigned int m_iTimerAmount;
++ unsigned int m_iRecordingTimerAmount;
++ int m_iActiveClients;
++ CStdString m_strPlayingClientName;
++ CStdString m_strBackendName;
++ CStdString m_strBackendVersion;
++ CStdString m_strBackendHost;
++ CStdString m_strBackendDiskspace;
++ CStdString m_strBackendTimers;
++ CStdString m_strBackendRecordings;
++ CStdString m_strBackendChannels;
++ CStdString m_strTotalDiskspace;
++ unsigned int m_iDuration;
++
++ bool m_bHasNonRecordingTimers;
++ bool m_bIsPlayingTV;
++ bool m_bIsPlayingRadio;
++ bool m_bIsPlayingRecording;
++ bool m_bIsPlayingEncryptedStream;
++ //@}
++
++ PVR_SIGNAL_STATUS m_qualityInfo; /*!< stream quality information */
++ unsigned int m_iAddonInfoToggleStart;
++ unsigned int m_iAddonInfoToggleCurrent;
++ unsigned int m_iTimerInfoToggleStart;
++ unsigned int m_iTimerInfoToggleCurrent;
++ unsigned int m_iToggleShowInfo;
++ EPG::CEpgInfoTag * m_playingEpgTag;
++
++ CCriticalSection m_critSection;
++ };
++}
+diff --git a/xbmc/pvr/PVRManager.cpp b/xbmc/pvr/PVRManager.cpp
+new file mode 100644
+index 0000000..5919155
+--- /dev/null
++++ b/xbmc/pvr/PVRManager.cpp
+@@ -0,0 +1,1116 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "Application.h"
++#include "GUIInfoManager.h"
++#include "dialogs/GUIDialogOK.h"
++#include "dialogs/GUIDialogProgress.h"
++#include "dialogs/GUIDialogExtendedProgressBar.h"
++#include "dialogs/GUIDialogKaiToast.h"
++#include "guilib/GUIWindowManager.h"
++#include "guilib/LocalizeStrings.h"
++#include "music/tags/MusicInfoTag.h"
++#include "settings/GUISettings.h"
++#include "settings/Settings.h"
++#include "threads/SingleLock.h"
++#include "windows/GUIWindowPVR.h"
++#include "utils/log.h"
++#include "utils/StringUtils.h"
++#include "threads/Atomics.h"
++
++#include "PVRManager.h"
++#include "PVRDatabase.h"
++#include "PVRGUIInfo.h"
++#include "addons/PVRClients.h"
++#include "channels/PVRChannelGroupsContainer.h"
++#include "epg/EpgContainer.h"
++#include "recordings/PVRRecordings.h"
++#include "timers/PVRTimers.h"
++
++using namespace std;
++using namespace MUSIC_INFO;
++using namespace PVR;
++using namespace EPG;
++
++CPVRManager::CPVRManager(void) :
++ CThread("PVR manager"),
++ m_channelGroups(NULL),
++ m_recordings(NULL),
++ m_timers(NULL),
++ m_addons(NULL),
++ m_guiInfo(NULL),
++ m_triggerEvent(true),
++ m_currentFile(NULL),
++ m_database(new CPVRDatabase),
++ m_bFirstStart(true),
++ m_loadingProgressDialog(NULL),
++ m_managerState(ManagerStateStopped)
++{
++ ResetProperties();
++}
++
++CPVRManager::~CPVRManager(void)
++{
++ Stop();
++ if (m_database->IsOpen())
++ m_database->Close();
++ CLog::Log(LOGDEBUG,"PVRManager - destroyed");
++}
++
++CPVRManager &CPVRManager::Get(void)
++{
++ static CPVRManager pvrManagerInstance;
++ return pvrManagerInstance;
++}
++
++void CPVRManager::Cleanup(void)
++{
++ CSingleLock lock(m_critSection);
++
++ if (m_addons) delete m_addons; m_addons = NULL;
++ if (m_guiInfo) delete m_guiInfo; m_guiInfo = NULL;
++ if (m_timers) delete m_timers; m_timers = NULL;
++ if (m_recordings) delete m_recordings; m_recordings = NULL;
++ if (m_channelGroups) delete m_channelGroups; m_channelGroups = NULL;
++ m_triggerEvent.Set();
++
++ m_currentFile = NULL;
++ m_PreviousChannel[0] = -1;
++ m_PreviousChannel[1] = -1;
++ m_PreviousChannelIndex = 0;
++ m_LastChannel = 0;
++ m_bIsSwitchingChannels = false;
++
++ for (unsigned int iJobPtr = 0; iJobPtr < m_pendingUpdates.size(); iJobPtr++)
++ delete m_pendingUpdates.at(iJobPtr);
++ m_pendingUpdates.clear();
++
++ SetState(ManagerStateStopped);
++}
++
++void CPVRManager::ResetProperties(void)
++{
++ CSingleLock lock(m_critSection);
++ Cleanup();
++
++ if (!g_application.m_bStop)
++ {
++ m_addons = new CPVRClients;
++ m_channelGroups = new CPVRChannelGroupsContainer;
++ m_recordings = new CPVRRecordings;
++ m_timers = new CPVRTimers;
++ m_guiInfo = new CPVRGUIInfo;
++ }
++}
++
++void CPVRManager::Start(void)
++{
++ CSingleLock lock(m_critSection);
++
++ /* first stop and remove any clients */
++ Stop();
++
++ /* don't start if Settings->Video->TV->Enable isn't checked */
++ if (!g_guiSettings.GetBool("pvrmanager.enabled"))
++ return;
++
++ ResetProperties();
++ SetState(ManagerStateStarting);
++
++ m_database->Open();
++
++ /* create the supervisor thread to do all background activities */
++ StartUpdateThreads();
++}
++
++void CPVRManager::Stop(void)
++{
++ /* check whether the pvrmanager is loaded */
++ if (GetState() == ManagerStateStopping ||
++ GetState() == ManagerStateStopped)
++ return;
++
++ SetState(ManagerStateStopping);
++
++ /* stop the EPG updater, since it might be using the pvr add-ons */
++ g_EpgContainer.Unload();
++
++ CLog::Log(LOGNOTICE, "PVRManager - stopping");
++
++ /* stop playback if needed */
++ if (IsPlaying())
++ {
++ CLog::Log(LOGNOTICE,"PVRManager - %s - stopping PVR playback", __FUNCTION__);
++ g_application.getApplicationMessenger().MediaStop();
++ }
++
++ /* stop all update threads */
++ StopUpdateThreads();
++
++ /* executes the set wakeup command */
++ SetWakeupCommand();
++
++ /* unload all data */
++ Cleanup();
++}
++
++ManagerState CPVRManager::GetState(void) const
++{
++ CSingleLock lock(m_managerStateMutex);
++ return m_managerState;
++}
++
++void CPVRManager::SetState(ManagerState state)
++{
++ CSingleLock lock(m_managerStateMutex);
++ m_managerState = state;
++}
++
++void CPVRManager::Process(void)
++{
++ g_EpgContainer.Stop();
++
++ /* load the pvr data from the db and clients if it's not already loaded */
++ if (!Load())
++ {
++ CLog::Log(LOGERROR, "PVRManager - %s - failed to load PVR data", __FUNCTION__);
++ return;
++ }
++
++ if (GetState() == ManagerStateStarting)
++ SetState(ManagerStateStarted);
++ else
++ return;
++
++ /* main loop */
++ CLog::Log(LOGDEBUG, "PVRManager - %s - entering main loop", __FUNCTION__);
++ g_EpgContainer.Start();
++
++ bool bRestart(false);
++ while (GetState() == ManagerStateStarted && m_addons && m_addons->HasConnectedClients() && !bRestart)
++ {
++ /* continue last watched channel after first startup */
++ if (m_bFirstStart && g_guiSettings.GetInt("pvrplayback.startlast") != START_LAST_CHANNEL_OFF)
++ ContinueLastChannel();
++
++ /* execute the next pending jobs if there are any */
++ try
++ {
++ ExecutePendingJobs();
++ }
++ catch (...)
++ {
++ CLog::Log(LOGERROR, "PVRManager - %s - an error occured while trying to execute the last update job, trying to recover", __FUNCTION__);
++ bRestart = true;
++ }
++
++ if (GetState() == ManagerStateStarted && !bRestart)
++ m_triggerEvent.WaitMSec(1000);
++ }
++
++ if (GetState() == ManagerStateStarted)
++ {
++ CLog::Log(LOGNOTICE, "PVRManager - %s - no add-ons enabled anymore. restarting the pvrmanager", __FUNCTION__);
++ Stop();
++ Start();
++ return;
++ }
++}
++
++bool CPVRManager::SetWakeupCommand(void)
++{
++ if (!g_guiSettings.GetBool("pvrpowermanagement.enabled"))
++ return false;
++
++ const CStdString strWakeupCommand = g_guiSettings.GetString("pvrpowermanagement.setwakeupcmd", false);
++ if (!strWakeupCommand.IsEmpty() && m_timers)
++ {
++ time_t iWakeupTime;
++ const CDateTime nextEvent = m_timers->GetNextEventTime();
++ nextEvent.GetAsTime(iWakeupTime);
++
++ CStdString strExecCommand;
++ strExecCommand.Format("%s %d", strWakeupCommand, iWakeupTime);
++
++ const int iReturn = system(strExecCommand.c_str());
++ if (iReturn != 0)
++ CLog::Log(LOGERROR, "%s - failed to execute wakeup command '%s': %s (%d)", __FUNCTION__, strExecCommand.c_str(), strerror(iReturn), iReturn);
++
++ return iReturn == 0;
++ }
++
++ return false;
++}
++
++bool CPVRManager::StartUpdateThreads(void)
++{
++ StopUpdateThreads();
++ CLog::Log(LOGNOTICE, "PVRManager - starting up");
++
++ /* create the pvrmanager thread, which will ensure that all data will be loaded */
++ SetState(ManagerStateStarting);
++ Create();
++ SetPriority(-1);
++
++ return true;
++}
++
++void CPVRManager::StopUpdateThreads(void)
++{
++ SetState(ManagerStateInterrupted);
++
++ StopThread();
++ m_guiInfo->Stop();
++ m_addons->Stop();
++}
++
++bool CPVRManager::Load(void)
++{
++ /* start the add-on update thread */
++ m_addons->Start();
++
++ /* load at least one client */
++ while (GetState() == ManagerStateStarting && m_addons && !m_addons->HasConnectedClients())
++ Sleep(50);
++
++ if (GetState() != ManagerStateStarting || !m_addons || !m_addons->HasConnectedClients())
++ return false;
++
++ CLog::Log(LOGDEBUG, "PVRManager - %s - active clients found. continue to start", __FUNCTION__);
++
++ /* load all channels and groups */
++ ShowProgressDialog(g_localizeStrings.Get(19236), 0);
++ if (!m_channelGroups->Load() || GetState() != ManagerStateStarting)
++ return false;
++
++ /* get timers from the backends */
++ ShowProgressDialog(g_localizeStrings.Get(19237), 50);
++ m_timers->Load();
++
++ /* get recordings from the backend */
++ ShowProgressDialog(g_localizeStrings.Get(19238), 75);
++ m_recordings->Load();
++
++ CSingleLock lock(m_critSection);
++ if (GetState() != ManagerStateStarting)
++ return false;
++
++ CGUIWindowPVR *pWindow = (CGUIWindowPVR *) g_windowManager.GetWindow(WINDOW_PVR);
++ if (pWindow)
++ pWindow->Reset();
++
++ /* start the other pvr related update threads */
++ ShowProgressDialog(g_localizeStrings.Get(19239), 85);
++ m_guiInfo->Start();
++
++ /* close the progess dialog */
++ HideProgressDialog();
++
++ return true;
++}
++
++void CPVRManager::ShowProgressDialog(const CStdString &strText, int iProgress)
++{
++ if (!m_loadingProgressDialog)
++ {
++ m_loadingProgressDialog = (CGUIDialogExtendedProgressBar *)g_windowManager.GetWindow(WINDOW_DIALOG_EXT_PROGRESS);
++ m_loadingProgressDialog->Show();
++ m_loadingProgressDialog->SetHeader(g_localizeStrings.Get(19235));
++ }
++
++ m_loadingProgressDialog->SetProgress(iProgress, 100);
++ m_loadingProgressDialog->SetTitle(strText);
++ m_loadingProgressDialog->UpdateState();
++}
++
++void CPVRManager::HideProgressDialog(void)
++{
++ if (m_loadingProgressDialog)
++ {
++ m_loadingProgressDialog->Close(true, 0, true, false);
++ m_loadingProgressDialog = NULL;
++ }
++}
++
++bool CPVRManager::ChannelSwitch(unsigned int iChannelNumber)
++{
++ CSingleLock lock(m_critSection);
++
++ const CPVRChannelGroup *playingGroup = GetPlayingGroup(m_addons->IsPlayingRadio());
++ if (playingGroup == NULL)
++ {
++ CLog::Log(LOGERROR, "PVRManager - %s - cannot get the selected group", __FUNCTION__);
++ return false;
++ }
++
++ const CPVRChannel *channel = playingGroup->GetByChannelNumber(iChannelNumber);
++ if (channel == NULL)
++ {
++ CLog::Log(LOGERROR, "PVRManager - %s - cannot find channel %d", __FUNCTION__, iChannelNumber);
++ return false;
++ }
++
++ return PerformChannelSwitch(*channel, false);
++}
++
++bool CPVRManager::ChannelUpDown(unsigned int *iNewChannelNumber, bool bPreview, bool bUp)
++{
++ bool bReturn = false;
++ if (IsPlayingTV() || IsPlayingRadio())
++ {
++ CFileItem currentFile(g_application.CurrentFileItem());
++ CPVRChannel *currentChannel = currentFile.GetPVRChannelInfoTag();
++ const CPVRChannelGroup *group = GetPlayingGroup(currentChannel->IsRadio());
++ if (group)
++ {
++ const CPVRChannel *newChannel = bUp ? group->GetByChannelUp(*currentChannel) : group->GetByChannelDown(*currentChannel);
++ if (newChannel && PerformChannelSwitch(*newChannel, bPreview))
++ {
++ *iNewChannelNumber = newChannel->ChannelNumber();
++ bReturn = true;
++ }
++ }
++ }
++
++ return bReturn;
++}
++
++bool CPVRManager::ContinueLastChannel(void)
++{
++ CSingleLock lock(m_critSection);
++ if (!m_bFirstStart)
++ return true;
++ m_bFirstStart = false;
++ lock.Leave();
++
++ bool bReturn(false);
++ const CPVRChannel *channel = m_channelGroups->GetLastPlayedChannel();
++ if (channel != NULL)
++ {
++ CLog::Log(LOGNOTICE, "PVRManager - %s - continue playback on channel '%s'",
++ __FUNCTION__, channel->ChannelName().c_str());
++ bReturn = StartPlayback(channel, (g_guiSettings.GetInt("pvrplayback.startlast") == START_LAST_CHANNEL_MIN));
++ }
++
++ return bReturn;
++}
++
++void CPVRManager::ResetDatabase(bool bShowProgress /* = true */)
++{
++ CLog::Log(LOGNOTICE,"PVRManager - %s - clearing the PVR database", __FUNCTION__);
++
++ g_EpgContainer.Stop();
++
++ CGUIDialogProgress* pDlgProgress = NULL;
++ if (bShowProgress)
++ {
++ pDlgProgress = (CGUIDialogProgress*)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
++ pDlgProgress->SetLine(0, StringUtils::EmptyString);
++ pDlgProgress->SetLine(1, g_localizeStrings.Get(19186));
++ pDlgProgress->SetLine(2, StringUtils::EmptyString);
++ pDlgProgress->StartModal();
++ pDlgProgress->Progress();
++ }
++
++ if (m_addons && m_addons->IsPlaying())
++ {
++ CLog::Log(LOGNOTICE,"PVRManager - %s - stopping playback", __FUNCTION__);
++ g_application.getApplicationMessenger().MediaStop();
++ }
++
++ if (bShowProgress)
++ {
++ pDlgProgress->SetPercentage(10);
++ pDlgProgress->Progress();
++ }
++
++ /* stop the thread */
++ if (g_guiSettings.GetBool("pvrmanager.enabled"))
++ Stop();
++
++ if (bShowProgress)
++ {
++ pDlgProgress->SetPercentage(20);
++ pDlgProgress->Progress();
++ }
++
++ if (m_database && m_database->Open())
++ {
++ /* clean the EPG database */
++ g_EpgContainer.Clear(true);
++ if (bShowProgress)
++ {
++ pDlgProgress->SetPercentage(30);
++ pDlgProgress->Progress();
++ }
++
++ m_database->DeleteChannelGroups();
++ if (bShowProgress)
++ {
++ pDlgProgress->SetPercentage(50);
++ pDlgProgress->Progress();
++ }
++
++ /* delete all channels */
++ m_database->DeleteChannels();
++ if (bShowProgress)
++ {
++ pDlgProgress->SetPercentage(70);
++ pDlgProgress->Progress();
++ }
++
++ /* delete all channel settings */
++ m_database->DeleteChannelSettings();
++ if (bShowProgress)
++ {
++ pDlgProgress->SetPercentage(80);
++ pDlgProgress->Progress();
++ }
++
++ /* delete all client information */
++ m_database->DeleteClients();
++ if (bShowProgress)
++ {
++ pDlgProgress->SetPercentage(90);
++ pDlgProgress->Progress();
++ }
++
++ m_database->Close();
++ }
++
++ CLog::Log(LOGNOTICE,"PVRManager - %s - PVR database cleared", __FUNCTION__);
++
++ g_EpgContainer.Start();
++
++ if (g_guiSettings.GetBool("pvrmanager.enabled"))
++ {
++ CLog::Log(LOGNOTICE,"PVRManager - %s - restarting the PVRManager", __FUNCTION__);
++ m_database->Open();
++ Cleanup();
++ Start();
++ }
++
++ if (bShowProgress)
++ {
++ pDlgProgress->SetPercentage(100);
++ pDlgProgress->Close();
++ }
++}
++
++void CPVRManager::ResetEPG(void)
++{
++ CLog::Log(LOGNOTICE,"PVRManager - %s - clearing the EPG database", __FUNCTION__);
++
++ StopUpdateThreads();
++ g_EpgContainer.Stop();
++ g_EpgContainer.Reset();
++
++ if (g_guiSettings.GetBool("pvrmanager.enabled"))
++ {
++ m_channelGroups->GetGroupAllTV()->CreateChannelEpgs(true);
++ m_channelGroups->GetGroupAllRadio()->CreateChannelEpgs(true);
++ g_EpgContainer.Start();
++ StartUpdateThreads();
++ }
++}
++
++bool CPVRManager::IsPlaying(void) const
++{
++ return IsStarted() && m_addons && m_addons->IsPlaying();
++}
++
++bool CPVRManager::GetCurrentChannel(CPVRChannel &channel) const
++{
++ return IsPlaying() && m_addons && m_addons->GetPlayingChannel(channel);
++}
++
++int CPVRManager::GetCurrentEpg(CFileItemList &results) const
++{
++ int iReturn = -1;
++
++ CPVRChannel channel;
++ if (m_addons->GetPlayingChannel(channel))
++ iReturn = channel.GetEPG(results);
++ else
++ CLog::Log(LOGDEBUG,"PVRManager - %s - no current channel set", __FUNCTION__);
++
++ return iReturn;
++}
++
++void CPVRManager::ResetPlayingTag(void)
++{
++ CSingleLock lock(m_critSection);
++ if (GetState() == ManagerStateStarted && m_guiInfo)
++ m_guiInfo->ResetPlayingTag();
++}
++
++int CPVRManager::GetPreviousChannel(void)
++{
++ //XXX this must be the craziest way to store the last channel
++ int iReturn = -1;
++ CPVRChannel channel;
++ if (m_addons->GetPlayingChannel(channel))
++ {
++ int iLastChannel = channel.ChannelNumber();
++
++ if ((m_PreviousChannel[m_PreviousChannelIndex ^ 1] == iLastChannel || iLastChannel != m_PreviousChannel[0]) &&
++ iLastChannel != m_PreviousChannel[1])
++ m_PreviousChannelIndex ^= 1;
++
++ iReturn = m_PreviousChannel[m_PreviousChannelIndex ^= 1];
++ }
++
++ return iReturn;
++}
++
++bool CPVRManager::ToggleRecordingOnChannel(unsigned int iChannelId)
++{
++ bool bReturn = false;
++
++ CPVRChannel *channel;
++ channel = m_channelGroups->GetChannelById(iChannelId);
++ if (!channel)
++ return bReturn;
++
++ if (m_addons->HasTimerSupport(channel->ClientID()))
++ {
++ /* timers are supported on this channel */
++ if (!channel->IsRecording())
++ {
++ CPVRTimerInfoTag *newTimer = m_timers->InstantTimer(channel);
++ if (!newTimer)
++ CGUIDialogOK::ShowAndGetInput(19033,0,19164,0);
++ else
++ bReturn = true;
++ }
++ else
++ {
++ /* delete active timers */
++ bReturn = m_timers->DeleteTimersOnChannel(*channel, false, true);
++ }
++ }
++
++ return bReturn;
++}
++
++bool CPVRManager::StartRecordingOnPlayingChannel(bool bOnOff)
++{
++ bool bReturn = false;
++
++ CPVRChannel channel;
++ if (!m_addons->GetPlayingChannel(channel))
++ return bReturn;
++
++ if (m_addons->HasTimerSupport(channel.ClientID()))
++ {
++ /* timers are supported on this channel */
++ if (bOnOff && !channel.IsRecording())
++ {
++ CPVRTimerInfoTag *newTimer = m_timers->InstantTimer(&channel);
++ if (!newTimer)
++ CGUIDialogOK::ShowAndGetInput(19033,0,19164,0);
++ else
++ bReturn = true;
++ }
++ else if (!bOnOff && channel.IsRecording())
++ {
++ /* delete active timers */
++ bReturn = m_timers->DeleteTimersOnChannel(channel, false, true);
++ }
++ }
++
++ return bReturn;
++}
++
++void CPVRManager::SaveCurrentChannelSettings(void)
++{
++ m_addons->SaveCurrentChannelSettings();
++}
++
++void CPVRManager::LoadCurrentChannelSettings()
++{
++ m_addons->LoadCurrentChannelSettings();
++}
++
++void CPVRManager::SetPlayingGroup(CPVRChannelGroup *group)
++{
++ m_channelGroups->Get(group->IsRadio())->SetSelectedGroup(group);
++}
++
++CPVRChannelGroup *CPVRManager::GetPlayingGroup(bool bRadio /* = false */)
++{
++ return m_channelGroups->GetSelectedGroup(bRadio);
++}
++
++bool CPVRRecordingsUpdateJob::DoWork(void)
++{
++ g_PVRRecordings->Update();
++ return true;
++}
++
++bool CPVRTimersUpdateJob::DoWork(void)
++{
++ return g_PVRTimers->Update();
++}
++
++bool CPVRChannelsUpdateJob::DoWork(void)
++{
++ return g_PVRChannelGroups->Update(true);
++}
++
++bool CPVRChannelGroupsUpdateJob::DoWork(void)
++{
++ return g_PVRChannelGroups->Update(false);
++}
++
++bool CPVRChannelSettingsSaveJob::DoWork(void)
++{
++ g_PVRManager.SaveCurrentChannelSettings();
++ return true;
++}
++
++bool CPVRManager::OpenLiveStream(const CPVRChannel &tag)
++{
++ bool bReturn(false);
++ CLog::Log(LOGDEBUG,"PVRManager - %s - opening live stream on channel '%s'",
++ __FUNCTION__, tag.ChannelName().c_str());
++
++ if ((bReturn = m_addons->OpenLiveStream(tag)) != false)
++ {
++ CSingleLock lock(m_critSection);
++ if(m_currentFile)
++ delete m_currentFile;
++ m_currentFile = new CFileItem(tag);
++ }
++
++ return bReturn;
++}
++
++bool CPVRManager::OpenRecordedStream(const CPVRRecording &tag)
++{
++ bool bReturn = false;
++ CSingleLock lock(m_critSection);
++
++ CLog::Log(LOGDEBUG,"PVRManager - %s - opening recorded stream '%s'",
++ __FUNCTION__, tag.m_strFile.c_str());
++
++ if ((bReturn = m_addons->OpenRecordedStream(tag)) != false)
++ {
++ delete m_currentFile;
++ m_currentFile = new CFileItem(tag);
++ }
++
++ return bReturn;
++}
++
++void CPVRManager::CloseStream(void)
++{
++ CSingleLock lock(m_critSection);
++
++ if (m_addons->IsReadingLiveStream())
++ {
++ CPVRChannel channel;
++ if (m_addons->GetPlayingChannel(channel))
++ {
++ /* store current time in iLastWatched */
++ time_t tNow;
++ CDateTime::GetCurrentDateTime().GetAsTime(tNow);
++ channel.SetLastWatched(tNow, true);
++ }
++ }
++
++ m_addons->CloseStream();
++ if (m_currentFile)
++ {
++ delete m_currentFile;
++ m_currentFile = NULL;
++ }
++}
++
++void CPVRManager::UpdateCurrentFile(void)
++{
++ CSingleLock lock(m_critSection);
++ if (m_currentFile)
++ UpdateItem(*m_currentFile);
++}
++
++bool CPVRManager::UpdateItem(CFileItem& item)
++{
++ /* Don't update if a recording is played */
++ if (item.IsPVRRecording())
++ return false;
++
++ if (!item.IsPVRChannel())
++ {
++ CLog::Log(LOGERROR, "CPVRManager - %s - no channel tag provided", __FUNCTION__);
++ return false;
++ }
++
++ CSingleLock lock(m_critSection);
++ if (!m_currentFile || *m_currentFile->GetPVRChannelInfoTag() == *item.GetPVRChannelInfoTag())
++ return false;
++
++ g_application.CurrentFileItem() = *m_currentFile;
++ g_infoManager.SetCurrentItem(*m_currentFile);
++
++ CPVRChannel* channelTag = item.GetPVRChannelInfoTag();
++ CEpgInfoTag epgTagNow;
++ bool bHasTagNow = channelTag->GetEPGNow(epgTagNow);
++
++ if (channelTag->IsRadio())
++ {
++ CMusicInfoTag* musictag = item.GetMusicInfoTag();
++ if (musictag)
++ {
++ musictag->SetTitle(bHasTagNow ? epgTagNow.Title() : g_localizeStrings.Get(19055));
++ musictag->SetGenre(bHasTagNow ? epgTagNow.Genre() : StringUtils::EmptyString);
++ musictag->SetDuration(bHasTagNow ? epgTagNow.GetDuration() : 3600);
++ musictag->SetURL(channelTag->Path());
++ musictag->SetArtist(channelTag->ChannelName());
++ musictag->SetAlbumArtist(channelTag->ChannelName());
++ musictag->SetLoaded(true);
++ musictag->SetComment(StringUtils::EmptyString);
++ musictag->SetLyrics(StringUtils::EmptyString);
++ }
++ }
++ else
++ {
++ CVideoInfoTag *videotag = item.GetVideoInfoTag();
++ if (videotag)
++ {
++ videotag->m_strTitle = bHasTagNow ? epgTagNow.Title() : g_localizeStrings.Get(19055);
++ videotag->m_strGenre = bHasTagNow ? epgTagNow.Genre() : StringUtils::EmptyString;
++ videotag->m_strPath = channelTag->Path();
++ videotag->m_strFileNameAndPath = channelTag->Path();
++ videotag->m_strPlot = bHasTagNow ? epgTagNow.Plot() : StringUtils::EmptyString;
++ videotag->m_strPlotOutline = bHasTagNow ? epgTagNow.PlotOutline() : StringUtils::EmptyString;
++ videotag->m_iEpisode = bHasTagNow ? epgTagNow.EpisodeNum() : 0;
++ }
++ }
++
++ CPVRChannel* tagPrev = item.GetPVRChannelInfoTag();
++ if (tagPrev && tagPrev->ChannelNumber() != m_LastChannel)
++ {
++ m_LastChannel = tagPrev->ChannelNumber();
++ m_LastChannelChanged = XbmcThreads::SystemClockMillis();
++ }
++ if (XbmcThreads::SystemClockMillis() - m_LastChannelChanged >= (unsigned int) g_guiSettings.GetInt("pvrplayback.channelentrytimeout") && m_LastChannel != m_PreviousChannel[m_PreviousChannelIndex])
++ m_PreviousChannel[m_PreviousChannelIndex ^= 1] = m_LastChannel;
++ else
++ m_LastChannelChanged = XbmcThreads::SystemClockMillis();
++
++ return false;
++}
++
++bool CPVRManager::StartPlayback(const CPVRChannel *channel, bool bPreview /* = false */)
++{
++ g_settings.m_bStartVideoWindowed = bPreview;
++ g_application.getApplicationMessenger().MediaPlay(CFileItem(*channel));
++ CLog::Log(LOGNOTICE, "PVRManager - %s - started playback on channel '%s'",
++ __FUNCTION__, channel->ChannelName().c_str());
++ return true;
++}
++
++bool CPVRManager::PerformChannelSwitch(const CPVRChannel &channel, bool bPreview)
++{
++ bool bSwitched(false);
++
++ CSingleLock lock(m_critSection);
++ if (m_bIsSwitchingChannels)
++ {
++ CLog::Log(LOGDEBUG, "PVRManager - %s - can't switch to channel '%s'. waiting for the previous switch to complete",
++ __FUNCTION__, channel.ChannelName().c_str());
++ return bSwitched;
++ }
++ m_bIsSwitchingChannels = true;
++
++ CLog::Log(LOGDEBUG, "PVRManager - %s - switching to channel '%s'",
++ __FUNCTION__, channel.ChannelName().c_str());
++
++ /* make sure that channel settings are persisted */
++ if (!bPreview)
++ SaveCurrentChannelSettings();
++
++ if (m_currentFile)
++ {
++ delete m_currentFile;
++ m_currentFile = NULL;
++ }
++
++ lock.Leave();
++
++ if (!bPreview && (channel.ClientID() < 0 || !m_addons->SwitchChannel(channel)))
++ {
++ lock.Enter();
++ m_bIsSwitchingChannels = false;
++ lock.Leave();
++
++ CLog::Log(LOGERROR, "PVRManager - %s - failed to switch to channel '%s'",
++ __FUNCTION__, channel.ChannelName().c_str());
++ }
++ else
++ {
++ bSwitched = true;
++
++ lock.Enter();
++ m_currentFile = new CFileItem(channel);
++
++ if (!bPreview)
++ CLog::Log(LOGNOTICE, "PVRManager - %s - switched to channel '%s'",
++ __FUNCTION__, channel.ChannelName().c_str());
++
++ m_bIsSwitchingChannels = false;
++ }
++
++ if (!bSwitched)
++ {
++ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error,
++ g_localizeStrings.Get(19166),
++ g_localizeStrings.Get(19035));
++ }
++
++ return bSwitched;
++}
++
++int CPVRManager::GetTotalTime(void) const
++{
++ return IsStarted() && m_guiInfo ? m_guiInfo->GetDuration() : 0;
++}
++
++int CPVRManager::GetStartTime(void) const
++{
++ return IsStarted() && m_guiInfo ? m_guiInfo->GetStartTime() : 0;
++}
++
++bool CPVRManager::TranslateBoolInfo(DWORD dwInfo) const
++{
++ return IsStarted() && m_guiInfo ? m_guiInfo->TranslateBoolInfo(dwInfo) : false;
++}
++
++bool CPVRManager::TranslateCharInfo(DWORD dwInfo, CStdString &strValue) const
++{
++ return IsStarted() && m_guiInfo ? m_guiInfo->TranslateCharInfo(dwInfo, strValue) : false;
++}
++
++int CPVRManager::TranslateIntInfo(DWORD dwInfo) const
++{
++ return IsStarted() && m_guiInfo ? m_guiInfo->TranslateIntInfo(dwInfo) : 0;
++}
++
++bool CPVRManager::HasTimers(void) const
++{
++ return IsStarted() && m_timers ? m_timers->GetNumTimers() > 0 : false;
++}
++
++bool CPVRManager::IsRecording(void) const
++{
++ return IsStarted() && m_timers ? m_timers->GetNumActiveRecordings() > 0 : false;
++}
++
++bool CPVRManager::IsIdle(void) const
++{
++ if (!IsStarted())
++ return true;
++
++ if (IsRecording() || IsPlaying()) // pvr recording or playing?
++ {
++ return false;
++ }
++ else if (m_timers) // has active timers, etc.?
++ {
++ const CDateTime now = CDateTime::GetUTCDateTime();
++ const CDateTimeSpan idle(0, 0, g_guiSettings.GetInt("pvrpowermanagement.backendidletime"), 0);
++
++ const CDateTime next = m_timers->GetNextEventTime();
++ const CDateTimeSpan delta = next - now;
++
++ if (delta < idle)
++ {
++ return false;
++ }
++ }
++
++ return true;
++}
++
++void CPVRManager::ShowPlayerInfo(int iTimeout)
++{
++ if (IsStarted() && m_guiInfo)
++ m_guiInfo->ShowPlayerInfo(iTimeout);
++}
++
++void CPVRManager::LocalizationChanged(void)
++{
++ CSingleLock lock(m_critSection);
++ if (IsStarted())
++ {
++ m_channelGroups->GetGroupAllRadio()->CheckGroupName();
++ m_channelGroups->GetGroupAllTV()->CheckGroupName();
++ }
++}
++
++bool CPVRManager::IsInitialising(void) const
++{
++ return GetState() == ManagerStateStarting;
++}
++
++bool CPVRManager::IsStarted(void) const
++{
++ return GetState() == ManagerStateStarted;
++}
++
++bool CPVRManager::IsPlayingTV(void) const
++{
++ return IsStarted() && m_addons && m_addons->IsPlayingTV();
++}
++
++bool CPVRManager::IsPlayingRadio(void) const
++{
++ return IsStarted() && m_addons && m_addons->IsPlayingRadio();
++}
++
++bool CPVRManager::IsPlayingRecording(void) const
++{
++ return IsStarted() && m_addons && m_addons->IsPlayingRecording();
++}
++
++bool CPVRManager::IsRunningChannelScan(void) const
++{
++ return IsStarted() && m_addons && m_addons->IsRunningChannelScan();
++}
++
++PVR_ADDON_CAPABILITIES CPVRManager::GetCurrentAddonCapabilities(void)
++{
++ PVR_ADDON_CAPABILITIES props;
++ memset(&props, 0, sizeof(PVR_ADDON_CAPABILITIES));
++
++ if (IsStarted() && m_addons)
++ props = m_addons->GetCurrentAddonCapabilities();
++
++ return props;
++}
++
++void CPVRManager::StartChannelScan(void)
++{
++ if (IsStarted() && m_addons)
++ m_addons->StartChannelScan();
++}
++
++void CPVRManager::SearchMissingChannelIcons(void)
++{
++ if (IsStarted() && m_channelGroups)
++ m_channelGroups->SearchMissingChannelIcons();
++}
++
++bool CPVRManager::IsJobPending(const char *strJobName) const
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSectionTriggers);
++ for (unsigned int iJobPtr = 0; IsStarted() && iJobPtr < m_pendingUpdates.size(); iJobPtr++)
++ {
++ if (!strcmp(m_pendingUpdates.at(iJobPtr)->GetType(), strJobName))
++ {
++ bReturn = true;
++ break;
++ }
++ }
++
++ return bReturn;
++}
++
++void CPVRManager::TriggerRecordingsUpdate(void)
++{
++ CSingleLock lock(m_critSectionTriggers);
++ if (!IsStarted() || IsJobPending("pvr-update-recordings"))
++ return;
++
++ m_pendingUpdates.push_back(new CPVRRecordingsUpdateJob());
++
++ lock.Leave();
++ m_triggerEvent.Set();
++}
++
++void CPVRManager::TriggerTimersUpdate(void)
++{
++ CSingleLock lock(m_critSectionTriggers);
++ if (!IsStarted() || IsJobPending("pvr-update-timers"))
++ return;
++
++ m_pendingUpdates.push_back(new CPVRTimersUpdateJob());
++
++ lock.Leave();
++ m_triggerEvent.Set();
++}
++
++void CPVRManager::TriggerChannelsUpdate(void)
++{
++ CSingleLock lock(m_critSectionTriggers);
++ if (!IsStarted() || IsJobPending("pvr-update-channels"))
++ return;
++
++ m_pendingUpdates.push_back(new CPVRChannelsUpdateJob());
++
++ lock.Leave();
++ m_triggerEvent.Set();
++}
++
++void CPVRManager::TriggerChannelGroupsUpdate(void)
++{
++ CSingleLock lock(m_critSectionTriggers);
++ if (!IsStarted() || IsJobPending("pvr-update-channelgroups"))
++ return;
++
++ m_pendingUpdates.push_back(new CPVRChannelGroupsUpdateJob());
++
++ lock.Leave();
++ m_triggerEvent.Set();
++}
++
++void CPVRManager::TriggerSaveChannelSettings(void)
++{
++ CSingleLock lock(m_critSectionTriggers);
++ if (!IsStarted() || IsJobPending("pvr-save-channelsettings"))
++ return;
++
++ m_pendingUpdates.push_back(new CPVRChannelSettingsSaveJob());
++
++ lock.Leave();
++ m_triggerEvent.Set();
++}
++
++void CPVRManager::ExecutePendingJobs(void)
++{
++ CSingleLock lock(m_critSectionTriggers);
++
++ while (m_pendingUpdates.size() > 0)
++ {
++ CJob *job = m_pendingUpdates.at(0);
++ m_pendingUpdates.erase(m_pendingUpdates.begin());
++ lock.Leave();
++
++ job->DoWork();
++ delete job;
++
++ lock.Enter();
++ }
++
++ m_triggerEvent.Reset();
++}
+diff --git a/xbmc/pvr/PVRManager.h b/xbmc/pvr/PVRManager.h
+new file mode 100644
+index 0000000..2e28f20
+--- /dev/null
++++ b/xbmc/pvr/PVRManager.h
+@@ -0,0 +1,595 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "threads/Thread.h"
++#include "utils/Observer.h"
++#include "utils/JobManager.h"
++#include "threads/Event.h"
++#include "windows/GUIWindowPVRCommon.h"
++#include "addons/include/xbmc_pvr_types.h"
++
++class CGUIDialogExtendedProgressBar;
++
++namespace EPG
++{
++ class CEpgContainer;
++}
++
++namespace PVR
++{
++ class CPVRClients;
++ class CPVRChannelGroupsContainer;
++ class CPVRChannelGroup;
++ class CPVRRecordings;
++ class CPVRTimers;
++ class CPVRGUIInfo;
++ class CPVRDatabase;
++
++ enum ManagerState
++ {
++ ManagerStateError = 0,
++ ManagerStateStopped,
++ ManagerStateStarting,
++ ManagerStateStopping,
++ ManagerStateInterrupted,
++ ManagerStateStarted
++ };
++
++ #define g_PVRManager CPVRManager::Get()
++ #define g_PVRChannelGroups g_PVRManager.ChannelGroups()
++ #define g_PVRTimers g_PVRManager.Timers()
++ #define g_PVRRecordings g_PVRManager.Recordings()
++ #define g_PVRClients g_PVRManager.Clients()
++
++ class CPVRManager : private CThread
++ {
++ friend class CPVRClients;
++
++ private:
++ /*!
++ * @brief Create a new CPVRManager instance, which handles all PVR related operations in XBMC.
++ */
++ CPVRManager(void);
++
++ public:
++ /*!
++ * @brief Stop the PVRManager and destroy all objects it created.
++ */
++ virtual ~CPVRManager(void);
++
++ /*!
++ * @brief Get the instance of the PVRManager.
++ * @return The PVRManager instance.
++ */
++ static CPVRManager &Get(void);
++
++ /*!
++ * @brief Get the channel groups container.
++ * @return The groups container.
++ */
++ CPVRChannelGroupsContainer *ChannelGroups(void) const { return m_channelGroups; }
++
++ /*!
++ * @brief Get the recordings container.
++ * @return The recordings container.
++ */
++ CPVRRecordings *Recordings(void) const { return m_recordings; }
++
++ /*!
++ * @brief Get the timers container.
++ * @return The timers container.
++ */
++ CPVRTimers *Timers(void) const { return m_timers; }
++
++ /*!
++ * @brief Get the timers container.
++ * @return The timers container.
++ */
++ CPVRClients *Clients(void) const { return m_addons; }
++
++ /*!
++ * @brief Start the PVRManager, which loads all PVR data and starts some threads to update the PVR data.
++ */
++ void Start(void);
++
++ /*!
++ * @brief Stop the PVRManager and destroy all objects it created.
++ */
++ void Stop(void);
++
++ /*!
++ * @brief Delete PVRManager's objects.
++ */
++ void Cleanup(void);
++
++ public:
++
++ /*!
++ * @brief Get the TV database.
++ * @return The TV database.
++ */
++ CPVRDatabase *GetTVDatabase(void) const { return m_database; }
++
++ /*!
++ * @brief Updates the recordings and the "now" and "next" timers.
++ */
++ void UpdateRecordingsCache(void);
++
++ /*!
++ * @brief Get a GUIInfoManager character string.
++ * @param dwInfo The string to get.
++ * @return The requested string or an empty one if it wasn't found.
++ */
++ bool TranslateCharInfo(DWORD dwInfo, CStdString &strValue) const;
++
++ /*!
++ * @brief Get a GUIInfoManager integer.
++ * @param dwInfo The integer to get.
++ * @return The requested integer or 0 if it wasn't found.
++ */
++ int TranslateIntInfo(DWORD dwInfo) const;
++
++ /*!
++ * @brief Get a GUIInfoManager boolean.
++ * @param dwInfo The boolean to get.
++ * @return The requested boolean or false if it wasn't found.
++ */
++ bool TranslateBoolInfo(DWORD dwInfo) const;
++
++ /*!
++ * @brief Show the player info.
++ * @param iTimeout Hide the player info after iTimeout seconds.
++ * @todo not really the right place for this :-)
++ */
++ void ShowPlayerInfo(int iTimeout);
++
++ /*!
++ * @brief Reset the TV database to it's initial state and delete all the data inside.
++ * @param bShowProgress True to show a progress bar, false otherwise.
++ */
++ void ResetDatabase(bool bShowProgress = true);
++
++ /*!
++ * @brief Delete all EPG data from the database and reload it from the clients.
++ */
++ void ResetEPG(void);
++
++ /*!
++ * @brief Check if a TV channel, radio channel or recording is playing.
++ * @return True if it's playing, false otherwise.
++ */
++ bool IsPlaying(void) const;
++
++ /*!
++ * @return True while the PVRManager is initialising.
++ */
++ bool IsInitialising(void) const;
++
++ /*!
++ * @brief Return the channel that is currently playing.
++ * @param channel The channel or NULL if none is playing.
++ * @return True if a channel is playing, false otherwise.
++ */
++ bool GetCurrentChannel(CPVRChannel &channel) const;
++
++ /*!
++ * @brief Return the EPG for the channel that is currently playing.
++ * @param channel The EPG or NULL if no channel is playing.
++ * @return The amount of results that was added or -1 if none.
++ */
++ int GetCurrentEpg(CFileItemList &results) const;
++
++ /*!
++ * @brief Check whether the PVRManager has fully started.
++ * @return True if started, false otherwise.
++ */
++ bool IsStarted(void) const;
++
++ /*!
++ * @brief Reset the playing EPG tag.
++ */
++ void ResetPlayingTag(void);
++
++ /*!
++ * @brief Switch to the given channel.
++ * @param channel The channel to switch to.
++ * @param bPreview True to show a preview, false otherwise.
++ * @return Trrue if the switch was successful, false otherwise.
++ */
++ bool PerformChannelSwitch(const CPVRChannel &channel, bool bPreview);
++
++ /*!
++ * @brief Close an open PVR stream.
++ */
++ void CloseStream(void);
++
++ /*!
++ * @brief Open a stream from the given channel.
++ * @param tag The channel to open.
++ * @return True if the stream was opened, false otherwise.
++ */
++ bool OpenLiveStream(const CPVRChannel &tag);
++
++ /*!
++ * @brief Open a stream from the given recording.
++ * @param tag The recording to open.
++ * @return True if the stream was opened, false otherwise.
++ */
++ bool OpenRecordedStream(const CPVRRecording &tag);
++
++ /*!
++ * @brief Start recording on a given channel if it is not already recording, stop if it is.
++ * @param channel the channel to start/stop recording.
++ * @return True if the recording was started or stopped successfully, false otherwise.
++ */
++ bool ToggleRecordingOnChannel(unsigned int iChannelId);
++
++ /*!
++ * @brief Start or stop recording on the channel that is currently being played.
++ * @param bOnOff True to start recording, false to stop.
++ * @return True if the recording was started or stopped successfully, false otherwise.
++ */
++ bool StartRecordingOnPlayingChannel(bool bOnOff);
++
++ /*!
++ * @brief Get the channel number of the previously selected channel.
++ * @return The requested channel number or -1 if it wasn't found.
++ */
++ int GetPreviousChannel(void);
++
++ /*!
++ * @brief Check whether there are active timers.
++ * @return True if there are active timers, false otherwise.
++ */
++ bool HasTimers(void) const;
++
++ /*!
++ * @brief Check whether there are active recordings.
++ * @return True if there are active recordings, false otherwise.
++ */
++ bool IsRecording(void) const;
++
++ /*!
++ * @brief Check whether the pvr backend is idle.
++ * @return True if there are no active timers/recordings/wake-ups within the configured time span.
++ */
++ bool IsIdle(void) const;
++
++ /*!
++ * @brief Set the current playing group, used to load the right channel.
++ * @param group The new group.
++ */
++ void SetPlayingGroup(CPVRChannelGroup *group);
++
++ /*!
++ * @brief Get the current playing group, used to load the right channel.
++ * @param bRadio True to get the current radio group, false to get the current TV group.
++ * @return The current group or the group containing all channels if it's not set.
++ */
++ CPVRChannelGroup *GetPlayingGroup(bool bRadio = false);
++
++ /*!
++ * @brief Let the background thread update the recordings list.
++ */
++ void TriggerRecordingsUpdate(void);
++
++ /*!
++ * @brief Let the background thread update the timer list.
++ */
++ void TriggerTimersUpdate(void);
++
++ /*!
++ * @brief Let the background thread update the channel list.
++ */
++ void TriggerChannelsUpdate(void);
++
++ /*!
++ * @brief Let the background thread update the channel groups list.
++ */
++ void TriggerChannelGroupsUpdate(void);
++
++ /*!
++ * @brief Let the background thread save the current video settings.
++ */
++ void TriggerSaveChannelSettings(void);
++
++ /*!
++ * @brief Update the channel that is currently active.
++ * @param item The new channel.
++ * @return True if it was updated correctly, false otherwise.
++ */
++ bool UpdateItem(CFileItem& item);
++
++ /*!
++ * @brief Switch to a channel given it's channel number.
++ * @param iChannelNumber The channel number to switch to.
++ * @return True if the channel was switched, false otherwise.
++ */
++ bool ChannelSwitch(unsigned int iChannelNumber);
++
++ /*!
++ * @brief Switch to the next channel in this group.
++ * @param iNewChannelNumber The new channel number after the switch.
++ * @param bPreview If true, don't do the actual switch but just update channel pointers.
++ * Used to display event info while doing "fast channel switching"
++ * @return True if the channel was switched, false otherwise.
++ */
++ bool ChannelUp(unsigned int *iNewChannelNumber, bool bPreview = false) { return ChannelUpDown(iNewChannelNumber, bPreview, true); }
++
++ /*!
++ * @brief Switch to the previous channel in this group.
++ * @param iNewChannelNumber The new channel number after the switch.
++ * @param bPreview If true, don't do the actual switch but just update channel pointers.
++ * Used to display event info while doing "fast channel switching"
++ * @return True if the channel was switched, false otherwise.
++ */
++ bool ChannelDown(unsigned int *iNewChannelNumber, bool bPreview = false) { return ChannelUpDown(iNewChannelNumber, bPreview, false); }
++
++ /*!
++ * @brief Get the total duration of the currently playing LiveTV item.
++ * @return The total duration in milliseconds or NULL if no channel is playing.
++ */
++ int GetTotalTime(void) const;
++
++ /*!
++ * @brief Get the current position in milliseconds since the start of a LiveTV item.
++ * @return The position in milliseconds or NULL if no channel is playing.
++ */
++ int GetStartTime(void) const;
++
++ /*!
++ * @brief Start playback on a channel.
++ * @param channel The channel to start to play.
++ * @param bPreview If true, open minimised.
++ * @return True if playback was started, false otherwise.
++ */
++ bool StartPlayback(const CPVRChannel *channel, bool bPreview = false);
++
++ /*!
++ * @brief Update the current playing file in the guiinfomanager and application.
++ */
++ void UpdateCurrentFile(void);
++
++ /*!
++ * @brief Check whether names are still correct after the language settings changed.
++ */
++ void LocalizationChanged(void);
++
++ /*!
++ * @brief Check if a TV channel is playing.
++ * @return True if it's playing, false otherwise.
++ */
++ bool IsPlayingTV(void) const;
++
++ /*!
++ * @brief Check if a radio channel is playing.
++ * @return True if it's playing, false otherwise.
++ */
++ bool IsPlayingRadio(void) const;
++
++ /*!
++ * @brief Check if a recording is playing.
++ * @return True if it's playing, false otherwise.
++ */
++ bool IsPlayingRecording(void) const;
++
++ /*!
++ * @return True when a channel scan is currently running, false otherwise.
++ */
++ bool IsRunningChannelScan(void) const;
++
++ /*!
++ * @brief Get the capabilities of the current playing client.
++ * @return The capabilities.
++ */
++ PVR_ADDON_CAPABILITIES GetCurrentAddonCapabilities(void);
++
++ /*!
++ * @brief Open a selection dialog and start a channel scan on the selected client.
++ */
++ void StartChannelScan(void);
++
++ /*!
++ * @brief Try to find missing channel icons automatically
++ */
++ void SearchMissingChannelIcons(void);
++
++ /*!
++ * @brief Persist the current channel settings in the database.
++ */
++ void SaveCurrentChannelSettings(void);
++
++ /*!
++ * @brief Load the settings for the current channel from the database.
++ */
++ void LoadCurrentChannelSettings(void);
++
++ protected:
++ /*!
++ * @brief PVR update and control thread.
++ */
++ virtual void Process(void);
++
++ private:
++
++
++
++ /*!
++ * @brief Load at least one client and load all other PVR data after loading the client.
++ * If some clients failed to load here, the pvrmanager will retry to load them every second.
++ * @return If at least one client and all pvr data was loaded, false otherwise.
++ */
++ bool Load(void);
++
++ /*!
++ * @brief Update all recordings.
++ */
++ void UpdateRecordings(void);
++
++ /*!
++ * @brief Update all timers.
++ */
++ void UpdateTimers(void);
++
++ /*!
++ * @brief Update all channels.
++ */
++ void UpdateChannels(void);
++
++ /*!
++ * @brief Update all channel groups and channels in them.
++ */
++ void UpdateChannelGroups(void);
++
++ /*!
++ * @brief Reset all properties.
++ */
++ void ResetProperties(void);
++
++ /*!
++ * @brief Called by ChannelUp() and ChannelDown() to perform a channel switch.
++ * @param iNewChannelNumber The new channel number after the switch.
++ * @param bPreview Preview window if true.
++ * @param bUp Go one channel up if true, one channel down if false.
++ * @return True if the switch was successful, false otherwise.
++ */
++ bool ChannelUpDown(unsigned int *iNewChannelNumber, bool bPreview, bool bUp);
++
++ /*!
++ * @brief Stop the EPG and PVR threads but do not remove their data.
++ */
++ void StopUpdateThreads(void);
++
++ /*!
++ * @brief Restart the EPG and PVR threads after they've been stopped by StopUpdateThreads()
++ */
++ bool StartUpdateThreads(void);
++
++ /*!
++ * @brief Continue playback on the last channel if it was stored in the database.
++ * @return True if playback was continued, false otherwise.
++ */
++ bool ContinueLastChannel(void);
++
++ /*!
++ * @brief Show or update the progress dialog.
++ * @param strText The current status.
++ * @param iProgress The current progress in %.
++ */
++ void ShowProgressDialog(const CStdString &strText, int iProgress);
++
++ /*!
++ * @brief Executes "pvrpowermanagement.setwakeupcmd"
++ */
++ bool SetWakeupCommand(void);
++
++ /*!
++ * @brief Hide the progress dialog if it's visible.
++ */
++ void HideProgressDialog(void);
++
++ void ExecutePendingJobs(void);
++
++ bool IsJobPending(const char *strJobName) const;
++
++ ManagerState GetState(void) const;
++
++ void SetState(ManagerState state);
++ /** @name containers */
++ //@{
++ CPVRChannelGroupsContainer * m_channelGroups; /*!< pointer to the channel groups container */
++ CPVRRecordings * m_recordings; /*!< pointer to the recordings container */
++ CPVRTimers * m_timers; /*!< pointer to the timers container */
++ CPVRClients * m_addons; /*!< pointer to the pvr addon container */
++ CPVRGUIInfo * m_guiInfo; /*!< pointer to the guiinfo data */
++ //@}
++
++ CCriticalSection m_critSectionTriggers; /*!< critical section for triggered updates */
++ CEvent m_triggerEvent; /*!< triggers an update */
++ std::vector<CJob *> m_pendingUpdates; /*!< vector of pending pvr updates */
++
++ CFileItem * m_currentFile; /*!< the PVR file that is currently playing */
++ CPVRDatabase * m_database; /*!< the database for all PVR related data */
++ CCriticalSection m_critSection; /*!< critical section for all changes to this class, except for changes to triggers */
++ bool m_bFirstStart; /*!< true when the PVR manager was started first, false otherwise */
++ bool m_bIsSwitchingChannels; /*!< true while switching channels */
++ CGUIDialogExtendedProgressBar * m_loadingProgressDialog; /*!< progress dialog that is displayed while the pvrmanager is loading */
++
++ int m_PreviousChannel[2];
++ int m_PreviousChannelIndex;
++ int m_LastChannel;
++ unsigned int m_LastChannelChanged;
++
++ CCriticalSection m_managerStateMutex;
++ ManagerState m_managerState;
++ };
++
++ class CPVRRecordingsUpdateJob : public CJob
++ {
++ public:
++ CPVRRecordingsUpdateJob(void) {}
++ virtual ~CPVRRecordingsUpdateJob() {}
++ virtual const char *GetType() const { return "pvr-update-recordings"; }
++
++ virtual bool DoWork();
++ };
++
++ class CPVRTimersUpdateJob : public CJob
++ {
++ public:
++ CPVRTimersUpdateJob(void) {}
++ virtual ~CPVRTimersUpdateJob() {}
++ virtual const char *GetType() const { return "pvr-update-timers"; }
++
++ virtual bool DoWork();
++ };
++
++ class CPVRChannelsUpdateJob : public CJob
++ {
++ public:
++ CPVRChannelsUpdateJob(void) {}
++ virtual ~CPVRChannelsUpdateJob() {}
++ virtual const char *GetType() const { return "pvr-update-channels"; }
++
++ virtual bool DoWork();
++ };
++
++ class CPVRChannelGroupsUpdateJob : public CJob
++ {
++ public:
++ CPVRChannelGroupsUpdateJob(void) {}
++ virtual ~CPVRChannelGroupsUpdateJob() {}
++ virtual const char *GetType() const { return "pvr-update-channelgroups"; }
++
++ virtual bool DoWork();
++ };
++
++ class CPVRChannelSettingsSaveJob : public CJob
++ {
++ public:
++ CPVRChannelSettingsSaveJob(void) {}
++ virtual ~CPVRChannelSettingsSaveJob() {}
++ virtual const char *GetType() const { return "pvr-save-channelsettings"; }
++
++ virtual bool DoWork();
++ };
++}
+diff --git a/xbmc/pvr/addons/Makefile b/xbmc/pvr/addons/Makefile
+new file mode 100644
+index 0000000..8bda610
+--- /dev/null
++++ b/xbmc/pvr/addons/Makefile
+@@ -0,0 +1,7 @@
++SRCS=PVRClient.cpp \
++ PVRClients.cpp
++
++LIB=pvraddons.a
++
++include ../../../Makefile.include
++-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
+diff --git a/xbmc/pvr/addons/PVRClient.cpp b/xbmc/pvr/addons/PVRClient.cpp
+new file mode 100644
+index 0000000..a7e8b45
+--- /dev/null
++++ b/xbmc/pvr/addons/PVRClient.cpp
+@@ -0,0 +1,1137 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <vector>
++#include "Application.h"
++#include "FileItem.h"
++#include "PVRClient.h"
++#include "URL.h"
++#include "guilib/LocalizeStrings.h"
++#include "pvr/PVRManager.h"
++#include "epg/Epg.h"
++#include "pvr/channels/PVRChannelGroups.h"
++#include "pvr/timers/PVRTimers.h"
++#include "pvr/timers/PVRTimerInfoTag.h"
++#include "pvr/recordings/PVRRecordings.h"
++#include "settings/AdvancedSettings.h"
++#include "utils/log.h"
++#include "utils/StringUtils.h"
++
++using namespace std;
++using namespace ADDON;
++using namespace PVR;
++using namespace EPG;
++
++CPVRClient::CPVRClient(const AddonProps& props) :
++ CAddonDll<DllPVRClient, PVRClient, PVR_PROPERTIES>(props)
++{
++ ResetProperties();
++}
++
++CPVRClient::CPVRClient(const cp_extension_t *ext) :
++ CAddonDll<DllPVRClient, PVRClient, PVR_PROPERTIES>(ext)
++{
++ ResetProperties();
++}
++
++CPVRClient::~CPVRClient(void)
++{
++ if (m_pInfo)
++ SAFE_DELETE(m_pInfo);
++}
++
++void CPVRClient::ResetProperties(void)
++{
++ CLog::Log(LOGDEBUG, "PVR - %s - creating PVR add-on instance '%s'", __FUNCTION__, Name().c_str());
++
++ /* initialise members */
++ if (!m_pInfo)
++ m_pInfo = new PVR_PROPERTIES;
++ m_pInfo->iClientId = -1;
++ m_pInfo->strClientPath = "";
++ m_pInfo->strUserPath = "";
++
++ m_bReadyToUse = false;
++ m_bGotBackendName = false;
++ m_bGotBackendVersion = false;
++ m_bGotConnectionString = false;
++ m_bGotFriendlyName = false;
++ m_bGotAddonCapabilities = false;
++ m_strBackendVersion = "unknown";
++ m_strConnectionString = "unknown";
++ m_strFriendlyName = "unknown";
++ m_strHostName = "unknown";
++ m_strBackendName = "unknown";
++ ResetAddonCapabilities();
++}
++
++void CPVRClient::ResetAddonCapabilities(void)
++{
++ m_addonCapabilities.bSupportsChannelSettings = false;
++ m_addonCapabilities.bSupportsTimeshift = false;
++ m_addonCapabilities.bSupportsEPG = false;
++ m_addonCapabilities.bSupportsTV = false;
++ m_addonCapabilities.bSupportsRadio = false;
++ m_addonCapabilities.bSupportsRecordings = false;
++ m_addonCapabilities.bSupportsTimers = false;
++ m_addonCapabilities.bSupportsChannelGroups = false;
++ m_addonCapabilities.bSupportsChannelScan = false;
++ m_addonCapabilities.bHandlesInputStream = false;
++ m_addonCapabilities.bHandlesDemuxing = false;
++ m_addonCapabilities.bSupportsRecordingFolders = false;
++}
++
++bool CPVRClient::Create(int iClientId)
++{
++ m_pInfo->iClientId = iClientId;
++ CStdString userpath = _P(Profile());
++ m_pInfo->strUserPath = userpath.c_str();
++ CStdString clientpath = _P(Path());
++ m_pInfo->strClientPath = clientpath.c_str();
++
++ /* initialise the add-on */
++ if (CAddonDll<DllPVRClient, PVRClient, PVR_PROPERTIES>::Create())
++ {
++ SetAddonCapabilities();
++ m_strHostName = m_pStruct->GetConnectionString();
++ m_bReadyToUse = true;
++ }
++ /* don't log failed inits here because it will spam the log file as this is called in a loop */
++ return m_bReadyToUse;
++}
++
++void CPVRClient::Destroy(void)
++{
++ CLog::Log(LOGDEBUG, "PVR - %s - destroying PVR add-on '%s'", __FUNCTION__, GetFriendlyName().c_str());
++ m_bReadyToUse = false;
++
++ try
++ {
++ /* Tell the client to destroy */
++ CAddonDll<DllPVRClient, PVRClient, PVR_PROPERTIES>::Destroy();
++ m_menuhooks.clear();
++ SAFE_DELETE(m_pInfo);
++ }
++ catch (exception &e)
++ {
++ CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to destroy addon '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), GetFriendlyName().c_str(), Author().c_str());
++ }
++}
++
++void CPVRClient::ReCreate(void)
++{
++ int clientID = m_pInfo->iClientId;
++ Destroy();
++ Create(clientID);
++}
++
++bool CPVRClient::ReadyToUse(void) const
++{
++ return m_bReadyToUse;
++}
++
++int CPVRClient::GetID(void) const
++{
++ return m_pInfo->iClientId;
++}
++
++/*!
++ * @brief Copy over group info from xbmcGroup to addonGroup.
++ * @param xbmcGroup The group on XBMC's side.
++ * @param addonGroup The group on the addon's side.
++ */
++inline void PVRWriteClientGroupInfo(const CPVRChannelGroup &xbmcGroup, PVR_CHANNEL_GROUP &addonGroup)
++{
++ addonGroup.bIsRadio = xbmcGroup.IsRadio();
++ addonGroup.strGroupName = xbmcGroup.GroupName();
++}
++
++/*!
++ * @brief Copy over recording info from xbmcRecording to addonRecording.
++ * @param xbmcRecording The recording on XBMC's side.
++ * @param addonRecording The recording on the addon's side.
++ */
++inline void PVRWriteClientRecordingInfo(const CPVRRecording &xbmcRecording, PVR_RECORDING &addonRecording)
++{
++ time_t recTime;
++ xbmcRecording.RecordingTimeAsUTC().GetAsTime(recTime);
++
++ addonRecording.recordingTime = recTime - g_advancedSettings.m_iPVRTimeCorrection;
++ addonRecording.strRecordingId = xbmcRecording.m_strRecordingId.c_str();
++ addonRecording.strTitle = xbmcRecording.m_strTitle.c_str();
++ addonRecording.strPlotOutline = xbmcRecording.m_strPlotOutline.c_str();
++ addonRecording.strPlot = xbmcRecording.m_strPlot.c_str();
++ addonRecording.strChannelName = xbmcRecording.m_strChannelName.c_str();
++ addonRecording.iDuration = xbmcRecording.GetDuration();
++ addonRecording.iPriority = xbmcRecording.m_iPriority;
++ addonRecording.iLifetime = xbmcRecording.m_iLifetime;
++ addonRecording.strDirectory = xbmcRecording.m_strDirectory.c_str();
++ addonRecording.strStreamURL = xbmcRecording.m_strStreamURL.c_str();
++ addonRecording.strIconPath = xbmcRecording.m_defualt_icon.c_str();
++
++}
++
++/*!
++ * @brief Copy over timer info from xbmcTimer to addonTimer.
++ * @param xbmcTimer The timer on XBMC's side.
++ * @param addonTimer The timer on the addon's side.
++ */
++inline void PVRWriteClientTimerInfo(const CPVRTimerInfoTag &xbmcTimer, PVR_TIMER &addonTimer)
++{
++ time_t start, end, firstDay;
++ xbmcTimer.StartAsUTC().GetAsTime(start);
++ xbmcTimer.EndAsUTC().GetAsTime(end);
++ xbmcTimer.FirstDayAsUTC().GetAsTime(firstDay);
++ CEpgInfoTag *epgTag = xbmcTimer.GetEpgInfoTag();
++
++ addonTimer.iClientIndex = xbmcTimer.m_iClientIndex;
++ addonTimer.state = xbmcTimer.m_state;
++ addonTimer.iClientIndex = xbmcTimer.m_iClientIndex;
++ addonTimer.iClientChannelUid = xbmcTimer.m_iClientChannelUid;
++ addonTimer.strTitle = xbmcTimer.m_strTitle;
++ addonTimer.strDirectory = xbmcTimer.m_strDirectory;
++ addonTimer.iPriority = xbmcTimer.m_iPriority;
++ addonTimer.iLifetime = xbmcTimer.m_iLifetime;
++ addonTimer.bIsRepeating = xbmcTimer.m_bIsRepeating;
++ addonTimer.iWeekdays = xbmcTimer.m_iWeekdays;
++ addonTimer.startTime = start - g_advancedSettings.m_iPVRTimeCorrection;
++ addonTimer.endTime = end - g_advancedSettings.m_iPVRTimeCorrection;
++ addonTimer.firstDay = firstDay - g_advancedSettings.m_iPVRTimeCorrection;
++ addonTimer.iEpgUid = epgTag ? epgTag->UniqueBroadcastID() : -1;
++ addonTimer.strSummary = xbmcTimer.m_strSummary.c_str();
++ addonTimer.iMarginStart = xbmcTimer.m_iMarginStart;
++ addonTimer.iMarginEnd = xbmcTimer.m_iMarginEnd;
++ addonTimer.iGenreType = xbmcTimer.m_iGenreType;
++ addonTimer.iGenreSubType = xbmcTimer.m_iGenreSubType;
++}
++
++/*!
++ * @brief Copy over channel info from xbmcChannel to addonClient.
++ * @param xbmcChannel The channel on XBMC's side.
++ * @param addonChannel The channel on the addon's side.
++ */
++inline void PVRWriteClientChannelInfo(const CPVRChannel &xbmcChannel, PVR_CHANNEL &addonChannel)
++{
++ addonChannel.iUniqueId = xbmcChannel.UniqueID();
++ addonChannel.iChannelNumber = xbmcChannel.ClientChannelNumber();
++ addonChannel.strChannelName = xbmcChannel.ClientChannelName().c_str();
++ addonChannel.strIconPath = xbmcChannel.IconPath().c_str();
++ addonChannel.iEncryptionSystem = xbmcChannel.EncryptionSystem();
++ addonChannel.bIsRadio = xbmcChannel.IsRadio();
++ addonChannel.bIsHidden = xbmcChannel.IsHidden();
++ addonChannel.strInputFormat = xbmcChannel.InputFormat().c_str();
++ addonChannel.strStreamURL = xbmcChannel.StreamURL().c_str();
++}
++
++PVR_ADDON_CAPABILITIES CPVRClient::GetAddonCapabilities(void) const
++{
++ return m_addonCapabilities;
++}
++
++CStdString CPVRClient::GetBackendName(void)
++{
++ /* cached locally */
++ SetBackendName();
++
++ CStdString strReturn;
++ strReturn = m_strBackendName;
++ return strReturn;
++}
++
++CStdString CPVRClient::GetBackendVersion(void)
++{
++ /* cached locally */
++ SetBackendVersion();
++
++ CStdString strReturn;
++ strReturn = m_strBackendVersion;
++ return strReturn;
++}
++
++CStdString CPVRClient::GetConnectionString(void)
++{
++ /* cached locally */
++ SetConnectionString();
++
++ CStdString strReturn;
++ strReturn = m_strConnectionString;
++ return strReturn;
++}
++
++CStdString CPVRClient::GetFriendlyName(void)
++{
++ /* cached locally */
++ SetFriendlyName();
++
++ CStdString strReturn;
++ strReturn = m_strFriendlyName;
++ return strReturn;
++}
++
++PVR_ERROR CPVRClient::GetDriveSpace(long long *iTotal, long long *iUsed)
++{
++ if (!m_bReadyToUse)
++ return PVR_ERROR_UNKNOWN;
++
++ try
++ {
++ return m_pStruct->GetDriveSpace(iTotal, iUsed);
++ }
++ catch (exception &e)
++ {
++ CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to call GetDriveSpace() on addon '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), GetFriendlyName().c_str(), Author().c_str());
++ }
++
++ /* default to 0 on error */
++ *iTotal = 0;
++ *iUsed = 0;
++
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++//PVR_ERROR CPVRClient::GetBackendTime(time_t *localTime, int *iGmtOffset)
++//{
++// if (!m_bReadyToUse)
++// return PVR_ERROR_UNKNOWN;
++//
++// try
++// {
++// return m_pStruct->GetBackendTime(localTime, iGmtOffset);
++// }
++// catch (exception &e)
++// {
++// CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to call GetBackendTime() on addon '%s'. please contact the developer of this addon: %s",
++// __FUNCTION__, e.what(), GetFriendlyName(), Author().c_str());
++// }
++//
++// /* default to 0 on error */
++// *localTime = 0;
++// *iGmtOffset = 0;
++//
++// return PVR_ERROR_NOT_IMPLEMENTED;
++//}
++
++PVR_ERROR CPVRClient::StartChannelScan(void)
++{
++ if (!m_bReadyToUse)
++ return PVR_ERROR_UNKNOWN;
++
++ if (!m_addonCapabilities.bSupportsChannelScan)
++ return PVR_ERROR_NOT_IMPLEMENTED;
++
++ try
++ {
++ return m_pStruct->DialogChannelScan();
++ }
++ catch (exception &e)
++ {
++ CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to call StartChannelScan() on addon '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), GetFriendlyName().c_str(), Author().c_str());
++ }
++
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++void CPVRClient::CallMenuHook(const PVR_MENUHOOK &hook)
++{
++ if (!m_bReadyToUse)
++ return;
++
++ try
++ {
++ m_pStruct->MenuHook(hook);
++ }
++ catch (exception &e)
++ {
++ CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to call CallMenuHook() on addon '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), GetFriendlyName().c_str(), Author().c_str());
++ }
++}
++
++PVR_ERROR CPVRClient::GetEPGForChannel(const CPVRChannel &channel, CEpg *epg, time_t start /* = 0 */, time_t end /* = 0 */, bool bSaveInDb /* = false*/)
++{
++ PVR_ERROR retVal = PVR_ERROR_UNKNOWN;
++ if (!m_bReadyToUse)
++ return retVal;
++
++ if (!m_addonCapabilities.bSupportsEPG)
++ return PVR_ERROR_NOT_IMPLEMENTED;
++
++ try
++ {
++ PVR_CHANNEL addonChannel;
++ PVRWriteClientChannelInfo(channel, addonChannel);
++
++ PVR_HANDLE_STRUCT handle;
++ handle.callerAddress = this;
++ handle.dataAddress = (CEpg*) epg;
++ handle.dataIdentifier = bSaveInDb ? 1 : 0; // used by the callback method CAddonCallbacksPVR::PVRTransferEpgEntry()
++ retVal = m_pStruct->GetEpg(&handle,
++ addonChannel,
++ start ? start - g_advancedSettings.m_iPVRTimeCorrection : 0,
++ end ? end - g_advancedSettings.m_iPVRTimeCorrection : 0);
++
++ LogError(retVal, __FUNCTION__);
++ }
++ catch (exception &e)
++ {
++ CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to call GetEPGForChannel() on addon '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), GetFriendlyName().c_str(), Author().c_str());
++ }
++
++ return retVal;
++}
++
++int CPVRClient::GetChannelGroupsAmount(void)
++{
++ int iReturn = -1;
++ if (!m_bReadyToUse)
++ return iReturn;
++
++ if (!m_addonCapabilities.bSupportsChannelGroups)
++ return iReturn;
++
++ try
++ {
++ iReturn = m_pStruct->GetChannelGroupsAmount();
++ }
++ catch (exception &e)
++ {
++ CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to call GetChannelGroupsAmount() on addon '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), GetFriendlyName().c_str(), Author().c_str());
++ }
++
++ return iReturn;
++}
++
++PVR_ERROR CPVRClient::GetChannelGroups(CPVRChannelGroups *groups)
++{
++ PVR_ERROR retVal = PVR_ERROR_UNKNOWN;
++ if (!m_bReadyToUse)
++ return retVal;
++
++ if (!m_addonCapabilities.bSupportsChannelGroups)
++ return PVR_ERROR_NOT_IMPLEMENTED;
++
++ try
++ {
++ PVR_HANDLE_STRUCT handle;
++ handle.callerAddress = this;
++ handle.dataAddress = groups;
++ retVal = m_pStruct->GetChannelGroups(&handle, groups->IsRadio());
++
++ LogError(retVal, __FUNCTION__);
++ }
++ catch (exception &e)
++ {
++ CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to call GetChannelGroups() on addon '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), GetFriendlyName().c_str(), Author().c_str());
++ }
++
++ return retVal;
++}
++
++PVR_ERROR CPVRClient::GetChannelGroupMembers(CPVRChannelGroup *group)
++{
++ PVR_ERROR retVal = PVR_ERROR_UNKNOWN;
++ if (!m_bReadyToUse)
++ return retVal;
++
++ if (!m_addonCapabilities.bSupportsChannelGroups)
++ return PVR_ERROR_NOT_IMPLEMENTED;
++
++ try
++ {
++ PVR_HANDLE_STRUCT handle;
++ handle.callerAddress = this;
++ handle.dataAddress = group;
++
++ PVR_CHANNEL_GROUP tag;
++ PVRWriteClientGroupInfo(*group, tag);
++
++ CLog::Log(LOGDEBUG, "PVRClient - %s - get group members for group '%s' from add-on '%s'",
++ __FUNCTION__, tag.strGroupName, GetFriendlyName().c_str());
++ retVal = m_pStruct->GetChannelGroupMembers(&handle, tag);
++
++ LogError(retVal, __FUNCTION__);
++ }
++ catch (exception &e)
++ {
++ CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to call GetChannelGroupMembers() on addon '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), GetFriendlyName().c_str(), Author().c_str());
++ }
++
++ return retVal;
++}
++
++int CPVRClient::GetChannelsAmount(void)
++{
++ int iReturn = -1;
++ if (!m_bReadyToUse)
++ return iReturn;
++
++ try
++ {
++ iReturn = m_pStruct->GetChannelsAmount();
++ }
++ catch (exception &e)
++ {
++ CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to call GetChannelsAmount() on addon '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), GetFriendlyName().c_str(), Author().c_str());
++ }
++
++ return iReturn;
++}
++
++PVR_ERROR CPVRClient::GetChannels(CPVRChannelGroup &channels, bool radio)
++{
++ PVR_ERROR retVal = PVR_ERROR_UNKNOWN;
++ if (!m_bReadyToUse)
++ return retVal;
++
++ if ((!m_addonCapabilities.bSupportsRadio && radio) ||
++ (!m_addonCapabilities.bSupportsTV && !radio))
++ return PVR_ERROR_NOT_IMPLEMENTED;
++
++ try
++ {
++ PVR_HANDLE_STRUCT handle;
++ handle.callerAddress = this;
++ handle.dataAddress = (CPVRChannelGroup*) &channels;
++ retVal = m_pStruct->GetChannels(&handle, radio);
++
++ LogError(retVal, __FUNCTION__);
++ }
++ catch (exception &e)
++ {
++ CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to call GetChannels() on addon '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), GetFriendlyName().c_str(), Author().c_str());
++ }
++
++ return retVal;
++}
++
++int CPVRClient::GetRecordingsAmount(void)
++{
++ int iReturn = -1;
++ if (!m_bReadyToUse)
++ return iReturn;
++
++ if (!m_addonCapabilities.bSupportsRecordings)
++ return iReturn;
++
++ try
++ {
++ iReturn = m_pStruct->GetRecordingsAmount();
++ }
++ catch (exception &e)
++ {
++ CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to call GetRecordingsAmount() on addon '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), GetFriendlyName().c_str(), Author().c_str());
++ }
++
++ return iReturn;
++}
++
++PVR_ERROR CPVRClient::GetRecordings(CPVRRecordings *results)
++{
++ PVR_ERROR retVal = PVR_ERROR_UNKNOWN;
++ if (!m_bReadyToUse)
++ return retVal;
++
++ if (!m_addonCapabilities.bSupportsRecordings)
++ return PVR_ERROR_NOT_IMPLEMENTED;
++
++ try
++ {
++ PVR_HANDLE_STRUCT handle;
++ handle.callerAddress = this;
++ handle.dataAddress = (CPVRRecordings*) results;
++ retVal = m_pStruct->GetRecordings(&handle);
++
++ LogError(retVal, __FUNCTION__);
++ }
++ catch (exception &e)
++ {
++ CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to call GetRecordings() on addon '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), GetFriendlyName().c_str(), Author().c_str());
++ }
++
++ return retVal;
++}
++
++PVR_ERROR CPVRClient::DeleteRecording(const CPVRRecording &recording)
++{
++ PVR_ERROR retVal = PVR_ERROR_UNKNOWN;
++ if (!m_bReadyToUse)
++ return retVal;
++
++ if (!m_addonCapabilities.bSupportsRecordings)
++ return PVR_ERROR_NOT_IMPLEMENTED;
++
++ try
++ {
++ PVR_RECORDING tag;
++ PVRWriteClientRecordingInfo(recording, tag);
++
++ retVal = m_pStruct->DeleteRecording(tag);
++
++ LogError(retVal, __FUNCTION__);
++ }
++ catch (exception &e)
++ {
++ CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to call DeleteRecording() on addon '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), GetFriendlyName().c_str(), Author().c_str());
++ }
++
++ return retVal;
++}
++
++PVR_ERROR CPVRClient::RenameRecording(const CPVRRecording &recording)
++{
++ PVR_ERROR retVal = PVR_ERROR_UNKNOWN;
++ if (!m_bReadyToUse)
++ return retVal;
++
++ if (!m_addonCapabilities.bSupportsRecordings)
++ return PVR_ERROR_NOT_IMPLEMENTED;
++
++ try
++ {
++ PVR_RECORDING tag;
++ PVRWriteClientRecordingInfo(recording, tag);
++
++ retVal = m_pStruct->RenameRecording(tag);
++
++ LogError(retVal, __FUNCTION__);
++ }
++ catch (exception &e)
++ {
++ CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to call RenameRecording() on addon '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), GetFriendlyName().c_str(), Author().c_str());
++ }
++
++ return retVal;
++}
++
++int CPVRClient::GetTimersAmount(void)
++{
++ int iReturn = -1;
++ if (!m_bReadyToUse)
++ return iReturn;
++
++ if (!m_addonCapabilities.bSupportsTimers)
++ return iReturn;
++
++ try
++ {
++ iReturn = m_pStruct->GetTimersAmount();
++ }
++ catch (exception &e)
++ {
++ CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to call GetTimersAmount() on addon '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), GetFriendlyName().c_str(), Author().c_str());
++ }
++
++ return iReturn;
++}
++
++PVR_ERROR CPVRClient::GetTimers(CPVRTimers *results)
++{
++ PVR_ERROR retVal = PVR_ERROR_UNKNOWN;
++ if (!m_bReadyToUse)
++ return retVal;
++
++ if (!m_addonCapabilities.bSupportsTimers)
++ return PVR_ERROR_NOT_IMPLEMENTED;
++
++ try
++ {
++ PVR_HANDLE_STRUCT handle;
++ handle.callerAddress = this;
++ handle.dataAddress = (CPVRTimers*) results;
++ retVal = m_pStruct->GetTimers(&handle);
++
++ LogError(retVal, __FUNCTION__);
++ }
++ catch (exception &e)
++ {
++ CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to call GetTimers() on addon '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), GetFriendlyName().c_str(), Author().c_str());
++ }
++
++ return retVal;
++}
++
++PVR_ERROR CPVRClient::AddTimer(const CPVRTimerInfoTag &timer)
++{
++ PVR_ERROR retVal = PVR_ERROR_UNKNOWN;
++ if (!m_bReadyToUse)
++ return retVal;
++
++ if (!m_addonCapabilities.bSupportsTimers)
++ return PVR_ERROR_NOT_IMPLEMENTED;
++
++ try
++ {
++ PVR_TIMER tag;
++ PVRWriteClientTimerInfo(timer, tag);
++
++ retVal = m_pStruct->AddTimer(tag);
++
++ LogError(retVal, __FUNCTION__);
++ }
++ catch (exception &e)
++ {
++ CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to call AddTimer() on addon '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), GetFriendlyName().c_str(), Author().c_str());
++ }
++
++ return retVal;
++}
++
++PVR_ERROR CPVRClient::DeleteTimer(const CPVRTimerInfoTag &timer, bool bForce /* = false */)
++{
++ PVR_ERROR retVal = PVR_ERROR_UNKNOWN;
++ if (!m_bReadyToUse)
++ return retVal;
++
++ if (!m_addonCapabilities.bSupportsTimers)
++ return PVR_ERROR_NOT_IMPLEMENTED;
++
++ try
++ {
++ PVR_TIMER tag;
++ PVRWriteClientTimerInfo(timer, tag);
++
++ retVal = m_pStruct->DeleteTimer(tag, bForce);
++
++ LogError(retVal, __FUNCTION__);
++ }
++ catch (exception &e)
++ {
++ CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to call DeleteTimer() on addon '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), GetFriendlyName().c_str(), Author().c_str());
++ }
++
++ return retVal;
++}
++
++PVR_ERROR CPVRClient::RenameTimer(const CPVRTimerInfoTag &timer, const CStdString &strNewName)
++{
++ PVR_ERROR retVal = PVR_ERROR_UNKNOWN;
++ if (!m_bReadyToUse)
++ return retVal;
++
++ if (!m_addonCapabilities.bSupportsTimers)
++ return PVR_ERROR_NOT_IMPLEMENTED;
++
++ try
++ {
++ PVR_TIMER tag;
++ PVRWriteClientTimerInfo(timer, tag);
++
++ retVal = m_pStruct->UpdateTimer(tag);
++
++ LogError(retVal, __FUNCTION__);
++ }
++ catch (exception &e)
++ {
++ CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to call RenameTimer() on addon '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), GetFriendlyName().c_str(), Author().c_str());
++ }
++
++ return retVal;
++}
++
++PVR_ERROR CPVRClient::UpdateTimer(const CPVRTimerInfoTag &timer)
++{
++ PVR_ERROR retVal = PVR_ERROR_UNKNOWN;
++ if (!m_bReadyToUse)
++ return retVal;
++
++ if (!m_addonCapabilities.bSupportsTimers)
++ return PVR_ERROR_NOT_IMPLEMENTED;
++
++ try
++ {
++ PVR_TIMER tag;
++ PVRWriteClientTimerInfo(timer, tag);
++
++ retVal = m_pStruct->UpdateTimer(tag);
++
++ LogError(retVal, __FUNCTION__);
++ }
++ catch (exception &e)
++ {
++ CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to call UpdateTimer() on addon '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), GetFriendlyName().c_str(), Author().c_str());
++ }
++
++ return retVal;
++}
++
++bool CPVRClient::OpenLiveStream(const CPVRChannel &channel)
++{
++ bool bReturn = false;
++ if (!m_bReadyToUse)
++ return bReturn;
++
++ if ((!m_addonCapabilities.bSupportsTV && !channel.IsRadio()) ||
++ (!m_addonCapabilities.bSupportsRadio && channel.IsRadio()))
++ return bReturn;
++
++ try
++ {
++ PVR_CHANNEL tag;
++ PVRWriteClientChannelInfo(channel, tag);
++ bReturn = m_pStruct->OpenLiveStream(tag);
++ }
++ catch (exception &e)
++ {
++ CLog::Log(LOGERROR,"PVRClient expection handled by function %s",__FUNCTION__);
++ CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to call OpenLiveStream() on addon '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), GetFriendlyName().c_str(), Author().c_str());
++ }
++
++ return bReturn;
++}
++
++void CPVRClient::CloseLiveStream(void)
++{
++ if (!m_bReadyToUse)
++ return;
++
++ try
++ {
++ m_pStruct->CloseLiveStream();
++ }
++ catch (exception &e)
++ {
++ CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to call CloseLiveStream() on addon '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), GetFriendlyName().c_str(), Author().c_str());
++ }
++}
++
++int CPVRClient::ReadLiveStream(void* lpBuf, int64_t uiBufSize)
++{
++ return m_pStruct->ReadLiveStream((unsigned char *)lpBuf, (int)uiBufSize);
++}
++
++int64_t CPVRClient::SeekLiveStream(int64_t iFilePosition, int iWhence/* = SEEK_SET*/)
++{
++ return m_pStruct->SeekLiveStream(iFilePosition, iWhence);
++}
++
++int64_t CPVRClient::PositionLiveStream(void)
++{
++ return m_pStruct->PositionLiveStream();
++}
++
++int64_t CPVRClient::LengthLiveStream(void)
++{
++ return m_pStruct->LengthLiveStream();
++}
++
++int CPVRClient::GetCurrentClientChannel(void)
++{
++ return m_pStruct->GetCurrentClientChannel();
++}
++
++bool CPVRClient::SwitchChannel(const CPVRChannel &channel)
++{
++ PVR_CHANNEL tag;
++ PVRWriteClientChannelInfo(channel, tag);
++ return m_pStruct->SwitchChannel(tag);
++}
++
++bool CPVRClient::SignalQuality(PVR_SIGNAL_STATUS &qualityinfo)
++{
++ bool bReturn = false;
++ if (!m_bReadyToUse)
++ return bReturn;
++
++ try
++ {
++ PVR_ERROR retVal = m_pStruct->SignalStatus(qualityinfo);
++ if (LogError(retVal, __FUNCTION__))
++ bReturn = true;
++ }
++ catch (exception &e)
++ {
++ CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to call SignalQuality() on addon '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), GetFriendlyName().c_str(), Author().c_str());
++ }
++
++ return bReturn;
++}
++
++CStdString CPVRClient::GetLiveStreamURL(const CPVRChannel &channel)
++{
++ if (!m_bReadyToUse)
++ return StringUtils::EmptyString;
++
++ CStdString strReturn;
++ try
++ {
++ PVR_CHANNEL tag;
++ PVRWriteClientChannelInfo(channel, tag);
++ strReturn = m_pStruct->GetLiveStreamURL(tag);
++ }
++ catch (exception &e)
++ {
++ CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to call GetLiveStreamURL() on addon '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), GetFriendlyName().c_str(), Author().c_str());
++ }
++
++ return strReturn;
++}
++
++bool CPVRClient::OpenRecordedStream(const CPVRRecording &recording)
++{
++ if (!m_addonCapabilities.bSupportsRecordings)
++ return false;
++
++ PVR_RECORDING tag;
++ PVRWriteClientRecordingInfo(recording, tag);
++ return m_pStruct->OpenRecordedStream(tag);
++}
++
++void CPVRClient::CloseRecordedStream(void)
++{
++ return m_pStruct->CloseRecordedStream();
++}
++
++int CPVRClient::ReadRecordedStream(void* lpBuf, int64_t uiBufSize)
++{
++ return m_pStruct->ReadRecordedStream((unsigned char *)lpBuf, (int)uiBufSize);
++}
++
++int64_t CPVRClient::SeekRecordedStream(int64_t iFilePosition, int iWhence/* = SEEK_SET*/)
++{
++ return m_pStruct->SeekRecordedStream(iFilePosition, iWhence);
++}
++
++int64_t CPVRClient::PositionRecordedStream()
++{
++ return m_pStruct->PositionRecordedStream();
++}
++
++int64_t CPVRClient::LengthRecordedStream(void)
++{
++ return m_pStruct->LengthRecordedStream();
++}
++
++PVR_ERROR CPVRClient::GetStreamProperties(PVR_STREAM_PROPERTIES *props)
++{
++ try
++ {
++ return m_pStruct->GetStreamProperties(props);
++ }
++ catch (exception &e)
++ {
++ CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to call GetStreamProperties() on addon '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), GetFriendlyName().c_str(), Author().c_str());
++
++ /* Set all properties in a case of exception to not supported */
++ }
++ return PVR_ERROR_UNKNOWN;
++}
++
++void CPVRClient::DemuxReset(void)
++{
++ m_pStruct->DemuxReset();
++}
++
++void CPVRClient::DemuxAbort(void)
++{
++ m_pStruct->DemuxAbort();
++}
++
++void CPVRClient::DemuxFlush(void)
++{
++ m_pStruct->DemuxFlush();
++}
++
++DemuxPacket* CPVRClient::DemuxRead(void)
++{
++ return m_pStruct->DemuxRead();
++}
++
++ADDON_STATUS CPVRClient::SetSetting(const char *settingName, const void *settingValue)
++{
++// try
++// {
++// return m_pDll->SetSetting(settingName, settingValue);
++// }
++// catch (exception &e)
++// {
++// CLog::Log(LOGERROR, "PVR: %s/%s - exception '%s' during SetSetting occurred, contact Developer '%s' of this AddOn", Name().c_str(), m_hostName.c_str(), e.what(), Author().c_str());
++ return ADDON_STATUS_UNKNOWN;
++// }
++}
++
++int CPVRClient::GetClientID(void) const
++{
++ return m_pInfo->iClientId;
++}
++
++bool CPVRClient::HaveMenuHooks(void) const
++{
++ return m_menuhooks.size() > 0;
++}
++
++PVR_MENUHOOKS *CPVRClient::GetMenuHooks(void)
++{
++ return &m_menuhooks;
++}
++
++void CPVRClient::SetBackendName(void)
++{
++ if (m_bGotBackendName || !m_bReadyToUse)
++ return;
++
++ m_bGotBackendName = true;
++
++ try
++ {
++ m_strBackendName = m_pStruct->GetBackendName();
++ }
++ catch (exception &e)
++ {
++ CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to call GetBackendName() on addon '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), GetFriendlyName().c_str(), Author().c_str());
++ }
++}
++
++void CPVRClient::SetBackendVersion(void)
++{
++ if (m_bGotBackendVersion || !m_bReadyToUse)
++ return;
++
++ m_bGotBackendVersion = true;
++
++ try
++ {
++ m_strBackendVersion = m_pStruct->GetBackendVersion();
++ }
++ catch (exception &e)
++ {
++ CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to call GetBackendVersion() on addon '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), GetFriendlyName().c_str(), Author().c_str());
++ }
++}
++
++void CPVRClient::SetConnectionString(void)
++{
++ if (m_bGotConnectionString || !m_bReadyToUse)
++ return;
++
++ m_bGotConnectionString = true;
++
++ try
++ {
++ m_strConnectionString = m_pStruct->GetConnectionString();
++ }
++ catch (exception &e)
++ {
++ CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to call GetConnectionString() on addon '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), GetFriendlyName().c_str(), Author().c_str());
++ }
++}
++
++void CPVRClient::SetFriendlyName(void)
++{
++ if (m_bGotFriendlyName || !m_bReadyToUse)
++ return;
++
++ m_bGotFriendlyName = true;
++
++ m_strFriendlyName.Format("%s:%s", GetBackendName().c_str(), GetConnectionString().c_str());
++}
++
++PVR_ERROR CPVRClient::SetAddonCapabilities(void)
++{
++ if (m_bGotAddonCapabilities)
++ return PVR_ERROR_NO_ERROR;
++
++ ResetAddonCapabilities();
++
++ /* try to get the addon properties */
++ try
++ {
++ PVR_ERROR retVal = m_pStruct->GetAddonCapabilities(&m_addonCapabilities);
++ if (retVal == PVR_ERROR_NO_ERROR)
++ m_bGotAddonCapabilities = true;
++
++ return retVal;
++ }
++ catch (exception &e)
++ {
++ CLog::Log(LOGERROR, "PVRClient - %s - exception '%s' caught while trying to call GetProperties() on addon '%s'. please contact the developer of this addon: %s",
++ __FUNCTION__, e.what(), GetFriendlyName().c_str(), Author().c_str());
++ }
++
++ return PVR_ERROR_SERVER_ERROR;
++}
++
++const char *CPVRClient::ToString(const PVR_ERROR error) const
++{
++ switch (error)
++ {
++ case PVR_ERROR_NO_ERROR:
++ return "no error";
++ case PVR_ERROR_NOT_IMPLEMENTED:
++ return "not implemented";
++ case PVR_ERROR_SERVER_ERROR:
++ return "server error";
++ case PVR_ERROR_SERVER_TIMEOUT:
++ return "server timeout";
++ case PVR_ERROR_NOT_SYNC:
++ return "timers not synced";
++ case PVR_ERROR_NOT_DELETED:
++ return "not deleted";
++ case PVR_ERROR_NOT_SAVED:
++ return "not saved";
++ case PVR_ERROR_RECORDING_RUNNING:
++ return "recording already running";
++ case PVR_ERROR_ALREADY_PRESENT:
++ return "already present";
++ case PVR_ERROR_NOT_POSSIBLE:
++ return "not possible";
++ case PVR_ERROR_UNKNOWN:
++ default:
++ return "unknown error";
++ }
++}
++
++bool CPVRClient::LogError(const PVR_ERROR error, const char *strMethod)
++{
++ if (error != PVR_ERROR_NO_ERROR)
++ {
++ CLog::Log(LOGERROR, "PVRClient - %s - addon '%s' returned an error: %s",
++ strMethod, GetFriendlyName().c_str(), ToString(error));
++ return false;
++ }
++ return true;
++}
+diff --git a/xbmc/pvr/addons/PVRClient.h b/xbmc/pvr/addons/PVRClient.h
+new file mode 100644
+index 0000000..4fccb3c
+--- /dev/null
++++ b/xbmc/pvr/addons/PVRClient.h
+@@ -0,0 +1,499 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "addons/Addon.h"
++#include "addons/AddonDll.h"
++#include "addons/DllPVRClient.h"
++
++namespace EPG
++{
++ class CEpg;
++}
++
++namespace PVR
++{
++ class CPVRChannelGroup;
++ class CPVRChannelGroupInternal;
++ class CPVRChannelGroups;
++ class CPVRTimers;
++ class CPVRTimerInfoTag;
++ class CPVRRecordings;
++ class CPVRRecording;
++ class CPVREpgContainer;
++
++ typedef std::vector<PVR_MENUHOOK> PVR_MENUHOOKS;
++
++ /*!
++ * Interface from XBMC to a PVR add-on.
++ *
++ * Also translates XBMC's C++ structures to the addon's C structures.
++ */
++ class CPVRClient : public ADDON::CAddonDll<DllPVRClient, PVRClient, PVR_PROPERTIES>
++ {
++ public:
++ CPVRClient(const ADDON::AddonProps& props);
++ CPVRClient(const cp_extension_t *ext);
++ ~CPVRClient(void);
++
++ /** @name PVR add-on methods */
++ //@{
++
++ /*!
++ * @brief Initialise the instance of this add-on.
++ * @param iClientId The ID of this add-on.
++ */
++ bool Create(int iClientId);
++
++ /*!
++ * @brief Destroy the instance of this add-on.
++ */
++ void Destroy(void);
++
++ /*!
++ * @brief Destroy and recreate this add-on.
++ */
++ void ReCreate(void);
++
++ /*!
++ * @return True if this instance is initialised, false otherwise.
++ */
++ bool ReadyToUse(void) const;
++
++ /*!
++ * @return The ID of this instance.
++ */
++ int GetID(void) const;
++
++ /*!
++ * @brief Change a setting in the add-on.
++ * @param settingName The name of the setting.
++ * @param settingValue The new value.
++ * @return The status reported by the add-on.
++ */
++ virtual ADDON_STATUS SetSetting(const char *settingName, const void *settingValue);
++
++ //@}
++ /** @name PVR server methods */
++ //@{
++
++ /*!
++ * @brief Query this add-on's capabilities.
++ * @return pCapabilities The add-on's capabilities.
++ */
++ PVR_ADDON_CAPABILITIES GetAddonCapabilities(void) const;
++
++ /*!
++ * @brief Get the stream properties of the stream that's currently being read.
++ * @param pProperties The properties.
++ * @return PVR_ERROR_NO_ERROR if the properties have been fetched successfully.
++ */
++ PVR_ERROR GetStreamProperties(PVR_STREAM_PROPERTIES *pProperties);
++
++ /*!
++ * @return The name reported by the backend.
++ */
++ CStdString GetBackendName(void);
++
++ /*!
++ * @return The version string reported by the backend.
++ */
++ CStdString GetBackendVersion(void);
++
++ /*!
++ * @return The connection string reported by the backend.
++ */
++ CStdString GetConnectionString(void);
++
++ /*!
++ * @return A friendly name for this add-on that can be used in log messages.
++ */
++ CStdString GetFriendlyName(void);
++
++ /*!
++ * @brief Get the disk space reported by the server.
++ * @param iTotal The total disk space.
++ * @param iUsed The used disk space.
++ * @return PVR_ERROR_NO_ERROR if the drive space has been fetched successfully.
++ */
++ PVR_ERROR GetDriveSpace(long long *iTotal, long long *iUsed);
++
++ // /*!
++ // * @brief Get the time reported by the backend.
++ // * @param localTime The local time.
++ // * @param iGmtOffset The GMT offset used.
++ // * @return PVR_ERROR_NO_ERROR if the time has been fetched successfully.
++ // */
++ // PVR_ERROR GetBackendTime(time_t *localTime, int *iGmtOffset);
++
++ /*!
++ * @brief Start a channel scan on the server.
++ * @return PVR_ERROR_NO_ERROR if the channel scan has been started successfully.
++ */
++ PVR_ERROR StartChannelScan(void);
++
++ /*!
++ * @return The ID of the client.
++ */
++ int GetClientID(void) const;
++
++ /*!
++ * @return True if this add-on has menu hooks, false otherwise.
++ */
++ bool HaveMenuHooks(void) const;
++
++ /*!
++ * @return The menu hooks for this add-on.
++ */
++ PVR_MENUHOOKS *GetMenuHooks(void);
++
++ /*!
++ * @brief Call one of the menu hooks of this client.
++ * @param hook The hook to call.
++ */
++ void CallMenuHook(const PVR_MENUHOOK &hook);
++
++ //@}
++ /** @name PVR EPG methods */
++ //@{
++
++ /*!
++ * @brief Request an EPG table for a channel from the client.
++ * @param channel The channel to get the EPG table for.
++ * @param epg The table to write the data to.
++ * @param start The start time to use.
++ * @param end The end time to use.
++ * @param bSaveInDb If true, tell the callback method to save any new entry in the database or not. see CAddonCallbacksPVR::PVRTransferEpgEntry()
++ * @return PVR_ERROR_NO_ERROR if the table has been fetched successfully.
++ */
++ PVR_ERROR GetEPGForChannel(const CPVRChannel &channel, EPG::CEpg *epg, time_t start = 0, time_t end = 0, bool bSaveInDb = false);
++
++ //@}
++ /** @name PVR channel group methods */
++ //@{
++
++ /*!
++ * @return The total amount of channel groups on the server or -1 on error.
++ */
++ int GetChannelGroupsAmount(void);
++
++ /*!
++ * @brief Request the list of all channel groups from the backend.
++ * @param groups The groups container to get the groups for.
++ * @return PVR_ERROR_NO_ERROR if the list has been fetched successfully.
++ */
++ PVR_ERROR GetChannelGroups(CPVRChannelGroups *groups);
++
++ /*!
++ * @brief Request the list of all group members from the backend.
++ * @param groups The group to get the members for.
++ * @return PVR_ERROR_NO_ERROR if the list has been fetched successfully.
++ */
++ PVR_ERROR GetChannelGroupMembers(CPVRChannelGroup *group);
++
++ //@}
++ /** @name PVR channel methods */
++ //@{
++
++ /*!
++ * @return The total amount of channels on the server or -1 on error.
++ */
++ int GetChannelsAmount(void);
++
++ /*!
++ * @brief Request the list of all channels from the backend.
++ * @param channels The channel group to add the channels to.
++ * @param bRadio True to get the radio channels, false to get the TV channels.
++ * @return PVR_ERROR_NO_ERROR if the list has been fetched successfully.
++ */
++ PVR_ERROR GetChannels(CPVRChannelGroup &channels, bool bRadio);
++
++ //@}
++ /** @name PVR recording methods */
++ //@{
++
++ /*!
++ * @return The total amount of channels on the server or -1 on error.
++ */
++ int GetRecordingsAmount(void);
++
++ /*!
++ * @brief Request the list of all recordings from the backend.
++ * @param results The container to add the recordings to.
++ * @return PVR_ERROR_NO_ERROR if the list has been fetched successfully.
++ */
++ PVR_ERROR GetRecordings(CPVRRecordings *results);
++
++ /*!
++ * @brief Delete a recording on the backend.
++ * @param recording The recording to delete.
++ * @return PVR_ERROR_NO_ERROR if the recording has been deleted successfully.
++ */
++ PVR_ERROR DeleteRecording(const CPVRRecording &recording);
++
++ /*!
++ * @brief Rename a recording on the backend.
++ * @param recording The recording to rename.
++ * @return PVR_ERROR_NO_ERROR if the recording has been renamed successfully.
++ */
++ PVR_ERROR RenameRecording(const CPVRRecording &recording);
++
++ //@}
++ /** @name PVR timer methods */
++ //@{
++
++ /*!
++ * @return The total amount of timers on the backend or -1 on error.
++ */
++ int GetTimersAmount(void);
++
++ /*!
++ * @brief Request the list of all timers from the backend.
++ * @param results The container to store the result in.
++ * @return PVR_ERROR_NO_ERROR if the list has been fetched successfully.
++ */
++ PVR_ERROR GetTimers(CPVRTimers *results);
++
++ /*!
++ * @brief Add a timer on the backend.
++ * @param timer The timer to add.
++ * @return PVR_ERROR_NO_ERROR if the timer has been added successfully.
++ */
++ PVR_ERROR AddTimer(const CPVRTimerInfoTag &timer);
++
++ /*!
++ * @brief Delete a timer on the backend.
++ * @param timer The timer to delete.
++ * @param bForce Set to true to delete a timer that is currently recording a program.
++ * @return PVR_ERROR_NO_ERROR if the timer has been deleted successfully.
++ */
++ PVR_ERROR DeleteTimer(const CPVRTimerInfoTag &timer, bool bForce = false);
++
++ /*!
++ * @brief Rename a timer on the server.
++ * @param timer The timer to rename.
++ * @param strNewName The new name of the timer.
++ * @return PVR_ERROR_NO_ERROR if the timer has been renamed successfully.
++ */
++ PVR_ERROR RenameTimer(const CPVRTimerInfoTag &timer, const CStdString &strNewName);
++
++ /*!
++ * @brief Update the timer information on the server.
++ * @param timer The timer to update.
++ * @return PVR_ERROR_NO_ERROR if the timer has been updated successfully.
++ */
++ PVR_ERROR UpdateTimer(const CPVRTimerInfoTag &timer);
++
++ //@}
++ /** @name PVR live stream methods */
++ //@{
++
++ /*!
++ * @brief Open a live stream on the server.
++ * @param channel The channel to stream.
++ * @return True if the stream opened successfully, false otherwise.
++ */
++ bool OpenLiveStream(const CPVRChannel &channel);
++
++ /*!
++ * @brief Close an open live stream.
++ */
++ void CloseLiveStream(void);
++
++ /*!
++ * @brief Read from an open live stream.
++ * @param lpBuf The buffer to store the data in.
++ * @param uiBufSize The amount of bytes to read.
++ * @return The amount of bytes that were actually read from the stream.
++ */
++ int ReadLiveStream(void* lpBuf, int64_t uiBufSize);
++
++ /*!
++ * @brief Seek in a live stream on a backend that supports timeshifting.
++ * @param iFilePosition The position to seek to.
++ * @param iWhence ?
++ * @return The new position.
++ */
++ int64_t SeekLiveStream(int64_t iFilePosition, int iWhence = SEEK_SET);
++
++ /*!
++ * @return The position in the stream that's currently being read.
++ */
++ int64_t PositionLiveStream(void);
++
++ /*!
++ * @return The total length of the stream that's currently being read.
++ */
++ int64_t LengthLiveStream(void);
++
++ /*!
++ * @return The channel number on the server of the live stream that's currently being read.
++ */
++ int GetCurrentClientChannel(void);
++
++ /*!
++ * @brief Switch to another channel. Only to be called when a live stream has already been opened.
++ * @param channel The channel to switch to.
++ * @return True if the switch was successful, false otherwise.
++ */
++ bool SwitchChannel(const CPVRChannel &channel);
++
++ /*!
++ * @brief Get the signal quality of the stream that's currently open.
++ * @param qualityinfo The signal quality.
++ * @return True if the signal quality has been read successfully, false otherwise.
++ */
++ bool SignalQuality(PVR_SIGNAL_STATUS &qualityinfo);
++
++ /*!
++ * @brief Get the stream URL for a channel from the server. Used by the MediaPortal add-on.
++ * @param channel The channel to get the stream URL for.
++ * @return The requested URL.
++ */
++ CStdString GetLiveStreamURL(const CPVRChannel &channel);
++
++ //@}
++ /** @name PVR recording stream methods */
++ //@{
++
++ /*!
++ * @brief Open a recording on the server.
++ * @param recording The recording to open.
++ * @return True if the stream has been opened succesfully, false otherwise.
++ */
++ bool OpenRecordedStream(const CPVRRecording &recording);
++
++ /*!
++ * @brief Close an open stream from a recording.
++ */
++ void CloseRecordedStream(void);
++
++ /*!
++ * @brief Read from a recording.
++ * @param lpBuf The buffer to store the data in.
++ * @param uiBufSize The amount of bytes to read.
++ * @return The amount of bytes that were actually read from the stream.
++ */
++ int ReadRecordedStream(void* lpBuf, int64_t uiBufSize);
++
++ /*!
++ * @brief Seek in a recorded stream.
++ * @param iFilePosition The position to seek to.
++ * @param iWhence ?
++ * @return The new position.
++ */
++ int64_t SeekRecordedStream(int64_t iFilePosition, int iWhence = SEEK_SET);
++
++ /*!
++ * @return The position in the stream that's currently being read.
++ */
++ int64_t PositionRecordedStream(void);
++
++ /*!
++ * @return The total length of the stream that's currently being read.
++ */
++ int64_t LengthRecordedStream(void);
++
++ //@}
++ /** @name PVR demultiplexer methods */
++ //@{
++
++ /*!
++ * @brief Reset the demultiplexer in the add-on.
++ */
++ void DemuxReset(void);
++
++ /*!
++ * @brief Abort the demultiplexer thread in the add-on.
++ */
++ void DemuxAbort(void);
++
++ /*!
++ * @brief Flush all data that's currently in the demultiplexer buffer in the add-on.
++ */
++ void DemuxFlush(void);
++
++ /*!
++ * @brief Read a packet from the demultiplexer.
++ * @return The packet.
++ */
++ DemuxPacket *DemuxRead(void);
++
++ //@}
++
++ protected:
++ bool m_bReadyToUse; /*!< true if this add-on is connected to the backend, false otherwise */
++ CStdString m_strHostName; /*!< the host name */
++ PVR_MENUHOOKS m_menuhooks; /*!< the menu hooks for this add-on */
++
++ /* cached data */
++ CStdString m_strBackendName; /*!< the cached backend version */
++ bool m_bGotBackendName; /*!< true if the backend name has already been fetched */
++ CStdString m_strBackendVersion; /*!< the cached backend version */
++ bool m_bGotBackendVersion; /*!< true if the backend version has already been fetched */
++ CStdString m_strConnectionString; /*!< the cached connection string */
++ bool m_bGotConnectionString; /*!< true if the connection string has already been fetched */
++ CStdString m_strFriendlyName; /*!< the cached friendly name */
++ bool m_bGotFriendlyName; /*!< true if the friendly name has already been fetched */
++ PVR_ADDON_CAPABILITIES m_addonCapabilities; /*!< the cached add-on capabilities */
++ bool m_bGotAddonCapabilities; /*!< true if the add-on capabilities have already been fetched */
++
++ private:
++ /*!
++ * @brief Get the backend name from the server and store it locally.
++ */
++ void SetBackendName(void);
++
++ /*!
++ * @brief Get the backend version from the server and store it locally.
++ */
++ void SetBackendVersion(void);
++
++ /*!
++ * @brief Get the connection string from the server and store it locally.
++ */
++ void SetConnectionString(void);
++
++ /*!
++ * @brief Get the friendly from the server and store it locally.
++ */
++ void SetFriendlyName(void);
++
++ /*!
++ * @brief Get the backend properties from the server and store it locally.
++ */
++ PVR_ERROR SetAddonCapabilities(void);
++
++ /*!
++ * @brief Resets all class members to their defaults. Called by the constructors.
++ */
++ void ResetProperties(void);
++
++ /*!
++ * @brief Reset all add-on capabilities to false.
++ */
++ void ResetAddonCapabilities(void);
++
++ private:
++ const char *ToString(const PVR_ERROR error) const;
++ bool LogError(const PVR_ERROR error, const char *strMethod);
++ };
++}
+diff --git a/xbmc/pvr/addons/PVRClients.cpp b/xbmc/pvr/addons/PVRClients.cpp
+new file mode 100644
+index 0000000..a02aed2
+--- /dev/null
++++ b/xbmc/pvr/addons/PVRClients.cpp
+@@ -0,0 +1,1365 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "PVRClients.h"
++#include "PVRClient.h"
++
++#include "Application.h"
++#include "settings/GUISettings.h"
++#include "dialogs/GUIDialogOK.h"
++#include "dialogs/GUIDialogSelect.h"
++#include "threads/SingleLock.h"
++#include "pvr/PVRManager.h"
++#include "pvr/PVRDatabase.h"
++#include "guilib/GUIWindowManager.h"
++#include "settings/AdvancedSettings.h"
++#include "settings/Settings.h"
++#include "pvr/channels/PVRChannelGroups.h"
++#include "pvr/recordings/PVRRecordings.h"
++#include "pvr/timers/PVRTimers.h"
++#include "pvr/channels/PVRChannelGroupInternal.h"
++#include "utils/StringUtils.h"
++
++#ifdef HAS_VIDEO_PLAYBACK
++#include "cores/VideoRenderers/RenderManager.h"
++#endif
++
++using namespace std;
++using namespace ADDON;
++using namespace PVR;
++using namespace EPG;
++
++CPVRClients::CPVRClients(void) :
++ CThread("PVR add-on updater"),
++ m_bChannelScanRunning(false),
++ m_bAllClientsConnected(false),
++ m_bIsSwitchingChannels(false),
++ m_bIsValidChannelSettings(false),
++ m_bIsPlayingLiveTV(false),
++ m_bIsPlayingRecording(false),
++ m_scanStart(0)
++{
++ ResetQualityData(m_qualityInfo);
++}
++
++CPVRClients::~CPVRClients(void)
++{
++ Unload();
++}
++
++void CPVRClients::Start(void)
++{
++ Stop();
++
++ ResetQualityData(m_qualityInfo);
++
++ Create();
++ SetPriority(-1);
++}
++
++void CPVRClients::Stop(void)
++{
++ StopThread();
++}
++
++bool CPVRClients::IsConnectedClient(int iClientId)
++{
++ boost::shared_ptr<CPVRClient> client;
++ return GetConnectedClient(iClientId, client);
++}
++
++int CPVRClients::GetClientId(const AddonPtr client) const
++{
++ CSingleLock lock(m_critSection);
++
++ for (CLIENTMAPCITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++)
++ if (itr->second->ID() == client->ID())
++ return itr->first;
++
++ return -1;
++}
++
++bool CPVRClients::GetConnectedClient(int iClientId, boost::shared_ptr<CPVRClient> &addon) const
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ CLIENTMAPCITR itr = m_clientMap.find(iClientId);
++ if (itr != m_clientMap.end() && itr->second->ReadyToUse())
++ {
++ addon = itr->second;
++ bReturn = true;
++ }
++ else
++ {
++ CLog::Log(LOGDEBUG, "%s - client %d is not connected", __FUNCTION__, iClientId);
++ }
++
++ return bReturn;
++}
++
++bool CPVRClients::RequestRestart(AddonPtr addon, bool bDataChanged)
++{
++ return StopClient(addon, true);
++}
++
++bool CPVRClients::RequestRemoval(AddonPtr addon)
++{
++ return StopClient(addon, false);
++}
++
++void CPVRClients::Unload(void)
++{
++ Stop();
++
++ CSingleLock lock(m_critSection);
++
++ /* destroy all clients */
++ for (CLIENTMAPITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++)
++ itr->second->Destroy();
++
++ /* reset class properties */
++ m_bChannelScanRunning = false;
++ m_bAllClientsConnected = false;
++ m_bIsPlayingLiveTV = false;
++ m_bIsPlayingRecording = false;
++ m_strPlayingClientName = StringUtils::EmptyString;
++
++ m_clientMap.clear();
++}
++
++int CPVRClients::GetFirstConnectedClientID(void)
++{
++ CSingleLock lock(m_critSection);
++
++ for (CLIENTMAPITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++)
++ if (itr->second->ReadyToUse())
++ return itr->second->GetID();
++
++ return -1;
++}
++
++bool CPVRClients::AllClientsConnected(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_bAllClientsConnected;
++}
++
++int CPVRClients::EnabledClientAmount(void) const
++{
++ int iReturn(0);
++ CSingleLock lock(m_critSection);
++
++ for (CLIENTMAPCITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++)
++ if (itr->second->Enabled())
++ ++iReturn;
++
++ return iReturn;
++}
++
++bool CPVRClients::HasEnabledClients(void) const
++{
++ CSingleLock lock(m_critSection);
++
++ for (CLIENTMAPCITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++)
++ if (itr->second->Enabled())
++ return true;
++
++ return false;
++}
++
++bool CPVRClients::StopClient(AddonPtr client, bool bRestart)
++{
++ bool bFoundClient(false);
++ if (!client)
++ return bFoundClient;
++
++ boost::shared_ptr<CPVRClient> mappedClient;
++ {
++ CSingleLock lock(m_critSection);
++ int iId = GetClientId(client);
++ if (iId != -1)
++ {
++ bFoundClient = true;
++ mappedClient = m_clientMap[iId];
++
++ if (!bRestart)
++ m_clientMap.erase(iId);
++ }
++ }
++
++ if (bFoundClient)
++ {
++ g_PVRManager.StopUpdateThreads();
++ if (bRestart)
++ mappedClient->ReCreate();
++ else
++ mappedClient->Destroy();
++ g_PVRManager.StartUpdateThreads();
++ }
++
++ return bFoundClient;
++}
++
++int CPVRClients::ConnectedClientAmount(void)
++{
++ int iReturn(0);
++ CSingleLock lock(m_critSection);
++
++ for (CLIENTMAPITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++)
++ if (itr->second->ReadyToUse())
++ ++iReturn;
++
++ return iReturn;
++}
++
++bool CPVRClients::HasConnectedClients(void)
++{
++ CSingleLock lock(m_critSection);
++
++ for (CLIENTMAPITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++)
++ if (itr->second->ReadyToUse())
++ return true;
++
++ return false;
++}
++
++bool CPVRClients::GetClientName(int iClientId, CStdString &strName)
++{
++ bool bReturn(false);
++ boost::shared_ptr<CPVRClient> client;
++ if ((bReturn = GetConnectedClient(iClientId, client)) == true)
++ strName = client->GetFriendlyName();
++
++ return bReturn;
++}
++
++int CPVRClients::GetConnectedClients(CLIENTMAP *clients)
++{
++ int iReturn(0);
++ CSingleLock lock(m_critSection);
++
++ for (CLIENTMAPITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++)
++ {
++ if (itr->second->ReadyToUse())
++ {
++ clients->insert(std::make_pair(itr->second->GetID(), itr->second));
++ ++iReturn;
++ }
++ }
++
++ return iReturn;
++}
++
++int CPVRClients::GetPlayingClientID(void) const
++{
++ int iReturn(-1);
++ CSingleLock lock(m_critSection);
++
++ if (m_bIsPlayingLiveTV)
++ iReturn = m_currentChannel.ClientID();
++ else if (m_bIsPlayingRecording)
++ iReturn = m_currentRecording.m_iClientId;
++
++ return iReturn;
++}
++
++PVR_ADDON_CAPABILITIES CPVRClients::GetAddonCapabilities(int iClientId) const
++{
++ PVR_ADDON_CAPABILITIES props;
++ memset(&props, 0, sizeof(PVR_ADDON_CAPABILITIES));
++
++ boost::shared_ptr<CPVRClient> client;
++ if (GetConnectedClient(iClientId, client))
++ props = client->GetAddonCapabilities();
++
++ return props;
++}
++
++PVR_ADDON_CAPABILITIES CPVRClients::GetCurrentAddonCapabilities(void)
++{
++ PVR_ADDON_CAPABILITIES props;
++ memset(&props, 0, sizeof(PVR_ADDON_CAPABILITIES));
++
++ CSingleLock lock(m_critSection);
++ if (m_bIsPlayingLiveTV)
++ props = m_clientMap[m_currentChannel.ClientID()]->GetAddonCapabilities();
++ else if (m_bIsPlayingRecording)
++ props = m_clientMap[m_currentRecording.m_iClientId]->GetAddonCapabilities();
++
++ return props;
++}
++
++bool CPVRClients::IsPlaying(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_bIsPlayingRecording || m_bIsPlayingLiveTV;
++}
++
++const CStdString CPVRClients::GetPlayingClientName(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_strPlayingClientName;
++}
++
++int CPVRClients::ReadStream(void* lpBuf, int64_t uiBufSize)
++{
++ CSingleLock lock(m_critSection);
++
++ if (m_bIsPlayingLiveTV)
++ return m_clientMap[m_currentChannel.ClientID()]->ReadLiveStream(lpBuf, uiBufSize);
++ else if (m_bIsPlayingRecording)
++ return m_clientMap[m_currentRecording.m_iClientId]->ReadRecordedStream(lpBuf, uiBufSize);
++
++ return 0;
++}
++
++int64_t CPVRClients::LengthStream(void)
++{
++ int64_t streamLength(0);
++ CSingleLock lock(m_critSection);
++
++ if(GetCurrentAddonCapabilities().bSupportsTimeshift && m_bIsPlayingLiveTV)
++ streamLength = m_clientMap[m_currentChannel.ClientID()]->LengthLiveStream();
++ else if (m_bIsPlayingLiveTV)
++ streamLength = 0;
++ else if (m_bIsPlayingRecording)
++ streamLength = m_clientMap[m_currentRecording.m_iClientId]->LengthRecordedStream();
++
++ return streamLength;
++}
++
++int64_t CPVRClients::SeekStream(int64_t iFilePosition, int iWhence/* = SEEK_SET*/)
++{
++ int64_t streamNewPos(0);
++ CSingleLock lock(m_critSection);
++ if(GetCurrentAddonCapabilities().bSupportsTimeshift && m_bIsPlayingLiveTV)
++ streamNewPos = m_clientMap[m_currentChannel.ClientID()]->SeekLiveStream(iFilePosition, iWhence);
++ else if (m_bIsPlayingLiveTV)
++ streamNewPos = 0;
++ else if (m_bIsPlayingRecording)
++ streamNewPos = m_clientMap[m_currentRecording.m_iClientId]->SeekRecordedStream(iFilePosition, iWhence);
++
++ return streamNewPos;
++}
++
++int64_t CPVRClients::GetStreamPosition(void)
++{
++ int64_t streamPos(0);
++ CSingleLock lock(m_critSection);
++
++ if(GetCurrentAddonCapabilities().bSupportsTimeshift && m_bIsPlayingLiveTV)
++ streamPos = m_clientMap[m_currentChannel.ClientID()]->PositionLiveStream();
++ else if (m_bIsPlayingLiveTV)
++ streamPos = 0;
++ else if (m_bIsPlayingRecording)
++ streamPos = m_clientMap[m_currentRecording.m_iClientId]->PositionRecordedStream();
++
++ return streamPos;
++}
++
++void CPVRClients::CloseStream(void)
++{
++ CSingleLock lock(m_critSection);
++ CloseLiveStream() || CloseRecordedStream();
++ m_strPlayingClientName = StringUtils::EmptyString;
++}
++
++PVR_STREAM_PROPERTIES *CPVRClients::GetCurrentStreamProperties(void)
++{
++ PVR_STREAM_PROPERTIES *props = NULL;
++ CSingleLock lock(m_critSection);
++
++ if (m_bIsPlayingLiveTV)
++ {
++ int iChannelId = m_currentChannel.ClientID();
++ m_clientMap[iChannelId]->GetStreamProperties(&m_streamProps[iChannelId]);
++
++ props = &m_streamProps[iChannelId];
++ }
++
++ return props;
++}
++
++CStdString CPVRClients::GetCurrentInputFormat(void) const
++{
++ CStdString strReturn;
++ CPVRChannel currentChannel;
++ if (GetPlayingChannel(currentChannel))
++ strReturn = currentChannel.InputFormat();
++
++ return strReturn;
++}
++
++bool CPVRClients::IsReadingLiveStream(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_bIsPlayingLiveTV;
++}
++
++bool CPVRClients::IsPlayingTV(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_bIsPlayingLiveTV && !m_currentChannel.IsRadio();
++}
++
++bool CPVRClients::IsPlayingRadio(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_bIsPlayingLiveTV && m_currentChannel.IsRadio();
++}
++
++bool CPVRClients::IsEncrypted(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_bIsPlayingLiveTV && m_currentChannel.IsEncrypted();
++}
++
++bool CPVRClients::OpenLiveStream(const CPVRChannel &tag)
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ m_bIsPlayingLiveTV = false;
++ m_bIsPlayingRecording = false;
++
++ ResetQualityData(m_qualityInfo);
++
++ /* try to open the stream on the client */
++ boost::shared_ptr<CPVRClient> client;
++ if (tag.StreamURL().IsEmpty() == false ||
++ (GetConnectedClient(tag.ClientID(), client) &&
++ client->GetAddonCapabilities().bHandlesInputStream &&
++ client->OpenLiveStream(tag)))
++ {
++ m_currentChannel = tag;
++ m_bIsPlayingLiveTV = true;
++ if (tag.ClientID() == XBMC_VIRTUAL_CLIENTID)
++ m_strPlayingClientName = g_localizeStrings.Get(19209);
++ else if (!tag.IsVirtual())
++ GetClientName(tag.ClientID(), m_strPlayingClientName);
++ else
++ m_strPlayingClientName = g_localizeStrings.Get(13205);
++
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CPVRClients::CloseLiveStream(void)
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++ ResetQualityData(m_qualityInfo);
++
++ if (!m_bIsPlayingLiveTV)
++ return bReturn;
++
++ if ((m_currentChannel.StreamURL().IsEmpty()) || (m_currentChannel.StreamURL().compare(0,13, "pvr://stream/") == 0))
++ {
++ m_clientMap[m_currentChannel.ClientID()]->CloseLiveStream();
++ bReturn = true;
++ }
++
++ m_bIsPlayingLiveTV = false;
++ return bReturn;
++}
++
++CStdString CPVRClients::GetStreamURL(const CPVRChannel &tag)
++{
++ CStdString strReturn;
++ boost::shared_ptr<CPVRClient> client;
++ if (GetConnectedClient(tag.ClientID(), client))
++ strReturn = client->GetLiveStreamURL(tag);
++ else
++ CLog::Log(LOGERROR, "PVR - %s - cannot find client %d",__FUNCTION__, tag.ClientID());
++
++ return strReturn;
++}
++
++bool CPVRClients::SwitchChannel(const CPVRChannel &channel)
++{
++ bool bSwitchSuccessful(false);
++ bool bNewStreamOpened(false);
++
++ {
++ CSingleLock lock(m_critSection);
++ if (m_bIsSwitchingChannels)
++ {
++ CLog::Log(LOGDEBUG, "PVRClients - %s - can't switch to channel '%s'. waiting for the previous switch to complete", __FUNCTION__, channel.ChannelName().c_str());
++ return false;
++ }
++ m_bIsSwitchingChannels = true;
++ }
++
++ CPVRChannel currentChannel;
++ bool bGotPlayingChannel = GetPlayingChannel(currentChannel);
++ if (bGotPlayingChannel)
++ {
++ if (currentChannel != channel)
++ {
++ /* different client add-on */
++ if (currentChannel.ClientID() != channel.ClientID() ||
++ /* switch from radio -> tv or tv -> radio */
++ currentChannel.IsRadio() != channel.IsRadio())
++ {
++ CloseStream();
++ bSwitchSuccessful = OpenLiveStream(channel);
++ }
++ else if (!channel.StreamURL().IsEmpty() || !currentChannel.StreamURL().IsEmpty())
++ {
++ // StreamURL should always be opened as a new file
++ CFileItem m_currentFile(channel);
++ g_application.getApplicationMessenger().PlayFile(m_currentFile, false);
++ bSwitchSuccessful = true;
++ bNewStreamOpened = true;
++ }
++ else
++ {
++ boost::shared_ptr<CPVRClient> client;
++ if (GetConnectedClient(channel.ClientID(), client))
++ bSwitchSuccessful = client->SwitchChannel(channel);
++ }
++ }
++ else
++ {
++ bSwitchSuccessful = true;
++ }
++ }
++
++ {
++ CSingleLock lock(m_critSection);
++ m_bIsSwitchingChannels = false;
++ if (bSwitchSuccessful && !bNewStreamOpened)
++ {
++ m_currentChannel = channel;
++ m_bIsPlayingLiveTV = true;
++ ResetQualityData(m_qualityInfo);
++ m_bIsValidChannelSettings = false;
++ }
++ }
++
++ if (!bSwitchSuccessful)
++ CLog::Log(LOGERROR, "PVR - %s - cannot switch channel on client %d",__FUNCTION__, channel.ClientID());
++
++ return bSwitchSuccessful;
++}
++
++bool CPVRClients::GetPlayingChannel(CPVRChannel &channel) const
++{
++ CSingleLock lock(m_critSection);
++
++ if (m_bIsPlayingLiveTV)
++ channel = m_currentChannel;
++ return m_bIsPlayingLiveTV;
++}
++
++bool CPVRClients::IsPlayingRecording(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_bIsPlayingRecording;
++}
++
++bool CPVRClients::OpenRecordedStream(const CPVRRecording &tag)
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ m_bIsPlayingLiveTV = false;
++ m_bIsPlayingRecording = false;
++
++ /* try to open the recording stream on the client */
++ boost::shared_ptr<CPVRClient> client;
++ if (GetConnectedClient(tag.m_iClientId, client) &&
++ client->OpenRecordedStream(tag))
++ {
++ m_currentRecording = tag;
++ m_bIsPlayingRecording = true;
++ m_strPlayingClientName = client->GetFriendlyName();
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CPVRClients::CloseRecordedStream(void)
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ if (!m_bIsPlayingRecording)
++ return bReturn;
++
++ if (m_currentRecording.m_iClientId > 0 && m_clientMap[m_currentRecording.m_iClientId])
++ {
++ m_clientMap[m_currentRecording.m_iClientId]->CloseRecordedStream();
++ bReturn = true;
++ }
++
++ m_bIsPlayingRecording = false;
++ return bReturn;
++}
++
++bool CPVRClients::GetPlayingRecording(CPVRRecording &recording) const
++{
++ CSingleLock lock(m_critSection);
++ if (m_bIsPlayingRecording)
++ recording = m_currentRecording;
++
++ return m_bIsPlayingRecording;
++}
++
++void CPVRClients::DemuxReset(void)
++{
++ /* don't lock here cause it'll cause a dead lock when the client connection is dropped while playing */
++ if (m_bIsPlayingLiveTV)
++ m_clientMap[m_currentChannel.ClientID()]->DemuxReset();
++}
++
++void CPVRClients::DemuxAbort(void)
++{
++ /* don't lock here cause it'll cause a dead lock when the client connection is dropped while playing */
++ if (m_bIsPlayingLiveTV)
++ m_clientMap[m_currentChannel.ClientID()]->DemuxAbort();
++}
++
++void CPVRClients::DemuxFlush(void)
++{
++ /* don't lock here cause it'll cause a dead lock when the client connection is dropped while playing */
++ if (m_bIsPlayingLiveTV)
++ m_clientMap[m_currentChannel.ClientID()]->DemuxFlush();
++}
++
++DemuxPacket* CPVRClients::ReadDemuxStream(void)
++{
++ /* don't lock here cause it'll cause a dead lock when the client connection is dropped while playing */
++ if (m_bIsPlayingLiveTV)
++ return m_clientMap[m_currentChannel.ClientID()]->DemuxRead();
++
++ return NULL;
++}
++
++void CPVRClients::GetQualityData(PVR_SIGNAL_STATUS *status) const
++{
++ CSingleLock lock(m_critSection);
++ *status = m_qualityInfo;
++}
++
++int CPVRClients::GetSignalLevel(void) const
++{
++ CSingleLock lock(m_critSection);
++ return (int) ((float) m_qualityInfo.iSignal / 0xFFFF * 100);
++}
++
++int CPVRClients::GetSNR(void) const
++{
++ CSingleLock lock(m_critSection);
++ return (int) ((float) m_qualityInfo.iSNR / 0xFFFF * 100);
++}
++
++bool CPVRClients::HasTimerSupport(int iClientId)
++{
++ CSingleLock lock(m_critSection);
++
++ return IsConnectedClient(iClientId) && m_clientMap[iClientId]->GetAddonCapabilities().bSupportsTimers;
++}
++
++int CPVRClients::GetTimers(CPVRTimers *timers)
++{
++ int iCurSize = timers->GetNumActiveTimers();
++ CLIENTMAP clients;
++ GetConnectedClients(&clients);
++
++ /* get the timer list from each client */
++ boost::shared_ptr<CPVRClient> client;
++ CLIENTMAPITR itrClients = clients.begin();
++ while (itrClients != clients.end())
++ {
++ client = (*itrClients).second;
++ if (client->GetAddonCapabilities().bSupportsTimers)
++ client->GetTimers(timers);
++
++ ++itrClients;
++ }
++
++ return timers->GetNumActiveTimers() - iCurSize;
++}
++
++bool CPVRClients::AddTimer(const CPVRTimerInfoTag &timer, PVR_ERROR *error)
++{
++ *error = PVR_ERROR_UNKNOWN;
++ boost::shared_ptr<CPVRClient> client;
++ if (GetConnectedClient(timer.m_iClientId, client) && client->GetAddonCapabilities().bSupportsTimers)
++ *error = client->AddTimer(timer);
++ else
++ CLog::Log(LOGERROR, "PVR - %s - client %d does not support timers",__FUNCTION__, timer.m_iClientId);
++
++ return *error == PVR_ERROR_NO_ERROR;
++}
++
++bool CPVRClients::UpdateTimer(const CPVRTimerInfoTag &timer, PVR_ERROR *error)
++{
++ *error = PVR_ERROR_UNKNOWN;
++ boost::shared_ptr<CPVRClient> client;
++ if (GetConnectedClient(timer.m_iClientId, client) && client->GetAddonCapabilities().bSupportsTimers)
++ *error = client->UpdateTimer(timer);
++ else
++ CLog::Log(LOGERROR, "PVR - %s - client %d doest not support timers",__FUNCTION__, timer.m_iClientId);
++
++ return *error == PVR_ERROR_NO_ERROR;
++}
++
++bool CPVRClients::DeleteTimer(const CPVRTimerInfoTag &timer, bool bForce, PVR_ERROR *error)
++{
++ *error = PVR_ERROR_UNKNOWN;
++ boost::shared_ptr<CPVRClient> client;
++ if (GetConnectedClient(timer.m_iClientId, client) && client->GetAddonCapabilities().bSupportsTimers)
++ *error = client->DeleteTimer(timer, bForce);
++ else
++ CLog::Log(LOGERROR, "PVR - %s - client %d does not support timers",__FUNCTION__, timer.m_iClientId);
++
++ return *error == PVR_ERROR_NO_ERROR;
++}
++
++bool CPVRClients::RenameTimer(const CPVRTimerInfoTag &timer, const CStdString &strNewName, PVR_ERROR *error)
++{
++ *error = PVR_ERROR_UNKNOWN;
++ boost::shared_ptr<CPVRClient> client;
++ if (GetConnectedClient(timer.m_iClientId, client) && client->GetAddonCapabilities().bSupportsTimers)
++ *error = client->RenameTimer(timer, strNewName);
++ else
++ CLog::Log(LOGERROR, "PVR - %s - client %d does not support timers",__FUNCTION__, timer.m_iClientId);
++
++ return *error == PVR_ERROR_NO_ERROR;
++}
++
++bool CPVRClients::HasRecordingsSupport(int iClientId)
++{
++ CSingleLock lock(m_critSection);
++
++ return IsConnectedClient(iClientId) && m_clientMap[iClientId]->GetAddonCapabilities().bSupportsRecordings;
++}
++
++int CPVRClients::GetRecordings(CPVRRecordings *recordings)
++{
++ int iCurSize = recordings->size();
++ CLIENTMAP clients;
++ GetConnectedClients(&clients);
++
++ boost::shared_ptr<CPVRClient> client;
++ CLIENTMAPITR itrClients = clients.begin();
++ while (itrClients != clients.end())
++ {
++ client = (*itrClients).second;
++ if (client->GetAddonCapabilities().bSupportsRecordings)
++ client->GetRecordings(recordings);
++
++ itrClients++;
++ }
++
++ return recordings->size() - iCurSize;
++}
++
++bool CPVRClients::RenameRecording(const CPVRRecording &recording, PVR_ERROR *error)
++{
++ *error = PVR_ERROR_UNKNOWN;
++ boost::shared_ptr<CPVRClient> client;
++ if (GetConnectedClient(recording.m_iClientId, client) && client->GetAddonCapabilities().bSupportsRecordings)
++ *error = client->RenameRecording(recording);
++ else
++ CLog::Log(LOGERROR, "PVR - %s - client %d does not support recordings",__FUNCTION__, recording.m_iClientId);
++
++ return *error == PVR_ERROR_NO_ERROR;
++}
++
++bool CPVRClients::DeleteRecording(const CPVRRecording &recording, PVR_ERROR *error)
++{
++ *error = PVR_ERROR_UNKNOWN;
++ boost::shared_ptr<CPVRClient> client;
++ if (GetConnectedClient(recording.m_iClientId, client) && client->GetAddonCapabilities().bSupportsRecordings)
++ *error = client->DeleteRecording(recording);
++ else
++ CLog::Log(LOGERROR, "PVR - %s - client %d does not support recordings",__FUNCTION__, recording.m_iClientId);
++
++ return *error == PVR_ERROR_NO_ERROR;
++}
++
++bool CPVRClients::IsRecordingOnPlayingChannel(void) const
++{
++ CPVRChannel currentChannel;
++ return GetPlayingChannel(currentChannel) && currentChannel.IsRecording();
++}
++
++bool CPVRClients::CanRecordInstantly(void)
++{
++ CPVRChannel currentChannel;
++ return GetPlayingChannel(currentChannel) && HasRecordingsSupport(currentChannel.ClientID());
++}
++
++bool CPVRClients::HasEPGSupport(int iClientId)
++{
++ CSingleLock lock(m_critSection);
++
++ return IsConnectedClient(iClientId) && m_clientMap[iClientId]->GetAddonCapabilities().bSupportsEPG;
++}
++
++bool CPVRClients::GetEPGForChannel(const CPVRChannel &channel, CEpg *epg, time_t start, time_t end, PVR_ERROR *error)
++{
++ *error = PVR_ERROR_UNKNOWN;
++ boost::shared_ptr<CPVRClient> client;
++ if (GetConnectedClient(channel.ClientID(), client) && client->GetAddonCapabilities().bSupportsEPG)
++ *error = client->GetEPGForChannel(channel, epg, start, end);
++ else
++ CLog::Log(LOGERROR, "PVR - %s - client %d does not support EPG",__FUNCTION__, channel.ClientID());
++
++ return *error == PVR_ERROR_NO_ERROR;
++}
++
++int CPVRClients::GetChannels(CPVRChannelGroupInternal *group, PVR_ERROR *error)
++{
++ *error = PVR_ERROR_NO_ERROR;
++ int iCurSize = group->Size();
++ CLIENTMAP clients;
++ GetConnectedClients(&clients);
++
++ /* get the channel list from each client */
++ boost::shared_ptr<CPVRClient> client;
++ for (CLIENTMAPITR itrClients = clients.begin(); itrClients != clients.end(); itrClients++)
++ {
++ PVR_ERROR currentError;
++ client = (*itrClients).second;
++
++ if (group->IsRadio() && !client->GetAddonCapabilities().bSupportsRadio)
++ continue;
++ else if (!group->IsRadio() && !client->GetAddonCapabilities().bSupportsTV)
++ continue;
++ else if ((currentError = client->GetChannels(*group, group->IsRadio())) != PVR_ERROR_NO_ERROR)
++ *error = currentError;
++ }
++
++ return group->Size() - iCurSize;
++}
++
++bool CPVRClients::HasChannelGroupSupport(int iClientId)
++{
++ CSingleLock lock(m_critSection);
++
++ return IsConnectedClient(iClientId) && m_clientMap[iClientId]->GetAddonCapabilities().bSupportsChannelGroups;
++}
++
++int CPVRClients::GetChannelGroups(CPVRChannelGroups *groups, PVR_ERROR *error)
++{
++ *error = PVR_ERROR_UNKNOWN;
++ int iCurSize = groups->size();
++ CLIENTMAP clients;
++ GetConnectedClients(&clients);
++
++ /* get the channel groups list from each client */
++ boost::shared_ptr<CPVRClient> client;
++ CLIENTMAPITR itrClients = clients.begin();
++ while (itrClients != clients.end())
++ {
++ client = (*itrClients).second;
++ if (client->GetAddonCapabilities().bSupportsChannelGroups)
++ client->GetChannelGroups(groups);
++
++ itrClients++;
++ }
++
++ return groups->size() - iCurSize;
++}
++
++int CPVRClients::GetChannelGroupMembers(CPVRChannelGroup *group, PVR_ERROR *error)
++{
++ *error = PVR_ERROR_NO_ERROR;
++ int iCurSize = group->Size();
++ CLIENTMAP clients;
++ GetConnectedClients(&clients);
++
++ /* get the member list from each client */
++ boost::shared_ptr<CPVRClient> client;
++ CLIENTMAPITR itrClients = clients.begin();
++ while (itrClients != clients.end())
++ {
++ client = (*itrClients).second;
++ if (client->GetAddonCapabilities().bSupportsChannelGroups)
++ {
++ PVR_ERROR currentError;
++ if ((currentError = client->GetChannelGroupMembers(group)) != PVR_ERROR_NO_ERROR)
++ *error = currentError;
++ }
++
++ itrClients++;
++ }
++
++ return group->Size() - iCurSize;
++}
++
++bool CPVRClients::HasMenuHooks(int iClientID)
++{
++ if (iClientID < 0)
++ iClientID = GetPlayingClientID();
++
++ boost::shared_ptr<CPVRClient> client;
++ return (GetConnectedClient(iClientID, client) &&
++ client->HaveMenuHooks());
++}
++
++bool CPVRClients::GetMenuHooks(int iClientID, PVR_MENUHOOKS *hooks)
++{
++ bool bReturn(false);
++
++ if (iClientID < 0)
++ iClientID = GetPlayingClientID();
++
++ boost::shared_ptr<CPVRClient> client;
++ if (GetConnectedClient(iClientID, client) && client->HaveMenuHooks())
++ {
++ hooks = client->GetMenuHooks();
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++void CPVRClients::ProcessMenuHooks(int iClientID)
++{
++ PVR_MENUHOOKS *hooks = NULL;
++
++ if (iClientID < 0)
++ iClientID = GetPlayingClientID();
++
++ boost::shared_ptr<CPVRClient> client;
++ if (GetConnectedClient(iClientID, client) && client->HaveMenuHooks())
++ {
++ hooks = client->GetMenuHooks();
++ std::vector<int> hookIDs;
++
++ CGUIDialogSelect* pDialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
++ pDialog->Reset();
++ pDialog->SetHeading(19196);
++ for (unsigned int i = 0; i < hooks->size(); i++)
++ pDialog->Add(client->GetString(hooks->at(i).iLocalizedStringId));
++ pDialog->DoModal();
++
++ int selection = pDialog->GetSelectedLabel();
++ if (selection >= 0)
++ client->CallMenuHook(hooks->at(selection));
++ }
++}
++
++bool CPVRClients::IsRunningChannelScan(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_bChannelScanRunning;
++}
++
++void CPVRClients::StartChannelScan(void)
++{
++ CLIENTMAP clients;
++ vector< boost::shared_ptr<CPVRClient> > possibleScanClients;
++ boost::shared_ptr<CPVRClient> scanClient;
++ CSingleLock lock(m_critSection);
++ GetConnectedClients(&clients);
++ m_bChannelScanRunning = true;
++
++ /* get clients that support channel scanning */
++ for (CLIENTMAPITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++)
++ {
++ if (itr->second->ReadyToUse() && itr->second->GetAddonCapabilities().bSupportsChannelScan)
++ possibleScanClients.push_back(itr->second);
++ }
++
++ /* multiple clients found */
++ if (possibleScanClients.size() > 1)
++ {
++ CGUIDialogSelect* pDialog= (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
++
++ pDialog->Reset();
++ pDialog->SetHeading(19119);
++
++ for (unsigned int i = 0; i < possibleScanClients.size(); i++)
++ pDialog->Add(possibleScanClients[i]->GetFriendlyName());
++
++ pDialog->DoModal();
++
++ int selection = pDialog->GetSelectedLabel();
++ if (selection >= 0)
++ scanClient = possibleScanClients[selection];
++ }
++ /* one client found */
++ else if (possibleScanClients.size() == 1)
++ {
++ scanClient = possibleScanClients[0];
++ }
++ /* no clients found */
++ else if (!scanClient)
++ {
++ CGUIDialogOK::ShowAndGetInput(19033,0,19192,0);
++ return;
++ }
++
++ /* start the channel scan */
++ CLog::Log(LOGNOTICE,"PVR - %s - starting to scan for channels on client %s",
++ __FUNCTION__, scanClient->GetFriendlyName().c_str());
++ long perfCnt = XbmcThreads::SystemClockMillis();
++
++ /* stop the supervisor thread */
++ g_PVRManager.StopUpdateThreads();
++
++ /* do the scan */
++ if (scanClient->StartChannelScan() != PVR_ERROR_NO_ERROR)
++ /* an error occured */
++ CGUIDialogOK::ShowAndGetInput(19111,0,19193,0);
++
++ /* restart the supervisor thread */
++ g_PVRManager.StartUpdateThreads();
++
++ CLog::Log(LOGNOTICE, "PVRManager - %s - channel scan finished after %li.%li seconds",
++ __FUNCTION__, (XbmcThreads::SystemClockMillis()-perfCnt)/1000, (XbmcThreads::SystemClockMillis()-perfCnt)%1000);
++ m_bChannelScanRunning = false;
++}
++
++int CPVRClients::AddClientToDb(const AddonPtr client)
++{
++ /* add this client to the database if it's not in there yet */
++ CPVRDatabase *database = GetPVRDatabase();
++ int iClientDbId = database ? database->Persist(client) : -1;
++ if (iClientDbId == -1)
++ {
++ CLog::Log(LOGERROR, "PVR - %s - can't add client '%s' to the database",
++ __FUNCTION__, client->Name().c_str());
++ }
++
++ return iClientDbId;
++}
++
++bool CPVRClients::IsKnownClient(const AddonPtr client) const
++{
++ CSingleLock lock(m_critSection);
++
++ for (CLIENTMAPCITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++)
++ if (itr->second->ID() == client->ID())
++ return true;
++
++ return false;
++}
++
++bool CPVRClients::InitialiseClient(AddonPtr client)
++{
++ bool bReturn(false);
++ if (!client->Enabled())
++ return bReturn;
++
++ CLog::Log(LOGDEBUG, "%s - initialising add-on '%s'", __FUNCTION__, client->Name().c_str());
++
++ /* register this client in the db */
++ int iClientId = AddClientToDb(client);
++ if (iClientId == -1)
++ return bReturn;
++
++ /* load and initialise the client libraries */
++ boost::shared_ptr<CPVRClient> addon;
++ {
++ CSingleLock lock(m_critSection);
++ CLIENTMAPITR existingClient = m_clientMap.find(iClientId);
++ if (existingClient != m_clientMap.end())
++ {
++ addon = existingClient->second;
++ }
++ else
++ {
++ addon = boost::dynamic_pointer_cast<CPVRClient>(client);
++ m_clientMap.insert(std::make_pair(iClientId, addon));
++ }
++ }
++
++ if (addon)
++ bReturn = addon->Create(iClientId);
++
++ if (!bReturn)
++ CLog::Log(LOGERROR, "PVR - %s - can't initialise add-on '%s'", __FUNCTION__, client->Name().c_str());
++
++ return bReturn;
++}
++
++bool CPVRClients::UpdateAndInitialiseClients(bool bInitialiseAllClients /* = false */)
++{
++ bool bReturn(true);
++ ADDON::VECADDONS map;
++ {
++ CSingleLock lock(m_critSection);
++ map = m_addons;
++ }
++
++ for (unsigned iClientPtr = 0; iClientPtr < map.size(); iClientPtr++)
++ {
++ const AddonPtr clientAddon = map.at(iClientPtr);
++
++ if (!clientAddon->Enabled() && IsKnownClient(clientAddon))
++ {
++ /* stop the client and remove it from the db */
++ bReturn &= StopClient(clientAddon, false) && bReturn;
++ }
++ else if (clientAddon->Enabled() && (bInitialiseAllClients || !IsKnownClient(clientAddon)))
++ {
++ /* register the new client and initialise it */
++ bReturn &= InitialiseClient(clientAddon) && bReturn;
++ }
++ }
++
++ /* check whether all clients are (still) connected */
++ {
++ CSingleLock lock(m_critSection);
++ m_bAllClientsConnected = (ConnectedClientAmount() == EnabledClientAmount());
++ }
++
++ return bReturn;
++}
++
++void CPVRClients::ResetQualityData(PVR_SIGNAL_STATUS &qualityInfo)
++{
++ if (g_guiSettings.GetBool("pvrplayback.signalquality"))
++ {
++ strncpy(qualityInfo.strAdapterName, g_localizeStrings.Get(13205).c_str(), 1024);
++ strncpy(qualityInfo.strAdapterStatus, g_localizeStrings.Get(13205).c_str(), 1024);
++ }
++ else
++ {
++ strncpy(qualityInfo.strAdapterName, g_localizeStrings.Get(13106).c_str(), 1024);
++ strncpy(qualityInfo.strAdapterStatus, g_localizeStrings.Get(13106).c_str(), 1024);
++ }
++ qualityInfo.iSNR = 0;
++ qualityInfo.iSignal = 0;
++ qualityInfo.iSNR = 0;
++ qualityInfo.iUNC = 0;
++ qualityInfo.dVideoBitrate = 0;
++ qualityInfo.dAudioBitrate = 0;
++ qualityInfo.dDolbyBitrate = 0;
++}
++
++int CPVRClients::ReadLiveStream(void* lpBuf, int64_t uiBufSize)
++{
++ CSingleLock lock(m_critSection);
++ return m_bIsPlayingLiveTV ? m_clientMap[m_currentChannel.ClientID()]->ReadLiveStream(lpBuf, uiBufSize) : 0;
++}
++
++int CPVRClients::ReadRecordedStream(void* lpBuf, int64_t uiBufSize)
++{
++ CSingleLock lock(m_critSection);
++ return m_bIsPlayingRecording ? m_clientMap[m_currentRecording.m_iClientId]->ReadRecordedStream(lpBuf, uiBufSize) : 0;
++}
++
++void CPVRClients::Process(void)
++{
++ bool bCheckedEnabledClientsOnStartup(false);
++
++ CAddonMgr::Get().RegisterAddonMgrCallback(ADDON_PVRDLL, this);
++ CAddonMgr::Get().RegisterObserver(this);
++
++ UpdateAddons();
++
++ while (!g_application.m_bStop && !m_bStop)
++ {
++ UpdateAndInitialiseClients();
++
++ if (!bCheckedEnabledClientsOnStartup)
++ {
++ bCheckedEnabledClientsOnStartup = true;
++ if (!HasEnabledClients())
++ ShowDialogNoClientsEnabled();
++ }
++ UpdateCharInfoSignalStatus();
++ Sleep(1000);
++ }
++}
++
++void CPVRClients::ShowDialogNoClientsEnabled(void)
++{
++ CGUIDialogOK::ShowAndGetInput(19240, 19241, 19242, 19243);
++
++ vector<CStdString> params;
++ params.push_back("addons://enabled/xbmc.pvrclient");
++ params.push_back("return");
++ g_windowManager.ActivateWindow(WINDOW_ADDON_BROWSER, params);
++}
++
++void CPVRClients::UpdateCharInfoSignalStatus(void)
++{
++ CPVRChannel currentChannel;
++ boost::shared_ptr<CPVRClient> client;
++ PVR_SIGNAL_STATUS qualityInfo;
++ ResetQualityData(qualityInfo);
++
++ if (GetPlayingChannel(currentChannel) &&
++ g_guiSettings.GetBool("pvrplayback.signalquality") &&
++ !currentChannel.IsVirtual() &&
++ GetConnectedClient(currentChannel.ClientID(), client))
++ {
++ client->SignalQuality(qualityInfo);
++ }
++
++ CSingleLock lock(m_critSection);
++ m_qualityInfo = qualityInfo;
++}
++
++void CPVRClients::SaveCurrentChannelSettings(void)
++{
++ CPVRChannel channel;
++ {
++ CSingleLock lock(m_critSection);
++ if (!GetPlayingChannel(channel) || !m_bIsValidChannelSettings)
++ return;
++ }
++
++ CPVRDatabase *database = GetPVRDatabase();
++ if (!database)
++ return;
++
++ if (g_settings.m_currentVideoSettings != g_settings.m_defaultVideoSettings)
++ {
++ CLog::Log(LOGDEBUG, "PVR - %s - persisting custom channel settings for channel '%s'",
++ __FUNCTION__, channel.ChannelName().c_str());
++ database->PersistChannelSettings(channel, g_settings.m_currentVideoSettings);
++ }
++ else
++ {
++ CLog::Log(LOGDEBUG, "PVR - %s - no custom channel settings for channel '%s'",
++ __FUNCTION__, channel.ChannelName().c_str());
++ database->DeleteChannelSettings(channel);
++ }
++}
++
++void CPVRClients::LoadCurrentChannelSettings(void)
++{
++ CPVRChannel channel;
++ {
++ CSingleLock lock(m_critSection);
++ if (!GetPlayingChannel(channel))
++ return;
++ }
++
++ CPVRDatabase *database = GetPVRDatabase();
++ if (!database)
++ return;
++
++ if (g_application.m_pPlayer)
++ {
++ /* set the default settings first */
++ CVideoSettings loadedChannelSettings = g_settings.m_defaultVideoSettings;
++
++ /* try to load the settings from the database */
++ database->GetChannelSettings(channel, loadedChannelSettings);
++
++ g_settings.m_currentVideoSettings = g_settings.m_defaultVideoSettings;
++ g_settings.m_currentVideoSettings.m_Brightness = loadedChannelSettings.m_Brightness;
++ g_settings.m_currentVideoSettings.m_Contrast = loadedChannelSettings.m_Contrast;
++ g_settings.m_currentVideoSettings.m_Gamma = loadedChannelSettings.m_Gamma;
++ g_settings.m_currentVideoSettings.m_Crop = loadedChannelSettings.m_Crop;
++ g_settings.m_currentVideoSettings.m_CropLeft = loadedChannelSettings.m_CropLeft;
++ g_settings.m_currentVideoSettings.m_CropRight = loadedChannelSettings.m_CropRight;
++ g_settings.m_currentVideoSettings.m_CropTop = loadedChannelSettings.m_CropTop;
++ g_settings.m_currentVideoSettings.m_CropBottom = loadedChannelSettings.m_CropBottom;
++ g_settings.m_currentVideoSettings.m_CustomPixelRatio = loadedChannelSettings.m_CustomPixelRatio;
++ g_settings.m_currentVideoSettings.m_CustomZoomAmount = loadedChannelSettings.m_CustomZoomAmount;
++ g_settings.m_currentVideoSettings.m_CustomVerticalShift = loadedChannelSettings.m_CustomVerticalShift;
++ g_settings.m_currentVideoSettings.m_NoiseReduction = loadedChannelSettings.m_NoiseReduction;
++ g_settings.m_currentVideoSettings.m_Sharpness = loadedChannelSettings.m_Sharpness;
++ g_settings.m_currentVideoSettings.m_InterlaceMethod = loadedChannelSettings.m_InterlaceMethod;
++ g_settings.m_currentVideoSettings.m_OutputToAllSpeakers = loadedChannelSettings.m_OutputToAllSpeakers;
++ g_settings.m_currentVideoSettings.m_AudioDelay = loadedChannelSettings.m_AudioDelay;
++ g_settings.m_currentVideoSettings.m_AudioStream = loadedChannelSettings.m_AudioStream;
++ g_settings.m_currentVideoSettings.m_SubtitleOn = loadedChannelSettings.m_SubtitleOn;
++ g_settings.m_currentVideoSettings.m_SubtitleDelay = loadedChannelSettings.m_SubtitleDelay;
++ g_settings.m_currentVideoSettings.m_CustomNonLinStretch = loadedChannelSettings.m_CustomNonLinStretch;
++ g_settings.m_currentVideoSettings.m_ScalingMethod = loadedChannelSettings.m_ScalingMethod;
++ g_settings.m_currentVideoSettings.m_PostProcess = loadedChannelSettings.m_PostProcess;
++ g_settings.m_currentVideoSettings.m_DeinterlaceMode = loadedChannelSettings.m_DeinterlaceMode;
++
++ /* only change the view mode if it's different */
++ if (g_settings.m_currentVideoSettings.m_ViewMode != loadedChannelSettings.m_ViewMode)
++ {
++ g_settings.m_currentVideoSettings.m_ViewMode = loadedChannelSettings.m_ViewMode;
++
++ g_renderManager.SetViewMode(g_settings.m_currentVideoSettings.m_ViewMode);
++ g_settings.m_currentVideoSettings.m_CustomZoomAmount = g_settings.m_fZoomAmount;
++ g_settings.m_currentVideoSettings.m_CustomPixelRatio = g_settings.m_fPixelRatio;
++ }
++
++ /* only change the subtitle stream, if it's different */
++ if (g_settings.m_currentVideoSettings.m_SubtitleStream != loadedChannelSettings.m_SubtitleStream)
++ {
++ g_settings.m_currentVideoSettings.m_SubtitleStream = loadedChannelSettings.m_SubtitleStream;
++
++ g_application.m_pPlayer->SetSubtitle(g_settings.m_currentVideoSettings.m_SubtitleStream);
++ }
++
++ /* only change the audio stream if it's different */
++ if (g_application.m_pPlayer->GetAudioStream() != g_settings.m_currentVideoSettings.m_AudioStream)
++ g_application.m_pPlayer->SetAudioStream(g_settings.m_currentVideoSettings.m_AudioStream);
++
++ g_application.m_pPlayer->SetAVDelay(g_settings.m_currentVideoSettings.m_AudioDelay);
++ g_application.m_pPlayer->SetDynamicRangeCompression((long)(g_settings.m_currentVideoSettings.m_VolumeAmplification * 100));
++ g_application.m_pPlayer->SetSubtitleVisible(g_settings.m_currentVideoSettings.m_SubtitleOn);
++ g_application.m_pPlayer->SetSubTitleDelay(g_settings.m_currentVideoSettings.m_SubtitleDelay);
++
++ /* settings can be saved on next channel switch */
++ m_bIsValidChannelSettings = true;
++ }
++}
++
++bool CPVRClients::UpdateAddons(void)
++{
++ ADDON::VECADDONS addons;
++ bool bReturn(CAddonMgr::Get().GetAddons(ADDON_PVRDLL, addons, true, false));
++
++ if (bReturn)
++ {
++ CSingleLock lock(m_critSection);
++ m_addons = addons;
++ }
++
++ return bReturn;
++}
++
++void CPVRClients::Notify(const Observable &obs, const CStdString& msg)
++{
++ if (msg.Equals("addons"))
++ {
++ UpdateAddons();
++ UpdateAndInitialiseClients();
++ }
++}
++
++bool CPVRClients::GetClient(const CStdString &strId, ADDON::AddonPtr &addon) const
++{
++ CSingleLock lock(m_critSection);
++ for (CLIENTMAPCITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++)
++ {
++ if (itr->second->ID() == strId)
++ {
++ addon = itr->second;
++ return true;
++ }
++ }
++ return false;
++}
+diff --git a/xbmc/pvr/addons/PVRClients.h b/xbmc/pvr/addons/PVRClients.h
+new file mode 100644
+index 0000000..a48f061
+--- /dev/null
++++ b/xbmc/pvr/addons/PVRClients.h
+@@ -0,0 +1,681 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "threads/CriticalSection.h"
++#include "threads/Thread.h"
++#include "utils/Observer.h"
++#include "PVRClient.h"
++#include "pvr/channels/PVRChannel.h"
++#include "pvr/recordings/PVRRecording.h"
++
++#include <vector>
++#include <deque>
++
++namespace EPG
++{
++ class CEpg;
++}
++
++namespace PVR
++{
++ class CPVRGUIInfo;
++
++ typedef std::map< int, boost::shared_ptr<CPVRClient> > CLIENTMAP;
++ typedef std::map< int, boost::shared_ptr<CPVRClient> >::iterator CLIENTMAPITR;
++ typedef std::map< int, boost::shared_ptr<CPVRClient> >::const_iterator CLIENTMAPCITR;
++ typedef std::map< int, PVR_STREAM_PROPERTIES > STREAMPROPS;
++
++ #define XBMC_VIRTUAL_CLIENTID -1
++
++ class CPVRClients : public ADDON::IAddonMgrCallback,
++ public Observer,
++ private CThread
++ {
++ friend class CPVRGUIInfo;
++
++ public:
++ CPVRClients(void);
++ virtual ~CPVRClients(void);
++
++ /*!
++ * @brief Start the backend info updater thread.
++ */
++ void Start(void);
++
++ /*!
++ * @brief Stop the backend info updater thread.
++ */
++ void Stop(void);
++
++ /*!
++ * @brief Load the settings for the current channel from the database.
++ */
++ void LoadCurrentChannelSettings(void);
++
++ /*!
++ * @brief Persist the current channel settings in the database.
++ */
++ void SaveCurrentChannelSettings(void);
++
++ /*! @name Backend methods */
++ //@{
++
++ /*!
++ * @brief Check whether a client ID points to a valid and connected add-on.
++ * @param iClientId The client ID.
++ * @return True when the client ID is valid and connected, false otherwise.
++ */
++ bool IsConnectedClient(int iClientId);
++
++ /*!
++ * @brief Restart a single client add-on.
++ * @param addon The add-on to restart.
++ * @param bDataChanged True if the client's data changed, false otherwise (unused).
++ * @return True if the client was found and restarted, false otherwise.
++ */
++ bool RequestRestart(ADDON::AddonPtr addon, bool bDataChanged);
++
++ /*!
++ * @brief Remove a single client add-on.
++ * @param addon The add-on to remove.
++ * @return True if the client was found and removed, false otherwise.
++ */
++ bool RequestRemoval(ADDON::AddonPtr addon);
++
++ /*!
++ * @brief Unload all loaded add-ons and reset all class properties.
++ */
++ void Unload(void);
++
++ /*!
++ * @brief The ID of the first active client or -1 if no clients are active;
++ */
++ int GetFirstConnectedClientID(void);
++
++ /*!
++ * @return True when all clients are connected, false otherwise.
++ */
++ bool AllClientsConnected(void) const;
++
++ /*!
++ * @return True when at least one client is known and enabled, false otherwise.
++ */
++ bool HasEnabledClients(void) const;
++
++ /*!
++ * @return The amount of enabled clients.
++ */
++ int EnabledClientAmount(void) const;
++
++ /*!
++ * @brief Stop a client.
++ * @param addon The client to stop.
++ * @param bRestart If true, restart the client.
++ * @return True if the client was found, false otherwise.
++ */
++ bool StopClient(ADDON::AddonPtr client, bool bRestart);
++
++ /*!
++ * @return The amount of connected clients.
++ */
++ int ConnectedClientAmount(void);
++
++ /*!
++ * @brief Check whether there are any connected clients.
++ * @return True if at least one client is connected.
++ */
++ bool HasConnectedClients(void);
++
++ /*!
++ * @brief Get the friendly name for the client with the given id.
++ * @param iClientId The id of the client.
++ * @param strName The friendly name of the client or an empty string when it wasn't found.
++ * @return True if the client was found, false otherwise.
++ */
++ bool GetClientName(int iClientId, CStdString &strName);
++
++ /*!
++ * @bried Get all connected clients.
++ * @param clients Store the active clients in this map.
++ * @return The amount of added clients.
++ */
++ int GetConnectedClients(CLIENTMAP *clients);
++
++ /*!
++ * @return The client ID of the client that is currently playing a stream or -1 if no client is playing.
++ */
++ int GetPlayingClientID(void) const;
++
++ /*!
++ * @brief Get the capabilities for a specific client.
++ * @param clientID The ID of the client.
++ * @return The add-on's capabilities.
++ */
++ PVR_ADDON_CAPABILITIES GetAddonCapabilities(int iClientId) const;
++
++ /*!
++ * @brief Get the capabilities of the current playing client.
++ * @return The add-on's capabilities.
++ */
++ PVR_ADDON_CAPABILITIES GetCurrentAddonCapabilities(void);
++
++ //@}
++
++ /*! @name Stream methods */
++ //@{
++
++ /*!
++ * @return True if a stream is playing, false otherwise.
++ */
++ bool IsPlaying(void) const;
++
++ /*!
++ * @return The friendly name of the client that is currently playing or an empty string if nothing is playing.
++ */
++ const CStdString GetPlayingClientName(void) const;
++
++ /*!
++ * @brief Read from an open stream.
++ * @param lpBuf Target buffer.
++ * @param uiBufSize The size of the buffer.
++ * @return The amount of bytes that was added.
++ */
++ int ReadStream(void* lpBuf, int64_t uiBufSize);
++
++ /*!
++ * @brief Return the filesize of the currently running stream.
++ * Limited to recordings playback at the moment.
++ * @return The size of the stream.
++ */
++ int64_t LengthStream(void);
++
++ /*!
++ * @brief Seek to a position in a stream.
++ * Limited to recordings playback at the moment.
++ * @param iFilePosition The position to seek to.
++ * @param iWhence Specify how to seek ("new position=pos", "new position=pos+actual postion" or "new position=filesize-pos")
++ * @return The new stream position.
++ */
++ int64_t SeekStream(int64_t iFilePosition, int iWhence = SEEK_SET);
++
++ /*!
++ * @brief Get the currently playing position in a stream.
++ * @return The current position.
++ */
++ int64_t GetStreamPosition(void);
++
++ /*!
++ * @brief Close a PVR stream.
++ */
++ void CloseStream(void);
++
++ /*!
++ * @brief Get the properties of the current playing stream content.
++ * @return A pointer to the properties or NULL if no stream is playing.
++ */
++ PVR_STREAM_PROPERTIES *GetCurrentStreamProperties(void);
++
++ /*!
++ * @brief Get the input format name of the current playing stream content.
++ * @return A pointer to the properties or NULL if no stream is playing.
++ */
++ CStdString GetCurrentInputFormat(void) const;
++ //@}
++
++ /*! @name Live TV stream methods */
++ //@{
++
++ /*!
++ * @return True if a live stream is playing, false otherwise.
++ */
++ bool IsReadingLiveStream(void) const;
++
++ /*!
++ * @return True if a TV channel is playing, false otherwise.
++ */
++ bool IsPlayingTV(void) const;
++
++ /*!
++ * @return True if a radio channel playing, false otherwise.
++ */
++ bool IsPlayingRadio(void) const;
++
++ /*!
++ * @return True if the currently playing channel is encrypted, false otherwise.
++ */
++ bool IsEncrypted(void) const;
++
++ /*!
++ * @brief Open a stream on the given channel.
++ * @param tag The channel to start playing.
++ * @return True if the stream was opened successfully, false otherwise.
++ */
++ bool OpenLiveStream(const CPVRChannel &tag);
++
++ /*!
++ * @brief Close an opened live stream.
++ * @return True if the stream was closed successfully, false otherwise.
++ */
++ bool CloseLiveStream(void);
++
++ /*!
++ * @brief Get the URL for the stream to the given channel.
++ * @param tag The channel to get the stream url for.
++ * @return The requested stream url or an empty string if it wasn't found.
++ */
++ CStdString GetStreamURL(const CPVRChannel &tag);
++
++ /*!
++ * @brief Switch an opened live tv stream to another channel.
++ * @param channel The channel to switch to.
++ * @return True if the switch was successfull, false otherwise.
++ */
++ bool SwitchChannel(const CPVRChannel &channel);
++
++ /*!
++ * @brief Get the channel that is currently playing.
++ * @param channel A copy of the channel that is currently playing.
++ * @return True if a channel is playing, false otherwise.
++ */
++ bool GetPlayingChannel(CPVRChannel &channel) const;
++
++ //@}
++
++ /*! @name Recording stream methods */
++ //@{
++
++ /*!
++ * @return True if a recording is playing, false otherwise.
++ */
++ bool IsPlayingRecording(void) const;
++
++ /*!
++ * @brief Open a stream from the given recording.
++ * @param tag The recording to start playing.
++ * @return True if the stream was opened successfully, false otherwise.
++ */
++ bool OpenRecordedStream(const CPVRRecording &tag);
++
++ /*!
++ * @brief Close an opened stream from a recording.
++ * @return True if the stream was closed successfully, false otherwise.
++ */
++ bool CloseRecordedStream(void);
++
++ /*!
++ * @brief Get the recordings that is currently playing.
++ * @param recording A copy of the recording that is currently playing.
++ * @return True if a recording is playing, false otherwise.
++ */
++ bool GetPlayingRecording(CPVRRecording &recording) const;
++
++ //@}
++
++ /*! @name Stream demux methods */
++ //@{
++
++ /*!
++ * @brief Reset the demuxer.
++ */
++ void DemuxReset(void);
++
++ /*!
++ * @brief Abort any internal reading that might be stalling main thread.
++ * NOTICE - this can be called from another thread.
++ */
++ void DemuxAbort(void);
++
++ /*!
++ * @brief Flush the demuxer. If any data is kept in buffers, this should be freed now.
++ */
++ void DemuxFlush(void);
++
++ /*!
++ * @brief Read the stream from the demuxer.
++ * @return An allocated demuxer packet.
++ */
++ DemuxPacket* ReadDemuxStream(void);
++
++ //@}
++
++ /*! @name Signal status methods */
++ //@{
++
++ /*!
++ * @brief Get the quality data for the live stream that is currently playing.
++ * @param status A copy of the quality data.
++ */
++ void GetQualityData(PVR_SIGNAL_STATUS *status) const;
++
++ /*!
++ * @return The current signal quality level.
++ */
++ int GetSignalLevel(void) const;
++
++ /*!
++ * @return The current signal/noise ratio.
++ */
++ int GetSNR(void) const;
++
++ //@}
++
++ /*! @name Timer methods */
++ //@{
++
++ /*!
++ * @brief Check whether a client supports timers.
++ * @param iClientId The id of the client to check.
++ * @return True if the supports timers, false otherwise.
++ */
++ bool HasTimerSupport(int iClientId);
++
++ /*!
++ * @brief Get all timers from clients
++ * @param timers Store the timers in this container.
++ * @return The amount of timers that were added.
++ */
++ int GetTimers(CPVRTimers *timers);
++
++ /*!
++ * @brief Add a new timer to a backend.
++ * @param timer The timer to add.
++ * @param error An error if it occured.
++ * @return True if the timer was added successfully, false otherwise.
++ */
++ bool AddTimer(const CPVRTimerInfoTag &timer, PVR_ERROR *error);
++
++ /*!
++ * @brief Update a timer on the backend.
++ * @param timer The timer to update.
++ * @param error An error if it occured.
++ * @return True if the timer was updated successfully, false otherwise.
++ */
++ bool UpdateTimer(const CPVRTimerInfoTag &timer, PVR_ERROR *error);
++
++ /*!
++ * @brief Delete a timer from the backend.
++ * @param timer The timer to delete.
++ * @param bForce Also delete when currently recording if true.
++ * @param error An error if it occured.
++ * @return True if the timer was deleted successfully, false otherwise.
++ */
++ bool DeleteTimer(const CPVRTimerInfoTag &timer, bool bForce, PVR_ERROR *error);
++
++ /*!
++ * @brief Rename a timer on the backend.
++ * @param timer The timer to rename.
++ * @param strNewName The new name.
++ * @param error An error if it occured.
++ * @return True if the timer was renamed successfully, false otherwise.
++ */
++ bool RenameTimer(const CPVRTimerInfoTag &timer, const CStdString &strNewName, PVR_ERROR *error);
++
++ //@}
++
++ /*! @name Recording methods */
++ //@{
++
++ /*!
++ * @brief Check whether a client supports recordings.
++ * @param iClientId The id of the client to check.
++ * @return True if the supports recordings, false otherwise.
++ */
++ bool HasRecordingsSupport(int iClientId);
++
++ /*!
++ * @brief Get all recordings from clients
++ * @param recordings Store the recordings in this container.
++ * @return The amount of recordings that were added.
++ */
++ int GetRecordings(CPVRRecordings *recordings);
++
++ /*!
++ * @brief Rename a recordings on the backend.
++ * @param recording The recordings to rename.
++ * @param error An error if it occured.
++ * @return True if the recording was renamed successfully, false otherwise.
++ */
++ bool RenameRecording(const CPVRRecording &recording, PVR_ERROR *error);
++
++ /*!
++ * @brief Delete a recording from the backend.
++ * @param recording The recording to delete.
++ * @param error An error if it occured.
++ * @return True if the recordings was deleted successfully, false otherwise.
++ */
++ bool DeleteRecording(const CPVRRecording &recording, PVR_ERROR *error);
++
++ /*!
++ * @brief Check whether there is an active recording on the current channel.
++ * @return True if there is, false otherwise.
++ */
++ bool IsRecordingOnPlayingChannel(void) const;
++
++ /*!
++ * @brief Check whether the current channel can be recorded instantly.
++ * @return True if it can, false otherwise.
++ */
++ bool CanRecordInstantly(void);
++
++ //@}
++
++ /*! @name EPG methods */
++ //@{
++
++ /*!
++ * @brief Check whether a client supports EPG transfer.
++ * @param iClientId The id of the client to check.
++ * @return True if the supports EPG transfer, false otherwise.
++ */
++ bool HasEPGSupport(int iClientId);
++
++ /*!
++ * @brief Get the EPG table for a channel.
++ * @param channel The channel to get the EPG table for.
++ * @param epg Store the EPG in this container.
++ * @param start Get entries after this start time.
++ * @param end Get entries before this end time.
++ * @param error An error if it occured.
++ * @return True if the EPG was transfered successfully, false otherwise.
++ */
++ bool GetEPGForChannel(const CPVRChannel &channel, EPG::CEpg *epg, time_t start, time_t end, PVR_ERROR *error);
++
++ //@}
++
++ /*! @name Channel methods */
++ //@{
++
++ /*!
++ * @brief Get all channels from backends.
++ * @param group The container to store the channels in.
++ * @param error An error if it occured.
++ * @return The amount of channels that were added.
++ */
++ int GetChannels(CPVRChannelGroupInternal *group, PVR_ERROR *error);
++
++ /*!
++ * @brief Check whether a client supports channel groups.
++ * @param iClientId The id of the client to check.
++ * @return True if the supports channel groups, false otherwise.
++ */
++ bool HasChannelGroupSupport(int iClientId);
++
++ /*!
++ * @brief Get all channel groups from backends.
++ * @param groups Store the channel groups in this container.
++ * @param error An error if it occured.
++ * @return The amount of groups that were added.
++ */
++ int GetChannelGroups(CPVRChannelGroups *groups, PVR_ERROR *error);
++
++ /*!
++ * @brief Get all group members of a channel group.
++ * @param group The group to get the member for.
++ * @param error An error if it occured.
++ * @return The amount of channels that were added.
++ */
++ int GetChannelGroupMembers(CPVRChannelGroup *group, PVR_ERROR *error);
++
++ //@}
++
++ /*! @name Menu hook methods */
++ //@{
++
++ /*!
++ * @brief Check whether a client has any PVR specific menu entries.
++ * @param iClientId The ID of the client to get the menu entries for. Get the menu for the active channel if iClientId < 0.
++ * @return True if the client has any menu hooks, false otherwise.
++ */
++ bool HasMenuHooks(int iClientId);
++
++ /*!
++ * @brief Open selection and progress PVR actions.
++ * @param iClientId The ID of the client to process the menu entries for. Process the menu entries for the active channel if iClientId < 0.
++ */
++ void ProcessMenuHooks(int iClientID);
++
++ //@}
++
++ /*! @name Channel scan methods */
++ //@{
++
++ /*!
++ * @return True when a channel scan is currently running, false otherwise.
++ */
++ bool IsRunningChannelScan(void) const;
++
++ /*!
++ * @brief Open a selection dialog and start a channel scan on the selected client.
++ */
++ void StartChannelScan(void);
++
++ //@}
++
++ void Notify(const Observable &obs, const CStdString& msg);
++
++ bool GetClient(const CStdString &strId, ADDON::AddonPtr &addon) const;
++ private:
++ /*!
++ * @brief Update add-ons from the AddonManager
++ * @return True when updated, false otherwise
++ */
++ bool UpdateAddons(void);
++
++ /*!
++ * @brief Register a client in the db if it's not been registered yet.
++ * @param client The client to register.
++ * @return The database id of the client or -1 if an error occured.
++ */
++ int AddClientToDb(const ADDON::AddonPtr client);
++
++ /*!
++ * @brief Read from a livetv stream.
++ * @param lpBuf The buffer to store the data in.
++ * @param uiBufSize The length to read.
++ * @return The number of bytes read.
++ */
++ int ReadLiveStream(void* lpBuf, int64_t uiBufSize);
++
++ /*!
++ * @brief Read from a recorded tv stream.
++ * @param lpBuf The buffer to store the data in.
++ * @param uiBufSize The length to read.
++ * @return The number of bytes read.
++ */
++ int ReadRecordedStream(void* lpBuf, int64_t uiBufSize);
++
++ /*!
++ * @brief Get the menu hooks for a client.
++ * @param iClientID The client to get the hooks for.
++ * @param hooks The container to add the hooks to.
++ * @return True if the hooks were added successfully (if any), false otherwise.
++ */
++ bool GetMenuHooks(int iClientID, PVR_MENUHOOKS *hooks);
++
++ /*!
++ * @brief Update the signal status for the tv stream that's currently being read.
++ */
++ void UpdateCharInfoSignalStatus(void);
++
++ /*!
++ * @brief Reset the signal quality data to the initial values.
++ */
++ void ResetQualityData(PVR_SIGNAL_STATUS &qualityInfo);
++
++ /*!
++ * @brief Updates the backend information
++ */
++ void Process(void);
++
++ /*!
++ * @brief Show a dialog to guide new users who have no clients enabled.
++ */
++ void ShowDialogNoClientsEnabled(void);
++
++ /*!
++ * @brief Get the instance of the client, if it's connected.
++ * @param iClientId The id of the client to get.
++ * @param addon The client.
++ * @return True if the client is connected, false otherwise.
++ */
++ bool GetConnectedClient(int iClientId, boost::shared_ptr<CPVRClient> &addon) const;
++
++ /*!
++ * @brief Check whether a client is registered.
++ * @param client The client to check.
++ * @return True if this client is registered, false otherwise.
++ */
++ bool IsKnownClient(const ADDON::AddonPtr client) const;
++
++ /*!
++ * @brief Check whether there are any new pvr add-ons enabled or whether any of the known clients has been disabled.
++ * @param bInitialiseAllClients True to initialise all clients, false to only initialise new clients.
++ * @return True if all clients were updated successfully, false otherwise.
++ */
++ bool UpdateAndInitialiseClients(bool bInitialiseAllClients = false);
++
++ /*!
++ * @brief Initialise and connect a client.
++ * @param client The client to initialise.
++ * @return True if the client was initialised successfully, false otherwise.
++ */
++ bool InitialiseClient(ADDON::AddonPtr client);
++
++ int GetClientId(const ADDON::AddonPtr client) const;
++
++ bool m_bChannelScanRunning; /*!< true when a channel scan is currently running, false otherwise */
++ bool m_bAllClientsConnected; /*!< true when all clients are loaded, false otherwise */
++ bool m_bIsSwitchingChannels; /*!< true while switching channels */
++ bool m_bIsValidChannelSettings; /*!< true if current channel settings are valid and can be saved */
++ CPVRChannel m_currentChannel; /*!< the channel that is currently playing or NULL if nothing is playing */
++ bool m_bIsPlayingLiveTV;
++ CPVRRecording m_currentRecording; /*!< the recording that is currently playing or NULL if nothing is playing */
++ bool m_bIsPlayingRecording;
++ DWORD m_scanStart; /*!< scan start time to check for non present streams */
++ CStdString m_strPlayingClientName; /*!< the name client that is currenty playing a stream or an empty string if nothing is playing */
++ ADDON::VECADDONS m_addons;
++ CLIENTMAP m_clientMap; /*!< a map of all known clients */
++ PVR_SIGNAL_STATUS m_qualityInfo; /*!< stream quality information */
++ STREAMPROPS m_streamProps; /*!< the current stream's properties */
++ CCriticalSection m_critSection;
++ };
++}
+diff --git a/xbmc/pvr/channels/Makefile b/xbmc/pvr/channels/Makefile
+new file mode 100644
+index 0000000..cb85663
+--- /dev/null
++++ b/xbmc/pvr/channels/Makefile
+@@ -0,0 +1,10 @@
++SRCS=PVRChannel.cpp \
++ PVRChannelGroup.cpp \
++ PVRChannelGroupInternal.cpp \
++ PVRChannelGroups.cpp \
++ PVRChannelGroupsContainer.cpp
++
++LIB=pvrchannels.a
++
++include ../../../Makefile.include
++-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
+diff --git a/xbmc/pvr/channels/PVRChannel.cpp b/xbmc/pvr/channels/PVRChannel.cpp
+new file mode 100644
+index 0000000..71d4eb4
+--- /dev/null
++++ b/xbmc/pvr/channels/PVRChannel.cpp
+@@ -0,0 +1,797 @@
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "FileItem.h"
++#include "guilib/LocalizeStrings.h"
++#include "utils/log.h"
++#include "TextureCache.h"
++#include "Util.h"
++#include "filesystem/File.h"
++#include "music/tags/MusicInfoTag.h"
++#include "settings/GUISettings.h"
++#include "utils/URIUtils.h"
++#include "utils/StringUtils.h"
++#include "threads/SingleLock.h"
++
++#include "PVRChannelGroupsContainer.h"
++#include "epg/EpgContainer.h"
++#include "pvr/timers/PVRTimers.h"
++#include "pvr/PVRDatabase.h"
++#include "pvr/PVRManager.h"
++
++using namespace XFILE;
++using namespace MUSIC_INFO;
++using namespace PVR;
++using namespace EPG;
++
++bool CPVRChannel::operator==(const CPVRChannel &right) const
++{
++ if (this == &right) return true;
++
++ return (m_bIsRadio == right.m_bIsRadio &&
++ m_iUniqueId == right.m_iUniqueId &&
++ m_iClientId == right.m_iClientId);
++}
++
++bool CPVRChannel::operator!=(const CPVRChannel &right) const
++{
++ return !(*this == right);
++}
++
++CPVRChannel::CPVRChannel(bool bRadio /* = false */)
++{
++ m_iChannelId = -1;
++ m_bIsRadio = bRadio;
++ m_bIsHidden = false;
++ m_bIsUserSetIcon = false;
++ m_strIconPath = StringUtils::EmptyString;
++ m_strChannelName = StringUtils::EmptyString;
++ m_bIsVirtual = false;
++ m_iLastWatched = 0;
++ m_bChanged = false;
++ m_iCachedChannelNumber = 0;
++
++ m_iEpgId = -1;
++ m_bEPGCreated = false;
++ m_bEPGEnabled = true;
++ m_strEPGScraper = "client";
++
++ m_iUniqueId = -1;
++ m_iClientId = -1;
++ m_iClientChannelNumber = -1;
++ m_strClientChannelName = StringUtils::EmptyString;
++ m_strInputFormat = StringUtils::EmptyString;
++ m_strStreamURL = StringUtils::EmptyString;
++ m_strFileNameAndPath = StringUtils::EmptyString;
++ m_iClientEncryptionSystem = -1;
++}
++
++CPVRChannel::CPVRChannel(const PVR_CHANNEL &channel, unsigned int iClientId)
++{
++ m_iChannelId = -1;
++ m_bIsRadio = channel.bIsRadio;
++ m_bIsHidden = channel.bIsHidden;
++ m_bIsUserSetIcon = false;
++ m_strIconPath = channel.strIconPath;
++ m_strChannelName = channel.strChannelName;
++ m_iUniqueId = channel.iUniqueId;
++ m_iClientChannelNumber = channel.iChannelNumber;
++ m_strClientChannelName = channel.strChannelName;
++ m_strInputFormat = channel.strInputFormat;
++ m_strStreamURL = channel.strStreamURL;
++ m_iClientEncryptionSystem = channel.iEncryptionSystem;
++ m_iCachedChannelNumber = 0;
++ m_iClientId = iClientId;
++ m_strFileNameAndPath = StringUtils::EmptyString;
++ m_bIsVirtual = false;
++ m_iLastWatched = 0;
++ m_bEPGEnabled = true;
++ m_strEPGScraper = "client";
++ m_iEpgId = -1;
++ m_bEPGCreated = false;
++ m_bChanged = false;
++
++ if (m_strChannelName.IsEmpty())
++ m_strChannelName.Format("%s %d", g_localizeStrings.Get(19029), m_iUniqueId);
++
++ UpdateEncryptionName();
++}
++
++CPVRChannel::CPVRChannel(const CPVRChannel &channel)
++{
++ *this = channel;
++}
++
++CPVRChannel &CPVRChannel::operator=(const CPVRChannel &channel)
++{
++ m_iChannelId = channel.m_iChannelId;
++ m_bIsRadio = channel.m_bIsRadio;
++ m_bIsHidden = channel.m_bIsHidden;
++ m_bIsUserSetIcon = channel.m_bIsUserSetIcon;
++ m_strIconPath = channel.m_strIconPath;
++ m_strChannelName = channel.m_strChannelName;
++ m_bIsVirtual = channel.m_bIsVirtual;
++ m_iLastWatched = channel.m_iLastWatched;
++ m_bEPGEnabled = channel.m_bEPGEnabled;
++ m_strEPGScraper = channel.m_strEPGScraper;
++ m_iUniqueId = channel.m_iUniqueId;
++ m_iClientId = channel.m_iClientId;
++ m_iClientChannelNumber = channel.m_iClientChannelNumber;
++ m_strClientChannelName = channel.m_strClientChannelName;
++ m_strInputFormat = channel.m_strInputFormat;
++ m_strStreamURL = channel.m_strStreamURL;
++ m_strFileNameAndPath = channel.m_strFileNameAndPath;
++ m_iClientEncryptionSystem = channel.m_iClientEncryptionSystem;
++ m_iCachedChannelNumber = channel.m_iCachedChannelNumber;
++ m_iEpgId = channel.m_iEpgId;
++ m_bEPGCreated = channel.m_bEPGCreated;
++ m_bChanged = channel.m_bChanged;
++
++ UpdateEncryptionName();
++
++ return *this;
++}
++
++/********** XBMC related channel methods **********/
++
++bool CPVRChannel::Delete(void)
++{
++ bool bReturn = false;
++ CPVRDatabase *database = GetPVRDatabase();
++ if (!database)
++ return bReturn;
++
++ /* delete the EPG table */
++ CEpg *epg = GetEPG();
++ if (epg)
++ {
++ g_EpgContainer.DeleteEpg(*epg, true);
++ CSingleLock lock(m_critSection);
++ m_bEPGCreated = false;
++ }
++
++ bReturn = database->Delete(*this);
++ return bReturn;
++}
++
++CEpg *CPVRChannel::GetEPG(void) const
++{
++ CEpg *epg(NULL);
++ {
++ CSingleLock lock(m_critSection);
++ if (!m_bIsHidden && m_bEPGEnabled && m_iEpgId > 0)
++ epg = g_EpgContainer.GetById(m_iEpgId);
++ }
++ return epg;
++}
++
++bool CPVRChannel::UpdateFromClient(const CPVRChannel &channel)
++{
++ SetClientID(channel.ClientID());
++ SetClientChannelNumber(channel.ClientChannelNumber());
++ SetInputFormat(channel.InputFormat());
++ SetStreamURL(channel.StreamURL());
++ SetEncryptionSystem(channel.EncryptionSystem());
++ SetClientChannelName(channel.ClientChannelName());
++
++ CSingleLock lock(m_critSection);
++ if (m_strChannelName.IsEmpty())
++ SetChannelName(channel.ClientChannelName());
++ if (m_strIconPath.IsEmpty()||(!m_strIconPath.Equals(channel.IconPath()) && !IsUserSetIcon()))
++ SetIconPath(channel.IconPath(), false, false);
++
++ return m_bChanged;
++}
++
++bool CPVRChannel::Persist(bool bQueueWrite /* = false */)
++{
++ bool bReturn(true);
++ CSingleLock lock(m_critSection);
++ if (!m_bChanged && m_iChannelId > 0)
++ return bReturn;
++
++ if (CPVRDatabase *database = GetPVRDatabase())
++ {
++ if (!bQueueWrite)
++ {
++ bReturn = database->Persist(*this, false);
++ m_bChanged = !bReturn;
++ }
++ else
++ {
++ bReturn = database->Persist(*this, true);
++ }
++ }
++ else
++ {
++ bReturn = false;
++ }
++
++ return bReturn;
++}
++
++bool CPVRChannel::SetChannelID(int iChannelId, bool bSaveInDb /* = false */)
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ if (m_iChannelId != iChannelId)
++ {
++ /* update the id */
++ m_iChannelId = iChannelId;
++ SetChanged();
++ m_bChanged = true;
++
++ /* persist the changes */
++ if (bSaveInDb)
++ Persist();
++
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++int CPVRChannel::ChannelNumber(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_iCachedChannelNumber;
++}
++
++bool CPVRChannel::SetHidden(bool bIsHidden, bool bSaveInDb /* = false */)
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ if (m_bIsHidden != bIsHidden)
++ {
++ /* update the hidden flag */
++ m_bIsHidden = bIsHidden;
++ SetChanged();
++ m_bChanged = true;
++
++ /* persist the changes */
++ if (bSaveInDb)
++ Persist();
++
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CPVRChannel::IsRecording(void) const
++{
++ return g_PVRTimers->IsRecordingOnChannel(*this);
++}
++
++bool CPVRChannel::SetIconPath(const CStdString &strIconPath, bool bSaveInDb /* = false */, bool bIsUserSetIcon /* = true */)
++{
++ bool bReturn(true); // different from the behaviour of the rest of this class
++ CSingleLock lock(m_critSection);
++
++ /* check if the path is valid */
++ if (!CFile::Exists(strIconPath) && !strIconPath.IsEmpty())
++ return false;
++
++ if (m_strIconPath != strIconPath)
++ {
++ /* update the path */
++ m_strIconPath.Format("%s", strIconPath);
++ SetChanged();
++ m_bChanged = true;
++
++ /* did the user change the icon? */
++ if (bIsUserSetIcon) {
++ if (!m_strIconPath.IsEmpty()) {
++ m_bIsUserSetIcon = true;
++ }
++ else {
++ m_bIsUserSetIcon = false;
++ }
++ }
++
++ /* persist the changes */
++ if (bSaveInDb)
++ Persist();
++
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CPVRChannel::SetChannelName(const CStdString &strChannelName, bool bSaveInDb /* = false */)
++{
++ bool bReturn(false);
++ CStdString strName(strChannelName);
++
++ if (strName.IsEmpty())
++ {
++ strName.Format(g_localizeStrings.Get(19085), ClientChannelNumber());
++ }
++
++ CSingleLock lock(m_critSection);
++ if (m_strChannelName != strName)
++ {
++ /* update the channel name */
++ m_strChannelName = strName;
++ SetChanged();
++ m_bChanged = true;
++
++ /* persist the changes */
++ if (bSaveInDb)
++ Persist();
++
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CPVRChannel::SetVirtual(bool bIsVirtual, bool bSaveInDb /* = false */)
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ if (m_bIsVirtual != bIsVirtual)
++ {
++ /* update the virtual flag */
++ m_bIsVirtual = bIsVirtual;
++ SetChanged();
++ m_bChanged = true;
++
++ /* persist the changes */
++ if (bSaveInDb)
++ Persist();
++
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CPVRChannel::SetLastWatched(time_t iLastWatched, bool bSaveInDb /* = false */)
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ if (m_iLastWatched != iLastWatched)
++ {
++ /* update last watched */
++ m_iLastWatched = iLastWatched;
++ SetChanged();
++ m_bChanged = true;
++
++ /* persist the changes */
++ if (bSaveInDb)
++ Persist();
++
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CPVRChannel::IsEmpty() const
++{
++ CSingleLock lock(m_critSection);
++ return (m_strFileNameAndPath.IsEmpty() ||
++ m_strStreamURL.IsEmpty());
++}
++
++/********** Client related channel methods **********/
++
++bool CPVRChannel::SetUniqueID(int iUniqueId, bool bSaveInDb /* = false */)
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ if (m_iUniqueId != iUniqueId)
++ {
++ /* update the unique ID */
++ m_iUniqueId = iUniqueId;
++ SetChanged();
++ m_bChanged = true;
++
++ /* persist the changes */
++ if (bSaveInDb)
++ Persist();
++
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CPVRChannel::SetClientID(int iClientId, bool bSaveInDb /* = false */)
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ if (m_iClientId != iClientId)
++ {
++ /* update the client ID */
++ m_iClientId = iClientId;
++ SetChanged();
++ m_bChanged = true;
++
++ /* persist the changes */
++ if (bSaveInDb)
++ Persist();
++
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CPVRChannel::SetClientChannelNumber(int iClientChannelNumber, bool bSaveInDb /* = false */)
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ if (m_iClientChannelNumber != iClientChannelNumber && iClientChannelNumber > 0)
++ {
++ /* update the client channel number */
++ m_iClientChannelNumber = iClientChannelNumber;
++ SetChanged();
++ m_bChanged = true;
++
++ /* persist the changes */
++ if (bSaveInDb)
++ Persist();
++
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CPVRChannel::SetClientChannelName(const CStdString &strClientChannelName)
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ if (m_strClientChannelName != strClientChannelName)
++ {
++ /* update the client channel name */
++ m_strClientChannelName.Format("%s", strClientChannelName);
++ SetChanged();
++
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CPVRChannel::SetInputFormat(const CStdString &strInputFormat, bool bSaveInDb /* = false */)
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ if (m_strInputFormat != strInputFormat)
++ {
++ /* update the input format */
++ m_strInputFormat.Format("%s", strInputFormat);
++ SetChanged();
++ m_bChanged = true;
++
++ /* persist the changes */
++ if (bSaveInDb)
++ Persist();
++
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CPVRChannel::SetStreamURL(const CStdString &strStreamURL, bool bSaveInDb /* = false */)
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ if (m_strStreamURL != strStreamURL)
++ {
++ /* update the stream url */
++ m_strStreamURL.Format("%s", strStreamURL);
++ SetChanged();
++ m_bChanged = true;
++
++ /* persist the changes */
++ if (bSaveInDb)
++ Persist();
++
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++void CPVRChannel::UpdatePath(unsigned int iNewChannelNumber)
++{
++ CStdString strFileNameAndPath;
++ CSingleLock lock(m_critSection);
++ CPVRChannelGroup *group = g_PVRChannelGroups->GetGroupAll(m_bIsRadio);
++
++ if (group)
++ {
++ strFileNameAndPath.Format("pvr://channels/%s/%s/%i.pvr", (m_bIsRadio ? "radio" : "tv"), group->GroupName().c_str(), iNewChannelNumber);
++ if (m_strFileNameAndPath != strFileNameAndPath)
++ {
++ m_strFileNameAndPath = strFileNameAndPath;
++ SetChanged();
++ }
++ }
++}
++
++bool CPVRChannel::SetEncryptionSystem(int iClientEncryptionSystem, bool bSaveInDb /* = false */)
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ if (m_iClientEncryptionSystem != iClientEncryptionSystem)
++ {
++ /* update the client encryption system */
++ m_iClientEncryptionSystem = iClientEncryptionSystem;
++ UpdateEncryptionName();
++ SetChanged();
++ m_bChanged = true;
++
++ /* persist the changes */
++ if (bSaveInDb)
++ Persist();
++
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++void CPVRChannel::UpdateEncryptionName(void)
++{
++ // http://www.dvb.org/index.php?id=174
++ // http://en.wikipedia.org/wiki/Conditional_access_system
++ CStdString strName;
++ CSingleLock lock(m_critSection);
++
++ if ( m_iClientEncryptionSystem == 0x0000)
++ strName = g_localizeStrings.Get(19013); /* Free To Air */
++ else if (m_iClientEncryptionSystem < 0x0000)
++ strName = g_localizeStrings.Get(13205); /* Unknown */
++ else
++ {
++ if ( m_iClientEncryptionSystem >= 0x0001 &&
++ m_iClientEncryptionSystem <= 0x009F)
++ strName = g_localizeStrings.Get(19014); /* Fixed */
++ else if (m_iClientEncryptionSystem >= 0x00A0 &&
++ m_iClientEncryptionSystem <= 0x00A1)
++ strName = g_localizeStrings.Get(338); /* Analog */
++ else if (m_iClientEncryptionSystem >= 0x00A2 &&
++ m_iClientEncryptionSystem <= 0x00FF)
++ strName = g_localizeStrings.Get(19014); /* Fixed */
++ else if (m_iClientEncryptionSystem >= 0x0100 &&
++ m_iClientEncryptionSystem <= 0x01FF)
++ strName = "SECA Mediaguard";
++ else if (m_iClientEncryptionSystem == 0x0464)
++ strName = "EuroDec";
++ else if (m_iClientEncryptionSystem >= 0x0500 &&
++ m_iClientEncryptionSystem <= 0x05FF)
++ strName = "Viaccess";
++ else if (m_iClientEncryptionSystem >= 0x0600 &&
++ m_iClientEncryptionSystem <= 0x06FF)
++ strName = "Irdeto";
++ else if (m_iClientEncryptionSystem >= 0x0900 &&
++ m_iClientEncryptionSystem <= 0x09FF)
++ strName = "NDS Videoguard";
++ else if (m_iClientEncryptionSystem >= 0x0B00 &&
++ m_iClientEncryptionSystem <= 0x0BFF)
++ strName = "Conax";
++ else if (m_iClientEncryptionSystem >= 0x0D00 &&
++ m_iClientEncryptionSystem <= 0x0DFF)
++ strName = "CryptoWorks";
++ else if (m_iClientEncryptionSystem >= 0x0E00 &&
++ m_iClientEncryptionSystem <= 0x0EFF)
++ strName = "PowerVu";
++ else if (m_iClientEncryptionSystem == 0x1000)
++ strName = "RAS";
++ else if (m_iClientEncryptionSystem >= 0x1200 &&
++ m_iClientEncryptionSystem <= 0x12FF)
++ strName = "NagraVision";
++ else if (m_iClientEncryptionSystem >= 0x1700 &&
++ m_iClientEncryptionSystem <= 0x17FF)
++ strName = "BetaCrypt";
++ else if (m_iClientEncryptionSystem >= 0x1800 &&
++ m_iClientEncryptionSystem <= 0x18FF)
++ strName = "NagraVision";
++ else if (m_iClientEncryptionSystem == 0x22F0)
++ strName = "Codicrypt";
++ else if (m_iClientEncryptionSystem == 0x2600)
++ strName = "BISS";
++ else if (m_iClientEncryptionSystem == 0x4347)
++ strName = "CryptOn";
++ else if (m_iClientEncryptionSystem == 0x4800)
++ strName = "Accessgate";
++ else if (m_iClientEncryptionSystem == 0x4900)
++ strName = "China Crypt";
++ else if (m_iClientEncryptionSystem == 0x4A10)
++ strName = "EasyCas";
++ else if (m_iClientEncryptionSystem == 0x4A20)
++ strName = "AlphaCrypt";
++ else if (m_iClientEncryptionSystem == 0x4A70)
++ strName = "DreamCrypt";
++ else if (m_iClientEncryptionSystem == 0x4A60)
++ strName = "SkyCrypt";
++ else if (m_iClientEncryptionSystem == 0x4A61)
++ strName = "Neotioncrypt";
++ else if (m_iClientEncryptionSystem == 0x4A62)
++ strName = "SkyCrypt";
++ else if (m_iClientEncryptionSystem == 0x4A63)
++ strName = "Neotion SHL";
++ else if (m_iClientEncryptionSystem >= 0x4A64 &&
++ m_iClientEncryptionSystem <= 0x4A6F)
++ strName = "SkyCrypt";
++ else if (m_iClientEncryptionSystem == 0x4A80)
++ strName = "ThalesCrypt";
++ else if (m_iClientEncryptionSystem == 0x4AA1)
++ strName = "KeyFly";
++ else if (m_iClientEncryptionSystem == 0x4ABF)
++ strName = "DG-Crypt";
++ else if (m_iClientEncryptionSystem >= 0x4AD0 &&
++ m_iClientEncryptionSystem <= 0x4AD1)
++ strName = "X-Crypt";
++ else if (m_iClientEncryptionSystem == 0x4AD4)
++ strName = "OmniCrypt";
++ else if (m_iClientEncryptionSystem == 0x4AE0)
++ strName = "RossCrypt";
++ else if (m_iClientEncryptionSystem == 0x5500)
++ strName = "Z-Crypt";
++ else if (m_iClientEncryptionSystem == 0x5501)
++ strName = "Griffin";
++ else
++ strName = g_localizeStrings.Get(19499); /* Unknown */
++
++ strName.AppendFormat(" (%04X)", m_iClientEncryptionSystem);
++ }
++
++ m_strClientEncryptionName = strName;
++}
++
++/********** EPG methods **********/
++
++bool CPVRChannel::CreateEPG(bool bForce /* = false */)
++{
++ CSingleLock lock(m_critSection);
++ if (!m_bEPGCreated || bForce)
++ {
++ CEpg epgTmp(this, false);
++ if (g_EpgContainer.UpdateEntry(epgTmp))
++ {
++ CEpg *epg = g_EpgContainer.GetByChannel(*this);
++ if (epg)
++ {
++ m_bEPGCreated = true;
++ if (epg->EpgID() != m_iEpgId)
++ {
++ m_iEpgId = epg->EpgID();
++ m_bChanged = true;
++ }
++ }
++ }
++ }
++
++ return m_bEPGCreated;
++}
++
++int CPVRChannel::GetEPG(CFileItemList &results) const
++{
++ CEpg *epg = GetEPG();
++ if (!epg)
++ {
++ CLog::Log(LOGDEBUG, "PVR - %s - cannot get EPG for channel '%s'",
++ __FUNCTION__, m_strChannelName.c_str());
++ return -1;
++ }
++
++ return epg->Get(results);
++}
++
++bool CPVRChannel::ClearEPG() const
++{
++ CEpg *epg = GetEPG();
++ if (epg)
++ epg->Clear();
++
++ return true;
++}
++
++bool CPVRChannel::GetEPGNow(CEpgInfoTag &tag) const
++{
++ CEpg *epg = GetEPG();
++ return epg ? epg->InfoTagNow(tag) : false;
++}
++
++bool CPVRChannel::GetEPGNext(CEpgInfoTag &tag) const
++{
++ CEpg *epg = GetEPG();
++ return epg ? epg->InfoTagNext(tag) : false;
++}
++
++bool CPVRChannel::SetEPGEnabled(bool bEPGEnabled /* = true */, bool bSaveInDb /* = false */)
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ if (m_bEPGEnabled != bEPGEnabled)
++ {
++ /* update the EPG flag */
++ m_bEPGEnabled = bEPGEnabled;
++ SetChanged();
++ m_bChanged = true;
++
++ /* persist the changes */
++ if (bSaveInDb)
++ Persist();
++
++ /* clear the previous EPG entries if needed */
++ if (!m_bEPGEnabled && m_bEPGCreated)
++ ClearEPG();
++
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CPVRChannel::SetEPGScraper(const CStdString &strScraper, bool bSaveInDb /* = false */)
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ if (m_strEPGScraper != strScraper)
++ {
++ bool bCleanEPG = !m_strEPGScraper.IsEmpty() || strScraper.IsEmpty();
++
++ /* update the scraper name */
++ m_strEPGScraper.Format("%s", strScraper);
++ SetChanged();
++ m_bChanged = true;
++
++ /* persist the changes */
++ if (bSaveInDb)
++ Persist();
++
++ /* clear the previous EPG entries if needed */
++ if (bCleanEPG && m_bEPGEnabled && m_bEPGCreated)
++ ClearEPG();
++
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++void CPVRChannel::SetCachedChannelNumber(unsigned int iChannelNumber)
++{
++ CSingleLock lock(m_critSection);
++ m_iCachedChannelNumber = iChannelNumber;
++}
+diff --git a/xbmc/pvr/channels/PVRChannel.h b/xbmc/pvr/channels/PVRChannel.h
+new file mode 100644
+index 0000000..3386f78
+--- /dev/null
++++ b/xbmc/pvr/channels/PVRChannel.h
+@@ -0,0 +1,513 @@
++#pragma once
++
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "XBDateTime.h"
++#include "FileItem.h"
++#include "addons/include/xbmc_pvr_types.h"
++#include "utils/Observer.h"
++#include "threads/CriticalSection.h"
++
++namespace EPG
++{
++ class CEpg;
++}
++
++namespace PVR
++{
++ class CPVRChannelGroup;
++ class CPVRChannelGroupInternal;
++ class CPVRDatabase;
++ class CPVREpgContainer;
++ class CPVRChannelIconCacheJob;
++
++ /** PVR Channel class */
++
++ class CPVRChannel : public Observable
++ {
++ friend class CPVRChannelGroup;
++ friend class CPVRChannelGroupInternal;
++ friend class CPVRDatabase;
++ friend class CPVREpgContainer;
++ friend class EPG::CEpg;
++ friend class CPVRChannelIconCacheJob;
++
++ private:
++ /*! @name XBMC related channel data
++ */
++ //@{
++ int m_iChannelId; /*!< the identifier given to this channel by the TV database */
++ bool m_bIsRadio; /*!< true if this channel is a radio channel, false if not */
++ bool m_bIsHidden; /*!< true if this channel is hidden, false if not */
++ bool m_bIsUserSetIcon; /*!< true if user set the icon via GUI, false if not */
++ CStdString m_strIconPath; /*!< the path to the icon for this channel */
++ CStdString m_strChannelName; /*!< the name for this channel used by XBMC */
++ bool m_bIsVirtual; /*!< true if this channel is marked as virtual, false if not */
++ time_t m_iLastWatched; /*!< last time channel has been watched */
++ bool m_bChanged; /*!< true if anything in this entry was changed that needs to be persisted */
++ unsigned int m_iCachedChannelNumber; /*!< the cached channel number in the selected group */
++ //@}
++
++ /*! @name EPG related channel data
++ */
++ //@{
++ int m_iEpgId; /*!< the id of the EPG for this channel */
++ bool m_bEPGCreated; /*!< true if an EPG has been created for this channel */
++ bool m_bEPGEnabled; /*!< don't use an EPG for this channel if set to false */
++ CStdString m_strEPGScraper; /*!< the name of the scraper to be used for this channel */
++ //@}
++
++ /*! @name Client related channel data
++ */
++ //@{
++ int m_iUniqueId; /*!< the unique identifier for this channel */
++ int m_iClientId; /*!< the identifier of the client that serves this channel */
++ int m_iClientChannelNumber; /*!< the channel number on the client */
++ CStdString m_strClientChannelName; /*!< the name of this channel on the client */
++ CStdString m_strInputFormat; /*!< the stream input type based on ffmpeg/libavformat/allformats.c */
++ CStdString m_strStreamURL; /*!< URL of the stream. Use the client to read stream if this is empty */
++ CStdString m_strFileNameAndPath; /*!< the filename to be used by PVRManager to open and read the stream */
++ int m_iClientEncryptionSystem; /*!< the encryption system used by this channel. 0 for FreeToAir, -1 for unknown */
++ CStdString m_strClientEncryptionName; /*!< the name of the encryption system used by this channel */
++ //@}
++
++ CCriticalSection m_critSection;
++
++ public:
++ /*! @brief Create a new channel */
++ CPVRChannel(bool bRadio = false);
++ CPVRChannel(const PVR_CHANNEL &channel, unsigned int iClientId);
++ CPVRChannel(const CPVRChannel &channel);
++
++ bool operator ==(const CPVRChannel &right) const;
++ bool operator !=(const CPVRChannel &right) const;
++ CPVRChannel &operator=(const CPVRChannel &channel);
++
++ /*! @name XBMC related channel methods
++ */
++ //@{
++
++ /*!
++ * @brief Delete this channel from the database and delete the corresponding EPG table if it exists.
++ * @return True if it was deleted successfully, false otherwise.
++ */
++ bool Delete(void);
++
++ /*!
++ * @brief Update this channel tag with the data of the given channel tag.
++ * @param channel The new channel data.
++ * @return True if something changed, false otherwise.
++ */
++ bool UpdateFromClient(const CPVRChannel &channel);
++
++ /*!
++ * @brief Persists the changes in the database.
++ * @param bQueueWrite Queue the change and write changes later.
++ * @return True if the changes were saved succesfully, false otherwise.
++ */
++ bool Persist(bool bQueueWrite = false);
++
++ /*!
++ * @brief The identifier given to this channel by the TV database.
++ * @return The identifier given to this channel by the TV database.
++ */
++ int ChannelID(void) const { return m_iChannelId; }
++
++ bool IsNew(void) const { return m_iChannelId <= 0; }
++
++ /*!
++ * @brief Set the identifier for this channel.
++ * @param iDatabaseId The new channel ID
++ * @param bSaveInDb Save in the database or not.
++ * @return True if the something changed, false otherwise.
++ */
++ bool SetChannelID(int iDatabaseId, bool bSaveInDb = false);
++
++ /*!
++ * @brief The channel number used by XBMC by the currently active group.
++ * @return The channel number used by XBMC.
++ */
++ int ChannelNumber(void) const;
++
++ /*!
++ * @brief True if this channel is a radio channel, false if not.
++ * @return True if this channel is a radio channel, false if not.
++ */
++ bool IsRadio(void) const { return m_bIsRadio; }
++
++ /*!
++ * @brief True if this channel is hidden. False if not.
++ * @return True if this channel is hidden. False if not.
++ */
++ bool IsHidden(void) const { return m_bIsHidden; }
++
++ /*!
++ * @brief Set to true to hide this channel. Set to false to unhide it.
++ *
++ * Set to true to hide this channel. Set to false to unhide it.
++ * The EPG of hidden channels won't be updated.
++ * @param bIsHidden The new setting.
++ * @param bSaveInDb Save in the database or not.
++ * @return True if the something changed, false otherwise.
++ */
++ bool SetHidden(bool bIsHidden, bool bSaveInDb = false);
++
++ /*!
++ * @brief True if a recording is currently running on this channel. False if not.
++ * @return True if a recording is currently running on this channel. False if not.
++ */
++ bool IsRecording(void) const;
++
++ /*!
++ * @brief The path to the icon for this channel.
++ * @return The path to the icon for this channel.
++ */
++ const CStdString &IconPath(void) const { return m_strIconPath; }
++
++ /*!
++ * @brief True if this user changed icon via GUI. False if not.
++ * @return True if this user changed icon via GUI. False if not.
++ */
++ bool IsUserSetIcon(void) const { return m_bIsUserSetIcon; }
++
++ /*!
++ * @brief Set the path to the icon for this channel.
++ * @param strIconPath The new path.
++ * @param bSaveInDb Save in the database or not.
++ * @param bIsUserSetIcon true if user changed the icon via GUI, false otherwise.
++ * @return True if the something changed, false otherwise.
++ */
++ bool SetIconPath(const CStdString &strIconPath, bool bSaveInDb = false, bool bIsUserSetIcon = true);
++
++ /*!
++ * @brief The name for this channel used by XBMC.
++ * @return The name for this channel used by XBMC.
++ */
++ const CStdString &ChannelName(void) const { return m_strChannelName; }
++
++ /*!
++ * @brief Set the name for this channel used by XBMC.
++ * @param strChannelName The new channel name.
++ * @param bSaveInDb Save in the database or not.
++ * @return True if the something changed, false otherwise.
++ */
++ bool SetChannelName(const CStdString &strChannelName, bool bSaveInDb = false);
++
++ /*!
++ * @brief True if this channel is marked as virtual. False if not.
++ * @return True if this channel is marked as virtual. False if not.
++ */
++ bool IsVirtual() const { return m_bIsVirtual; }
++
++ /*!
++ * @brief True if this channel is marked as virtual. False if not.
++ * @param bIsVirtual The new value.
++ * @param bSaveInDb Save in the database or not.
++ * @return True if the something changed, false otherwise.
++ */
++ bool SetVirtual(bool bIsVirtual, bool bSaveInDb = false);
++
++ /*!
++ * @brief Last time channel has been watched.
++ * @return Time channel has been watched last.
++ */
++ time_t LastWatched() const { return m_iLastWatched; }
++
++ /*!
++ * @brief Last time channel has been watched
++ * @param iLastWatched The new value.
++ * @param bSaveInDb Save in the database or not.
++ * @return True if the something changed, false otherwise.
++ */
++ bool SetLastWatched(time_t iLastWatched, bool bSaveInDb = false);
++
++ /*!
++ * @brief True if this channel has no file or stream name
++ * @return True if this channel has no file or stream name
++ */
++ bool IsEmpty() const;
++
++ bool IsChanged() const { return m_bChanged; }
++ //@}
++
++ /*! @name Client related channel methods
++ */
++ //@{
++
++ /*!
++ * @brief A unique identifier for this channel.
++ *
++ * A unique identifier for this channel.
++ * It can be used to find the same channel on different providers
++ *
++ * @return The Unique ID.
++ */
++ int UniqueID(void) const { return m_iUniqueId; }
++
++ /*!
++ * @brief Change the unique identifier for this channel.
++ * @param iUniqueId The new unique ID.
++ * @param bSaveInDb Save in the database or not.
++ * @return True if the something changed, false otherwise.
++ */
++ bool SetUniqueID(int iUniqueId, bool bSaveInDb = false);
++
++ /*!
++ * @brief The identifier of the client that serves this channel.
++ * @return The identifier of the client that serves this channel.
++ */
++ int ClientID(void) const { return m_iClientId; }
++
++ /*!
++ * @brief Set the identifier of the client that serves this channel.
++ * @param iClientId The new ID.
++ * @param bSaveInDb Save in the database or not.
++ * @return True if the something changed, false otherwise.
++ */
++ bool SetClientID(int iClientId, bool bSaveInDb = false);
++
++ /*!
++ * @brief The channel number on the client.
++ * @return The channel number on the client.
++ */
++ int ClientChannelNumber(void) const { return m_iClientChannelNumber; }
++
++ /*!
++ * @brief Set the channel number on the client.
++ *
++ * Set the channel number on the client.
++ * It will only be changed in this tag and won't change anything on the client.
++ *
++ * @param iClientChannelNumber The new channel number
++ * @param bSaveInDb Save in the database or not.
++ * @return True if the something changed, false otherwise.
++ */
++ bool SetClientChannelNumber(int iClientChannelNumber, bool bSaveInDb = false);
++
++ /*!
++ * @brief The name of this channel on the client.
++ * @return The name of this channel on the client.
++ */
++ const CStdString &ClientChannelName(void) const { return m_strClientChannelName; }
++
++ /*!
++ * @brief Set the name of this channel on the client.
++ *
++ * Set the name of this channel on the client.
++ * It will only be changed in this tag and won't change anything on the client.
++ *
++ * @param strClientChannelName The new channel name
++ * @return True if the something changed, false otherwise.
++ */
++ bool SetClientChannelName(const CStdString &strClientChannelName);
++
++ /*!
++ * @brief The stream input type
++ *
++ * The stream input type
++ * If it is empty, ffmpeg will try to scan the stream to find the right input format.
++ * See "xbmc/cores/dvdplayer/Codecs/ffmpeg/libavformat/allformats.c" for a
++ * list of the input formats.
++ *
++ * @return The stream input type
++ */
++ const CStdString &InputFormat(void) const { return m_strInputFormat; }
++
++ /*!
++ * @brief Set the stream input type
++ * @param strInputFormat The new input format.
++ * @param bSaveInDb Save in the database or not.
++ * @return True if the something changed, false otherwise.
++ */
++ bool SetInputFormat(const CStdString &strInputFormat, bool bSaveInDb = false);
++
++ /*!
++ * @brief The stream URL to access this channel.
++ *
++ * The stream URL to access this channel.
++ * If this is empty, then the client should be used to read from the channel.
++ *
++ * @return The stream URL to access this channel.
++ */
++ const CStdString &StreamURL(void) const { return m_strStreamURL; }
++
++ /*!
++ * @brief Set the stream URL to access this channel.
++ *
++ * Set the stream URL to access this channel.
++ * If this is empty, then the client should be used to read from the channel.
++ *
++ * @param strStreamURL The new stream URL.
++ * @param bSaveInDb Save in the database or not.
++ * @return True if the something changed, false otherwise.
++ */
++ bool SetStreamURL(const CStdString &strStreamURL, bool bSaveInDb = false);
++
++ /*!
++ * @brief The path in the XBMC VFS to be used by PVRManager to open and read the stream.
++ * @return The path in the XBMC VFS to be used by PVRManager to open and read the stream.
++ */
++ const CStdString &Path(void) const { return m_strFileNameAndPath; }
++
++ private:
++ /*!
++ * @brief Update the path after the channel number in the internal group changed.
++ */
++ void UpdatePath(unsigned int iNewChannelNumber);
++
++ /*!
++ * @brief Update the encryption name after SetEncryptionSystem() has been called.
++ */
++ void UpdateEncryptionName(void);
++
++ void SetCachedChannelNumber(unsigned int iChannelNumber);
++
++ public:
++ /*!
++ * @brief Return true if this channel is encrypted.
++ *
++ * Return true if this channel is encrypted. Does not inform whether XBMC can play the file.
++ * Decryption should be done by the client.
++ *
++ * @return Return true if this channel is encrypted.
++ */
++ bool IsEncrypted(void) const { return m_iClientEncryptionSystem > 0; }
++
++
++ /*!
++ * @brief Return the encryption system ID for this channel. 0 for FTA.
++ *
++ * Return the encryption system ID for this channel. 0 for FTA.
++ * The values are documented on: http://www.dvb.org/index.php?id=174.
++ *
++ * @return Return the encryption system ID for this channel.
++ */
++ int EncryptionSystem(void) const { return m_iClientEncryptionSystem; }
++
++ /*!
++ * @brief Set the encryption ID (CAID) for this channel.
++ * @param iClientEncryptionSystem The new CAID.
++ * @param bSaveInDb Save in the database or not.
++ * @return True if the something changed, false otherwise.
++ */
++ bool SetEncryptionSystem(int iClientEncryptionSystem, bool bSaveInDb = false);
++
++ /*!
++ * @return A friendly name for the used encryption system.
++ */
++ const CStdString &EncryptionName() const { return m_strClientEncryptionName; }
++ //@}
++
++ /*! @name EPG methods
++ */
++ //@{
++
++ /*!
++ * @return The ID of the EPG table to use for this channel or -1 if it isn't set.
++ */
++ int EpgID() const { return m_iEpgId; };
++
++ /*!
++ * @brief Get the EPG table for this channel.
++ * @return The EPG for this channel.
++ */
++ EPG::CEpg *GetEPG() const;
++
++ /*!
++ * @brief Create the EPG table for this channel.
++ * @brief bForce Create a table, even if it already has been created before.
++ * @return True if the table was created successfully, false otherwise.
++ */
++ bool CreateEPG(bool bForce = false);
++
++ /*!
++ * @brief Get the EPG table for this channel.
++ * @param results The file list to store the results in.
++ * @return The number of tables that were added.
++ */
++ int GetEPG(CFileItemList &results) const;
++
++ /*!
++ * @brief Clear the EPG for this channel.
++ * @return True if it was cleared, false if not.
++ */
++ bool ClearEPG() const;
++
++ /*!
++ * @brief Get the EPG tag that is active on this channel now.
++ *
++ * Get the EPG tag that is active on this channel now.
++ * Will return an empty tag if there is none.
++ *
++ * @return The EPG tag that is active on this channel now.
++ */
++ bool GetEPGNow(EPG::CEpgInfoTag &tag) const;
++
++ /*!
++ * @brief Get the EPG tag that is active on this channel next.
++ *
++ * Get the EPG tag that is active on this channel next.
++ * Will return an empty tag if there is none.
++ *
++ * @return The EPG tag that is active on this channel next.
++ */
++ bool GetEPGNext(EPG::CEpgInfoTag &tag) const;
++
++ /*!
++ * @brief Don't use an EPG for this channel if set to false.
++ * @return Don't use an EPG for this channel if set to false.
++ */
++ bool EPGEnabled() const { return m_bEPGEnabled; }
++
++ /*!
++ * @brief Set to true if an EPG should be used for this channel. Set to false otherwise.
++ * @param bEPGEnabled The new value.
++ * @param bSaveInDb Save in the database or not.
++ * @return True if the something changed, false otherwise.
++ */
++ bool SetEPGEnabled(bool bEPGEnabled = true, bool bSaveInDb = false);
++
++ /*!
++ * @brief Get the name of the scraper to be used for this channel.
++ *
++ * Get the name of the scraper to be used for this channel.
++ * The default is 'client', which means the EPG should be loaded from the backend.
++ *
++ * @return The name of the scraper to be used for this channel.
++ */
++ const CStdString &EPGScraper(void) const { return m_strEPGScraper; }
++
++ /*!
++ * @brief Set the name of the scraper to be used for this channel.
++ *
++ * Set the name of the scraper to be used for this channel.
++ * Set to "client" to load the EPG from the backend
++ *
++ * @param strScraper The new scraper name.
++ * @param bSaveInDb Save in the database or not.
++ * @return True if the something changed, false otherwise.
++ */
++ bool SetEPGScraper(const CStdString &strScraper, bool bSaveInDb = false);
++
++ //@}
++ };
++}
+diff --git a/xbmc/pvr/channels/PVRChannelGroup.cpp b/xbmc/pvr/channels/PVRChannelGroup.cpp
+new file mode 100644
+index 0000000..1b90d0b
+--- /dev/null
++++ b/xbmc/pvr/channels/PVRChannelGroup.cpp
+@@ -0,0 +1,1073 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++/**
++ * TODO:
++ * - use Observable here, so we can use event driven operations later
++ */
++
++#include "settings/GUISettings.h"
++#include "guilib/GUIWindowManager.h"
++#include "dialogs/GUIDialogYesNo.h"
++#include "dialogs/GUIDialogOK.h"
++#include "music/tags/MusicInfoTag.h"
++#include "utils/log.h"
++#include "utils/StringUtils.h"
++#include "threads/SingleLock.h"
++
++#include "PVRChannelGroupsContainer.h"
++#include "pvr/PVRDatabase.h"
++#include "pvr/PVRManager.h"
++#include "pvr/addons/PVRClients.h"
++#include "epg/EpgContainer.h"
++
++using namespace PVR;
++using namespace EPG;
++
++CPVRChannelGroup::CPVRChannelGroup(bool bRadio, unsigned int iGroupId, const CStdString &strGroupName) :
++ m_bRadio(bRadio),
++ m_iGroupType(PVR_GROUP_TYPE_DEFAULT),
++ m_iGroupId(iGroupId),
++ m_strGroupName(strGroupName),
++ m_bLoaded(false),
++ m_bChanged(false),
++ m_bUsingBackendChannelOrder(false)
++{
++}
++
++CPVRChannelGroup::CPVRChannelGroup(bool bRadio) :
++ m_bRadio(bRadio),
++ m_iGroupType(PVR_GROUP_TYPE_DEFAULT),
++ m_iGroupId(-1),
++ m_bLoaded(false),
++ m_bChanged(false),
++ m_bUsingBackendChannelOrder(false)
++{
++}
++
++CPVRChannelGroup::CPVRChannelGroup(const PVR_CHANNEL_GROUP &group) :
++ m_bRadio(group.bIsRadio),
++ m_iGroupType(PVR_GROUP_TYPE_DEFAULT),
++ m_iGroupId(-1),
++ m_strGroupName(group.strGroupName),
++ m_bLoaded(false),
++ m_bChanged(false),
++ m_bUsingBackendChannelOrder(false)
++{
++}
++
++CPVRChannelGroup::~CPVRChannelGroup(void)
++{
++ Unload();
++}
++
++bool CPVRChannelGroup::operator==(const CPVRChannelGroup& right) const
++{
++ if (this == &right) return true;
++
++ return (m_bRadio == right.m_bRadio &&
++ m_iGroupType == right.m_iGroupType &&
++ m_iGroupId == right.m_iGroupId &&
++ m_strGroupName.Equals(right.m_strGroupName));
++}
++
++bool CPVRChannelGroup::operator!=(const CPVRChannelGroup &right) const
++{
++ return !(*this == right);
++}
++
++CPVRChannelGroup::CPVRChannelGroup(const CPVRChannelGroup &group)
++{
++ m_bRadio = group.m_bRadio;
++ m_iGroupType = group.m_iGroupType;
++ m_iGroupId = group.m_iGroupId;
++ m_strGroupName = group.m_strGroupName;
++ m_bLoaded = group.m_bLoaded;
++ m_bChanged = group.m_bChanged;
++ m_bUsingBackendChannelOrder = group.m_bUsingBackendChannelOrder;
++ m_bUsingBackendChannelNumbers = group.m_bUsingBackendChannelNumbers;
++
++ for (int iPtr = 0; iPtr < group.Size(); iPtr++)
++ push_back(group.at(iPtr));
++}
++
++int CPVRChannelGroup::Load(void)
++{
++ /* make sure this container is empty before loading */
++ Unload();
++
++ m_bUsingBackendChannelOrder = g_guiSettings.GetBool("pvrmanager.backendchannelorder");
++ m_bUsingBackendChannelNumbers = g_guiSettings.GetBool("pvrmanager.usebackendchannelnumbers");
++
++ int iChannelCount = m_iGroupId > 0 ? LoadFromDb() : 0;
++ CLog::Log(LOGDEBUG, "PVRChannelGroup - %s - %d channels loaded from the database for group '%s'",
++ __FUNCTION__, iChannelCount, m_strGroupName.c_str());
++
++ Update();
++ if (size() - iChannelCount > 0)
++ {
++ CLog::Log(LOGDEBUG, "PVRChannelGroup - %s - %d channels added from clients to group '%s'",
++ __FUNCTION__, (int) size() - iChannelCount, m_strGroupName.c_str());
++ }
++
++ SortByChannelNumber();
++ Renumber();
++
++ g_guiSettings.RegisterObserver(this);
++ m_bLoaded = true;
++
++ return size();
++}
++
++void CPVRChannelGroup::Unload(void)
++{
++ g_guiSettings.UnregisterObserver(this);
++ clear();
++}
++
++bool CPVRChannelGroup::Update(void)
++{
++ if (GroupType() == PVR_GROUP_TYPE_USER_DEFINED)
++ return false;
++
++ CPVRChannelGroup PVRChannels_tmp(m_bRadio, m_iGroupId, m_strGroupName);
++ PVRChannels_tmp.LoadFromClients();
++
++ return UpdateGroupEntries(PVRChannels_tmp);
++}
++
++bool CPVRChannelGroup::SetChannelNumber(const CPVRChannel &channel, unsigned int iChannelNumber)
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ for (unsigned int iChannelPtr = 0; iChannelPtr < size(); iChannelPtr++)
++ {
++ if (*at(iChannelPtr).channel == channel)
++ {
++ if (at(iChannelPtr).iChannelNumber != iChannelNumber)
++ {
++ m_bChanged = true;
++ bReturn = true;
++ at(iChannelPtr).iChannelNumber = iChannelNumber;
++ }
++ break;
++ }
++ }
++
++ return bReturn;
++}
++
++bool CPVRChannelGroup::MoveChannel(unsigned int iOldChannelNumber, unsigned int iNewChannelNumber, bool bSaveInDb /* = true */)
++{
++ if (iOldChannelNumber == iNewChannelNumber)
++ return true;
++
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ /* make sure the list is sorted by channel number */
++ SortByChannelNumber();
++
++ /* old channel number out of range */
++ if (iOldChannelNumber > size())
++ return bReturn;
++
++ /* new channel number out of range */
++ if (iNewChannelNumber > size())
++ iNewChannelNumber = size();
++
++ /* move the channel in the list */
++ PVRChannelGroupMember entry = at(iOldChannelNumber - 1);
++ erase(begin() + iOldChannelNumber - 1);
++ insert(begin() + iNewChannelNumber - 1, entry);
++
++ /* renumber the list */
++ Renumber();
++
++ m_bChanged = true;
++
++ if (bSaveInDb)
++ bReturn = Persist();
++ else
++ bReturn = true;
++
++ CLog::Log(LOGNOTICE, "CPVRChannelGroup - %s - %s channel '%s' moved to channel number '%d'",
++ __FUNCTION__, (m_bRadio ? "radio" : "tv"), entry.channel->ChannelName().c_str(), iNewChannelNumber);
++
++ return true;
++}
++
++void CPVRChannelGroup::SearchAndSetChannelIcons(bool bUpdateDb /* = false */)
++{
++ if (g_guiSettings.GetString("pvrmenu.iconpath").IsEmpty())
++ return;
++
++ CPVRDatabase *database = GetPVRDatabase();
++ if (!database)
++ return;
++
++ CSingleLock lock(m_critSection);
++
++ for (unsigned int ptr = 0; ptr < size(); ptr++)
++ {
++ PVRChannelGroupMember groupMember = at(ptr);
++
++ /* skip if an icon is already set */
++ if (!groupMember.channel->IconPath().IsEmpty())
++ continue;
++
++ CStdString strBasePath = g_guiSettings.GetString("pvrmenu.iconpath");
++ CStdString strChannelName = groupMember.channel->ClientChannelName();
++
++ CStdString strIconPath = strBasePath + groupMember.channel->ClientChannelName();
++ CStdString strIconPathLower = strBasePath + strChannelName.ToLower();
++ CStdString strIconPathUid;
++ strIconPathUid.Format("%s/%08d", strBasePath, groupMember.channel->UniqueID());
++
++ groupMember.channel->SetIconPath(strIconPath + ".tbn", bUpdateDb) ||
++ groupMember.channel->SetIconPath(strIconPath + ".jpg", bUpdateDb) ||
++ groupMember.channel->SetIconPath(strIconPath + ".png", bUpdateDb) ||
++
++ groupMember.channel->SetIconPath(strIconPathLower + ".tbn", bUpdateDb) ||
++ groupMember.channel->SetIconPath(strIconPathLower + ".jpg", bUpdateDb) ||
++ groupMember.channel->SetIconPath(strIconPathLower + ".png", bUpdateDb) ||
++
++ groupMember.channel->SetIconPath(strIconPathUid + ".tbn", bUpdateDb) ||
++ groupMember.channel->SetIconPath(strIconPathUid + ".jpg", bUpdateDb) ||
++ groupMember.channel->SetIconPath(strIconPathUid + ".png", bUpdateDb);
++
++ /* TODO: start channel icon scraper here if nothing was found */
++ }
++}
++
++/********** sort methods **********/
++
++struct sortByClientChannelNumber
++{
++ bool operator()(const PVRChannelGroupMember &channel1, const PVRChannelGroupMember &channel2)
++ {
++ return channel1.channel->ClientChannelNumber() < channel2.channel->ClientChannelNumber();
++ }
++};
++
++struct sortByChannelNumber
++{
++ bool operator()(const PVRChannelGroupMember &channel1, const PVRChannelGroupMember &channel2)
++ {
++ return channel1.iChannelNumber < channel2.iChannelNumber;
++ }
++};
++
++void CPVRChannelGroup::SortByClientChannelNumber(void)
++{
++ CSingleLock lock(m_critSection);
++ sort(begin(), end(), sortByClientChannelNumber());
++}
++
++void CPVRChannelGroup::SortByChannelNumber(void)
++{
++ CSingleLock lock(m_critSection);
++ sort(begin(), end(), sortByChannelNumber());
++}
++
++/********** getters **********/
++
++CPVRChannel *CPVRChannelGroup::GetByClient(int iUniqueChannelId, int iClientID) const
++{
++ CPVRChannel *channel = NULL;
++ CSingleLock lock(m_critSection);
++
++ for (unsigned int ptr = 0; ptr < size(); ptr++)
++ {
++ PVRChannelGroupMember groupMember = at(ptr);
++ if (groupMember.channel->UniqueID() == iUniqueChannelId &&
++ groupMember.channel->ClientID() == iClientID)
++ {
++ channel = groupMember.channel;
++ break;
++ }
++ }
++
++ return channel;
++}
++
++CPVRChannel *CPVRChannelGroup::GetByChannelID(int iChannelID) const
++{
++ CPVRChannel *channel = NULL;
++ CSingleLock lock(m_critSection);
++
++ for (unsigned int ptr = 0; ptr < size(); ptr++)
++ {
++ PVRChannelGroupMember groupMember = at(ptr);
++ if (groupMember.channel->ChannelID() == iChannelID)
++ {
++ channel = groupMember.channel;
++ break;
++ }
++ }
++
++ return channel;
++}
++
++CPVRChannel *CPVRChannelGroup::GetByChannelEpgID(int iEpgID) const
++{
++ CPVRChannel *channel = NULL;
++ CSingleLock lock(m_critSection);
++
++ for (unsigned int ptr = 0; ptr < size(); ptr++)
++ {
++ PVRChannelGroupMember groupMember = at(ptr);
++ if (groupMember.channel->EpgID() == iEpgID)
++ {
++ channel = groupMember.channel;
++ break;
++ }
++ }
++
++ return channel;
++}
++
++CPVRChannel *CPVRChannelGroup::GetByUniqueID(int iUniqueID) const
++{
++ CPVRChannel *channel = NULL;
++ CSingleLock lock(m_critSection);
++
++ for (unsigned int ptr = 0; ptr < size(); ptr++)
++ {
++ PVRChannelGroupMember groupMember = at(ptr);
++ if (groupMember.channel->UniqueID() == iUniqueID)
++ {
++ channel = groupMember.channel;
++ break;
++ }
++ }
++
++ return channel;
++}
++
++CPVRChannel *CPVRChannelGroup::GetLastPlayedChannel(void) const
++{
++ CPVRChannel *channel = NULL;
++ CSingleLock lock(m_critSection);
++
++ for (unsigned int iChannelPtr = 0; iChannelPtr < size(); iChannelPtr++)
++ {
++ PVRChannelGroupMember groupMember = at(iChannelPtr);
++
++ /* check whether the client is loaded */
++ if (!g_PVRClients->IsConnectedClient(groupMember.channel->ClientID()))
++ continue;
++
++ /* always get the first channel */
++ if (channel == NULL)
++ {
++ channel = groupMember.channel;
++ continue;
++ }
++
++ /* check whether this channel has a later LastWatched time */
++ if (groupMember.channel->LastWatched() > channel->LastWatched())
++ channel = groupMember.channel;
++ }
++
++ return channel;
++}
++
++
++unsigned int CPVRChannelGroup::GetChannelNumber(const CPVRChannel &channel) const
++{
++ unsigned int iReturn = 0;
++ CSingleLock lock(m_critSection);
++ unsigned int iSize = size();
++
++ for (unsigned int iChannelPtr = 0; iChannelPtr < iSize; iChannelPtr++)
++ {
++ PVRChannelGroupMember member = at(iChannelPtr);
++ if (member.channel->ChannelID() == channel.ChannelID())
++ {
++ iReturn = member.iChannelNumber;
++ break;
++ }
++ }
++
++ return iReturn;
++}
++
++CPVRChannel *CPVRChannelGroup::GetByChannelNumber(unsigned int iChannelNumber) const
++{
++ CPVRChannel *channel = NULL;
++ CSingleLock lock(m_critSection);
++
++ for (unsigned int ptr = 0; ptr < size(); ptr++)
++ {
++ PVRChannelGroupMember groupMember = at(ptr);
++ if (groupMember.iChannelNumber == iChannelNumber)
++ {
++ channel = groupMember.channel;
++ break;
++ }
++ }
++
++ return channel;
++}
++
++CPVRChannel *CPVRChannelGroup::GetByChannelUpDown(const CPVRChannel &channel, bool bChannelUp) const
++{
++ CPVRChannel *retVal(NULL);
++ bool bGotChannel(false);
++ CSingleLock lock(m_critSection);
++ int iChannelIndex = GetIndex(channel);
++
++ while (!bGotChannel && !(retVal && *retVal == channel))
++ {
++ if (bChannelUp)
++ iChannelIndex++;
++ else
++ iChannelIndex--;
++
++ if (iChannelIndex >= (int)size())
++ iChannelIndex = 0;
++ else if (iChannelIndex < 0)
++ iChannelIndex = size() - 1;
++
++ retVal = GetByIndex(iChannelIndex);
++ if (!retVal->IsHidden())
++ bGotChannel = true;
++ }
++
++ return retVal;
++}
++
++CPVRChannel *CPVRChannelGroup::GetByChannelUp(const CPVRChannel &channel) const
++{
++ CPVRChannel *retVal(NULL);
++ retVal = GetByChannelUpDown(channel, true);
++ return retVal;
++}
++
++CPVRChannel *CPVRChannelGroup::GetByChannelDown(const CPVRChannel &channel) const
++{
++ CPVRChannel *retVal(NULL);
++ retVal = GetByChannelUpDown(channel, false);
++ return retVal;
++}
++
++CPVRChannel *CPVRChannelGroup::GetByIndex(unsigned int iIndex) const
++{
++ CSingleLock lock(m_critSection);
++ return iIndex < size() ?
++ at(iIndex).channel :
++ NULL;
++}
++
++int CPVRChannelGroup::GetIndex(const CPVRChannel &channel) const
++{
++ int iIndex(-1);
++ CSingleLock lock(m_critSection);
++
++ for (unsigned int iChannelPtr = 0; iChannelPtr < size(); iChannelPtr++)
++ {
++ if (*at(iChannelPtr).channel == channel)
++ {
++ iIndex = iChannelPtr;
++ break;
++ }
++ }
++
++ return iIndex;
++}
++
++int CPVRChannelGroup::GetMembers(CFileItemList &results, bool bGroupMembers /* = true */) const
++{
++ int iOrigSize = results.Size();
++ CSingleLock lock(m_critSection);
++
++ const CPVRChannelGroup *channels = bGroupMembers ? this : g_PVRChannelGroups->GetGroupAll(m_bRadio);
++ for (unsigned int iChannelPtr = 0; iChannelPtr < channels->size(); iChannelPtr++)
++ {
++ CPVRChannel *channel = channels->at(iChannelPtr).channel;
++ if (!channel)
++ continue;
++
++ if (bGroupMembers || !IsGroupMember(*channel))
++ {
++ CFileItemPtr pFileItem(new CFileItem(*channel));
++ results.Add(pFileItem);
++ }
++ }
++
++ return results.Size() - iOrigSize;
++}
++
++CPVRChannelGroup *CPVRChannelGroup::GetNextGroup(void) const
++{
++ return g_PVRChannelGroups->Get(m_bRadio)->GetNextGroup(*this);
++}
++
++/********** private methods **********/
++
++int CPVRChannelGroup::LoadFromDb(bool bCompress /* = false */)
++{
++ CPVRDatabase *database = GetPVRDatabase();
++ if (!database)
++ return -1;
++
++ int iChannelCount = size();
++
++ database->Get(*this);
++
++ return size() - iChannelCount;
++}
++
++int CPVRChannelGroup::LoadFromClients(void)
++{
++ int iCurSize = size();
++
++ /* get the channels from the backends */
++ PVR_ERROR error;
++ g_PVRClients->GetChannelGroupMembers(this, &error);
++ if (error != PVR_ERROR_NO_ERROR)
++ CLog::Log(LOGWARNING, "PVRChannelGroup - %s - got bad error (%d) on call to GetChannelGroupMembers", __FUNCTION__, error);
++
++ return size() - iCurSize;
++}
++
++bool CPVRChannelGroup::AddAndUpdateChannels(const CPVRChannelGroup &channels, bool bUseBackendChannelNumbers)
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ /* go through the channel list and check for new channels.
++ channels will only by updated in CPVRChannelGroupInternal to prevent dupe updates */
++ for (unsigned int iChannelPtr = 0; iChannelPtr < channels.size(); iChannelPtr++)
++ {
++ PVRChannelGroupMember member = channels.at(iChannelPtr);
++ if (!member.channel)
++ continue;
++
++ /* check whether this channel is known in the internal group */
++ CPVRChannel *existingChannel = (CPVRChannel *) g_PVRChannelGroups->GetGroupAll(m_bRadio)->GetByClient(member.channel->UniqueID(), member.channel->ClientID());
++ if (!existingChannel)
++ continue;
++
++ /* if it's found, add the channel to this group */
++ if (!IsGroupMember(*existingChannel))
++ {
++ int iChannelNumber = bUseBackendChannelNumbers ? member.channel->ClientChannelNumber() : 0;
++ AddToGroup(*existingChannel, iChannelNumber, false);
++
++ bReturn = true;
++ CLog::Log(LOGINFO,"PVRChannelGroup - %s - added %s channel '%s' at position %d in group '%s'",
++ __FUNCTION__, m_bRadio ? "radio" : "TV", existingChannel->ChannelName().c_str(), iChannelNumber, GroupName().c_str());
++ }
++ }
++
++ return bReturn;
++}
++
++bool CPVRChannelGroup::RemoveDeletedChannels(const CPVRChannelGroup &channels)
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ /* check for deleted channels */
++ for (int iChannelPtr = size() - 1; iChannelPtr >= 0; iChannelPtr--)
++ {
++ CPVRChannel *channel = at(iChannelPtr).channel;
++ if (!channel)
++ continue;
++
++ if (channels.GetByClient(channel->UniqueID(), channel->ClientID()) == NULL)
++ {
++ /* channel was not found */
++ CLog::Log(LOGINFO,"PVRChannelGroup - %s - deleted %s channel '%s' from group '%s'",
++ __FUNCTION__, m_bRadio ? "radio" : "TV", channel->ChannelName().c_str(), GroupName().c_str());
++
++ /* remove this channel from all non-system groups if this is the internal group */
++ if (IsInternalGroup())
++ {
++ g_PVRChannelGroups->Get(m_bRadio)->RemoveFromAllGroups(channel);
++
++ /* since it was not found in the internal group, it was deleted from the backend */
++ channel->Delete();
++ }
++
++ erase(begin() + iChannelPtr);
++ m_bChanged = true;
++ bReturn = true;
++ }
++ }
++
++ return bReturn;
++}
++
++bool CPVRChannelGroup::UpdateGroupEntries(const CPVRChannelGroup &channels)
++{
++ bool bReturn(false);
++ bool bChanged(false);
++ bool bRemoved(false);
++
++ CSingleLock lock(m_critSection);
++ /* sort by client channel number if this is the first time or if pvrmanager.backendchannelorder is true */
++ bool bUseBackendChannelNumbers(size() == 0 || m_bUsingBackendChannelOrder);
++
++ CPVRDatabase *database = GetPVRDatabase();
++ if (!database)
++ return bReturn;
++
++ bRemoved = RemoveDeletedChannels(channels);
++ bChanged = AddAndUpdateChannels(channels, bUseBackendChannelNumbers) || bRemoved;
++
++ if (bChanged)
++ {
++ if (bUseBackendChannelNumbers)
++ SortByClientChannelNumber();
++
++ /* renumber to make sure all channels have a channel number.
++ new channels were added at the back, so they'll get the highest numbers */
++ bool bRenumbered = Renumber();
++
++ SetChanged();
++ lock.Leave();
++
++ NotifyObservers(HasNewChannels() || bRemoved || bRenumbered ? "channelgroup-reset" : "channelgroup");
++
++ bReturn = Persist();
++ }
++ else
++ {
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++void CPVRChannelGroup::RemoveInvalidChannels(void)
++{
++ bool bDelete(false);
++ for (unsigned int ptr = 0; ptr < size(); ptr--)
++ {
++ bDelete = false;
++ CPVRChannel *channel = at(ptr).channel;
++ if (channel->IsVirtual())
++ continue;
++
++ if (at(ptr).channel->ClientChannelNumber() <= 0)
++ {
++ CLog::Log(LOGERROR, "PVRChannelGroup - %s - removing invalid channel '%s' from client '%i': no valid client channel number",
++ __FUNCTION__, channel->ChannelName().c_str(), channel->ClientID());
++ bDelete = true;
++ }
++
++ if (!bDelete && channel->UniqueID() <= 0)
++ {
++ CLog::Log(LOGERROR, "PVRChannelGroup - %s - removing invalid channel '%s' from client '%i': no valid unique ID",
++ __FUNCTION__, channel->ChannelName().c_str(), channel->ClientID());
++ bDelete = true;
++ }
++
++ /* remove this channel from all non-system groups if this is the internal group */
++ if (bDelete)
++ {
++ if (IsInternalGroup())
++ {
++ g_PVRChannelGroups->Get(m_bRadio)->RemoveFromAllGroups(channel);
++ channel->Delete();
++ }
++ else
++ {
++ erase(begin() + ptr);
++ }
++ m_bChanged = true;
++ }
++ }
++}
++
++bool CPVRChannelGroup::RemoveFromGroup(const CPVRChannel &channel)
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ for (unsigned int iChannelPtr = 0; iChannelPtr < size(); iChannelPtr++)
++ {
++ if (channel == *at(iChannelPtr).channel)
++ {
++ // TODO notify observers
++ erase(begin() + iChannelPtr);
++ bReturn = true;
++ m_bChanged = true;
++ break;
++ }
++ }
++
++ Renumber();
++
++ return bReturn;
++}
++
++bool CPVRChannelGroup::AddToGroup(CPVRChannel &channel, int iChannelNumber /* = 0 */, bool bSortAndRenumber /* = true */)
++{
++ CSingleLock lock(m_critSection);
++
++ bool bReturn(false);
++
++ if (!CPVRChannelGroup::IsGroupMember(channel))
++ {
++ if (iChannelNumber <= 0 || iChannelNumber > (int) size() + 1)
++ iChannelNumber = size() + 1;
++
++ CPVRChannel *realChannel = (IsInternalGroup()) ?
++ &channel :
++ (CPVRChannel *) g_PVRChannelGroups->GetGroupAll(m_bRadio)->GetByClient(channel.UniqueID(), channel.ClientID());
++
++ if (realChannel)
++ {
++ PVRChannelGroupMember newMember = { realChannel, iChannelNumber };
++ push_back(newMember);
++ m_bChanged = true;
++
++ if (bSortAndRenumber)
++ {
++ if (m_bUsingBackendChannelOrder)
++ SortByClientChannelNumber();
++ else
++ SortByChannelNumber();
++ Renumber();
++ }
++
++ // TODO notify observers
++ bReturn = true;
++ }
++ }
++
++ return bReturn;
++}
++
++bool CPVRChannelGroup::IsGroupMember(const CPVRChannel &channel) const
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ for (unsigned int iChannelPtr = 0; iChannelPtr < size(); iChannelPtr++)
++ {
++ if (channel == *at(iChannelPtr).channel)
++ {
++ bReturn = true;
++ break;
++ }
++ }
++
++ return bReturn;
++}
++
++bool CPVRChannelGroup::IsGroupMember(int iChannelId) const
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ for (unsigned int iChannelPtr = 0; iChannelPtr < size(); iChannelPtr++)
++ {
++ if (iChannelId == at(iChannelPtr).channel->ChannelID())
++ {
++ bReturn = true;
++ break;
++ }
++ }
++
++ return bReturn;
++}
++
++CPVRChannel *CPVRChannelGroup::GetFirstChannel(void) const
++{
++ CPVRChannel *channel = NULL;
++ CSingleLock lock(m_critSection);
++
++ if (size() > 0)
++ channel = at(0).channel;
++
++ return channel;
++}
++
++bool CPVRChannelGroup::SetGroupName(const CStdString &strGroupName, bool bSaveInDb /* = false */)
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ if (m_strGroupName != strGroupName)
++ {
++ /* update the name */
++ m_strGroupName = strGroupName;
++ m_bChanged = true;
++// SetChanged();
++
++ /* persist the changes */
++ if (bSaveInDb)
++ Persist();
++
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CPVRChannelGroup::Persist(void)
++{
++ bool bReturn(true);
++ CSingleLock lock(m_critSection);
++
++ if (!HasChanges())
++ return bReturn;
++
++ if (CPVRDatabase *database = GetPVRDatabase())
++ {
++ CLog::Log(LOGDEBUG, "CPVRChannelGroup - %s - persisting channel group '%s' with %d channels",
++ __FUNCTION__, GroupName().c_str(), (int) size());
++ m_bChanged = false;
++ lock.Leave();
++
++ bReturn = database->Persist(*this);
++ }
++ else
++ {
++ bReturn = false;
++ }
++
++ return bReturn;
++}
++
++bool CPVRChannelGroup::Renumber(void)
++{
++ bool bReturn(false);
++ unsigned int iChannelNumber(0);
++ bool bUseBackendChannelNumbers(g_guiSettings.GetBool("pvrmanager.usebackendchannelnumbers") && g_PVRClients->EnabledClientAmount() == 1);
++ CSingleLock lock(m_critSection);
++
++ for (unsigned int iChannelPtr = 0; iChannelPtr < size(); iChannelPtr++)
++ {
++ unsigned int iCurrentChannelNumber;
++ if (at(iChannelPtr).channel->IsHidden())
++ iCurrentChannelNumber = 0;
++ else if (bUseBackendChannelNumbers)
++ iCurrentChannelNumber = at(iChannelPtr).channel->ClientChannelNumber();
++ else
++ iCurrentChannelNumber = ++iChannelNumber;
++
++ if (at(iChannelPtr).iChannelNumber != iCurrentChannelNumber)
++ {
++ bReturn = true;
++ m_bChanged = true;
++ }
++
++ at(iChannelPtr).iChannelNumber = iCurrentChannelNumber;
++ }
++
++ SortByChannelNumber();
++ ResetChannelNumberCache();
++
++ return bReturn;
++}
++
++void CPVRChannelGroup::ResetChannelNumberCache(void)
++{
++ CPVRChannelGroup *playingGroup = g_PVRManager.GetPlayingGroup(m_bRadio);
++
++ if (this!=playingGroup) {
++ return;
++ }
++
++ CSingleLock lock(m_critSection);
++
++ /* reset the channel number cache */
++ if (!IsInternalGroup())
++ g_PVRChannelGroups->GetGroupAll(m_bRadio)->ResetChannelNumbers();
++
++ /* set all channel numbers on members of this group */
++ for (unsigned int iChannelPtr = 0; iChannelPtr < size(); iChannelPtr++)
++ at(iChannelPtr).channel->SetCachedChannelNumber(at(iChannelPtr).iChannelNumber);
++}
++
++bool CPVRChannelGroup::HasChangedChannels(void) const
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ for (unsigned int iChannelPtr = 0; iChannelPtr < size(); iChannelPtr++)
++ {
++ if (at(iChannelPtr).channel->IsChanged())
++ {
++ bReturn = true;
++ break;
++ }
++ }
++
++ return bReturn;
++}
++
++bool CPVRChannelGroup::HasNewChannels(void) const
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ for (unsigned int iChannelPtr = 0; iChannelPtr < size(); iChannelPtr++)
++ {
++ if (at(iChannelPtr).channel->ChannelID() <= 0)
++ {
++ bReturn = true;
++ break;
++ }
++ }
++
++ return bReturn;
++}
++
++bool CPVRChannelGroup::HasChanges(void) const
++{
++ CSingleLock lock(m_critSection);
++ return m_bChanged || HasNewChannels() || HasChangedChannels();
++}
++
++void CPVRChannelGroup::ResetChannelNumbers(void)
++{
++ CSingleLock lock(m_critSection);
++ for (unsigned int iChannelPtr = 0; iChannelPtr < size(); iChannelPtr++)
++ at(iChannelPtr).channel->SetCachedChannelNumber(0);
++}
++
++void CPVRChannelGroup::Notify(const Observable &obs, const CStdString& msg)
++{
++ if (msg.Equals("settings"))
++ {
++ CSingleLock lock(m_critSection);
++ bool bUsingBackendChannelOrder = g_guiSettings.GetBool("pvrmanager.backendchannelorder");
++ bool bUsingBackendChannelNumbers = g_guiSettings.GetBool("pvrmanager.usebackendchannelnumbers");
++ bool bChannelNumbersChanged = m_bUsingBackendChannelNumbers != bUsingBackendChannelNumbers;
++ bool bChannelOrderChanged = m_bUsingBackendChannelOrder != bUsingBackendChannelOrder;
++
++ m_bUsingBackendChannelOrder = bUsingBackendChannelOrder;
++ m_bUsingBackendChannelNumbers = bUsingBackendChannelNumbers;
++
++ /* check whether this channel group has to be renumbered */
++ if (bChannelOrderChanged || bChannelNumbersChanged)
++ {
++ CLog::Log(LOGDEBUG, "CPVRChannelGroup - %s - renumbering group '%s' to use the backend channel order and/or numbers",
++ __FUNCTION__, m_strGroupName.c_str());
++ SortByClientChannelNumber();
++ Renumber();
++ Persist();
++ }
++ }
++}
++
++bool CPVRPersistGroupJob::DoWork(void)
++{
++ return m_group->Persist();
++}
++
++int CPVRChannelGroup::GetEPGSearch(CFileItemList &results, const EpgSearchFilter &filter)
++{
++ int iInitialSize = results.Size();
++
++ /* get filtered results from all tables */
++ g_EpgContainer.GetEPGSearch(results, filter);
++
++ /* remove duplicate entries */
++ if (filter.m_bPreventRepeats)
++ EpgSearchFilter::RemoveDuplicates(results);
++
++ /* filter recordings */
++ if (filter.m_bIgnorePresentRecordings)
++ EpgSearchFilter::FilterRecordings(results);
++
++ /* filter timers */
++ if (filter.m_bIgnorePresentTimers)
++ EpgSearchFilter::FilterTimers(results);
++
++ return results.Size() - iInitialSize;
++}
++
++int CPVRChannelGroup::GetEPGNow(CFileItemList &results)
++{
++ int iInitialSize = results.Size();
++ CSingleLock lock(m_critSection);
++
++ for (unsigned int iChannelPtr = 0; iChannelPtr < size(); iChannelPtr++)
++ {
++ CPVRChannel *channel = at(iChannelPtr).channel;
++ CEpg *epg = channel->GetEPG();
++ if (!epg || !epg->HasValidEntries() || at(iChannelPtr).channel->IsHidden())
++ continue;
++
++ CEpgInfoTag epgNow;
++ if (!epg->InfoTagNow(epgNow))
++ continue;
++
++ CFileItemPtr entry(new CFileItem(epgNow));
++ entry->SetLabel2(epgNow.StartAsLocalTime().GetAsLocalizedTime(StringUtils::EmptyString, false));
++ entry->SetPath(channel->ChannelName());
++ entry->SetThumbnailImage(channel->IconPath());
++ results.Add(entry);
++ }
++
++ return results.Size() - iInitialSize;
++}
++
++int CPVRChannelGroup::GetEPGNext(CFileItemList &results)
++{
++ int iInitialSize = results.Size();
++ CSingleLock lock(m_critSection);
++
++ for (unsigned int iChannelPtr = 0; iChannelPtr < size(); iChannelPtr++)
++ {
++ CPVRChannel *channel = at(iChannelPtr).channel;
++ CEpg *epg = channel->GetEPG();
++ if (!epg || !epg->HasValidEntries() || at(iChannelPtr).channel->IsHidden())
++ continue;
++
++ CEpgInfoTag epgNow;
++ if (!epg->InfoTagNext(epgNow))
++ continue;
++
++ CFileItemPtr entry(new CFileItem(epgNow));
++ entry->SetLabel2(epgNow.StartAsLocalTime().GetAsLocalizedTime(StringUtils::EmptyString, false));
++ entry->SetPath(channel->ChannelName());
++ entry->SetThumbnailImage(channel->IconPath());
++ results.Add(entry);
++ }
++
++ return results.Size() - iInitialSize;
++}
++
++int CPVRChannelGroup::GetEPGAll(CFileItemList &results)
++{
++ int iInitialSize = results.Size();
++ CSingleLock lock(m_critSection);
++
++ for (unsigned int iChannelPtr = 0; iChannelPtr < size(); iChannelPtr++)
++ {
++ if (!at(iChannelPtr).channel || at(iChannelPtr).channel->IsHidden())
++ continue;
++
++ at(iChannelPtr).channel->GetEPG(results);
++ }
++
++ return results.Size() - iInitialSize;
++}
+diff --git a/xbmc/pvr/channels/PVRChannelGroup.h b/xbmc/pvr/channels/PVRChannelGroup.h
+new file mode 100644
+index 0000000..3c84c2c
+--- /dev/null
++++ b/xbmc/pvr/channels/PVRChannelGroup.h
+@@ -0,0 +1,467 @@
++#pragma once
++
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "FileItem.h"
++#include "PVRChannel.h"
++#include "utils/JobManager.h"
++
++namespace EPG
++{
++ struct EpgSearchFilter;
++}
++
++namespace PVR
++{
++#define XBMC_INTERNAL_GROUP_RADIO 1
++#define XBMC_INTERNAL_GROUP_TV 2
++
++#define PVR_GROUP_TYPE_DEFAULT 0
++#define PVR_GROUP_TYPE_INTERNAL 1
++#define PVR_GROUP_TYPE_USER_DEFINED 2
++
++ class CPVRChannelGroups;
++ class CPVRChannelGroupInternal;
++
++ typedef struct
++ {
++ CPVRChannel *channel;
++ unsigned int iChannelNumber;
++ } PVRChannelGroupMember;
++
++ /** A group of channels */
++ class CPVRChannelGroup : private std::vector<PVRChannelGroupMember>,
++ private Observer,
++ public Observable,
++ public IJobCallback
++
++ {
++ friend class CPVRChannelGroups;
++ friend class CPVRChannelGroupInternal;
++ friend class CPVRDatabase;
++
++ public:
++ /*!
++ * @brief Create a new channel group instance.
++ * @param bRadio True if this group holds radio channels.
++ * @param iGroupId The database ID of this group.
++ * @param strGroupName The name of this group.
++ */
++ CPVRChannelGroup(bool bRadio, unsigned int iGroupId, const CStdString &strGroupName);
++
++ /*!
++ * @brief Create a new channel group.
++ * @param bRadio True if this group holds radio channels.
++ */
++ CPVRChannelGroup(bool bRadio);
++
++ /*!
++ * @brief Create a new channel group instance from a channel group provided by an add-on.
++ * @param group The channel group provided by the add-on.
++ */
++ CPVRChannelGroup(const PVR_CHANNEL_GROUP &group);
++
++ CPVRChannelGroup(const CPVRChannelGroup &group);
++
++ /*!
++ * @brief Destruct this channel group.
++ */
++ virtual ~CPVRChannelGroup(void);
++
++ virtual bool operator ==(const CPVRChannelGroup &right) const;
++ virtual bool operator !=(const CPVRChannelGroup &right) const;
++
++ virtual int Size(void) const { return size(); }
++
++ /*!
++ * @brief Refresh the channel list from the clients.
++ */
++ virtual bool Update(void);
++
++ /*!
++ * @brief Change the channelnumber of a group. Used by CGUIDialogPVRChannelManager. Call SortByChannelNumber() and Renumber() after all changes are done.
++ * @param channel The channel to change the channel number for.
++ * @param iChannelNumber The new channel number.
++ */
++ virtual bool SetChannelNumber(const CPVRChannel &channel, unsigned int iChannelNumber);
++
++ /*!
++ * @brief Move a channel from position iOldIndex to iNewIndex.
++ * @param iOldChannelNumber The channel number of the channel to move.
++ * @param iNewChannelNumber The new channel number.
++ * @param bSaveInDb If true, save this change in the database.
++ * @return True if the channel was moved successfully, false otherwise.
++ */
++ virtual bool MoveChannel(unsigned int iOldChannelNumber, unsigned int iNewChannelNumber, bool bSaveInDb = true);
++
++ /*!
++ * @brief Search missing channel icons for all known channels.
++ * @param bUpdateDb If true, update the changed values in the database.
++ */
++ virtual void SearchAndSetChannelIcons(bool bUpdateDb = false);
++
++ /*!
++ * @brief Remove a channel from this container.
++ * @param channel The channel to remove.
++ * @return True if the channel was found and removed, false otherwise.
++ */
++ virtual bool RemoveFromGroup(const CPVRChannel &channel);
++
++ /*!
++ * @brief Add a channel to this container.
++ * @param channel The channel to add.
++ * @param iChannelNumber The channel number of the channel number to add. Use -1 to add it at the end.
++ * @param bSortAndRenumber Set to false to keep the channel list unsorted after adding a new channel.
++ * @return True if the channel was added, false otherwise.
++ */
++ virtual bool AddToGroup(CPVRChannel &channel, int iChannelNumber = 0, bool bSortAndRenumber = true);
++
++ /*!
++ * @brief Change the name of this group.
++ * @param strGroupName The new group name.
++ * @param bSaveInDb Save in the database or not.
++ * @return True if the something changed, false otherwise.
++ */
++ virtual bool SetGroupName(const CStdString &strGroupName, bool bSaveInDb = false);
++
++ /*!
++ * @brief Persist changed or new data.
++ * @return True if the channel was persisted, false otherwise.
++ */
++ virtual bool Persist(void);
++
++ /*!
++ * @brief Check whether a channel is in this container.
++ * @param channel The channel to find.
++ * @return True if the channel was found, false otherwise.
++ */
++ virtual bool IsGroupMember(const CPVRChannel &channel) const;
++
++ /*!
++ * @brief Check whether a channel is in this container.
++ * @param iChannelId The db id of the channel to find.
++ * @return True if the channel was found, false otherwise.
++ */
++ virtual bool IsGroupMember(int iChannelId) const;
++
++ /*!
++ * @brief Check if this group is the internal group containing all channels.
++ * @return True if it's the internal group, false otherwise.
++ */
++ virtual bool IsInternalGroup(void) const { return false; }
++
++ /*!
++ * @brief Get the first channel in this group.
++ * @return The first channel.
++ */
++ virtual CPVRChannel *GetFirstChannel(void) const;
++
++ /*!
++ * @brief True if this group holds radio channels, false if it holds TV channels.
++ * @return True if this group holds radio channels, false if it holds TV channels.
++ */
++ virtual bool IsRadio(void) const { return m_bRadio; }
++
++ /*!
++ * @brief The database ID of this group.
++ * @return The database ID of this group.
++ */
++ virtual int GroupID(void) const { return m_iGroupId; }
++
++ /*!
++ * @brief Set the database ID of this group.
++ * @param iGroupId The new database ID.
++ */
++ virtual void SetGroupID(int iGroupId) { m_iGroupId = iGroupId; }
++
++ /*!
++ * @brief Set the type of this group.
++ * @param the new type for this group.
++ */
++ virtual void SetGroupType(int iGroupType) { m_iGroupType = iGroupType; }
++
++ /*!
++ * @brief Return the type of this group.
++ */
++ virtual int GroupType(void) const { return m_iGroupType; }
++
++ /*!
++ * @brief The name of this group.
++ * @return The name of this group.
++ */
++ virtual const CStdString &GroupName(void) const { return m_strGroupName; }
++
++ /*! @name Sort methods
++ */
++ //@{
++
++ /*!
++ * @brief Sort the current channel list by client channel number.
++ */
++ virtual void SortByClientChannelNumber(void);
++
++ /*!
++ * @brief Sort the current channel list by channel number.
++ */
++ virtual void SortByChannelNumber(void);
++
++ //@}
++
++ virtual void ResetChannelNumbers(void);
++
++ virtual void Notify(const Observable &obs, const CStdString& msg);
++
++ /*! @name getters
++ */
++ //@{
++
++ /*!
++ * @brief Get a channel given the channel number on the client.
++ * @param iUniqueChannelId The unique channel id on the client.
++ * @param iClientID The ID of the client.
++ * @return The channel or NULL if it wasn't found.
++ */
++ virtual CPVRChannel *GetByClient(int iUniqueChannelId, int iClientID) const;
++
++ /*!
++ * @brief Get a channel given it's channel ID.
++ * @param iChannelID The channel ID.
++ * @return The channel or NULL if it wasn't found.
++ */
++ virtual CPVRChannel *GetByChannelID(int iChannelID) const;
++
++ /*!
++ * @brief Get a channel given it's EPG ID.
++ * @param iEpgID The channel EPG ID.
++ * @return The channel or NULL if it wasn't found.
++ */
++ virtual CPVRChannel *GetByChannelEpgID(int iEpgID) const;
++
++ /*!
++ * @brief Get a channel given it's unique ID.
++ * @param iUniqueID The unique ID.
++ * @return The channel or NULL if it wasn't found.
++ */
++ virtual CPVRChannel *GetByUniqueID(int iUniqueID) const;
++
++ /*!
++ * @brief The channel that was played last that has a valid client or NULL if there was none.
++ * @return The requested channel.
++ */
++ virtual CPVRChannel *GetLastPlayedChannel(void) const;
++
++ /*!
++ * @brief Get a channel given it's channel number.
++ * @param iChannelNumber The channel number.
++ * @return The channel or NULL if it wasn't found.
++ */
++ virtual CPVRChannel *GetByChannelNumber(unsigned int iChannelNumber) const;
++
++ /*!
++ * @brief Get the channel number in this group of the given channel.
++ * @param channel The channel to get the channel number for.
++ * @return The channel number in this group or 0 if the channel isn't a member of this group.
++ */
++ virtual unsigned int GetChannelNumber(const CPVRChannel &channel) const;
++
++ /*!
++ * @brief Get the next channel in this group.
++ * @param channel The current channel.
++ * @return The channel or NULL if it wasn't found.
++ */
++ virtual CPVRChannel *GetByChannelUp(const CPVRChannel &channel) const;
++
++ /*!
++ * @brief Get the previous channel in this group.
++ * @param channel The current channel.
++ * @return The channel or NULL if it wasn't found.
++ */
++ virtual CPVRChannel *GetByChannelDown(const CPVRChannel &channel) const;
++
++ /*!
++ * @brief Get a channel given it's index in this container.
++ * @param index The index in this container.
++ * @return The channel or NULL if it wasn't found.
++ */
++ virtual CPVRChannel *GetByIndex(unsigned int index) const;
++
++ /*!
++ * @brief Get the current index in this group of a channel.
++ * @param channel The channel to get the index for.
++ * @return The index or -1 if it wasn't found.
++ */
++ virtual int GetIndex(const CPVRChannel &channel) const;
++
++ /*!
++ * @brief Get the list of channels in a group.
++ * @param results The file list to store the results in.
++ * @param bGroupMembers If true, get the channels that are in this group. Get the channels that are not in this group otherwise.
++ * @return The amount of channels that were added to the list.
++ */
++ virtual int GetMembers(CFileItemList &results, bool bGroupMembers = true) const;
++
++ /*!
++ * @return The next channel group.
++ */
++ virtual CPVRChannelGroup *GetNextGroup(void) const;
++
++ /*!
++ * @brief The amount of hidden channels in this container.
++ * @return The amount of hidden channels in this container.
++ */
++ virtual int GetNumHiddenChannels(void) const { return 0; }
++
++ /*!
++ * @return True if there is at least one channel in this group with changes that haven't been persisted, false otherwise.
++ */
++ virtual bool HasChangedChannels(void) const;
++
++ /*!
++ * @return True if there is at least one new channel in this group that hasn't been persisted, false otherwise.
++ */
++ virtual bool HasNewChannels(void) const;
++
++ /*!
++ * @return True if anything changed in this group that hasn't been persisted, false otherwise.
++ */
++ virtual bool HasChanges(void) const;
++
++ //@}
++
++ /*!
++ * @brief Reset the channel number cache if this is the selected group in the UI.
++ */
++ virtual void ResetChannelNumberCache(void);
++
++ virtual void OnJobComplete(unsigned int jobID, bool success, CJob* job) {}
++
++ /*!
++ * @brief Get all EPG tables and apply a filter.
++ * @param results The fileitem list to store the results in.
++ * @param filter The filter to apply.
++ * @return The amount of entries that were added.
++ */
++ virtual int GetEPGSearch(CFileItemList &results, const EPG::EpgSearchFilter &filter);
++
++ /*!
++ * @brief Get all EPG tables.
++ * @param results The fileitem list to store the results in.
++ * @return The amount of entries that were added.
++ */
++ virtual int GetEPGAll(CFileItemList &results);
++
++ /*!
++ * @brief Get all entries that are active now.
++ * @param results The fileitem list to store the results in.
++ * @return The amount of entries that were added.
++ */
++ virtual int GetEPGNow(CFileItemList &results);
++
++ /*!
++ * @brief Get all entries that will be active next.
++ * @param results The fileitem list to store the results in.
++ * @return The amount of entries that were added.
++ */
++ virtual int GetEPGNext(CFileItemList &results);
++
++ protected:
++ /*!
++ * @brief Load the channels stored in the database.
++ * @param bCompress If true, compress the database after storing the channels.
++ * @return The amount of channels that were added.
++ */
++ virtual int LoadFromDb(bool bCompress = false);
++
++ /*!
++ * @brief Update the current channel list with the given list.
++ *
++ * Update the current channel list with the given list.
++ * Only the new channels will be present in the passed list after this call.
++ *
++ * @param channels The channels to use to update this list.
++ * @return True if everything went well, false otherwise.
++ */
++ virtual bool UpdateGroupEntries(const CPVRChannelGroup &channels);
++
++ virtual bool AddAndUpdateChannels(const CPVRChannelGroup &channels, bool bUseBackendChannelNumbers);
++ virtual bool RemoveDeletedChannels(const CPVRChannelGroup &channels);
++
++ /*!
++ * @brief Remove invalid channels from this container.
++ */
++ virtual void RemoveInvalidChannels(void);
++
++ /*!
++ * @brief Load the channels from the database.
++ * @return The amount of channels that were added or -1 if an error occured.
++ */
++ virtual int Load(void);
++
++ /*!
++ * @brief Clear this channel list.
++ */
++ virtual void Unload(void);
++
++ /*!
++ * @brief Load the channels from the clients.
++ * @return The amount of channels that were added.
++ */
++ virtual int LoadFromClients(void);
++
++ /*!
++ * @brief Remove invalid channels and updates the channel numbers.
++ * @return True if something changed, false otherwise.
++ */
++ virtual bool Renumber(void);
++
++ /*!
++ * @brief Get the previous or next channel in this group.
++ * @param channel The current channel.
++ * @param bChannelUp True to get the next channel, false to get the previous one.
++ * @return The requested channel or NULL if there is none.
++ */
++ virtual CPVRChannel *GetByChannelUpDown(const CPVRChannel &channel, bool bChannelUp) const;
++
++ bool m_bRadio; /*!< true if this container holds radio channels, false if it holds TV channels */
++ int m_iGroupType; /*!< The type of this group */
++ int m_iGroupId; /*!< The ID of this group in the database */
++ CStdString m_strGroupName; /*!< The name of this group */
++ bool m_bLoaded; /*!< True if this container is loaded, false otherwise */
++ bool m_bChanged; /*!< true if anything changed in this group that hasn't been persisted, false otherwise */
++ bool m_bUsingBackendChannelOrder; /*!< true to use the channel order from backends, false otherwise */
++ bool m_bUsingBackendChannelNumbers; /*!< true to use the channel numbers from 1 backend, false otherwise */
++ CCriticalSection m_critSection;
++ };
++
++ class CPVRPersistGroupJob : public CJob
++ {
++ public:
++ CPVRPersistGroupJob(CPVRChannelGroup *group) { m_group = group; }
++ virtual ~CPVRPersistGroupJob() {}
++ virtual const char *GetType() const { return "pvr-channelgroup-persist"; }
++
++ virtual bool DoWork();
++
++ private:
++ CPVRChannelGroup *m_group;
++ };
++}
+diff --git a/xbmc/pvr/channels/PVRChannelGroupInternal.cpp b/xbmc/pvr/channels/PVRChannelGroupInternal.cpp
+new file mode 100644
+index 0000000..5ae2244
+--- /dev/null
++++ b/xbmc/pvr/channels/PVRChannelGroupInternal.cpp
+@@ -0,0 +1,398 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "settings/GUISettings.h"
++#include "guilib/GUIWindowManager.h"
++#include "dialogs/GUIDialogYesNo.h"
++#include "dialogs/GUIDialogOK.h"
++#include "utils/log.h"
++
++#include "PVRChannelGroupsContainer.h"
++#include "pvr/PVRDatabase.h"
++#include "pvr/PVRManager.h"
++#include "epg/EpgContainer.h"
++#include "pvr/timers/PVRTimers.h"
++#include "pvr/addons/PVRClients.h"
++
++using namespace PVR;
++using namespace EPG;
++using namespace std;
++
++CPVRChannelGroupInternal::CPVRChannelGroupInternal(bool bRadio) :
++ CPVRChannelGroup(bRadio)
++{
++ m_iHiddenChannels = 0;
++ m_iGroupType = PVR_GROUP_TYPE_INTERNAL;
++ m_iGroupId = bRadio ? XBMC_INTERNAL_GROUP_RADIO : XBMC_INTERNAL_GROUP_TV;
++ m_strGroupName = g_localizeStrings.Get(bRadio ? 19216 : 19217);
++}
++
++CPVRChannelGroupInternal::CPVRChannelGroupInternal(const CPVRChannelGroup &group) :
++ CPVRChannelGroup(group)
++{
++ m_iHiddenChannels = group.GetNumHiddenChannels();
++}
++
++CPVRChannelGroupInternal::~CPVRChannelGroupInternal(void)
++{
++ Unload();
++}
++
++int CPVRChannelGroupInternal::Load(void)
++{
++ int iChannelCount = CPVRChannelGroup::Load();
++ UpdateChannelPaths();
++ CreateChannelEpgs();
++
++ return iChannelCount;
++}
++
++void CPVRChannelGroupInternal::CheckGroupName(void)
++{
++ CSingleLock lock(m_critSection);
++
++ /* check whether the group name is still correct, or channels will fail to load after the language setting changed */
++ CStdString strNewGroupName = g_localizeStrings.Get(m_bRadio ? 19216 : 19217);
++ if (!m_strGroupName.Equals(strNewGroupName))
++ {
++ SetGroupName(strNewGroupName, true);
++ UpdateChannelPaths();
++ }
++}
++
++void CPVRChannelGroupInternal::UpdateChannelPaths(void)
++{
++ for (unsigned int iChannelPtr = 0; iChannelPtr < size(); iChannelPtr++)
++ {
++ PVRChannelGroupMember member = at(iChannelPtr);
++ member.channel->UpdatePath(iChannelPtr);
++ }
++}
++
++void CPVRChannelGroupInternal::Unload()
++{
++ for (unsigned int iChannelPtr = 0; iChannelPtr < size(); iChannelPtr++)
++ {
++ delete at(iChannelPtr).channel;
++ }
++
++ CPVRChannelGroup::Unload();
++}
++
++bool CPVRChannelGroupInternal::UpdateFromClient(const CPVRChannel &channel)
++{
++ CSingleLock lock(m_critSection);
++ CPVRChannel *realChannel = (CPVRChannel *) GetByClient(channel.UniqueID(), channel.ClientID());
++ if (realChannel != NULL)
++ realChannel->UpdateFromClient(channel);
++ else
++ realChannel = new CPVRChannel(channel);
++
++ return CPVRChannelGroup::AddToGroup(*realChannel, 0, false);
++}
++
++bool CPVRChannelGroupInternal::InsertInGroup(CPVRChannel &channel, int iChannelNumber /* = 0 */, bool bSortAndRenumber /* = true */)
++{
++ CSingleLock lock(m_critSection);
++ return CPVRChannelGroup::AddToGroup(channel, iChannelNumber, bSortAndRenumber);
++}
++
++bool CPVRChannelGroupInternal::Update(void)
++{
++ CPVRChannelGroupInternal PVRChannels_tmp(m_bRadio);
++ PVRChannels_tmp.LoadFromClients();
++
++ return UpdateGroupEntries(PVRChannels_tmp);
++}
++
++bool CPVRChannelGroupInternal::UpdateTimers(void)
++{
++ CSingleLock lock(m_critSection);
++
++ /* update the timers with the new channel numbers */
++ vector<CPVRTimerInfoTag *> timers;
++ g_PVRTimers->GetActiveTimers(&timers);
++
++ for (unsigned int ptr = 0; ptr < timers.size(); ptr++)
++ {
++ CPVRTimerInfoTag *timer = timers.at(ptr);
++ const CPVRChannel *tag = GetByClient(timer->m_iClientChannelUid, timer->m_iClientId);
++ if (tag)
++ timer->m_channel = tag;
++ }
++
++ return true;
++}
++
++bool CPVRChannelGroupInternal::AddToGroup(CPVRChannel &channel, int iChannelNumber /* = 0 */, bool bSortAndRenumber /* = true */)
++{
++ CSingleLock lock(m_critSection);
++
++ bool bReturn(false);
++
++ /* get the actual channel since this is called from a fileitemlist copy */
++ CPVRChannel *realChannel = (CPVRChannel *) GetByChannelID(channel.ChannelID());
++ if (!realChannel)
++ return bReturn;
++
++ /* switch the hidden flag */
++ if (realChannel->IsHidden())
++ {
++ realChannel->SetHidden(false, true);
++ m_iHiddenChannels--;
++
++ if (bSortAndRenumber)
++ Renumber();
++ }
++
++ /* move this channel and persist */
++ bReturn = (iChannelNumber > 0) ?
++ MoveChannel(realChannel->ChannelNumber(), iChannelNumber, true) :
++ MoveChannel(realChannel->ChannelNumber(), size() - m_iHiddenChannels, true);
++
++ return bReturn;
++}
++
++bool CPVRChannelGroupInternal::RemoveFromGroup(const CPVRChannel &channel)
++{
++ CSingleLock lock(m_critSection);
++
++ /* check if this channel is currently playing if we are hiding it */
++ CPVRChannel currentChannel;
++ if (g_PVRManager.GetCurrentChannel(currentChannel) && currentChannel == channel)
++ {
++ CGUIDialogOK::ShowAndGetInput(19098,19101,0,19102);
++ return false;
++ }
++
++ /* get the actual channel since this is called from a fileitemlist copy */
++ CPVRChannel *realChannel = (CPVRChannel *) GetByChannelID(channel.ChannelID());
++ if (!realChannel)
++ return false;
++
++ /* switch the hidden flag */
++ if (!realChannel->IsHidden())
++ {
++ realChannel->SetHidden(true, true);
++ ++m_iHiddenChannels;
++ }
++ else
++ {
++ realChannel->SetHidden(false, true);
++ --m_iHiddenChannels;
++ }
++
++ /* renumber this list */
++ Renumber();
++
++ /* and persist */
++ return Persist();
++}
++
++bool CPVRChannelGroupInternal::MoveChannel(unsigned int iOldChannelNumber, unsigned int iNewChannelNumber, bool bSaveInDb /* = true */)
++{
++ CSingleLock lock(m_critSection);
++ /* new channel number out of range */
++ if (iNewChannelNumber > size() - m_iHiddenChannels)
++ iNewChannelNumber = size() - m_iHiddenChannels;
++
++ return CPVRChannelGroup::MoveChannel(iOldChannelNumber, iNewChannelNumber, bSaveInDb);
++}
++
++int CPVRChannelGroupInternal::GetMembers(CFileItemList &results, bool bGroupMembers /* = true */) const
++{
++ int iOrigSize = results.Size();
++ CSingleLock lock(m_critSection);
++
++ for (unsigned int iChannelPtr = 0; iChannelPtr < size(); iChannelPtr++)
++ {
++ CPVRChannel *channel = at(iChannelPtr).channel;
++ if (!channel)
++ continue;
++
++ if (bGroupMembers != channel->IsHidden())
++ {
++ CFileItemPtr pFileItem(new CFileItem(*channel));
++ results.Add(pFileItem);
++ }
++ }
++
++ return results.Size() - iOrigSize;
++}
++
++int CPVRChannelGroupInternal::LoadFromDb(bool bCompress /* = false */)
++{
++ CPVRDatabase *database = GetPVRDatabase();
++ if (!database)
++ return -1;
++
++ int iChannelCount = size();
++
++ if (database->Get(*this) > 0)
++ {
++ if (bCompress)
++ database->Compress(true);
++ }
++ else
++ {
++ CLog::Log(LOGINFO, "PVRChannelGroupInternal - %s - no channels in the database",
++ __FUNCTION__);
++ }
++
++ SortByChannelNumber();
++
++ return size() - iChannelCount;
++}
++
++int CPVRChannelGroupInternal::LoadFromClients(void)
++{
++ int iCurSize = size();
++
++ /* get the channels from the backends */
++ PVR_ERROR error;
++ g_PVRClients->GetChannels(this, &error);
++ if (error != PVR_ERROR_NO_ERROR)
++ CLog::Log(LOGWARNING, "CPVRChannelGroupInternal - %s - got bad error (%d) on call to GetChannels", __FUNCTION__, error);
++
++ return size() - iCurSize;
++}
++
++bool CPVRChannelGroupInternal::Renumber(void)
++{
++ CSingleLock lock(m_critSection);
++ bool bReturn(CPVRChannelGroup::Renumber());
++
++ m_iHiddenChannels = 0;
++ for (unsigned int iChannelPtr = 0; iChannelPtr < size(); iChannelPtr++)
++ {
++ if (at(iChannelPtr).channel->IsHidden())
++ m_iHiddenChannels++;
++ else
++ at(iChannelPtr).channel->UpdatePath(iChannelPtr);
++ }
++
++ return bReturn;
++}
++
++bool CPVRChannelGroupInternal::IsGroupMember(const CPVRChannel &channel) const
++{
++ return !channel.IsHidden();
++}
++
++bool CPVRChannelGroupInternal::UpdateChannel(const CPVRChannel &channel)
++{
++ CSingleLock lock(m_critSection);
++ CPVRChannel *updateChannel = (CPVRChannel *) GetByUniqueID(channel.UniqueID());
++
++ if (!updateChannel)
++ {
++ updateChannel = new CPVRChannel(channel.IsRadio());
++ PVRChannelGroupMember newMember = { updateChannel, 0 };
++ push_back(newMember);
++ updateChannel->SetUniqueID(channel.UniqueID());
++ }
++ updateChannel->UpdateFromClient(channel);
++
++ return updateChannel->Persist(!m_bLoaded);
++}
++
++bool CPVRChannelGroupInternal::AddAndUpdateChannels(const CPVRChannelGroup &channels, bool bUseBackendChannelNumbers)
++{
++ bool bReturn(false);
++ CSingleLock lock(m_critSection);
++
++ /* go through the channel list and check for updated or new channels */
++ for (unsigned int iChannelPtr = 0; iChannelPtr < channels.size(); iChannelPtr++)
++ {
++ PVRChannelGroupMember member = channels.at(iChannelPtr);
++ if (!member.channel)
++ continue;
++
++ /* check whether this channel is present in this container */
++ CPVRChannel *existingChannel = (CPVRChannel *) GetByClient(member.channel->UniqueID(), member.channel->ClientID());
++ if (existingChannel)
++ {
++ /* if it's present, update the current tag */
++ if (existingChannel->UpdateFromClient(*member.channel))
++ {
++ existingChannel->Persist(!m_bLoaded);
++
++ bReturn = true;
++ CLog::Log(LOGINFO,"PVRChannelGroupInternal - %s - updated %s channel '%s'",
++ __FUNCTION__, m_bRadio ? "radio" : "TV", member.channel->ChannelName().c_str());
++ }
++ }
++ else
++ {
++ /* new channel */
++ CPVRChannel *newChannel = new CPVRChannel(*member.channel);
++
++ /* insert the new channel in this group */
++ int iChannelNumber = bUseBackendChannelNumbers ? member.channel->ClientChannelNumber() : 0;
++ InsertInGroup(*newChannel, iChannelNumber, false);
++
++ bReturn = true;
++ CLog::Log(LOGINFO,"PVRChannelGroupInternal - %s - added %s channel '%s' at position %d",
++ __FUNCTION__, m_bRadio ? "radio" : "TV", member.channel->ChannelName().c_str(), iChannelNumber);
++ }
++ }
++
++ return bReturn;
++}
++
++bool CPVRChannelGroupInternal::UpdateGroupEntries(const CPVRChannelGroup &channels)
++{
++ bool bReturn(false);
++
++ if (CPVRChannelGroup::UpdateGroupEntries(channels))
++ {
++ /* try to find channel icons */
++ SearchAndSetChannelIcons();
++ Persist();
++
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CPVRChannelGroupInternal::CreateChannelEpgs(bool bForce /* = false */)
++{
++ {
++ CSingleLock lock(m_critSection);
++ for (unsigned int iChannelPtr = 0; iChannelPtr < size(); iChannelPtr++)
++ {
++ CPVRChannel *channel = at(iChannelPtr).channel;
++ if (!channel)
++ continue;
++
++ channel->CreateEPG(bForce);
++ }
++ }
++
++ if (HasChangedChannels())
++ {
++ g_EpgContainer.PersistTables();
++ return Persist();
++ }
++
++ return true;
++}
+diff --git a/xbmc/pvr/channels/PVRChannelGroupInternal.h b/xbmc/pvr/channels/PVRChannelGroupInternal.h
+new file mode 100644
+index 0000000..e8e948f
+--- /dev/null
++++ b/xbmc/pvr/channels/PVRChannelGroupInternal.h
+@@ -0,0 +1,184 @@
++#pragma once
++
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "PVRChannelGroup.h"
++
++namespace PVR
++{
++ class CPVRChannelGroups;
++ class CPVRDatabase;
++
++ /** XBMC's internal group, the group containing all channels */
++
++ class CPVRChannelGroupInternal : public CPVRChannelGroup
++ {
++ friend class CPVRChannelGroups;
++ friend class CPVRDatabase;
++
++ public:
++ /*!
++ * @brief Create a new internal channel group.
++ * @param bRadio True if this group holds radio channels.
++ */
++ CPVRChannelGroupInternal(bool bRadio);
++
++ CPVRChannelGroupInternal(const CPVRChannelGroup &group);
++
++ virtual ~CPVRChannelGroupInternal(void);
++
++ /**
++ * @brief The amount of channels in this container.
++ * @return The amount of channels in this container.
++ */
++ virtual int GetNumHiddenChannels() const { return m_iHiddenChannels; }
++
++ /*!
++ * @brief Update all channel numbers on timers.
++ * @return True if the channel number were updated, false otherwise.
++ */
++ virtual bool UpdateTimers(void);
++
++ /*!
++ * @brief Add or update a channel in this table.
++ * @param channel The channel to update.
++ * @return True if the channel was updated and persisted.
++ */
++ virtual bool UpdateChannel(const CPVRChannel &channel);
++
++ /*!
++ * @brief Add a channel to this internal group.
++ * @param iChannelNumber The channel number to use for this channel or 0 to add it to the back.
++ * @param bSortAndRenumber Set to false to not to sort the group after adding a channel
++ */
++ virtual bool InsertInGroup(CPVRChannel &channel, int iChannelNumber = 0, bool bSortAndRenumber = true);
++
++ /*!
++ * @brief Callback for add-ons to update a channel.
++ * @param channel The updated channel.
++ * @return True if the channel has been updated succesfully, false otherwise.
++ */
++ virtual bool UpdateFromClient(const CPVRChannel &channel);
++
++ /*!
++ * @see CPVRChannelGroup::IsGroupMember
++ */
++ virtual bool IsGroupMember(const CPVRChannel &channel) const;
++
++ /*!
++ * @see CPVRChannelGroup::AddToGroup
++ */
++ virtual bool AddToGroup(CPVRChannel &channel, int iChannelNumber = 0, bool bSortAndRenumber = true);
++
++ /*!
++ * @see CPVRChannelGroup::RemoveFromGroup
++ */
++ virtual bool RemoveFromGroup(const CPVRChannel &channel);
++
++ /*!
++ * @see CPVRChannelGroup::MoveChannel
++ */
++ virtual bool MoveChannel(unsigned int iOldChannelNumber, unsigned int iNewChannelNumber, bool bSaveInDb = true);
++
++ /*!
++ * @see CPVRChannelGroup::GetMembers
++ */
++ virtual int GetMembers(CFileItemList &results, bool bGroupMembers = true) const;
++
++ /*!
++ * @brief Check whether the group name is still correct after the language setting changed.
++ */
++ virtual void CheckGroupName(void);
++
++ /*!
++ * @brief Create an EPG table for each channel.
++ * @brief bForce Create the tables, even if they already have been created before.
++ * @return True if all tables were created successfully, false otherwise.
++ */
++ virtual bool CreateChannelEpgs(bool bForce = false);
++
++ protected:
++ /*!
++ * @brief Load all channels from the database.
++ * @param bCompress Compress the database after changing anything.
++ * @return The amount of channels that were loaded.
++ */
++ virtual int LoadFromDb(bool bCompress = false);
++
++ /*!
++ * @brief Load all channels from the clients.
++ * @return The amount of channels that were loaded.
++ */
++ virtual int LoadFromClients(void);
++
++ /*!
++ * @brief Check if this group is the internal group containing all channels.
++ * @return True if it's the internal group, false otherwise.
++ */
++ virtual bool IsInternalGroup(void) const { return true; }
++
++ /*!
++ * @brief Update the current channel list with the given list.
++ *
++ * Update the current channel list with the given list.
++ * Only the new channels will be present in the passed list after this call.
++ *
++ * @param channels The channels to use to update this list.
++ * @return True if everything went well, false otherwise.
++ */
++ virtual bool UpdateGroupEntries(const CPVRChannelGroup &channels);
++
++ virtual bool AddAndUpdateChannels(const CPVRChannelGroup &channels, bool bUseBackendChannelNumbers);
++
++ /*!
++ * @brief Refresh the channel list from the clients.
++ */
++ virtual bool Update(void);
++
++ /*!
++ * @brief Remove invalid channels and updates the channel numbers.
++ */
++ virtual bool Renumber(void);
++
++ /*!
++ * @brief Load the channels from the database.
++ *
++ * Load the channels from the database.
++ * If no channels are stored in the database, then the channels will be loaded from the clients.
++ *
++ * @return The amount of channels that were added.
++ */
++ virtual int Load(void);
++
++ /*!
++ * @brief Update the vfs paths of all channels.
++ */
++ virtual void UpdateChannelPaths(void);
++
++ /*!
++ * @brief Clear this channel list and destroy all channel instances in it.
++ */
++ virtual void Unload(void);
++
++ int m_iHiddenChannels; /*!< the amount of hidden channels in this container */
++ };
++}
+diff --git a/xbmc/pvr/channels/PVRChannelGroups.cpp b/xbmc/pvr/channels/PVRChannelGroups.cpp
+new file mode 100644
+index 0000000..5d63a03
+--- /dev/null
++++ b/xbmc/pvr/channels/PVRChannelGroups.cpp
+@@ -0,0 +1,509 @@
++/*
++ * Copyright (C) 2005-2008 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "FileItem.h"
++#include "settings/GUISettings.h"
++#include "guilib/GUIWindowManager.h"
++#include "dialogs/GUIDialogYesNo.h"
++#include "dialogs/GUIDialogOK.h"
++#include "guilib/LocalizeStrings.h"
++#include "utils/log.h"
++#include "URL.h"
++#include "filesystem/File.h"
++#include "music/tags/MusicInfoTag.h"
++
++#include "PVRChannelGroupsContainer.h"
++#include "pvr/PVRDatabase.h"
++#include "pvr/PVRManager.h"
++#include "pvr/addons/PVRClients.h"
++
++using namespace PVR;
++
++CPVRChannelGroups::CPVRChannelGroups(bool bRadio) :
++ m_bRadio(bRadio),
++ m_iSelectedGroup(0)
++{
++}
++
++CPVRChannelGroups::~CPVRChannelGroups(void)
++{
++ Clear();
++}
++
++void CPVRChannelGroups::Clear(void)
++{
++ CSingleLock lock(m_critSection);
++ for (unsigned int iGroupPtr = 0; iGroupPtr < size(); iGroupPtr++)
++ delete at(iGroupPtr);
++
++ clear();
++}
++
++bool CPVRChannelGroups::GetGroupsFromClients(void)
++{
++ if (! g_guiSettings.GetBool("pvrmanager.syncchannelgroups"))
++ return true;
++
++ /* get new groups from add-ons */
++ PVR_ERROR error;
++ CPVRChannelGroups groupsTmp(m_bRadio);
++ g_PVRClients->GetChannelGroups(&groupsTmp, &error);
++ return UpdateGroupsEntries(groupsTmp);
++}
++
++bool CPVRChannelGroups::UpdateFromClient(const CPVRChannelGroup &group)
++{
++ CSingleLock lock(m_critSection);
++ CPVRChannelGroup *newGroup = new CPVRChannelGroup(group.IsRadio(), 0, group.GroupName());
++ push_back(newGroup);
++
++ return true;
++}
++
++bool CPVRChannelGroups::Update(const CPVRChannelGroup &group, bool bSaveInDb)
++{
++ CSingleLock lock(m_critSection);
++
++ int iIndex = -1;
++ /* try to find the group by id */
++ if (group.GroupID() > 0)
++ iIndex = GetIndexForGroupID(group.GroupID());
++ /* try to find the group by name if we didn't find it yet */
++ if (iIndex < 0)
++ iIndex = GetIndexForGroupName(group.GroupName());
++
++ if (iIndex < 0)
++ {
++ CPVRChannelGroup *newGroup = new CPVRChannelGroup(m_bRadio, group.GroupID(), group.GroupName());
++ newGroup->SetGroupType(group.GroupType());
++ if (bSaveInDb)
++ newGroup->Persist();
++
++ push_back(newGroup);
++ }
++ else
++ {
++ at(iIndex)->SetGroupID(group.GroupID());
++ at(iIndex)->SetGroupName(group.GroupName());
++ at(iIndex)->SetGroupType(group.GroupType());
++
++ if (bSaveInDb)
++ at(iIndex)->Persist();
++ }
++
++ return true;
++}
++
++CPVRChannelGroup *CPVRChannelGroups::GetById(int iGroupId) const
++{
++ CPVRChannelGroup *group = NULL;
++
++ if (iGroupId == (m_bRadio ? XBMC_INTERNAL_GROUP_RADIO : XBMC_INTERNAL_GROUP_TV))
++ {
++ group = GetGroupAll();
++ }
++ else if (iGroupId > -1)
++ {
++ int iGroupIndex = GetIndexForGroupID(iGroupId);
++ if (iGroupIndex != -1)
++ group = at(iGroupIndex);
++ }
++
++ return group;
++}
++
++CPVRChannelGroup *CPVRChannelGroups::GetByName(const CStdString &strName) const
++{
++ CPVRChannelGroup *group = NULL;
++ CSingleLock lock(m_critSection);
++
++ for (unsigned int iGroupPtr = 0; iGroupPtr < size(); iGroupPtr++)
++ {
++ if (at(iGroupPtr)->GroupName().Equals(strName))
++ {
++ group = at(iGroupPtr);
++ break;
++ }
++ }
++
++ return group;
++}
++
++int CPVRChannelGroups::GetIndexForGroupID(int iGroupId) const
++{
++ int iReturn = -1;
++ CSingleLock lock(m_critSection);
++
++ for (unsigned int iGroupPtr = 0; iGroupPtr < size(); iGroupPtr++)
++ {
++ if (at(iGroupPtr)->GroupID() == iGroupId)
++ {
++ iReturn = iGroupPtr;
++ break;
++ }
++ }
++
++ return iReturn;
++}
++
++int CPVRChannelGroups::GetIndexForGroupName(const CStdString &strName) const
++{
++ int iReturn = -1;
++ CSingleLock lock(m_critSection);
++
++ for (unsigned int iGroupPtr = 0; iGroupPtr < size(); iGroupPtr++)
++ {
++ if (at(iGroupPtr)->GroupName().Equals(strName))
++ {
++ iReturn = iGroupPtr;
++ break;
++ }
++ }
++
++ return iReturn;
++}
++
++void CPVRChannelGroups::RemoveFromAllGroups(CPVRChannel *channel)
++{
++ CSingleLock lock(m_critSection);
++ /* start at position 2 because channels are only deleted from non-system groups.
++ system groups are entries 0 and 1 */
++ for (unsigned int iGroupPtr = 2; iGroupPtr < size(); iGroupPtr++)
++ {
++ CPVRChannelGroup *group = (CPVRChannelGroup *) at(iGroupPtr);
++ group->RemoveFromGroup(*channel);
++ }
++}
++
++bool CPVRChannelGroups::Update(bool bChannelsOnly /* = false */)
++{
++ bool bReturn = true;
++
++ if (!bChannelsOnly)
++ GetGroupsFromClients();
++
++ CSingleLock lock(m_critSection);
++
++ /* only update the internal group if group syncing is disabled */
++ unsigned int iUpdateGroups = !bChannelsOnly && g_guiSettings.GetBool("pvrmanager.syncchannelgroups") ? size() : 1;
++
++ /* system groups are updated first, so new channels are added before anything is done with user defined groups */
++ for (unsigned int iGroupPtr = 0; iGroupPtr < iUpdateGroups; iGroupPtr++)
++ bReturn = at(iGroupPtr)->Update() && bReturn;
++
++ if (bReturn)
++ PersistAll();
++
++ return bReturn;
++}
++
++bool CPVRChannelGroups::UpdateGroupsEntries(const CPVRChannelGroups &groups)
++{
++ CSingleLock lock(m_critSection);
++ /* go through groups list and check for deleted groups */
++ for (int iGroupPtr = size() - 1; iGroupPtr > 0; iGroupPtr--)
++ {
++ CPVRChannelGroup existingGroup(*at(iGroupPtr));
++ CPVRChannelGroup *group = (CPVRChannelGroup *) groups.GetByName(existingGroup.GroupName());
++ if (existingGroup.GroupType() == PVR_GROUP_TYPE_DEFAULT && group == NULL)
++ {
++ CLog::Log(LOGDEBUG, "PVRChannelGroups - %s - user defined group %s with ID '%u' does not exist on the client anymore. deleting",
++ __FUNCTION__, existingGroup.GroupName().c_str(), existingGroup.GroupID());
++ DeleteGroup(*at(iGroupPtr));
++ }
++ }
++ /* go through the groups list and check for new groups */
++ for (unsigned int iGroupPtr = 0; iGroupPtr < groups.size(); iGroupPtr++)
++ {
++ CPVRChannelGroup *group = groups.at(iGroupPtr);
++
++ /* check if this group is present in this container */
++ CPVRChannelGroup *existingGroup = (CPVRChannelGroup *) GetByName(group->GroupName());
++ if (existingGroup == NULL)
++ {
++ CPVRChannelGroup *newGroup = new CPVRChannelGroup(m_bRadio);
++ newGroup->SetGroupName(group->GroupName());
++ push_back(newGroup);
++ }
++ }
++
++ return true;
++}
++
++bool CPVRChannelGroups::LoadUserDefinedChannelGroups(void)
++{
++ CPVRDatabase *database = GetPVRDatabase();
++ if (!database)
++ return false;
++
++ CSingleLock lock(m_critSection);
++
++ /* load the other groups from the database */
++ int iSize = size();
++ database->Get(*this);
++ CLog::Log(LOGDEBUG, "PVRChannelGroups - %s - %d user defined %s channel groups fetched from the database",
++ __FUNCTION__, (int) (size() - iSize), m_bRadio ? "radio" : "TV");
++
++ iSize = size();
++ if (g_guiSettings.GetBool("pvrmanager.syncchannelgroups"))
++ {
++ GetGroupsFromClients();
++ CLog::Log(LOGDEBUG, "PVRChannelGroups - %s - %d new user defined %s channel groups fetched from clients",
++ __FUNCTION__, (int) (size() - iSize), m_bRadio ? "radio" : "TV");
++ }
++ else
++ CLog::Log(LOGDEBUG, "PVRChannelGroups - %s - 'synchannelgroups' is disabled; skipping groups from clients",
++ __FUNCTION__);
++
++ /* load group members */
++ for (unsigned int iGroupPtr = 1; iGroupPtr < size(); iGroupPtr++)
++ at(iGroupPtr)->Load();
++
++ return PersistAll();
++}
++
++bool CPVRChannelGroups::Load(void)
++{
++ CSingleLock lock(m_critSection);
++ CLog::Log(LOGDEBUG, "PVRChannelGroups - %s - loading all %s channel groups",
++ __FUNCTION__, m_bRadio ? "radio" : "TV");
++
++ Clear();
++
++ /* create and load the internal channel group */
++ CPVRChannelGroupInternal *internalChannels = new CPVRChannelGroupInternal(m_bRadio);
++ push_back(internalChannels);
++ internalChannels->Load();
++
++ /* load the other groups from the database */
++ LoadUserDefinedChannelGroups();
++
++ SetSelectedGroup(internalChannels);
++
++ CLog::Log(LOGDEBUG, "PVRChannelGroups - %s - %d %s channel groups loaded",
++ __FUNCTION__, (int) size(), m_bRadio ? "radio" : "TV");
++
++ return size() > 0;
++}
++
++bool CPVRChannelGroups::PersistAll(void)
++{
++ bool bReturn = true;
++ CSingleLock lock(m_critSection);
++ CLog::Log(LOGDEBUG, "CPVRChannelGroups - %s - persisting all changes in channel groups", __FUNCTION__);
++
++ for (unsigned int iGroupPtr = 0; iGroupPtr < size(); iGroupPtr++)
++ bReturn = at(iGroupPtr)->Persist() && bReturn;
++
++ return bReturn;
++}
++
++CPVRChannelGroupInternal *CPVRChannelGroups::GetGroupAll(void) const
++{
++ if (size() > 0)
++ return (CPVRChannelGroupInternal *) at(0);
++ else
++ return NULL;
++}
++
++int CPVRChannelGroups::GetGroupList(CFileItemList* results) const
++{
++ int iReturn = 0;
++ CSingleLock lock(m_critSection);
++
++ CStdString strPath;
++ for (unsigned int iGroupPtr = 0; iGroupPtr < size(); iGroupPtr++)
++ {
++ CFileItemPtr group(new CFileItem(at(iGroupPtr)->GroupName()));
++ group->m_strTitle = at(iGroupPtr)->GroupName();
++ strPath.Format("%i", at(iGroupPtr)->GroupID());
++ group->SetPath(strPath);
++ results->Add(group);
++ ++iReturn;
++ }
++
++ return iReturn;
++}
++
++int CPVRChannelGroups::GetPreviousGroupID(int iGroupId) const
++{
++ const CPVRChannelGroup *currentGroup = GetById(iGroupId);
++ if (!currentGroup)
++ currentGroup = GetGroupAll();
++
++ return GetPreviousGroup(*currentGroup)->GroupID();
++}
++
++CPVRChannelGroup *CPVRChannelGroups::GetPreviousGroup(const CPVRChannelGroup &group) const
++{
++ CPVRChannelGroup *returnGroup = NULL;
++ CSingleLock lock(m_critSection);
++
++ int iCurrentGroupIndex = GetIndexForGroupID(group.GroupID());
++ if (iCurrentGroupIndex - 1 < 0)
++ returnGroup = at(size() - 1);
++ else
++ returnGroup = at(iCurrentGroupIndex - 1);
++
++ if (!returnGroup)
++ returnGroup = GetGroupAll();
++
++ return returnGroup;
++}
++
++int CPVRChannelGroups::GetNextGroupID(int iGroupId) const
++{
++ const CPVRChannelGroup *currentGroup = GetById(iGroupId);
++ if (!currentGroup)
++ currentGroup = GetGroupAll();
++
++ return GetNextGroup(*currentGroup)->GroupID();
++}
++
++CPVRChannelGroup *CPVRChannelGroups::GetNextGroup(const CPVRChannelGroup &group) const
++{
++ CPVRChannelGroup *returnGroup = NULL;
++ CSingleLock lock(m_critSection);
++
++ int iCurrentGroupIndex = GetIndexForGroupID(group.GroupID());
++ if (iCurrentGroupIndex + 1 >= (int)size())
++ returnGroup = at(0);
++ else
++ returnGroup = at(iCurrentGroupIndex + 1);
++
++ if (!returnGroup)
++ returnGroup = GetGroupAll();
++
++ return returnGroup;
++}
++
++CPVRChannelGroup *CPVRChannelGroups::GetSelectedGroup(void) const
++{
++ CPVRChannelGroup *returnGroup = NULL;
++ CSingleLock lock(m_critSection);
++ if (m_iSelectedGroup > -1)
++ returnGroup = at(m_iSelectedGroup);
++
++ return returnGroup;
++}
++
++void CPVRChannelGroups::SetSelectedGroup(CPVRChannelGroup *group)
++{
++ CSingleLock lock(m_critSection);
++
++ m_iSelectedGroup = GetIndexForGroupID(group->GroupID());
++ group->Renumber();
++}
++
++bool CPVRChannelGroups::AddGroup(const CStdString &strName)
++{
++ bool bReturn = false;
++ CSingleLock lock(m_critSection);
++
++ CPVRChannelGroup *group = (CPVRChannelGroup *) GetByName(strName);
++ if (!group)
++ {
++ group = new CPVRChannelGroup(m_bRadio);
++ group->SetGroupName(strName);
++ push_back(group);
++
++ bReturn = group->Persist();
++ }
++ else
++ {
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CPVRChannelGroups::DeleteGroup(const CPVRChannelGroup &group)
++{
++ bool bReturn = false;
++ CSingleLock lock(m_critSection);
++
++ if (group.IsInternalGroup())
++ {
++ CLog::Log(LOGERROR, "CPVRChannelGroups - %s - cannot delete internal group '%s'",
++ __FUNCTION__, group.GroupName().c_str());
++ return bReturn;
++ }
++
++ CPVRDatabase *database = GetPVRDatabase();
++ if (!database)
++ return bReturn;
++
++ /* delete the group from the database */
++ bReturn = database->Delete(group);
++
++ /* delete the group in this container */
++ for (unsigned int iGroupPtr = 0; iGroupPtr < size(); iGroupPtr++)
++ {
++ if (at(iGroupPtr)->GroupID() == group.GroupID())
++ {
++ CPVRChannelGroup *selectedGroup = GetSelectedGroup();
++ if (selectedGroup && *selectedGroup == group)
++ g_PVRManager.SetPlayingGroup(GetGroupAll());
++
++ delete at(iGroupPtr);
++ erase(begin() + iGroupPtr);
++ break;
++ }
++ }
++
++ return bReturn;
++}
++
++const CStdString &CPVRChannelGroups::GetGroupName(int iGroupId) const
++{
++ CSingleLock lock(m_critSection);
++ for (unsigned int iGroupPtr = 0; iGroupPtr < size(); iGroupPtr++)
++ {
++ if (iGroupId == at(iGroupPtr)->GroupID())
++ return at(iGroupPtr)->GroupName();
++ }
++
++ return g_localizeStrings.Get(13205);
++}
++
++int CPVRChannelGroups::GetGroupId(CStdString strGroupName) const
++{
++ CSingleLock lock(m_critSection);
++ for (unsigned int iGroupPtr = 0; iGroupPtr < size(); iGroupPtr++)
++ {
++ if (strGroupName == at(iGroupPtr)->GroupName())
++ return at(iGroupPtr)->GroupID();
++ }
++ return -1;
++}
++
++bool CPVRChannelGroups::AddChannelToGroup(CPVRChannel *channel, int iGroupId)
++{
++ bool bReturn = false;
++ CSingleLock lock(m_critSection);
++ CPVRChannelGroup *group = (CPVRChannelGroup *) GetById(iGroupId);
++ if (group)
++ {
++ bReturn = group->AddToGroup(*channel);
++ }
++
++ return bReturn;
++}
+diff --git a/xbmc/pvr/channels/PVRChannelGroups.h b/xbmc/pvr/channels/PVRChannelGroups.h
+new file mode 100644
+index 0000000..2ae843c
+--- /dev/null
++++ b/xbmc/pvr/channels/PVRChannelGroups.h
+@@ -0,0 +1,211 @@
++#pragma once
++
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "video/VideoInfoTag.h"
++#include "XBDateTime.h"
++#include "FileItem.h"
++#include "PVRChannelGroup.h"
++#include "PVRChannelGroupInternal.h"
++#include "threads/SingleLock.h"
++
++namespace PVR
++{
++ class CPVRChannelGroupsContainer;
++
++ /** A container class for channel groups */
++
++ class CPVRChannelGroups : public std::vector<CPVRChannelGroup *>
++ {
++ friend class CPVRChannelGroupsContainer;
++
++ public:
++ /*!
++ * @brief Create a new group container.
++ * @param bRadio True if this is a container for radio channels, false if it is for tv channels.
++ */
++ CPVRChannelGroups(bool bRadio);
++ virtual ~CPVRChannelGroups(void);
++
++ /*!
++ * @brief Remove all channels from this group.
++ */
++ void Clear(void);
++
++ /*!
++ * @brief Load this container's contents from the database or PVR clients.
++ * @return True if it was loaded successfully, false if not.
++ */
++ bool Load(void);
++
++ /*!
++ * @brief Update a group or add it if it's not in here yet.
++ * @param group The group to update.
++ * @param bSaveInDb True to save the changes in the db.
++ * @return True if the group was added or update successfully, false otherwise.
++ */
++ bool Update(const CPVRChannelGroup &group, bool bSaveInDb = false);
++ bool UpdateFromClient(const CPVRChannelGroup &group);
++
++ /*!
++ * @brief Get a pointer to a channel group given it's ID.
++ * @param iGroupId The ID of the group.
++ * @return The group or NULL if it wasn't found.
++ */
++ CPVRChannelGroup *GetById(int iGroupId) const;
++
++ /*!
++ * @brief Get a group given it's name.
++ * @param strName The name.
++ * @return The group or NULL if it wan't found.
++ */
++ CPVRChannelGroup *GetByName(const CStdString &strName) const;
++
++ /*!
++ * @brief Get the group that contains all channels.
++ * @return The group that contains all channels.
++ */
++ CPVRChannelGroupInternal *GetGroupAll(void) const;
++
++ /*!
++ * @brief Get the list of groups.
++ * @param results The file list to store the results in.
++ * @return The amount of items that were added.
++ */
++ int GetGroupList(CFileItemList* results) const;
++
++ /*!
++ * @brief Get the ID of the previous group in this container.
++ * @param iGroupId The ID of the current group.
++ * @return The ID of the previous group or the ID of the group containing all channels if it wasn't found.
++ */
++ int GetPreviousGroupID(int iGroupId) const;
++
++ /*!
++ * @brief Get the previous group in this container.
++ * @param group The current group.
++ * @return The previous group or the group containing all channels if it wasn't found.
++ */
++ CPVRChannelGroup *GetPreviousGroup(const CPVRChannelGroup &group) const;
++
++ /*!
++ * @brief Get the ID of the next group in this container.
++ * @param iGroupId The ID of the current group.
++ * @return The ID of the next group or the ID of the group containing all channels if it wasn't found.
++ */
++ int GetNextGroupID(int iGroupId) const;
++
++ /*!
++ * @brief Get the next group in this container.
++ * @param group The current group.
++ * @return The next group or the group containing all channels if it wasn't found.
++ */
++ CPVRChannelGroup *GetNextGroup(const CPVRChannelGroup &group) const;
++
++ /*!
++ * @brief Get the group that is currently selected in the UI.
++ * @return The selected group.
++ */
++ virtual CPVRChannelGroup *GetSelectedGroup(void) const;
++
++ /*!
++ * @brief Change the selected group.
++ * @param group The group to select.
++ */
++ virtual void SetSelectedGroup(CPVRChannelGroup *group);
++
++ /*!
++ * @brief Add a group to this container.
++ * @param strName The name of the group.
++ * @return True if the group was added, false otherwise.
++ */
++ bool AddGroup(const CStdString &strName);
++
++ /*!
++ * @brief Delete a group in this container.
++ * @param group The group to delete.
++ * @return True if it was deleted successfully, false if not.
++ */
++ bool DeleteGroup(const CPVRChannelGroup &group);
++
++ /*!
++ * @brief Add a channel to the group with the given ID.
++ * @param channel The channel to add.
++ * @param iGroupId The ID of the group to add the channel to.
++ * @return True if the channel was added, false if not.
++ */
++ bool AddChannelToGroup(CPVRChannel *channel, int iGroupId);
++
++ /*!
++ * @brief Get the name of a group.
++ * @param iGroupId The ID of the group.
++ * @return The name of the group or localized string 953 if it wasn't found.
++ */
++ const CStdString &GetGroupName(int iGroupId) const;
++
++ /*!
++ * @brief Get the ID of a group given it's name.
++ * @param strGroupName The name of the group.
++ * @return The ID or -1 if it wasn't found.
++ */
++ int GetGroupId(CStdString strGroupName) const;
++
++ /*!
++ * @brief Remove a channel from all non-system groups.
++ * @param channel The channel to remove.
++ */
++ void RemoveFromAllGroups(CPVRChannel *channel);
++
++ /*!
++ * @brief Persist all changes in channel groups.
++ * @return True if everything was persisted, false otherwise.
++ */
++ bool PersistAll(void);
++
++ bool IsRadio(void) const { return m_bRadio; }
++
++ protected:
++ /*!
++ * @brief Update the contents of the groups in this container.
++ * @param bChannelsOnly Set to true to only update channels, not the groups themselves.
++ * @return True if the update was successful, false otherwise.
++ */
++ bool Update(bool bChannelsOnly = false);
++
++ bool UpdateGroupsEntries(const CPVRChannelGroups &groups);
++
++ private:
++ bool m_bRadio; /*!< true if this is a container for radio channels, false if it is for tv channels */
++ int m_iSelectedGroup; /*!< the index of the group that's currently selected in the UI */
++ CCriticalSection m_critSection;
++
++ /*!
++ * @brief Get the index in this container of the channel group with the given ID.
++ * @param iGroupId The ID to find.
++ * @return The index or -1 if it wasn't found.
++ */
++ int GetIndexForGroupID(int iGroupId) const;
++ int GetIndexForGroupName(const CStdString &strName) const;
++ bool LoadUserDefinedChannelGroups(void);
++ bool GetGroupsFromClients(void);
++ };
++}
+diff --git a/xbmc/pvr/channels/PVRChannelGroupsContainer.cpp b/xbmc/pvr/channels/PVRChannelGroupsContainer.cpp
+new file mode 100644
+index 0000000..5387c6d
+--- /dev/null
++++ b/xbmc/pvr/channels/PVRChannelGroupsContainer.cpp
+@@ -0,0 +1,336 @@
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "PVRChannelGroupsContainer.h"
++#include "URL.h"
++#include "dialogs/GUIDialogOK.h"
++#include "guilib/LocalizeStrings.h"
++#include "utils/URIUtils.h"
++#include "utils/log.h"
++#include "pvr/PVRManager.h"
++
++using namespace PVR;
++
++CPVRChannelGroupsContainer::CPVRChannelGroupsContainer(void) :
++ m_groupsRadio(new CPVRChannelGroups(true)),
++ m_groupsTV(new CPVRChannelGroups(false)),
++ m_bUpdateChannelsOnly(false),
++ m_bIsUpdating(false)
++{
++}
++
++CPVRChannelGroupsContainer::~CPVRChannelGroupsContainer(void)
++{
++ delete m_groupsRadio;
++ delete m_groupsTV;
++}
++
++bool CPVRChannelGroupsContainer::Update(bool bChannelsOnly /* = false */)
++{
++ CSingleLock lock(m_critSection);
++ if (m_bIsUpdating)
++ return false;
++ m_bIsUpdating = true;
++ m_bUpdateChannelsOnly = bChannelsOnly;
++ lock.Leave();
++
++ CLog::Log(LOGDEBUG, "CPVRChannelGroupsContainer - %s - updating %s", __FUNCTION__, bChannelsOnly ? "channels" : "channel groups");
++ bool bReturn = m_groupsRadio->Update(bChannelsOnly) &&
++ m_groupsTV->Update(bChannelsOnly);
++
++ lock.Enter();
++ m_bIsUpdating = false;
++ lock.Leave();
++
++ return bReturn;
++}
++
++bool CPVRChannelGroupsContainer::Load(void)
++{
++ Unload();
++
++ return m_groupsRadio->Load() &&
++ m_groupsTV->Load();
++}
++
++void CPVRChannelGroupsContainer::Unload(void)
++{
++ m_groupsRadio->Clear();
++ m_groupsTV->Clear();
++}
++
++CPVRChannelGroups *CPVRChannelGroupsContainer::Get(bool bRadio) const
++{
++ return bRadio ? m_groupsRadio : m_groupsTV;
++}
++
++CPVRChannelGroupInternal *CPVRChannelGroupsContainer::GetGroupAll(bool bRadio) const
++{
++ CPVRChannelGroupInternal *group = NULL;
++ const CPVRChannelGroups *groups = Get(bRadio);
++ if (groups)
++ group = groups->GetGroupAll();
++
++ return group;
++}
++
++CPVRChannelGroup *CPVRChannelGroupsContainer::GetById(bool bRadio, int iGroupId) const
++{
++ CPVRChannelGroup *group = NULL;
++ const CPVRChannelGroups *groups = Get(bRadio);
++ if (groups)
++ group = groups->GetById(iGroupId);
++
++ return group;
++}
++
++CPVRChannelGroup *CPVRChannelGroupsContainer::GetByIdFromAll(int iGroupId) const
++{
++ CPVRChannelGroup *group = m_groupsRadio->GetById(iGroupId);
++ if (!group)
++ group = m_groupsTV->GetById(iGroupId);
++
++ return group;
++}
++
++CPVRChannel *CPVRChannelGroupsContainer::GetChannelById(int iChannelId) const
++{
++ CPVRChannel *channel = m_groupsTV->GetGroupAll()->GetByChannelID(iChannelId);
++ if (!channel)
++ channel = m_groupsRadio->GetGroupAll()->GetByChannelID(iChannelId);
++
++ return channel;
++}
++
++CPVRChannel *CPVRChannelGroupsContainer::GetChannelByEpgId(int iEpgId) const
++{
++ CPVRChannel *channel = m_groupsTV->GetGroupAll()->GetByChannelEpgID(iEpgId);
++ if (!channel)
++ channel = m_groupsRadio->GetGroupAll()->GetByChannelEpgID(iEpgId);
++
++ return channel;
++}
++
++bool CPVRChannelGroupsContainer::GetGroupsDirectory(const CStdString &strBase, CFileItemList *results, bool bRadio)
++{
++ const CPVRChannelGroups *channelGroups = Get(bRadio);
++ CFileItemPtr item;
++
++ /* add all groups */
++ for (unsigned int ptr = 0; ptr < channelGroups->size(); ptr++)
++ {
++ const CPVRChannelGroup *group = channelGroups->at(ptr);
++ CStdString strGroup = strBase + "/" + group->GroupName() + "/";
++ item.reset(new CFileItem(strGroup, true));
++ item->SetLabel(group->GroupName());
++ item->SetLabelPreformated(true);
++ results->Add(item);
++ }
++
++ return true;
++}
++
++CPVRChannel *CPVRChannelGroupsContainer::GetByPath(const CStdString &strPath)
++{
++ const CPVRChannelGroup *channels = NULL;
++ int iChannelIndex(-1);
++
++ /* get the filename from curl */
++ CURL url(strPath);
++ CStdString strFileName = url.GetFileName();
++ URIUtils::RemoveSlashAtEnd(strFileName);
++
++ CStdString strCheckPath;
++ for (unsigned int bRadio = 0; bRadio <= 1; bRadio++)
++ {
++ const CPVRChannelGroups *groups = Get(bRadio == 1);
++ for (unsigned int iGroupPtr = 0; iGroupPtr < groups->size(); iGroupPtr++)
++ {
++ const CPVRChannelGroup *group = groups->at(iGroupPtr);
++ strCheckPath.Format("channels/%s/%s/", group->IsRadio() ? "radio" : "tv", group->GroupName().c_str());
++
++ if (strFileName.Left(strCheckPath.length()) == strCheckPath)
++ {
++ strFileName.erase(0, strCheckPath.length());
++ channels = group;
++ iChannelIndex = atoi(strFileName.c_str());
++ break;
++ }
++ }
++ }
++
++ return channels ? channels->GetByIndex(iChannelIndex) : NULL;
++}
++
++bool CPVRChannelGroupsContainer::GetDirectory(const CStdString& strPath, CFileItemList &results)
++{
++ CStdString strBase(strPath);
++
++ /* get the filename from curl */
++ CURL url(strPath);
++ CStdString fileName = url.GetFileName();
++ URIUtils::RemoveSlashAtEnd(fileName);
++
++ if (fileName == "channels")
++ {
++ CFileItemPtr item;
++
++ /* all tv channels */
++ item.reset(new CFileItem(strBase + "/tv/", true));
++ item->SetLabel(g_localizeStrings.Get(19020));
++ item->SetLabelPreformated(true);
++ results.Add(item);
++
++ /* all radio channels */
++ item.reset(new CFileItem(strBase + "/radio/", true));
++ item->SetLabel(g_localizeStrings.Get(19021));
++ item->SetLabelPreformated(true);
++ results.Add(item);
++
++ return true;
++ }
++ else if (fileName == "channels/tv")
++ {
++ return GetGroupsDirectory(strBase, &results, false);
++ }
++ else if (fileName == "channels/radio")
++ {
++ return GetGroupsDirectory(strBase, &results, true);
++ }
++ else if (fileName.Left(12) == "channels/tv/")
++ {
++ CStdString strGroupName(fileName.substr(12));
++ URIUtils::RemoveSlashAtEnd(strGroupName);
++ const CPVRChannelGroup *group = GetTV()->GetByName(strGroupName);
++ if (!group)
++ group = GetGroupAllTV();
++ if (group)
++ group->GetMembers(results, !fileName.Right(7).Equals(".hidden"));
++ return true;
++ }
++ else if (fileName.Left(15) == "channels/radio/")
++ {
++ CStdString strGroupName(fileName.substr(15));
++ URIUtils::RemoveSlashAtEnd(strGroupName);
++ const CPVRChannelGroup *group = GetRadio()->GetByName(strGroupName);
++ if (!group)
++ group = GetGroupAllRadio();
++ if (group)
++ group->GetMembers(results, !fileName.Right(7).Equals(".hidden"));
++ return true;
++ }
++
++ return false;
++}
++
++int CPVRChannelGroupsContainer::GetNumChannelsFromAll()
++{
++ return GetGroupAllTV()->Size() + GetGroupAllRadio()->Size();
++}
++
++CPVRChannelGroup *CPVRChannelGroupsContainer::GetSelectedGroup(bool bRadio) const
++{
++ return Get(bRadio)->GetSelectedGroup();
++}
++
++CPVRChannel *CPVRChannelGroupsContainer::GetByUniqueID(int iClientChannelNumber, int iClientID)
++{
++ CPVRChannel *channel = NULL;
++ const CPVRChannelGroup* channelgroup = GetGroupAllTV();
++
++ if (channelgroup == NULL)
++ channelgroup = GetGroupAllRadio();
++
++ if (channelgroup != NULL)
++ channel = channelgroup->GetByClient(iClientChannelNumber, iClientID);
++
++ return channel;
++}
++
++CPVRChannel *CPVRChannelGroupsContainer::GetByChannelIDFromAll(int iChannelID)
++{
++ CPVRChannel *channel = NULL;
++ const CPVRChannelGroup* channelgroup = GetGroupAllTV();
++ if (channelgroup)
++ channel = channelgroup->GetByChannelID(iChannelID);
++
++ if (!channel)
++ {
++ channelgroup = GetGroupAllRadio();
++ if (channelgroup)
++ channel = channelgroup->GetByChannelID(iChannelID);
++ }
++
++ return channel;
++}
++
++CPVRChannel *CPVRChannelGroupsContainer::GetByClientFromAll(unsigned int iClientId, unsigned int iChannelUid)
++{
++ CPVRChannel *channel = NULL;
++
++ channel = GetGroupAllTV()->GetByClient(iChannelUid, iClientId);
++
++ if (channel == NULL)
++ channel = GetGroupAllRadio()->GetByClient(iChannelUid, iClientId);
++
++ return channel;
++}
++
++CPVRChannel *CPVRChannelGroupsContainer::GetByUniqueIDFromAll(int iUniqueID)
++{
++ CPVRChannel *channel;
++ const CPVRChannelGroup* channelgroup = GetGroupAllTV();
++
++ if (channelgroup == NULL)
++ channelgroup = GetGroupAllRadio();
++
++ if (channelgroup != NULL)
++ channel = channelgroup->GetByUniqueID(iUniqueID);
++
++ return NULL;
++}
++
++void CPVRChannelGroupsContainer::SearchMissingChannelIcons(void)
++{
++ CLog::Log(LOGINFO, "PVRChannelGroupsContainer - %s - starting channel icon search", __FUNCTION__);
++
++ // TODO: Add Process dialog here
++ CPVRChannelGroup* channelgrouptv = (CPVRChannelGroup *) GetGroupAllTV();
++ CPVRChannelGroup* channelgroupradio =(CPVRChannelGroup *) GetGroupAllRadio();
++
++ if (channelgrouptv != NULL)
++ channelgrouptv->SearchAndSetChannelIcons(true);
++ if (channelgroupradio != NULL)
++ channelgroupradio->SearchAndSetChannelIcons(true);
++
++ CGUIDialogOK::ShowAndGetInput(19103,0,20177,0);
++}
++
++CPVRChannel *CPVRChannelGroupsContainer::GetLastPlayedChannel(void) const
++{
++ CPVRChannel *lastChannel = GetGroupAllTV()->GetLastPlayedChannel();
++
++ CPVRChannel *lastRadioChannel = GetGroupAllRadio()->GetLastPlayedChannel();
++ if (!lastChannel || (lastRadioChannel && lastChannel->LastWatched() < lastRadioChannel->LastWatched()))
++ lastChannel = lastRadioChannel;
++
++ return lastChannel;
++}
+diff --git a/xbmc/pvr/channels/PVRChannelGroupsContainer.h b/xbmc/pvr/channels/PVRChannelGroupsContainer.h
+new file mode 100644
+index 0000000..b041bed
+--- /dev/null
++++ b/xbmc/pvr/channels/PVRChannelGroupsContainer.h
+@@ -0,0 +1,215 @@
++#pragma once
++
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "PVRChannelGroups.h"
++#include "threads/Thread.h"
++#include "threads/CriticalSection.h"
++
++namespace PVR
++{
++ class CPVRManager;
++ class CPVRChannelsUpdateJob;
++ class CPVRChannelGroupsUpdateJob;
++
++ class CPVRChannelGroupsContainer
++ {
++ friend class CPVRManager;
++ friend class CPVRChannelsUpdateJob;
++ friend class CPVRChannelGroupsUpdateJob;
++
++ public:
++ /*!
++ * @brief Create a new container for all channel groups
++ */
++ CPVRChannelGroupsContainer(void);
++
++ /*!
++ * @brief Destroy this container.
++ */
++ virtual ~CPVRChannelGroupsContainer(void);
++
++ /*!
++ * @brief Load all channel groups and all channels in those channel groups.
++ * @return True if all groups were loaded, false otherwise.
++ */
++ virtual bool Load(void);
++
++ /*!
++ * @brief Unload and destruct all channel groups and all channels in them.
++ */
++ virtual void Unload(void);
++
++ /*!
++ * @brief Get the TV channel groups.
++ * @return The TV channel groups.
++ */
++ virtual CPVRChannelGroups *GetTV(void) const { return Get(false); }
++
++ /*!
++ * @brief Get the radio channel groups.
++ * @return The radio channel groups.
++ */
++ virtual CPVRChannelGroups *GetRadio(void) const { return Get(true); }
++
++ /*!
++ * @brief Get the radio or TV channel groups.
++ * @param bRadio If true, get the radio channel groups. Get the TV channel groups otherwise.
++ * @return The requested groups.
++ */
++ virtual CPVRChannelGroups *Get(bool bRadio) const;
++
++ /*!
++ * @brief Get the group containing all TV channels.
++ * @return The group containing all TV channels.
++ */
++ virtual CPVRChannelGroupInternal *GetGroupAllTV(void) const{ return GetGroupAll(false); }
++
++ /*!
++ * @brief Get the group containing all radio channels.
++ * @return The group containing all radio channels.
++ */
++ virtual CPVRChannelGroupInternal *GetGroupAllRadio(void) const{ return GetGroupAll(true); }
++
++ /*!
++ * @brief Get the group containing all TV or radio channels.
++ * @param bRadio If true, get the group containing all radio channels. Get the group containing all TV channels otherwise.
++ * @return The requested group.
++ */
++ virtual CPVRChannelGroupInternal *GetGroupAll(bool bRadio) const;
++
++ /*!
++ * @brief Get a group given it's ID.
++ * @param bRadio If true, search the radio channel container. Search the TV channels otherwise.
++ * @param iGroupId The ID of the group.
++ * @return The requested group or NULL if it wasn't found.
++ */
++ virtual CPVRChannelGroup *GetById(bool bRadio, int iGroupId) const;
++
++ /*!
++ * @brief Get a group given it's ID.
++ * @param iGroupId The ID of the group.
++ * @return The requested group or NULL if it wasn't found.
++ */
++ virtual CPVRChannelGroup *GetByIdFromAll(int iGroupId) const;
++
++ /*!
++ * @brief Get a channel given it's database ID.
++ * @param iChannelId The ID of the channel.
++ * @return The channel or NULL if it wasn't found.
++ */
++ virtual CPVRChannel *GetChannelById(int iChannelId) const;
++
++ /*!
++ * @brief Get a channel given it's EPG ID.
++ * @param iEpgId The EPG ID of the channel.
++ * @return The channel or NULL if it wasn't found.
++ */
++ virtual CPVRChannel *GetChannelByEpgId(int iEpgId) const;
++
++ /*!
++ * @brief Get the groups list for a directory.
++ * @param strBase The directory path.
++ * @param results The file list to store the results in.
++ * @param bRadio Get radio channels or tv channels.
++ * @return True if the list was filled succesfully.
++ */
++ virtual bool GetGroupsDirectory(const CStdString &strBase, CFileItemList *results, bool bRadio);
++
++ /*!
++ * @brief Get a channel given it's path.
++ * @param strPath The path.
++ * @return The channel or NULL if it wasn't found.
++ */
++ virtual CPVRChannel *GetByPath(const CStdString &strPath);
++
++ /*!
++ * @brief Get the directory for a path.
++ * @param strPath The path.
++ * @param results The file list to store the results in.
++ * @return True if the directory was found, false if not.
++ */
++ virtual bool GetDirectory(const CStdString& strPath, CFileItemList &results);
++
++ /*!
++ * @brief The total amount of unique channels in all containers.
++ * @return The total amount of unique channels in all containers.
++ */
++ virtual int GetNumChannelsFromAll(void);
++
++ /*!
++ * @brief Get the group that is currently selected in the UI.
++ * @param bRadio True to get the selected radio group, false to get the selected TV group.
++ * @return The selected group.
++ */
++ virtual CPVRChannelGroup *GetSelectedGroup(bool bRadio) const;
++
++ /*!
++ * @brief Get a channel given it's channel ID from all containers.
++ * @param iClientChannelNumber The channel number on the client.
++ * @param iClientID The ID of the client.
++ * @return The channel or NULL if it wasn't found.
++ */
++ virtual CPVRChannel *GetByUniqueID(int iClientChannelNumber, int iClientID);
++
++ /*!
++ * @brief Get a channel given it's channel ID from all containers.
++ * @param iChannelID The channel ID.
++ * @return The channel or NULL if it wasn't found.
++ */
++ virtual CPVRChannel *GetByChannelIDFromAll(int iChannelID);
++
++ virtual CPVRChannel *GetByClientFromAll(unsigned int iClientId, unsigned int iChannelUid);
++
++ /*!
++ * @brief Get a channel given it's unique ID.
++ * @param iUniqueID The unique ID of the channel.
++ * @return The channel or NULL if it wasn't found.
++ */
++ virtual CPVRChannel *GetByUniqueIDFromAll(int iUniqueID);
++
++ /*!
++ * @brief Try to find missing channel icons automatically
++ */
++ virtual void SearchMissingChannelIcons(void);
++
++ /*!
++ * @brief The channel that was played last that has a valid client or NULL if there was none.
++ * @return The requested channel.
++ */
++ virtual CPVRChannel *GetLastPlayedChannel(void) const;
++
++ protected:
++ /*!
++ * @brief Update the contents of all the groups in this container.
++ * @param bChannelsOnly Set to true to only update channels, not the groups themselves.
++ * @return True if the update was successful, false otherwise.
++ */
++ virtual bool Update(bool bChannelsOnly = false);
++
++ CPVRChannelGroups *m_groupsRadio; /*!< all radio channel groups */
++ CPVRChannelGroups *m_groupsTV; /*!< all TV channel groups */
++ CCriticalSection m_critSection;
++ bool m_bUpdateChannelsOnly;
++ bool m_bIsUpdating;
++ };
++}
+diff --git a/xbmc/pvr/dialogs/GUIDialogPVRChannelManager.cpp b/xbmc/pvr/dialogs/GUIDialogPVRChannelManager.cpp
+new file mode 100644
+index 0000000..bda6c88
+--- /dev/null
++++ b/xbmc/pvr/dialogs/GUIDialogPVRChannelManager.cpp
+@@ -0,0 +1,839 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "GUIDialogPVRChannelManager.h"
++
++#include "FileItem.h"
++#include "GUIDialogPVRGroupManager.h"
++#include "dialogs/GUIDialogFileBrowser.h"
++#include "dialogs/GUIDialogKeyboard.h"
++#include "dialogs/GUIDialogOK.h"
++#include "dialogs/GUIDialogProgress.h"
++#include "dialogs/GUIDialogSelect.h"
++#include "dialogs/GUIDialogYesNo.h"
++#include "guilib/GUIEditControl.h"
++#include "guilib/GUIRadioButtonControl.h"
++#include "guilib/GUISpinControlEx.h"
++#include "guilib/GUIWindowManager.h"
++#include "guilib/LocalizeStrings.h"
++#include "pvr/PVRManager.h"
++#include "pvr/channels/PVRChannelGroupsContainer.h"
++#include "pvr/addons/PVRClients.h"
++#include "settings/GUISettings.h"
++#include "settings/Settings.h"
++#include "storage/MediaManager.h"
++
++#define BUTTON_OK 4
++#define BUTTON_APPLY 5
++#define BUTTON_CANCEL 6
++#define RADIOBUTTON_ACTIVE 7
++#define EDIT_NAME 8
++#define BUTTON_CHANNEL_LOGO 9
++#define IMAGE_CHANNEL_LOGO 10
++#define RADIOBUTTON_USEEPG 12
++#define SPIN_EPGSOURCE_SELECTION 13
++#define CONTROL_LIST_CHANNELS 20
++#define BUTTON_GROUP_MANAGER 30
++#define BUTTON_EDIT_CHANNEL 31
++#define BUTTON_DELETE_CHANNEL 32
++#define BUTTON_NEW_CHANNEL 33
++#define BUTTON_RADIO_TV 34
++
++using namespace std;
++using namespace PVR;
++
++CGUIDialogPVRChannelManager::CGUIDialogPVRChannelManager(void) :
++ CGUIDialog(WINDOW_DIALOG_PVR_CHANNEL_MANAGER, "DialogPVRChannelManager.xml"),
++ m_bIsRadio(false),
++ m_channelItems(new CFileItemList)
++{
++}
++
++CGUIDialogPVRChannelManager::~CGUIDialogPVRChannelManager(void)
++{
++ delete m_channelItems;
++}
++
++bool CGUIDialogPVRChannelManager::OnActionClose(const CAction &action)
++{
++ bool bReturn(false);
++ int iActionId = action.GetID();
++ if (iActionId == ACTION_PREVIOUS_MENU || iActionId == ACTION_PARENT_DIR)
++ {
++ Close();
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CGUIDialogPVRChannelManager::OnActionMove(const CAction &action)
++{
++ bool bReturn(false);
++ int iActionId = action.GetID();
++ if (GetFocusedControlID() == CONTROL_LIST_CHANNELS &&
++ (iActionId == ACTION_MOVE_DOWN || iActionId == ACTION_MOVE_UP ||
++ iActionId == ACTION_PAGE_DOWN || iActionId == ACTION_PAGE_UP))
++ {
++ bReturn = true;
++ if (!m_bMovingMode)
++ {
++ CGUIDialog::OnAction(action);
++ int iSelected = m_viewControl.GetSelectedItem();
++ if (iSelected != m_iSelected)
++ {
++ m_iSelected = iSelected;
++ SetData(m_iSelected);
++ }
++ }
++ else
++ {
++ CStdString strNumber;
++ CGUIDialog::OnAction(action);
++
++ bool bMoveUp = iActionId == ACTION_PAGE_UP || iActionId == ACTION_MOVE_UP;
++ unsigned int iLines = bMoveUp ? abs(m_iSelected - m_viewControl.GetSelectedItem()) : 1;
++ bool bOutOfBounds = bMoveUp ? m_iSelected <= 0 : m_iSelected >= m_channelItems->Size() - 1;
++ if (bOutOfBounds)
++ {
++ bMoveUp = !bMoveUp;
++ iLines = m_channelItems->Size() - 1;
++ }
++
++ for (unsigned int iLine = 0; iLine < iLines; iLine++)
++ {
++ unsigned int iNewSelect = bMoveUp ? m_iSelected - 1 : m_iSelected + 1;
++ if (m_channelItems->Get(iNewSelect)->GetProperty("Number").asString() != "-")
++ {
++ strNumber.Format("%i", m_iSelected+1);
++ m_channelItems->Get(iNewSelect)->SetProperty("Number", strNumber);
++ strNumber.Format("%i", iNewSelect+1);
++ m_channelItems->Get(m_iSelected)->SetProperty("Number", strNumber);
++ }
++ m_channelItems->Swap(iNewSelect, m_iSelected);
++ m_iSelected = iNewSelect;
++ }
++
++ m_viewControl.SetItems(*m_channelItems);
++ m_viewControl.SetSelectedItem(m_iSelected);
++ }
++ }
++
++ return bReturn;
++}
++
++bool CGUIDialogPVRChannelManager::OnAction(const CAction& action)
++{
++ return OnActionClose(action) ||
++ OnActionMove(action) ||
++ CGUIDialog::OnAction(action);
++}
++
++bool CGUIDialogPVRChannelManager::OnMessageInit(CGUIMessage &message)
++{
++ CGUIWindow::OnMessage(message);
++ m_iSelected = 0;
++ m_bIsRadio = false;
++ m_bMovingMode = false;
++ m_bContainsChanges = false;
++ SetProperty("IsRadio", "");
++ Update();
++ SetData(m_iSelected);
++
++ return true;
++}
++
++bool CGUIDialogPVRChannelManager::OnClickListChannels(CGUIMessage &message)
++{
++ if (!m_bMovingMode)
++ {
++ int iAction = message.GetParam1();
++ int iItem = m_viewControl.GetSelectedItem();
++
++ /* Check file item is in list range and get his pointer */
++ if (iItem < 0 || iItem >= (int)m_channelItems->Size()) return true;
++
++ /* Process actions */
++ if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK)
++ {
++ /* Show Contextmenu */
++ OnPopupMenu(iItem);
++
++ return true;
++ }
++ }
++ else
++ {
++ CFileItemPtr pItem = m_channelItems->Get(m_iSelected);
++ if (pItem)
++ {
++ pItem->SetProperty("Changed", true);
++ pItem->Select(false);
++ m_bMovingMode = false;
++ m_bContainsChanges = true;
++ return true;
++ }
++ }
++
++ return false;
++}
++
++bool CGUIDialogPVRChannelManager::OnClickButtonOK(CGUIMessage &message)
++{
++ SaveList();
++ Close();
++ return true;
++}
++
++bool CGUIDialogPVRChannelManager::OnClickButtonApply(CGUIMessage &message)
++{
++ SaveList();
++ return true;
++}
++
++bool CGUIDialogPVRChannelManager::OnClickButtonCancel(CGUIMessage &message)
++{
++ Close();
++ return true;
++}
++
++bool CGUIDialogPVRChannelManager::OnClickButtonRadioTV(CGUIMessage &message)
++{
++ if (m_bContainsChanges)
++ {
++ CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
++ if (!pDialog)
++ return true;
++
++ pDialog->SetHeading(20052);
++ pDialog->SetLine(0, "");
++ pDialog->SetLine(1, 19212);
++ pDialog->SetLine(2, 20103);
++ pDialog->DoModal();
++
++ if (pDialog->IsConfirmed())
++ SaveList();
++ }
++
++ m_iSelected = 0;
++ m_bMovingMode = false;
++ m_bContainsChanges = false;
++ m_bIsRadio = !m_bIsRadio;
++ SetProperty("IsRadio", m_bIsRadio ? "true" : "");
++ Update();
++ SetData(m_iSelected);
++ return true;
++}
++
++bool CGUIDialogPVRChannelManager::OnClickButtonRadioActive(CGUIMessage &message)
++{
++ CGUIRadioButtonControl *pRadioButton = (CGUIRadioButtonControl *)GetControl(RADIOBUTTON_ACTIVE);
++ if (pRadioButton)
++ {
++ CFileItemPtr pItem = m_channelItems->Get(m_iSelected);
++ if (pItem)
++ {
++ pItem->SetProperty("Changed", true);
++ pItem->SetProperty("ActiveChannel", pRadioButton->IsSelected());
++ m_bContainsChanges = true;
++ Renumber();
++ return true;
++ }
++ }
++
++ return false;
++}
++
++bool CGUIDialogPVRChannelManager::OnClickButtonEditName(CGUIMessage &message)
++{
++ CGUIEditControl *pEdit = (CGUIEditControl *)GetControl(EDIT_NAME);
++ if (pEdit)
++ {
++ CFileItemPtr pItem = m_channelItems->Get(m_iSelected);
++ if (pItem)
++ {
++ pItem->SetProperty("Changed", true);
++ pItem->SetProperty("Name", pEdit->GetLabel2());
++ m_bContainsChanges = true;
++
++ return true;
++ }
++ }
++
++ return false;
++}
++
++bool CGUIDialogPVRChannelManager::OnClickButtonChannelLogo(CGUIMessage &message)
++{
++ CFileItemPtr pItem = m_channelItems->Get(m_iSelected);
++ if (!pItem)
++ return false;
++ if (g_settings.GetCurrentProfile().canWriteSources() && !g_passwordManager.IsProfileLockUnlocked())
++ return false;
++ else if (!g_passwordManager.IsMasterLockUnlocked(true))
++ return false;
++
++ // setup our thumb list
++ CFileItemList items;
++
++ // add the current thumb, if available
++ if (!pItem->GetProperty("Icon").asString().empty())
++ {
++ CFileItemPtr current(new CFileItem("thumb://Current", false));
++ current->SetThumbnailImage(pItem->GetPVRChannelInfoTag()->IconPath());
++ current->SetLabel(g_localizeStrings.Get(20016));
++ items.Add(current);
++ }
++ else if (pItem->HasThumbnail())
++ { // already have a thumb that the share doesn't know about - must be a local one, so we mayaswell reuse it.
++ CFileItemPtr current(new CFileItem("thumb://Current", false));
++ current->SetThumbnailImage(pItem->GetThumbnailImage());
++ current->SetLabel(g_localizeStrings.Get(20016));
++ items.Add(current);
++ }
++
++ // and add a "no thumb" entry as well
++ CFileItemPtr nothumb(new CFileItem("thumb://None", false));
++ nothumb->SetIconImage(pItem->GetIconImage());
++ nothumb->SetLabel(g_localizeStrings.Get(20018));
++ items.Add(nothumb);
++
++ CStdString strThumb;
++ VECSOURCES shares;
++ if (g_guiSettings.GetString("pvrmenu.iconpath") != "")
++ {
++ CMediaSource share1;
++ share1.strPath = g_guiSettings.GetString("pvrmenu.iconpath");
++ share1.strName = g_localizeStrings.Get(19018);
++ shares.push_back(share1);
++ }
++ g_mediaManager.GetLocalDrives(shares);
++ if (!CGUIDialogFileBrowser::ShowAndGetImage(items, shares, g_localizeStrings.Get(1030), strThumb))
++ return false;
++
++ if (strThumb == "thumb://Current")
++ return true;
++
++ if (strThumb == "thumb://None")
++ strThumb = "";
++
++ pItem->SetProperty("Icon", strThumb);
++ pItem->SetProperty("Changed", true);
++ m_bContainsChanges = true;
++ return true;
++}
++
++bool CGUIDialogPVRChannelManager::OnClickButtonUseEPG(CGUIMessage &message)
++{
++ CGUIRadioButtonControl *pRadioButton = (CGUIRadioButtonControl *)GetControl(RADIOBUTTON_USEEPG);
++ if (pRadioButton)
++ {
++ CFileItemPtr pItem = m_channelItems->Get(m_iSelected);
++ if (pItem)
++ {
++ pItem->SetProperty("Changed", true);
++ pItem->SetProperty("UseEPG", pRadioButton->IsSelected());
++ m_bContainsChanges = true;
++
++ return true;
++ }
++ }
++
++ return false;
++}
++
++bool CGUIDialogPVRChannelManager::OnClickEPGSourceSpin(CGUIMessage &message)
++{
++ // TODO: Add EPG scraper support
++ return true;
++// CGUISpinControlEx *pSpin = (CGUISpinControlEx *)GetControl(SPIN_EPGSOURCE_SELECTION);
++// if (pSpin)
++// {
++// CFileItemPtr pItem = m_channelItems->Get(m_iSelected);
++// if (pItem)
++// {
++// pItem->SetProperty("EPGSource", (int)0);
++// pItem->SetProperty("Changed", true);
++// m_bContainsChanges = true;
++// return true;
++// }
++// }
++}
++
++bool CGUIDialogPVRChannelManager::OnClickButtonGroupManager(CGUIMessage &message)
++{
++ /* Load group manager dialog */
++ CGUIDialogPVRGroupManager* pDlgInfo = (CGUIDialogPVRGroupManager*)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_GROUP_MANAGER);
++ if (!pDlgInfo)
++ return false;
++
++ pDlgInfo->SetRadio(m_bIsRadio);
++
++ /* Open dialog window */
++ pDlgInfo->DoModal();
++
++ Update();
++ return true;
++}
++
++bool CGUIDialogPVRChannelManager::OnClickButtonEditChannel(CGUIMessage &message)
++{
++ CFileItemPtr pItem = m_channelItems->Get(m_iSelected);
++ if (!pItem)
++ return false;
++
++ if (pItem->GetProperty("Virtual").asBoolean())
++ {
++ CStdString strURL = pItem->GetProperty("StreamURL").asString();
++ if (CGUIDialogKeyboard::ShowAndGetInput(strURL, g_localizeStrings.Get(19214), false))
++ pItem->SetProperty("StreamURL", strURL);
++ return true;
++ }
++
++ CGUIDialogOK::ShowAndGetInput(19033,19038,0,0);
++ return true;
++}
++
++bool CGUIDialogPVRChannelManager::OnClickButtonDeleteChannel(CGUIMessage &message)
++{
++ CFileItemPtr pItem = m_channelItems->Get(m_iSelected);
++ if (!pItem)
++ return false;
++
++ CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
++ if (!pDialog)
++ return true;
++
++ pDialog->SetHeading(19211);
++ pDialog->SetLine(0, "");
++ pDialog->SetLine(1, 750);
++ pDialog->SetLine(2, "");
++ pDialog->DoModal();
++
++ if (pDialog->IsConfirmed())
++ {
++ if (pItem->GetProperty("Virtual").asBoolean())
++ {
++ pItem->GetPVRChannelInfoTag()->SetVirtual(true, true);
++ m_channelItems->Remove(m_iSelected);
++ m_viewControl.SetItems(*m_channelItems);
++ Renumber();
++ return true;
++ }
++ CGUIDialogOK::ShowAndGetInput(19033,19038,0,0);
++ }
++ return true;
++}
++
++bool CGUIDialogPVRChannelManager::OnClickButtonNewChannel(CGUIMessage &message)
++{
++ std::vector<long> clients;
++
++ CGUIDialogSelect* pDlgSelect = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
++ if (!pDlgSelect)
++ return false;
++
++ pDlgSelect->SetHeading(19213); // Select Client
++ pDlgSelect->Add(g_localizeStrings.Get(19209));
++ clients.push_back(XBMC_VIRTUAL_CLIENTID);
++
++ CLIENTMAP clientMap;
++ if (g_PVRClients->GetConnectedClients(&clientMap) > 0)
++ {
++ CLIENTMAPITR itr;
++ for (itr = clientMap.begin() ; itr != clientMap.end(); itr++)
++ {
++ clients.push_back((*itr).first);
++ pDlgSelect->Add((*itr).second->Name());
++ }
++ }
++ pDlgSelect->DoModal();
++
++ int selection = pDlgSelect->GetSelectedLabel();
++ if (selection >= 0 && selection <= (int) clients.size())
++ {
++ int clientID = clients[selection];
++ if (clientID == XBMC_VIRTUAL_CLIENTID)
++ {
++ CStdString strURL = "";
++ if (CGUIDialogKeyboard::ShowAndGetInput(strURL, g_localizeStrings.Get(19214), false))
++ {
++ if (!strURL.IsEmpty())
++ {
++ CPVRChannel *newchannel = new CPVRChannel(m_bIsRadio);
++ newchannel->SetChannelName(g_localizeStrings.Get(19204));
++ newchannel->SetEPGEnabled(false);
++ newchannel->SetVirtual(true);
++ newchannel->SetStreamURL(strURL);
++ newchannel->SetClientID(XBMC_VIRTUAL_CLIENTID);
++ g_PVRChannelGroups->GetGroupAll(m_bIsRadio)->AddToGroup(*newchannel);
++ newchannel->Persist();
++
++ CFileItemPtr channel(new CFileItem(newchannel));
++ if (channel)
++ {
++ channel->SetProperty("ActiveChannel", true);
++ channel->SetProperty("Name", g_localizeStrings.Get(19204));
++ channel->SetProperty("UseEPG", false);
++ channel->SetProperty("Icon", newchannel->IconPath());
++ channel->SetProperty("EPGSource", (int)0);
++ channel->SetProperty("ClientName", g_localizeStrings.Get(19209));
++
++ m_channelItems->AddFront(channel, m_iSelected);
++ m_viewControl.SetItems(*m_channelItems);
++ Renumber();
++ }
++ }
++ }
++ }
++ else
++ {
++ CGUIDialogOK::ShowAndGetInput(19033,19038,0,0);
++ }
++ }
++ return true;
++}
++
++bool CGUIDialogPVRChannelManager::OnMessageClick(CGUIMessage &message)
++{
++ int iControl = message.GetSenderId();
++ switch(iControl)
++ {
++ case CONTROL_LIST_CHANNELS:
++ return OnClickListChannels(message);
++ case BUTTON_OK:
++ return OnClickButtonOK(message);
++ case BUTTON_APPLY:
++ return OnClickButtonApply(message);
++ case BUTTON_CANCEL:
++ return OnClickButtonCancel(message);
++ case BUTTON_RADIO_TV:
++ return OnClickButtonRadioTV(message);
++ case RADIOBUTTON_ACTIVE:
++ return OnClickButtonRadioActive(message);
++ case EDIT_NAME:
++ return OnClickButtonEditName(message);
++ case BUTTON_CHANNEL_LOGO:
++ return OnClickButtonChannelLogo(message);
++ case RADIOBUTTON_USEEPG:
++ return OnClickButtonUseEPG(message);
++ case SPIN_EPGSOURCE_SELECTION:
++ return OnClickEPGSourceSpin(message);
++ case BUTTON_GROUP_MANAGER:
++ return OnClickButtonGroupManager(message);
++ case BUTTON_EDIT_CHANNEL:
++ return OnClickButtonEditChannel(message);
++ case BUTTON_DELETE_CHANNEL:
++ return OnClickButtonDeleteChannel(message);
++ case BUTTON_NEW_CHANNEL:
++ return OnClickButtonNewChannel(message);
++ default:
++ return false;
++ }
++}
++
++bool CGUIDialogPVRChannelManager::OnMessage(CGUIMessage& message)
++{
++ unsigned int iMessage = message.GetMessage();
++
++ switch (iMessage)
++ {
++ case GUI_MSG_WINDOW_DEINIT:
++ Clear();
++ break;
++ case GUI_MSG_WINDOW_INIT:
++ return OnMessageInit(message);
++ case GUI_MSG_CLICKED:
++ return OnMessageClick(message);
++ }
++
++ return CGUIDialog::OnMessage(message);
++}
++
++void CGUIDialogPVRChannelManager::OnWindowLoaded(void)
++{
++ CGUIDialog::OnWindowLoaded();
++
++ m_viewControl.Reset();
++ m_viewControl.SetParentWindow(GetID());
++ m_viewControl.AddView(GetControl(CONTROL_LIST_CHANNELS));
++}
++
++void CGUIDialogPVRChannelManager::OnWindowUnload(void)
++{
++ CGUIDialog::OnWindowUnload();
++ m_viewControl.Reset();
++}
++
++CFileItemPtr CGUIDialogPVRChannelManager::GetCurrentListItem(int offset)
++{
++ return m_channelItems->Get(m_iSelected);
++}
++
++bool CGUIDialogPVRChannelManager::OnPopupMenu(int iItem)
++{
++ // popup the context menu
++ // grab our context menu
++ CContextButtons buttons;
++
++ // mark the item
++ if (iItem >= 0 && iItem < m_channelItems->Size())
++ m_channelItems->Get(iItem)->Select(true);
++ else
++ return false;
++
++ CFileItemPtr pItem = m_channelItems->Get(iItem);
++ if (!pItem)
++ return false;
++
++ buttons.Add(CONTEXT_BUTTON_MOVE, 116); /* Move channel up or down */
++ if (pItem->GetProperty("Virtual").asBoolean())
++ buttons.Add(CONTEXT_BUTTON_EDIT_SOURCE, 1027); /* Edit virtual channel URL */
++
++ int choice = CGUIDialogContextMenu::ShowAndGetChoice(buttons);
++
++ // deselect our item
++ if (iItem >= 0 && iItem < m_channelItems->Size())
++ m_channelItems->Get(iItem)->Select(false);
++
++ if (choice < 0)
++ return false;
++
++ return OnContextButton(iItem, (CONTEXT_BUTTON)choice);
++}
++
++bool CGUIDialogPVRChannelManager::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
++{
++ /* Check file item is in list range and get his pointer */
++ if (itemNumber < 0 || itemNumber >= (int)m_channelItems->Size()) return false;
++
++ CFileItemPtr pItem = m_channelItems->Get(itemNumber);
++ if (!pItem)
++ return false;
++
++ if (button == CONTEXT_BUTTON_MOVE)
++ {
++ m_bMovingMode = true;
++ pItem->Select(true);
++ }
++ else if (button == CONTEXT_BUTTON_EDIT_SOURCE)
++ {
++ CStdString strURL = pItem->GetProperty("StreamURL").asString();
++ if (CGUIDialogKeyboard::ShowAndGetInput(strURL, g_localizeStrings.Get(19214), false))
++ pItem->SetProperty("StreamURL", strURL);
++ }
++ return true;
++}
++
++void CGUIDialogPVRChannelManager::SetData(int iItem)
++{
++ CGUIEditControl *pEdit;
++ CGUIRadioButtonControl *pRadioButton;
++
++ /* Check file item is in list range and get his pointer */
++ if (iItem < 0 || iItem >= (int)m_channelItems->Size()) return;
++
++ CFileItemPtr pItem = m_channelItems->Get(iItem);
++ if (!pItem)
++ return;
++
++ pEdit = (CGUIEditControl *)GetControl(EDIT_NAME);
++ if (pEdit)
++ {
++ pEdit->SetLabel2(pItem->GetProperty("Name").asString());
++ pEdit->SetInputType(CGUIEditControl::INPUT_TYPE_TEXT, 19208);
++ }
++
++ pRadioButton = (CGUIRadioButtonControl *)GetControl(RADIOBUTTON_ACTIVE);
++ if (pRadioButton) pRadioButton->SetSelected(pItem->GetProperty("ActiveChannel").asBoolean());
++
++ pRadioButton = (CGUIRadioButtonControl *)GetControl(RADIOBUTTON_USEEPG);
++ if (pRadioButton) pRadioButton->SetSelected(pItem->GetProperty("UseEPG").asBoolean());
++}
++
++void CGUIDialogPVRChannelManager::Update()
++{
++ // lock our display, as this window is rendered from the player thread
++ g_graphicsContext.Lock();
++ m_viewControl.SetCurrentView(CONTROL_LIST_CHANNELS);
++
++ // empty the lists ready for population
++ Clear();
++
++ const CPVRChannelGroup *channels = g_PVRChannelGroups->GetGroupAll(m_bIsRadio);
++
++ // No channels available, nothing to do.
++ if( !channels )
++ return;
++
++ for (int iChannelPtr = 0; iChannelPtr < channels->Size(); iChannelPtr++)
++ {
++ const CPVRChannel *channel = channels->GetByIndex(iChannelPtr);
++ CFileItemPtr channelFile(new CFileItem(*channel));
++ channelFile->SetProperty("ActiveChannel", !channel->IsHidden());
++ channelFile->SetProperty("Name", channel->ChannelName());
++ channelFile->SetProperty("UseEPG", channel->EPGEnabled());
++ channelFile->SetProperty("Icon", channel->IconPath());
++ channelFile->SetProperty("EPGSource", (int)0);
++ CStdString number; number.Format("%i", channel->ChannelNumber());
++ channelFile->SetProperty("Number", number);
++
++ if (channel->IsVirtual())
++ {
++ channelFile->SetProperty("Virtual", true);
++ channelFile->SetProperty("StreamURL", channel->StreamURL());
++ }
++
++ CStdString clientName;
++ if (channel->ClientID() == XBMC_VIRTUAL_CLIENTID) /* XBMC internal */
++ clientName = g_localizeStrings.Get(19209);
++ else
++ g_PVRClients->GetClientName(channel->ClientID(), clientName);
++ channelFile->SetProperty("ClientName", clientName);
++
++ m_channelItems->Add(channelFile);
++ }
++
++ CGUISpinControlEx *pSpin = (CGUISpinControlEx *)GetControl(SPIN_EPGSOURCE_SELECTION);
++ if (pSpin)
++ {
++ pSpin->Clear();
++ pSpin->AddLabel(g_localizeStrings.Get(19210), 0);
++ /// TODO: Add Labels for EPG scrapers here
++ }
++
++ Renumber();
++ m_viewControl.SetItems(*m_channelItems);
++ m_viewControl.SetSelectedItem(m_iSelected);
++
++ g_graphicsContext.Unlock();
++}
++
++void CGUIDialogPVRChannelManager::Clear(void)
++{
++ m_viewControl.Clear();
++ m_channelItems->Clear();
++}
++
++bool CGUIDialogPVRChannelManager::PersistChannel(CFileItemPtr pItem, CPVRChannelGroup *group, unsigned int *iChannelNumber)
++{
++ if (!pItem || !pItem->HasPVRChannelInfoTag())
++ return false;
++
++ /* get the real channel from the group */
++ CPVRChannel *channel = (CPVRChannel *) group->GetByUniqueID(pItem->GetPVRChannelInfoTag()->UniqueID());
++ if (!channel)
++ return false;
++
++ /* get values from the form */
++ bool bHidden = !pItem->GetProperty("ActiveChannel").asBoolean();
++ bool bVirtual = pItem->GetProperty("Virtual").asBoolean();
++ bool bEPGEnabled = pItem->GetProperty("UseEPG").asBoolean();
++ int iEPGSource = pItem->GetProperty("EPGSource").asInteger();
++ CStdString strChannelName = pItem->GetProperty("Name").asString();
++ CStdString strIconPath = pItem->GetProperty("Icon").asString();
++ CStdString strStreamURL = pItem->GetProperty("StreamURL").asString();
++
++ channel->SetChannelName(strChannelName);
++ channel->SetHidden(bHidden);
++ channel->SetIconPath(strIconPath);
++ if (bVirtual)
++ channel->SetStreamURL(strStreamURL);
++ if (iEPGSource == 0)
++ channel->SetEPGScraper("client");
++ // TODO add other scrapers
++ channel->SetEPGEnabled(bEPGEnabled);
++
++ /* set new values in the channel tag */
++ if (bHidden)
++ {
++ group->SortByChannelNumber(); // or previous changes will be overwritten
++ group->RemoveFromGroup(*channel);
++ }
++ else
++ group->SetChannelNumber(*channel, ++(*iChannelNumber));
++
++ return true;
++}
++
++void CGUIDialogPVRChannelManager::SaveList(void)
++{
++ if (!m_bContainsChanges)
++ return;
++
++ /* display the progress dialog */
++ CGUIDialogProgress* pDlgProgress = (CGUIDialogProgress*)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
++ pDlgProgress->SetHeading(190);
++ pDlgProgress->SetLine(0, "");
++ pDlgProgress->SetLine(1, 328);
++ pDlgProgress->SetLine(2, "");
++ pDlgProgress->StartModal();
++ pDlgProgress->Progress();
++ pDlgProgress->SetPercentage(0);
++
++ /* persist all channels */
++ unsigned int iNextChannelNumber(0);
++ CPVRChannelGroup *group = g_PVRChannelGroups->GetGroupAll(m_bIsRadio);
++ if (!group)
++ return;
++ for (int iListPtr = 0; iListPtr < m_channelItems->Size(); iListPtr++)
++ {
++ CFileItemPtr pItem = m_channelItems->Get(iListPtr);
++ PersistChannel(pItem, group, &iNextChannelNumber);
++
++ pDlgProgress->SetPercentage(iListPtr * 100 / m_channelItems->Size());
++ }
++
++ group->SortByChannelNumber();
++ group->Persist();
++ group->ResetChannelNumberCache();
++ m_bContainsChanges = false;
++ SetItemsUnchanged();
++ pDlgProgress->Close();
++}
++
++void CGUIDialogPVRChannelManager::SetItemsUnchanged(void)
++{
++ for (int iItemPtr = 0; iItemPtr < m_channelItems->Size(); iItemPtr++)
++ {
++ CFileItemPtr pItem = m_channelItems->Get(iItemPtr);
++ if (pItem)
++ pItem->SetProperty("Changed", false);
++ }
++}
++
++void CGUIDialogPVRChannelManager::Renumber(void)
++{
++ int iNextChannelNumber(0);
++ CStdString strNumber;
++ CFileItemPtr pItem;
++ for (int iChannelPtr = 0; iChannelPtr < m_channelItems->Size(); iChannelPtr++)
++ {
++ pItem = m_channelItems->Get(iChannelPtr);
++ if (pItem->GetProperty("ActiveChannel").asBoolean())
++ {
++ strNumber.Format("%i", ++iNextChannelNumber);
++ pItem->SetProperty("Number", strNumber);
++ }
++ else
++ pItem->SetProperty("Number", "-");
++ }
++}
+diff --git a/xbmc/pvr/dialogs/GUIDialogPVRChannelManager.h b/xbmc/pvr/dialogs/GUIDialogPVRChannelManager.h
+new file mode 100644
+index 0000000..4dc8ffa
+--- /dev/null
++++ b/xbmc/pvr/dialogs/GUIDialogPVRChannelManager.h
+@@ -0,0 +1,85 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "guilib/GUIDialog.h"
++#include "dialogs/GUIDialogContextMenu.h"
++#include "GUIViewControl.h"
++
++namespace PVR
++{
++ class CPVRChannelGroup;
++
++ class CGUIDialogPVRChannelManager : public CGUIDialog
++ {
++ public:
++ CGUIDialogPVRChannelManager(void);
++ virtual ~CGUIDialogPVRChannelManager(void);
++ virtual bool OnMessage(CGUIMessage& message);
++ virtual bool OnAction(const CAction& action);
++ virtual void OnWindowLoaded(void);
++ virtual void OnWindowUnload(void);
++ virtual bool HasListItems() const { return true; };
++ virtual CFileItemPtr GetCurrentListItem(int offset = 0);
++
++ protected:
++ virtual bool OnPopupMenu(int iItem);
++ virtual bool OnContextButton(int itemNumber, CONTEXT_BUTTON button);
++
++ virtual bool OnActionClose(const CAction &action);
++ virtual bool OnActionMove(const CAction &action);
++
++ virtual bool OnMessageInit(CGUIMessage &message);
++ virtual bool OnMessageClick(CGUIMessage &message);
++
++ virtual bool OnClickListChannels(CGUIMessage &message);
++ virtual bool OnClickButtonOK(CGUIMessage &message);
++ virtual bool OnClickButtonApply(CGUIMessage &message);
++ virtual bool OnClickButtonCancel(CGUIMessage &message);
++ virtual bool OnClickButtonRadioTV(CGUIMessage &message);
++ virtual bool OnClickButtonRadioActive(CGUIMessage &message);
++ virtual bool OnClickButtonEditName(CGUIMessage &message);
++ virtual bool OnClickButtonChannelLogo(CGUIMessage &message);
++ virtual bool OnClickButtonUseEPG(CGUIMessage &message);
++ virtual bool OnClickEPGSourceSpin(CGUIMessage &message);
++ virtual bool OnClickButtonGroupManager(CGUIMessage &message);
++ virtual bool OnClickButtonEditChannel(CGUIMessage &message);
++ virtual bool OnClickButtonDeleteChannel(CGUIMessage &message);
++ virtual bool OnClickButtonNewChannel(CGUIMessage &message);
++
++ virtual bool PersistChannel(CFileItemPtr pItem, CPVRChannelGroup *group, unsigned int *iChannelNumber);
++ virtual void SetItemsUnchanged(void);
++
++ private:
++ void Clear(void);
++ void Update(void);
++ void SaveList(void);
++ void Renumber(void);
++ void SetData(int iItem);
++ bool m_bIsRadio;
++ bool m_bMovingMode;
++ bool m_bContainsChanges;
++
++ int m_iSelected;
++ CFileItemList* m_channelItems;
++ CGUIViewControl m_viewControl;
++ };
++}
+diff --git a/xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.cpp b/xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.cpp
+new file mode 100644
+index 0000000..fa983f4
+--- /dev/null
++++ b/xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.cpp
+@@ -0,0 +1,239 @@
++/*
++ * Copyright (C) 2005-2009 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "GUIDialogPVRChannelsOSD.h"
++#include "Application.h"
++#include "FileItem.h"
++#include "guilib/GUIWindowManager.h"
++#include "dialogs/GUIDialogOK.h"
++#include "GUIDialogPVRGuideInfo.h"
++#include "ViewState.h"
++#include "settings/GUISettings.h"
++#include "GUIInfoManager.h"
++
++#include "pvr/PVRManager.h"
++#include "pvr/channels/PVRChannelGroupsContainer.h"
++#include "epg/Epg.h"
++#include "pvr/timers/PVRTimerInfoTag.h"
++
++using namespace std;
++using namespace PVR;
++using namespace EPG;
++
++#define CONTROL_LIST 11
++
++CGUIDialogPVRChannelsOSD::CGUIDialogPVRChannelsOSD() :
++ CGUIDialog(WINDOW_DIALOG_PVR_OSD_CHANNELS, "DialogPVRChannelsOSD.xml"),
++ Observer()
++{
++ m_vecItems = new CFileItemList;
++}
++
++CGUIDialogPVRChannelsOSD::~CGUIDialogPVRChannelsOSD()
++{
++ delete m_vecItems;
++
++ if (IsObserving(g_infoManager))
++ g_infoManager.UnregisterObserver(this);
++}
++
++bool CGUIDialogPVRChannelsOSD::OnMessage(CGUIMessage& message)
++{
++ switch (message.GetMessage())
++ {
++ case GUI_MSG_WINDOW_DEINIT:
++ {
++ Clear();
++ }
++ break;
++
++ case GUI_MSG_WINDOW_INIT:
++ {
++ /* Close dialog immediately if now TV or radio channel is playing */
++ if (!g_PVRManager.IsPlaying())
++ {
++ Close();
++ return true;
++ }
++ CGUIWindow::OnMessage(message);
++ Update();
++ return true;
++ }
++ break;
++
++ case GUI_MSG_CLICKED:
++ {
++ int iControl = message.GetSenderId();
++
++ if (m_viewControl.HasControl(iControl)) // list/thumb control
++ {
++ int iItem = m_viewControl.GetSelectedItem();
++ int iAction = message.GetParam1();
++
++ if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK)
++ {
++ /* Switch to channel */
++ GotoChannel(iItem);
++ return true;
++ }
++ else if (iAction == ACTION_SHOW_INFO || iAction == ACTION_MOUSE_RIGHT_CLICK)
++ {
++ /* Show information Dialog */
++ ShowInfo(iItem);
++ return true;
++ }
++ }
++ }
++ break;
++ }
++
++ return CGUIDialog::OnMessage(message);
++}
++
++void CGUIDialogPVRChannelsOSD::Update()
++{
++
++ // empty the list ready for population. Outside gfx lock to prevent deadlock when calling CAnnouncementManager::RemoveAnnouncer during CPVRChannel destruction. as CGUIWindowHome::Announce() holds the announce mutex and needs the gfx lock
++ Clear();
++ // lock our display, as this window is rendered from the player thread
++ g_graphicsContext.Lock();
++
++ if (!IsObserving(g_infoManager))
++ g_infoManager.RegisterObserver(this);
++
++ m_viewControl.SetCurrentView(DEFAULT_VIEW_LIST);
++
++ // empty the list ready for population
++ //Clear();
++
++ CPVRChannel channel;
++ g_PVRManager.GetCurrentChannel(channel);
++ const CPVRChannelGroup *group = g_PVRManager.GetPlayingGroup(channel.IsRadio());
++
++ if (group)
++ {
++ group->GetMembers(*m_vecItems);
++ m_viewControl.SetItems(*m_vecItems);
++ m_viewControl.SetSelectedItem(group->GetIndex(channel));
++ }
++
++ g_graphicsContext.Unlock();
++}
++
++void CGUIDialogPVRChannelsOSD::Clear()
++{
++ m_viewControl.Clear();
++ m_vecItems->Clear();
++}
++
++void CGUIDialogPVRChannelsOSD::CloseOrSelect(unsigned int iItem)
++{
++ if (g_guiSettings.GetBool("pvrmenu.closechannelosdonswitch"))
++ Close();
++ else
++ m_viewControl.SetSelectedItem(iItem);
++}
++
++void CGUIDialogPVRChannelsOSD::GotoChannel(int item)
++{
++ /* Check file item is in list range and get his pointer */
++ if (item < 0 || item >= (int)m_vecItems->Size()) return;
++ CFileItemPtr pItem = m_vecItems->Get(item);
++
++ if (pItem->GetPath() == g_application.CurrentFile())
++ {
++ CloseOrSelect(item);
++ return;
++ }
++
++ if (g_PVRManager.IsPlaying() && pItem->HasPVRChannelInfoTag() && g_application.m_pPlayer)
++ {
++ if (!g_application.m_pPlayer->SwitchChannel(*pItem->GetPVRChannelInfoTag()))
++ {
++ Close(true);
++ return;
++ }
++ }
++ else
++ g_application.getApplicationMessenger().PlayFile(*pItem);
++
++ CloseOrSelect(item);
++}
++
++void CGUIDialogPVRChannelsOSD::ShowInfo(int item)
++{
++ /* Check file item is in list range and get his pointer */
++ if (item < 0 || item >= (int)m_vecItems->Size()) return;
++
++ CFileItemPtr pItem = m_vecItems->Get(item);
++ if (pItem && pItem->IsPVRChannel())
++ {
++ /* Get the current running show on this channel from the EPG storage */
++ CEpgInfoTag epgnow;
++ if (!pItem->GetPVRChannelInfoTag()->GetEPGNow(epgnow))
++ return;
++ CFileItem *itemNow = new CFileItem(epgnow);
++
++ /* Load programme info dialog */
++ CGUIDialogPVRGuideInfo* pDlgInfo = (CGUIDialogPVRGuideInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_GUIDE_INFO);
++ if (!pDlgInfo)
++ return;
++
++ /* inform dialog about the file item and open dialog window */
++ pDlgInfo->SetProgInfo(itemNow);
++ pDlgInfo->DoModal();
++ delete itemNow; /* delete previuosly created FileItem */
++ }
++
++ return;
++}
++
++void CGUIDialogPVRChannelsOSD::OnWindowLoaded()
++{
++ CGUIDialog::OnWindowLoaded();
++ m_viewControl.Reset();
++ m_viewControl.SetParentWindow(GetID());
++ m_viewControl.AddView(GetControl(CONTROL_LIST));
++}
++
++void CGUIDialogPVRChannelsOSD::OnWindowUnload()
++{
++ CGUIDialog::OnWindowUnload();
++ m_viewControl.Reset();
++}
++
++CGUIControl *CGUIDialogPVRChannelsOSD::GetFirstFocusableControl(int id)
++{
++ if (m_viewControl.HasControl(id))
++ id = m_viewControl.GetCurrentControl();
++
++ return CGUIWindow::GetFirstFocusableControl(id);
++}
++
++void CGUIDialogPVRChannelsOSD::Notify(const Observable &obs, const CStdString& msg)
++{
++ if (msg.Equals("current-item"))
++ {
++ g_graphicsContext.Lock();
++ m_viewControl.SetItems(*m_vecItems);
++ g_graphicsContext.Unlock();
++ }
++}
+diff --git a/xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.h b/xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.h
+new file mode 100644
+index 0000000..0bb7b6a
+--- /dev/null
++++ b/xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.h
+@@ -0,0 +1,53 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "guilib/GUIDialog.h"
++#include "GUIViewControl.h"
++#include "utils/Observer.h"
++
++class CFileItemList;
++
++namespace PVR
++{
++ class CGUIDialogPVRChannelsOSD : public CGUIDialog, public Observer
++ {
++ public:
++ CGUIDialogPVRChannelsOSD(void);
++ virtual ~CGUIDialogPVRChannelsOSD(void);
++ virtual bool OnMessage(CGUIMessage& message);
++ virtual void OnWindowLoaded();
++ virtual void OnWindowUnload();
++ virtual void Notify(const Observable &obs, const CStdString& msg);
++
++ protected:
++ void CloseOrSelect(unsigned int iItem);
++ void GotoChannel(int iItem);
++ void ShowInfo(int item);
++ void Clear();
++ void Update();
++ CGUIControl *GetFirstFocusableControl(int id);
++
++ CFileItemList *m_vecItems;
++ CGUIViewControl m_viewControl;
++ };
++}
++
+diff --git a/xbmc/pvr/dialogs/GUIDialogPVRCutterOSD.cpp b/xbmc/pvr/dialogs/GUIDialogPVRCutterOSD.cpp
+new file mode 100644
+index 0000000..1a4cd6c
+--- /dev/null
++++ b/xbmc/pvr/dialogs/GUIDialogPVRCutterOSD.cpp
+@@ -0,0 +1,62 @@
++/*
++ * Copyright (C) 2005-2009 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "GUIDialogPVRCutterOSD.h"
++#include "utils/log.h"
++#include "Application.h"
++
++using namespace std;
++using namespace PVR;
++
++CGUIDialogPVRCutterOSD::CGUIDialogPVRCutterOSD()
++ : CGUIDialog(WINDOW_DIALOG_PVR_OSD_CUTTER, "DialogPVRCutterOSD.xml")
++{
++}
++
++CGUIDialogPVRCutterOSD::~CGUIDialogPVRCutterOSD()
++{
++}
++
++bool CGUIDialogPVRCutterOSD::OnAction(const CAction& action)
++{
++ if (action.GetID() == ACTION_PREVIOUS_MENU || action.GetID() == ACTION_PARENT_DIR)
++ {
++ Close();
++ return true;
++ }
++
++ return CGUIDialog::OnAction(action);
++}
++
++bool CGUIDialogPVRCutterOSD::OnMessage(CGUIMessage& message)
++{
++ return CGUIDialog::OnMessage(message);
++}
++
++void CGUIDialogPVRCutterOSD::OnInitWindow()
++{
++ CGUIDialog::OnInitWindow();
++}
++
++void CGUIDialogPVRCutterOSD::OnDeinitWindow(int nextWindowID)
++{
++ CGUIDialog::OnDeinitWindow(nextWindowID);
++}
+diff --git a/xbmc/pvr/dialogs/GUIDialogPVRCutterOSD.h b/xbmc/pvr/dialogs/GUIDialogPVRCutterOSD.h
+new file mode 100644
+index 0000000..1e06c07
+--- /dev/null
++++ b/xbmc/pvr/dialogs/GUIDialogPVRCutterOSD.h
+@@ -0,0 +1,37 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2009 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "guilib/GUIDialog.h"
++
++namespace PVR
++{
++ class CGUIDialogPVRCutterOSD : public CGUIDialog
++ {
++ public:
++ CGUIDialogPVRCutterOSD(void);
++ virtual ~CGUIDialogPVRCutterOSD(void);
++ virtual bool OnMessage(CGUIMessage& message);
++ virtual bool OnAction(const CAction& action);
++ virtual void OnInitWindow();
++ virtual void OnDeinitWindow(int nextWindowID);
++ };
++}
+diff --git a/xbmc/pvr/dialogs/GUIDialogPVRDirectorOSD.cpp b/xbmc/pvr/dialogs/GUIDialogPVRDirectorOSD.cpp
+new file mode 100644
+index 0000000..e10abf0
+--- /dev/null
++++ b/xbmc/pvr/dialogs/GUIDialogPVRDirectorOSD.cpp
+@@ -0,0 +1,68 @@
++/*
++ * Copyright (C) 2005-2009 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++/*
++ * DESCRIPTION:
++ *
++ * Used in Fullscreen view to control, multifeed channel groups.
++ *
++ */
++
++#include "GUIDialogPVRDirectorOSD.h"
++#include "utils/log.h"
++#include "Application.h"
++
++using namespace PVR;
++
++CGUIDialogPVRDirectorOSD::CGUIDialogPVRDirectorOSD()
++ : CGUIDialog(WINDOW_DIALOG_PVR_OSD_DIRECTOR, "DialogPVRDirectorOSD.xml")
++{
++}
++
++CGUIDialogPVRDirectorOSD::~CGUIDialogPVRDirectorOSD()
++{
++}
++
++bool CGUIDialogPVRDirectorOSD::OnAction(const CAction& action)
++{
++ if (action.GetID() == ACTION_PREVIOUS_MENU || action.GetID() == ACTION_PARENT_DIR)
++ {
++ Close();
++ return true;
++ }
++
++ return CGUIDialog::OnAction(action);
++}
++
++bool CGUIDialogPVRDirectorOSD::OnMessage(CGUIMessage& message)
++{
++ return CGUIDialog::OnMessage(message);
++}
++
++void CGUIDialogPVRDirectorOSD::OnInitWindow()
++{
++ CGUIDialog::OnInitWindow();
++}
++
++void CGUIDialogPVRDirectorOSD::OnDeinitWindow(int nextWindowID)
++{
++ CGUIDialog::OnDeinitWindow(nextWindowID);
++}
+diff --git a/xbmc/pvr/dialogs/GUIDialogPVRDirectorOSD.h b/xbmc/pvr/dialogs/GUIDialogPVRDirectorOSD.h
+new file mode 100644
+index 0000000..079a6ff
+--- /dev/null
++++ b/xbmc/pvr/dialogs/GUIDialogPVRDirectorOSD.h
+@@ -0,0 +1,37 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2009 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "guilib/GUIDialog.h"
++
++namespace PVR
++{
++ class CGUIDialogPVRDirectorOSD : public CGUIDialog
++ {
++ public:
++ CGUIDialogPVRDirectorOSD(void);
++ virtual ~CGUIDialogPVRDirectorOSD(void);
++ virtual bool OnMessage(CGUIMessage& message);
++ virtual bool OnAction(const CAction& action);
++ virtual void OnInitWindow();
++ virtual void OnDeinitWindow(int nextWindowID);
++ };
++}
+diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGroupManager.cpp b/xbmc/pvr/dialogs/GUIDialogPVRGroupManager.cpp
+new file mode 100644
+index 0000000..81cd32a
+--- /dev/null
++++ b/xbmc/pvr/dialogs/GUIDialogPVRGroupManager.cpp
+@@ -0,0 +1,388 @@
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "GUIDialogPVRGroupManager.h"
++#include "Application.h"
++#include "FileItem.h"
++#include "dialogs/GUIDialogKeyboard.h"
++#include "dialogs/GUIDialogOK.h"
++#include "dialogs/GUIDialogYesNo.h"
++#include "guilib/GUIWindowManager.h"
++#include "guilib/LocalizeStrings.h"
++
++#include "pvr/PVRManager.h"
++#include "pvr/channels/PVRChannelGroupsContainer.h"
++
++using namespace std;
++using namespace PVR;
++
++#define CONTROL_LIST_CHANNELS_LEFT 11
++#define CONTROL_LIST_CHANNELS_RIGHT 12
++#define CONTROL_LIST_CHANNEL_GROUPS 13
++#define CONTROL_CURRENT_GROUP_LABEL 20
++#define CONTROL_UNGROUPED_LABEL 21
++#define CONTROL_IN_GROUP_LABEL 22
++#define BUTTON_NEWGROUP 26
++#define BUTTON_RENAMEGROUP 27
++#define BUTTON_DELGROUP 28
++#define BUTTON_OK 29
++
++CGUIDialogPVRGroupManager::CGUIDialogPVRGroupManager() :
++ CGUIDialog(WINDOW_DIALOG_PVR_GROUP_MANAGER, "DialogPVRGroupManager.xml")
++{
++ m_ungroupedChannels = new CFileItemList;
++ m_groupMembers = new CFileItemList;
++ m_channelGroups = new CFileItemList;
++ m_selectedGroup = NULL;
++}
++
++CGUIDialogPVRGroupManager::~CGUIDialogPVRGroupManager()
++{
++ delete m_ungroupedChannels;
++ delete m_groupMembers;
++ delete m_channelGroups;
++}
++
++bool CGUIDialogPVRGroupManager::PersistChanges(void)
++{
++ return g_PVRChannelGroups->Get(m_bIsRadio)->PersistAll();
++}
++
++bool CGUIDialogPVRGroupManager::CancelChanges(void)
++{
++ // TODO
++ return false;
++}
++
++bool CGUIDialogPVRGroupManager::ActionButtonOk(CGUIMessage &message)
++{
++ bool bReturn = false;
++ unsigned int iControl = message.GetSenderId();
++
++ if (iControl == BUTTON_OK)
++ {
++ PersistChanges();
++ Close();
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CGUIDialogPVRGroupManager::ActionButtonNewGroup(CGUIMessage &message)
++{
++ bool bReturn = false;
++ unsigned int iControl = message.GetSenderId();
++
++ if (iControl == BUTTON_NEWGROUP)
++ {
++ CStdString strGroupName = "";
++ /* prompt for a group name */
++ if (CGUIDialogKeyboard::ShowAndGetInput(strGroupName, g_localizeStrings.Get(19139), false))
++ {
++ if (strGroupName != "")
++ {
++ /* add the group if it doesn't already exist */
++ CPVRChannelGroups *groups = ((CPVRChannelGroups *) g_PVRChannelGroups->Get(m_bIsRadio));
++ if (groups->AddGroup(strGroupName))
++ {
++ g_PVRChannelGroups->Get(m_bIsRadio)->GetByName(strGroupName)->SetGroupType(PVR_GROUP_TYPE_USER_DEFINED);
++ m_iSelectedChannelGroup = groups->size() - 1;
++ Update();
++ }
++ }
++ }
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CGUIDialogPVRGroupManager::ActionButtonDeleteGroup(CGUIMessage &message)
++{
++ bool bReturn = false;
++ unsigned int iControl = message.GetSenderId();
++
++ if (iControl == BUTTON_DELGROUP)
++ {
++ if (!m_selectedGroup)
++ return bReturn;
++
++ CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
++ if (!pDialog)
++ return bReturn;
++
++ pDialog->SetHeading(117);
++ pDialog->SetLine(0, "");
++ pDialog->SetLine(1, m_selectedGroup->GroupName());
++ pDialog->SetLine(2, "");
++ pDialog->DoModal();
++
++ if (pDialog->IsConfirmed())
++ {
++ if (((CPVRChannelGroups *) g_PVRChannelGroups->Get(m_bIsRadio))->DeleteGroup(*m_selectedGroup))
++ Update();
++ }
++
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CGUIDialogPVRGroupManager::ActionButtonRenameGroup(CGUIMessage &message)
++{
++ bool bReturn = false;
++ unsigned int iControl = message.GetSenderId();
++
++ if (iControl == BUTTON_RENAMEGROUP)
++ {
++ if (!m_selectedGroup)
++ return bReturn;
++
++ CStdString strGroupName(m_selectedGroup->GroupName());
++ if (CGUIDialogKeyboard::ShowAndGetInput(strGroupName, g_localizeStrings.Get(19139), false))
++ {
++ if (strGroupName != "")
++ {
++ m_selectedGroup->SetGroupName(strGroupName, true);
++ Update();
++ }
++ }
++
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CGUIDialogPVRGroupManager::ActionButtonUngroupedChannels(CGUIMessage &message)
++{
++ bool bReturn = false;
++ unsigned int iControl = message.GetSenderId();
++
++ if (m_viewUngroupedChannels.HasControl(iControl)) // list/thumb control
++ {
++ m_iSelectedUngroupedChannel = m_viewUngroupedChannels.GetSelectedItem();
++ int iAction = message.GetParam1();
++
++ if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK)
++ {
++ if (m_channelGroups->GetFileCount() == 0)
++ {
++ CGUIDialogOK::ShowAndGetInput(19033,19137,0,19138);
++ }
++ else if (m_ungroupedChannels->GetFileCount() > 0)
++ {
++ CFileItemPtr pItemChannel = m_ungroupedChannels->Get(m_iSelectedUngroupedChannel);
++ if (m_selectedGroup->AddToGroup(*pItemChannel->GetPVRChannelInfoTag()))
++ Update();
++ }
++ }
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CGUIDialogPVRGroupManager::ActionButtonGroupMembers(CGUIMessage &message)
++{
++ bool bReturn = false;
++ unsigned int iControl = message.GetSenderId();
++
++ if (m_viewGroupMembers.HasControl(iControl)) // list/thumb control
++ {
++ m_iSelectedGroupMember = m_viewGroupMembers.GetSelectedItem();
++ int iAction = message.GetParam1();
++
++ if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK)
++ {
++ if (m_selectedGroup && m_groupMembers->GetFileCount() > 0)
++ {
++ CFileItemPtr pItemChannel = m_groupMembers->Get(m_iSelectedGroupMember);
++ m_selectedGroup->RemoveFromGroup(*pItemChannel->GetPVRChannelInfoTag());
++ Update();
++ }
++ }
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CGUIDialogPVRGroupManager::ActionButtonChannelGroups(CGUIMessage &message)
++{
++ bool bReturn = false;
++ unsigned int iControl = message.GetSenderId();
++
++ if (m_viewChannelGroups.HasControl(iControl)) // list/thumb control
++ {
++ int iAction = message.GetParam1();
++
++ if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK)
++ {
++ m_iSelectedChannelGroup = m_viewChannelGroups.GetSelectedItem();
++ Update();
++ }
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CGUIDialogPVRGroupManager::OnMessageClick(CGUIMessage &message)
++{
++ return ActionButtonOk(message) ||
++ ActionButtonNewGroup(message) ||
++ ActionButtonDeleteGroup(message) ||
++ ActionButtonRenameGroup(message) ||
++ ActionButtonUngroupedChannels(message) ||
++ ActionButtonGroupMembers(message) ||
++ ActionButtonChannelGroups(message);
++}
++
++bool CGUIDialogPVRGroupManager::OnMessage(CGUIMessage& message)
++{
++ unsigned int iMessage = message.GetMessage();
++
++ switch (iMessage)
++ {
++ case GUI_MSG_WINDOW_DEINIT:
++ {
++ Clear();
++ }
++ break;
++
++ case GUI_MSG_WINDOW_INIT:
++ {
++ CGUIWindow::OnMessage(message);
++ m_iSelectedUngroupedChannel = 0;
++ m_iSelectedGroupMember = 0;
++ m_iSelectedChannelGroup = 0;
++ Update();
++ return true;
++ }
++ break;
++
++ case GUI_MSG_CLICKED:
++ {
++ OnMessageClick(message);
++ }
++ break;
++ }
++
++ return CGUIDialog::OnMessage(message);
++}
++
++void CGUIDialogPVRGroupManager::OnWindowLoaded()
++{
++ CGUIDialog::OnWindowLoaded();
++
++ m_viewUngroupedChannels.Reset();
++ m_viewUngroupedChannels.SetParentWindow(GetID());
++ m_viewUngroupedChannels.AddView(GetControl(CONTROL_LIST_CHANNELS_LEFT));
++
++ m_viewGroupMembers.Reset();
++ m_viewGroupMembers.SetParentWindow(GetID());
++ m_viewGroupMembers.AddView(GetControl(CONTROL_LIST_CHANNELS_RIGHT));
++
++ m_viewChannelGroups.Reset();
++ m_viewChannelGroups.SetParentWindow(GetID());
++ m_viewChannelGroups.AddView(GetControl(CONTROL_LIST_CHANNEL_GROUPS));
++}
++
++void CGUIDialogPVRGroupManager::OnWindowUnload()
++{
++ CGUIDialog::OnWindowUnload();
++ m_viewUngroupedChannels.Reset();
++ m_viewGroupMembers.Reset();
++ m_viewChannelGroups.Reset();
++}
++
++void CGUIDialogPVRGroupManager::Update()
++{
++ m_selectedGroup = NULL;
++
++ /* lock our display, as this window is rendered from the player thread */
++ g_graphicsContext.Lock();
++ m_viewUngroupedChannels.SetCurrentView(CONTROL_LIST_CHANNELS_LEFT);
++ m_viewGroupMembers.SetCurrentView(CONTROL_LIST_CHANNELS_RIGHT);
++ m_viewChannelGroups.SetCurrentView(CONTROL_LIST_CHANNEL_GROUPS);
++
++ Clear();
++
++ /* get the groups list */
++ g_PVRChannelGroups->Get(m_bIsRadio)->GetGroupList(m_channelGroups);
++ m_viewChannelGroups.SetItems(*m_channelGroups);
++ m_viewChannelGroups.SetSelectedItem(m_iSelectedChannelGroup);
++
++ /* select a group or select the default group if no group was selected */
++ CFileItemPtr pItem = m_channelGroups->Get(m_viewChannelGroups.GetSelectedItem());
++ m_selectedGroup = (CPVRChannelGroup *) g_PVRChannelGroups->Get(m_bIsRadio)->GetByName(pItem->m_strTitle);
++ if (m_selectedGroup != NULL)
++ {
++ /* set this group in the pvrmanager, so it becomes the selected group in other dialogs too */
++ g_PVRManager.SetPlayingGroup(m_selectedGroup);
++ SET_CONTROL_LABEL(CONTROL_CURRENT_GROUP_LABEL, m_selectedGroup->GroupName());
++
++ if (m_selectedGroup->IsInternalGroup())
++ {
++ CStdString strNewLabel;
++ strNewLabel.Format("%s %s", g_localizeStrings.Get(19022), m_bIsRadio ? g_localizeStrings.Get(19024) : g_localizeStrings.Get(19023));
++ SET_CONTROL_LABEL(CONTROL_UNGROUPED_LABEL, strNewLabel);
++
++ strNewLabel.Format("%s %s", g_localizeStrings.Get(19218), m_bIsRadio ? g_localizeStrings.Get(19024) : g_localizeStrings.Get(19023));
++ SET_CONTROL_LABEL(CONTROL_IN_GROUP_LABEL, strNewLabel);
++ }
++ else
++ {
++ CStdString strNewLabel;
++ strNewLabel.Format("%s", g_localizeStrings.Get(19219));
++ SET_CONTROL_LABEL(CONTROL_UNGROUPED_LABEL, strNewLabel);
++
++ strNewLabel.Format("%s %s", g_localizeStrings.Get(19220), m_selectedGroup->GroupName());
++ SET_CONTROL_LABEL(CONTROL_IN_GROUP_LABEL, strNewLabel);
++ }
++
++ /* get all channels that are not in this group for the center part */
++ m_selectedGroup->GetMembers(*m_ungroupedChannels, false);
++ m_viewUngroupedChannels.SetItems(*m_ungroupedChannels);
++ m_viewUngroupedChannels.SetSelectedItem(m_iSelectedUngroupedChannel);
++
++ /* get all channels in this group for the right side part */
++ m_selectedGroup->GetMembers(*m_groupMembers, true);
++ m_viewGroupMembers.SetItems(*m_groupMembers);
++ m_viewGroupMembers.SetSelectedItem(m_iSelectedGroupMember);
++ }
++
++ g_graphicsContext.Unlock();
++}
++
++void CGUIDialogPVRGroupManager::Clear()
++{
++ m_viewUngroupedChannels.Clear();
++ m_viewGroupMembers.Clear();
++ m_viewChannelGroups.Clear();
++
++ m_ungroupedChannels->Clear();
++ m_groupMembers->Clear();
++ m_channelGroups->Clear();
++}
+diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGroupManager.h b/xbmc/pvr/dialogs/GUIDialogPVRGroupManager.h
+new file mode 100644
+index 0000000..742f1c0
+--- /dev/null
++++ b/xbmc/pvr/dialogs/GUIDialogPVRGroupManager.h
+@@ -0,0 +1,73 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "guilib/GUIDialog.h"
++#include "GUIViewControl.h"
++
++class CFileItemList;
++
++namespace PVR
++{
++ class CPVRChannelGroup;
++
++ class CGUIDialogPVRGroupManager : public CGUIDialog
++ {
++ public:
++ CGUIDialogPVRGroupManager(void);
++ virtual ~CGUIDialogPVRGroupManager(void);
++ virtual bool OnMessage(CGUIMessage& message);
++ virtual void OnWindowLoaded();
++ virtual void OnWindowUnload();
++ void SetRadio(bool IsRadio) { m_bIsRadio = IsRadio; }
++
++ protected:
++ void Clear();
++ void Update();
++
++ private:
++ bool PersistChanges(void);
++ bool CancelChanges(void);
++ bool ActionButtonOk(CGUIMessage &message);
++ bool ActionButtonNewGroup(CGUIMessage &message);
++ bool ActionButtonDeleteGroup(CGUIMessage &message);
++ bool ActionButtonRenameGroup(CGUIMessage &message);
++ bool ActionButtonUngroupedChannels(CGUIMessage &message);
++ bool ActionButtonGroupMembers(CGUIMessage &message);
++ bool ActionButtonChannelGroups(CGUIMessage &message);
++ bool OnMessageClick(CGUIMessage &message);
++
++ CPVRChannelGroup *m_selectedGroup;
++ bool m_bIsRadio;
++
++ unsigned int m_iSelectedUngroupedChannel;
++ unsigned int m_iSelectedGroupMember;
++ unsigned int m_iSelectedChannelGroup;
++
++ CFileItemList * m_ungroupedChannels;
++ CFileItemList * m_groupMembers;
++ CFileItemList * m_channelGroups;
++
++ CGUIViewControl m_viewUngroupedChannels;
++ CGUIViewControl m_viewGroupMembers;
++ CGUIViewControl m_viewChannelGroups;
++ };
++}
+diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.cpp b/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.cpp
+new file mode 100644
+index 0000000..2192022
+--- /dev/null
++++ b/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.cpp
+@@ -0,0 +1,224 @@
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "GUIDialogPVRGuideInfo.h"
++#include "Application.h"
++#include "guilib/GUIWindowManager.h"
++#include "dialogs/GUIDialogOK.h"
++#include "dialogs/GUIDialogYesNo.h"
++
++#include "pvr/PVRManager.h"
++#include "pvr/channels/PVRChannelGroupsContainer.h"
++#include "epg/EpgInfoTag.h"
++#include "pvr/timers/PVRTimers.h"
++#include "pvr/timers/PVRTimerInfoTag.h"
++
++using namespace std;
++using namespace PVR;
++using namespace EPG;
++
++#define CONTROL_BTN_SWITCH 5
++#define CONTROL_BTN_RECORD 6
++#define CONTROL_BTN_OK 7
++
++CGUIDialogPVRGuideInfo::CGUIDialogPVRGuideInfo(void)
++ : CGUIDialog(WINDOW_DIALOG_PVR_GUIDE_INFO, "DialogPVRGuideInfo.xml")
++ , m_progItem(new CFileItem)
++{
++}
++
++CGUIDialogPVRGuideInfo::~CGUIDialogPVRGuideInfo(void)
++{
++}
++
++bool CGUIDialogPVRGuideInfo::ActionStartTimer(const CEpgInfoTag *tag)
++{
++ bool bReturn = false;
++
++ // prompt user for confirmation of channel record
++ CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
++
++ if (pDialog)
++ {
++ pDialog->SetHeading(264);
++ pDialog->SetLine(0, "");
++ pDialog->SetLine(1, tag->Title());
++ pDialog->SetLine(2, "");
++ pDialog->DoModal();
++
++ if (pDialog->IsConfirmed())
++ {
++ Close();
++ CPVRTimerInfoTag *newTimer = CPVRTimerInfoTag::CreateFromEpg(*tag);
++ bReturn = CPVRTimers::AddTimer(*newTimer);
++ delete newTimer;
++ }
++ }
++
++ return bReturn;
++}
++
++bool CGUIDialogPVRGuideInfo::ActionCancelTimer(const CPVRTimerInfoTag *tag)
++{
++ bool bReturn = false;
++
++ // prompt user for confirmation of timer deletion
++ CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
++
++ if (pDialog)
++ {
++ pDialog->SetHeading(265);
++ pDialog->SetLine(0, "");
++ pDialog->SetLine(1, tag->m_strTitle);
++ pDialog->SetLine(2, "");
++ pDialog->DoModal();
++
++ if (pDialog->IsConfirmed())
++ {
++ Close();
++ bReturn = CPVRTimers::DeleteTimer(*tag);
++ }
++ }
++
++ return bReturn;
++}
++
++bool CGUIDialogPVRGuideInfo::OnClickButtonOK(CGUIMessage &message)
++{
++ bool bReturn = false;
++
++ if (message.GetSenderId() == CONTROL_BTN_OK)
++ {
++ Close();
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CGUIDialogPVRGuideInfo::OnClickButtonRecord(CGUIMessage &message)
++{
++ bool bReturn = false;
++
++ if (message.GetSenderId() == CONTROL_BTN_RECORD)
++ {
++ bReturn = true;
++
++ const CEpgInfoTag *tag = m_progItem->GetEPGInfoTag();
++ if (!tag || !tag->HasPVRChannel())
++ {
++ /* invalid channel */
++ CGUIDialogOK::ShowAndGetInput(19033,19067,0,0);
++ Close();
++ return bReturn;
++ }
++
++ const CPVRTimerInfoTag *timerTag = g_PVRTimers->GetMatch(tag);
++ bool bHasTimer = timerTag != NULL;
++
++ if (!bHasTimer)
++ ActionStartTimer(tag);
++ else
++ ActionCancelTimer(timerTag);
++ }
++
++ return bReturn;
++}
++
++bool CGUIDialogPVRGuideInfo::OnClickButtonSwitch(CGUIMessage &message)
++{
++ bool bReturn = false;
++
++ if (message.GetSenderId() == CONTROL_BTN_SWITCH)
++ {
++ Close();
++
++ if (!m_progItem->GetEPGInfoTag()->HasPVRChannel() ||
++ !g_application.PlayFile(CFileItem(*m_progItem->GetEPGInfoTag()->ChannelTag())))
++ CGUIDialogOK::ShowAndGetInput(19033,0,19035,0);
++ else
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CGUIDialogPVRGuideInfo::OnMessage(CGUIMessage& message)
++{
++ switch (message.GetMessage())
++ {
++ case GUI_MSG_WINDOW_INIT:
++ CGUIDialog::OnMessage(message);
++ Update();
++ break;
++ case GUI_MSG_CLICKED:
++ return OnClickButtonOK(message) ||
++ OnClickButtonRecord(message) ||
++ OnClickButtonSwitch(message);
++ }
++
++ return CGUIDialog::OnMessage(message);
++}
++
++void CGUIDialogPVRGuideInfo::SetProgInfo(const CFileItem *item)
++{
++ *m_progItem = *item;
++}
++
++CFileItemPtr CGUIDialogPVRGuideInfo::GetCurrentListItem(int offset)
++{
++ return m_progItem;
++}
++
++void CGUIDialogPVRGuideInfo::Update()
++{
++ const CEpgInfoTag *tag = m_progItem->GetEPGInfoTag();
++ if (!tag)
++ {
++ /* no epg event selected */
++ return;
++ }
++
++ if (tag->EndAsLocalTime() <= CDateTime::GetCurrentDateTime())
++ {
++ /* event has passed. hide the record button */
++ SET_CONTROL_HIDDEN(CONTROL_BTN_RECORD);
++ return;
++ }
++
++ bool bHasTimer = g_PVRTimers->GetMatch(tag) != NULL;
++ if (!bHasTimer)
++ {
++ /* no timer present on this tag */
++ if (tag->StartAsLocalTime() < CDateTime::GetCurrentDateTime())
++ SET_CONTROL_LABEL(CONTROL_BTN_RECORD, 264);
++ else
++ SET_CONTROL_LABEL(CONTROL_BTN_RECORD, 19061);
++ }
++ else
++ {
++ /* timer present on this tag */
++ if (tag->StartAsLocalTime() < CDateTime::GetCurrentDateTime())
++ SET_CONTROL_LABEL(CONTROL_BTN_RECORD, 19059);
++ else
++ SET_CONTROL_LABEL(CONTROL_BTN_RECORD, 19060);
++ }
++}
+diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.h b/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.h
+new file mode 100644
+index 0000000..d6cc27a
+--- /dev/null
++++ b/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.h
+@@ -0,0 +1,56 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "guilib/GUIDialog.h"
++
++namespace EPG
++{
++ class CEpgInfoTag;
++}
++
++namespace PVR
++{
++ class CPVRTimerInfoTag;
++
++ class CGUIDialogPVRGuideInfo : public CGUIDialog
++ {
++ public:
++ CGUIDialogPVRGuideInfo(void);
++ virtual ~CGUIDialogPVRGuideInfo(void);
++ virtual bool OnMessage(CGUIMessage& message);
++ virtual bool HasListItems() const { return true; };
++ virtual CFileItemPtr GetCurrentListItem(int offset = 0);
++
++ void SetProgInfo(const CFileItem *item);
++
++ protected:
++ void Update();
++ bool ActionStartTimer(const EPG::CEpgInfoTag *tag);
++ bool ActionCancelTimer(const CPVRTimerInfoTag *tag);
++
++ bool OnClickButtonOK(CGUIMessage &message);
++ bool OnClickButtonRecord(CGUIMessage &message);
++ bool OnClickButtonSwitch(CGUIMessage &message);
++
++ CFileItemPtr m_progItem;
++ };
++}
+diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGuideOSD.cpp b/xbmc/pvr/dialogs/GUIDialogPVRGuideOSD.cpp
+new file mode 100644
+index 0000000..586c02d
+--- /dev/null
++++ b/xbmc/pvr/dialogs/GUIDialogPVRGuideOSD.cpp
+@@ -0,0 +1,165 @@
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "GUIDialogPVRGuideOSD.h"
++#include "Application.h"
++#include "FileItem.h"
++#include "GUIDialogPVRGuideInfo.h"
++#include "guilib/GUIWindowManager.h"
++#include "ViewState.h"
++#include "epg/EpgInfoTag.h"
++
++#include "pvr/PVRManager.h"
++
++using namespace std;
++using namespace PVR;
++
++#define CONTROL_LIST 11
++
++CGUIDialogPVRGuideOSD::CGUIDialogPVRGuideOSD()
++ : CGUIDialog(WINDOW_DIALOG_PVR_OSD_GUIDE, "DialogPVRGuideOSD.xml")
++{
++ m_vecItems = new CFileItemList;
++}
++
++CGUIDialogPVRGuideOSD::~CGUIDialogPVRGuideOSD()
++{
++ delete m_vecItems;
++}
++
++bool CGUIDialogPVRGuideOSD::OnMessage(CGUIMessage& message)
++{
++ switch (message.GetMessage())
++ {
++ case GUI_MSG_WINDOW_DEINIT:
++ {
++ Clear();
++ }
++ break;
++
++ case GUI_MSG_WINDOW_INIT:
++ {
++ /* Close dialog immediately if now TV or radio channel is playing */
++ if (!g_PVRManager.IsPlaying())
++ {
++ Close();
++ return true;
++ }
++ CGUIWindow::OnMessage(message);
++ Update();
++ return true;
++ }
++ break;
++
++ case GUI_MSG_CLICKED:
++ {
++ int iControl = message.GetSenderId();
++
++ if (m_viewControl.HasControl(iControl)) // list/thumb control
++ {
++ int iItem = m_viewControl.GetSelectedItem();
++ int iAction = message.GetParam1();
++
++ if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK)
++ {
++ ShowInfo(iItem);
++ return true;
++ }
++ }
++ }
++ break;
++ }
++
++ return CGUIDialog::OnMessage(message);
++}
++
++void CGUIDialogPVRGuideOSD::Update()
++{
++ // lock our display, as this window is rendered from the player thread
++ g_graphicsContext.Lock();
++ m_viewControl.SetCurrentView(DEFAULT_VIEW_LIST);
++
++ // empty the list ready for population
++ Clear();
++
++ g_PVRManager.GetCurrentEpg(*m_vecItems);
++ m_viewControl.SetItems(*m_vecItems);
++
++ /* select the active entry */
++ unsigned int iSelectedItem = 0;
++ for (int iEpgPtr = 0; iEpgPtr < m_vecItems->Size(); iEpgPtr++)
++ {
++ CFileItemPtr entry = m_vecItems->Get(iEpgPtr);
++ if (entry->GetEPGInfoTag()->IsActive())
++ {
++ iSelectedItem = iEpgPtr;
++ break;
++ }
++ }
++ m_viewControl.SetSelectedItem(iSelectedItem);
++
++ g_graphicsContext.Unlock();
++}
++
++void CGUIDialogPVRGuideOSD::Clear()
++{
++ m_viewControl.Clear();
++ m_vecItems->Clear();
++}
++
++void CGUIDialogPVRGuideOSD::ShowInfo(int item)
++{
++ /* Check file item is in list range and get his pointer */
++ if (item < 0 || item >= (int)m_vecItems->Size()) return;
++
++ CFileItemPtr pItem = m_vecItems->Get(item);
++
++ /* Load programme info dialog */
++ CGUIDialogPVRGuideInfo* pDlgInfo = (CGUIDialogPVRGuideInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_GUIDE_INFO);
++ if (!pDlgInfo)
++ return;
++
++ /* inform dialog about the file item and open dialog window */
++ pDlgInfo->SetProgInfo(pItem.get());
++ pDlgInfo->DoModal();
++}
++
++void CGUIDialogPVRGuideOSD::OnWindowLoaded()
++{
++ CGUIDialog::OnWindowLoaded();
++ m_viewControl.Reset();
++ m_viewControl.SetParentWindow(GetID());
++ m_viewControl.AddView(GetControl(CONTROL_LIST));
++}
++
++void CGUIDialogPVRGuideOSD::OnWindowUnload()
++{
++ CGUIDialog::OnWindowUnload();
++ m_viewControl.Reset();
++}
++
++CGUIControl *CGUIDialogPVRGuideOSD::GetFirstFocusableControl(int id)
++{
++ if (m_viewControl.HasControl(id))
++ id = m_viewControl.GetCurrentControl();
++
++ return CGUIWindow::GetFirstFocusableControl(id);
++}
+diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGuideOSD.h b/xbmc/pvr/dialogs/GUIDialogPVRGuideOSD.h
+new file mode 100644
+index 0000000..8c335d8
+--- /dev/null
++++ b/xbmc/pvr/dialogs/GUIDialogPVRGuideOSD.h
+@@ -0,0 +1,49 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "guilib/GUIDialog.h"
++#include "GUIViewControl.h"
++
++class CFileItemList;
++
++namespace PVR
++{
++ class CGUIDialogPVRGuideOSD : public CGUIDialog
++ {
++ public:
++ CGUIDialogPVRGuideOSD(void);
++ virtual ~CGUIDialogPVRGuideOSD(void);
++ virtual bool OnMessage(CGUIMessage& message);
++ virtual void OnWindowLoaded();
++ virtual void OnWindowUnload();
++
++ protected:
++ void ShowInfo(int iItem);
++ void Clear();
++ void Update();
++
++ CGUIControl *GetFirstFocusableControl(int id);
++
++ CFileItemList *m_vecItems;
++ CGUIViewControl m_viewControl;
++ };
++}
+diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp b/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp
+new file mode 100644
+index 0000000..b1694d6
+--- /dev/null
++++ b/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp
+@@ -0,0 +1,369 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "GUIDialogPVRGuideSearch.h"
++#include "Application.h"
++#include "guilib/LocalizeStrings.h"
++#include "guilib/GUIEditControl.h"
++#include "guilib/GUIRadioButtonControl.h"
++#include "guilib/GUISpinControlEx.h"
++#include "guilib/GUIWindowManager.h"
++
++#include "addons/include/xbmc_pvr_types.h"
++#include "pvr/PVRManager.h"
++#include "epg/EpgSearchFilter.h"
++#include "pvr/channels/PVRChannelGroupsContainer.h"
++
++using namespace std;
++using namespace PVR;
++
++#define CONTROL_EDIT_SEARCH 9
++#define CONTROL_BTN_INC_DESC 10
++#define CONTROL_BTN_CASE_SENS 11
++#define CONTROL_SPIN_MIN_DURATION 12
++#define CONTROL_SPIN_MAX_DURATION 13
++#define CONTROL_EDIT_START_DATE 14
++#define CONTROL_EDIT_STOP_DATE 15
++#define CONTROL_EDIT_START_TIME 16
++#define CONTROL_EDIT_STOP_TIME 17
++#define CONTROL_SPIN_GENRE 18
++#define CONTROL_SPIN_NO_REPEATS 19
++#define CONTROL_BTN_UNK_GENRE 20
++#define CONTROL_SPIN_GROUPS 21
++#define CONTROL_BTN_FTA_ONLY 22
++#define CONTROL_SPIN_CHANNELS 23
++#define CONTROL_BTN_IGNORE_TMR 24
++#define CONTROL_BTN_CANCEL 25
++#define CONTROL_BTN_SEARCH 26
++#define CONTROL_BTN_IGNORE_REC 27
++#define CONTROL_BTN_DEFAULTS 28
++
++CGUIDialogPVRGuideSearch::CGUIDialogPVRGuideSearch(void) :
++ CGUIDialog(WINDOW_DIALOG_PVR_GUIDE_SEARCH, "DialogPVRGuideSearch.xml"),
++ m_bConfirmed(false),
++ m_bCanceled(false),
++ m_searchFilter(NULL)
++{
++}
++
++void CGUIDialogPVRGuideSearch::UpdateChannelSpin(void)
++{
++ CGUISpinControlEx *pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_CHANNELS);
++ CGUISpinControlEx *pSpinGroups = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_GROUPS);
++ if (!pSpin || !pSpinGroups)
++ return;
++
++ int iChannelGroup = pSpin->GetValue();
++
++ pSpin->Clear();
++ pSpin->AddLabel(g_localizeStrings.Get(19217), EPG_SEARCH_UNSET);
++
++ int iGroupId = (iChannelGroup == EPG_SEARCH_UNSET) ?
++ XBMC_INTERNAL_GROUP_TV :
++ iChannelGroup;
++ const CPVRChannelGroup *group = g_PVRChannelGroups->GetByIdFromAll(iGroupId);
++ if (!group)
++ group = g_PVRChannelGroups->GetGroupAllTV();
++
++ for (int iChannelPtr = 0; iChannelPtr < group->Size(); iChannelPtr++)
++ {
++ const CPVRChannel *channel = group->GetByIndex(iChannelPtr);
++ if (!channel)
++ continue;
++
++ int iChannelNumber = group->GetChannelNumber(*channel);
++ pSpin->AddLabel(channel->ChannelName().c_str(), iChannelNumber);
++ }
++}
++
++void CGUIDialogPVRGuideSearch::UpdateGroupsSpin(void)
++{
++ CFileItemList groups;
++ CGUISpinControlEx *pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_GROUPS);
++ if (!pSpin)
++ return;
++
++ /* tv groups */
++ g_PVRChannelGroups->GetTV()->GetGroupList(&groups);
++ for (int iGroupPtr = 0; iGroupPtr < groups.Size(); iGroupPtr++)
++ pSpin->AddLabel(groups[iGroupPtr]->GetLabel(), atoi(groups[iGroupPtr]->GetPath()));
++
++ /* radio groups */
++ groups.ClearItems();
++ g_PVRChannelGroups->GetRadio()->GetGroupList(&groups);
++ for (int iGroupPtr = 0; iGroupPtr < groups.Size(); iGroupPtr++)
++ pSpin->AddLabel(groups[iGroupPtr]->GetLabel(), atoi(groups[iGroupPtr]->GetPath()));
++
++ pSpin->SetValue(m_searchFilter->m_iChannelGroup);
++}
++
++void CGUIDialogPVRGuideSearch::UpdateGenreSpin(void)
++{
++ CGUISpinControlEx *pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_GENRE);
++ if (!pSpin)
++ return;
++
++ pSpin->Clear();
++ pSpin->AddLabel(g_localizeStrings.Get(593), EPG_SEARCH_UNSET);
++ pSpin->AddLabel(g_localizeStrings.Get(19500), EPG_EVENT_CONTENTMASK_MOVIEDRAMA);
++ pSpin->AddLabel(g_localizeStrings.Get(19516), EPG_EVENT_CONTENTMASK_NEWSCURRENTAFFAIRS);
++ pSpin->AddLabel(g_localizeStrings.Get(19532), EPG_EVENT_CONTENTMASK_SHOW);
++ pSpin->AddLabel(g_localizeStrings.Get(19548), EPG_EVENT_CONTENTMASK_SPORTS);
++ pSpin->AddLabel(g_localizeStrings.Get(19564), EPG_EVENT_CONTENTMASK_CHILDRENYOUTH);
++ pSpin->AddLabel(g_localizeStrings.Get(19580), EPG_EVENT_CONTENTMASK_MUSICBALLETDANCE);
++ pSpin->AddLabel(g_localizeStrings.Get(19596), EPG_EVENT_CONTENTMASK_ARTSCULTURE);
++ pSpin->AddLabel(g_localizeStrings.Get(19612), EPG_EVENT_CONTENTMASK_SOCIALPOLITICALECONOMICS);
++ pSpin->AddLabel(g_localizeStrings.Get(19628), EPG_EVENT_CONTENTMASK_EDUCATIONALSCIENCE);
++ pSpin->AddLabel(g_localizeStrings.Get(19644), EPG_EVENT_CONTENTMASK_LEISUREHOBBIES);
++ pSpin->AddLabel(g_localizeStrings.Get(19660), EPG_EVENT_CONTENTMASK_SPECIAL);
++ pSpin->AddLabel(g_localizeStrings.Get(19499), EPG_EVENT_CONTENTMASK_USERDEFINED);
++ pSpin->SetValue(m_searchFilter->m_iGenreType);
++}
++
++void CGUIDialogPVRGuideSearch::UpdateDurationSpin(void)
++{
++ /* minimum duration */
++ CGUISpinControlEx *pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_MIN_DURATION);
++ if (!pSpin)
++ return;
++
++ pSpin->Clear();
++ pSpin->AddLabel("-", EPG_SEARCH_UNSET);
++ for (int i = 1; i < 12*60/5; i++)
++ {
++ CStdString string;
++ string.Format(g_localizeStrings.Get(14044), i*5);
++ pSpin->AddLabel(string, i*5);
++ }
++ pSpin->SetValue(m_searchFilter->m_iMinimumDuration);
++
++ /* maximum duration */
++ pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_MAX_DURATION);
++ if (!pSpin)
++ return;
++
++ pSpin->Clear();
++ pSpin->AddLabel("-", EPG_SEARCH_UNSET);
++ for (int i = 1; i < 12*60/5; i++)
++ {
++ CStdString string;
++ string.Format(g_localizeStrings.Get(14044),i*5);
++ pSpin->AddLabel(string, i*5);
++ }
++ pSpin->SetValue(m_searchFilter->m_iMaximumDuration);
++}
++
++bool CGUIDialogPVRGuideSearch::OnMessage(CGUIMessage& message)
++{
++ CGUIDialog::OnMessage(message);
++
++ switch (message.GetMessage())
++ {
++ case GUI_MSG_WINDOW_INIT:
++ {
++ m_bConfirmed = false;
++ m_bCanceled = false;
++ }
++ break;
++
++ case GUI_MSG_CLICKED:
++ {
++ int iControl = message.GetSenderId();
++ if (iControl == CONTROL_BTN_SEARCH)
++ {
++ OnSearch();
++ m_bConfirmed = true;
++ m_bCanceled = false;
++ Close();
++ return true;
++ }
++ else if (iControl == CONTROL_BTN_CANCEL)
++ {
++ Close();
++ m_bCanceled = true;
++ return true;
++ }
++ else if (iControl == CONTROL_BTN_DEFAULTS)
++ {
++ if (m_searchFilter)
++ {
++ m_searchFilter->Reset();
++ Update();
++ }
++
++ return true;
++ }
++ else if (iControl == CONTROL_SPIN_GROUPS)
++ {
++ UpdateChannelSpin();
++ return true;
++ }
++ }
++ break;
++ }
++
++ return false;
++}
++
++void CGUIDialogPVRGuideSearch::OnWindowLoaded()
++{
++ Update();
++ return CGUIDialog::OnWindowLoaded();
++}
++
++void CGUIDialogPVRGuideSearch::ReadDateTime(const CStdString &strDate, const CStdString &strTime, CDateTime &dateTime) const
++{
++ int iHours, iMinutes;
++ sscanf(strTime, "%d:%d", &iHours, &iMinutes);
++ dateTime.SetFromDBDate(strDate);
++ dateTime.SetDateTime(dateTime.GetYear(), dateTime.GetMonth(), dateTime.GetDay(), iHours, iMinutes, 0);
++}
++
++void CGUIDialogPVRGuideSearch::OnSearch()
++{
++ CStdString strTmp;
++ CGUISpinControlEx *pSpin;
++ CGUIEditControl *pEdit;
++ CGUIRadioButtonControl *pRadioButton;
++
++ if (!m_searchFilter)
++ return;
++
++ pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_SEARCH);
++ if (pEdit) m_searchFilter->m_strSearchTerm = pEdit->GetLabel2();
++
++ pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_INC_DESC);
++ if (pRadioButton) m_searchFilter->m_bSearchInDescription = pRadioButton->IsSelected();
++
++ pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_CASE_SENS);
++ if (pRadioButton) m_searchFilter->m_bIsCaseSensitive = pRadioButton->IsSelected();
++
++ pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_FTA_ONLY);
++ if (pRadioButton) m_searchFilter->m_bFTAOnly = pRadioButton->IsSelected();
++
++ pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_UNK_GENRE);
++ if (pRadioButton) m_searchFilter->m_bIncludeUnknownGenres = pRadioButton->IsSelected();
++
++ pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_IGNORE_REC);
++ if (pRadioButton) m_searchFilter->m_bIgnorePresentRecordings = pRadioButton->IsSelected();
++
++ pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_IGNORE_TMR);
++ if (pRadioButton) m_searchFilter->m_bIgnorePresentTimers = pRadioButton->IsSelected();
++
++ pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_SPIN_NO_REPEATS);
++ if (pRadioButton) m_searchFilter->m_bPreventRepeats = pRadioButton->IsSelected();
++
++ pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_GENRE);
++ if (pSpin) m_searchFilter->m_iGenreType = pSpin->GetValue();
++
++ pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_MIN_DURATION);
++ if (pSpin) m_searchFilter->m_iMinimumDuration = pSpin->GetValue();
++
++ pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_MAX_DURATION);
++ if (pSpin) m_searchFilter->m_iMaximumDuration = pSpin->GetValue();
++
++ pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_CHANNELS);
++ if (pSpin) m_searchFilter->m_iChannelNumber = pSpin->GetValue();
++
++ pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_GROUPS);
++ if (pSpin) m_searchFilter->m_iChannelGroup = pSpin->GetValue();
++
++ pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_START_TIME);
++ if (pEdit) strTmp = pEdit->GetLabel2();
++
++ pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_START_DATE);
++ if (pEdit) ReadDateTime(pEdit->GetLabel2(), strTmp, m_searchFilter->m_startDateTime);
++ strTmp.clear();
++
++ pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_STOP_TIME);
++ if (pEdit) strTmp = pEdit->GetLabel2();
++
++ pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_STOP_DATE);
++ if (pEdit) ReadDateTime(pEdit->GetLabel2(), strTmp, m_searchFilter->m_endDateTime);
++}
++
++void CGUIDialogPVRGuideSearch::Update()
++{
++ CGUIEditControl *pEdit;
++ CGUIRadioButtonControl *pRadioButton;
++
++ if (!m_searchFilter)
++ return;
++
++ pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_SEARCH);
++ if (pEdit)
++ {
++ pEdit->SetLabel2(m_searchFilter->m_strSearchTerm);
++ pEdit->SetInputType(CGUIEditControl::INPUT_TYPE_TEXT, 16017);
++ }
++
++ pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_CASE_SENS);
++ if (pRadioButton) pRadioButton->SetSelected(m_searchFilter->m_bIsCaseSensitive);
++
++ pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_INC_DESC);
++ if (pRadioButton) pRadioButton->SetSelected(m_searchFilter->m_bSearchInDescription);
++
++ pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_FTA_ONLY);
++ if (pRadioButton) pRadioButton->SetSelected(m_searchFilter->m_bFTAOnly);
++
++ pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_UNK_GENRE);
++ if (pRadioButton) pRadioButton->SetSelected(m_searchFilter->m_bIncludeUnknownGenres);
++
++ pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_IGNORE_REC);
++ if (pRadioButton) pRadioButton->SetSelected(m_searchFilter->m_bIgnorePresentRecordings);
++
++ pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_IGNORE_TMR);
++ if (pRadioButton) pRadioButton->SetSelected(m_searchFilter->m_bIgnorePresentTimers);
++
++ pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_SPIN_NO_REPEATS);
++ if (pRadioButton) pRadioButton->SetSelected(m_searchFilter->m_bPreventRepeats);
++
++ /* Set time fields */
++ pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_START_TIME);
++ if (pEdit)
++ {
++ pEdit->SetLabel2(m_searchFilter->m_endDateTime.GetAsLocalizedTime("", false));
++ pEdit->SetInputType(CGUIEditControl::INPUT_TYPE_TIME, 14066);
++ }
++ pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_STOP_TIME);
++ if (pEdit)
++ {
++ pEdit->SetLabel2(m_searchFilter->m_startDateTime.GetAsLocalizedTime("", false));
++ pEdit->SetInputType(CGUIEditControl::INPUT_TYPE_TIME, 14066);
++ }
++ pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_START_DATE);
++ if (pEdit)
++ {
++ pEdit->SetLabel2(m_searchFilter->m_startDateTime.GetAsDBDate());
++ pEdit->SetInputType(CGUIEditControl::INPUT_TYPE_DATE, 14067);
++ }
++ pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_STOP_DATE);
++ if (pEdit)
++ {
++ pEdit->SetLabel2(m_searchFilter->m_endDateTime.GetAsDBDate());
++ pEdit->SetInputType(CGUIEditControl::INPUT_TYPE_DATE, 14067);
++ }
++
++ UpdateDurationSpin();
++ UpdateGroupsSpin();
++ UpdateChannelSpin();
++ UpdateGenreSpin();
++}
+diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.h b/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.h
+new file mode 100644
+index 0000000..dd92d2a
+--- /dev/null
++++ b/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.h
+@@ -0,0 +1,58 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "guilib/GUIDialog.h"
++#include "XBDateTime.h"
++
++namespace EPG
++{
++ struct EpgSearchFilter;
++}
++
++namespace PVR
++{
++ class CGUIDialogPVRGuideSearch : public CGUIDialog
++ {
++ public:
++ CGUIDialogPVRGuideSearch(void);
++ virtual ~CGUIDialogPVRGuideSearch(void) {}
++ virtual bool OnMessage(CGUIMessage& message);
++ virtual void OnWindowLoaded();
++
++ void SetFilterData(EPG::EpgSearchFilter *searchFilter) { m_searchFilter = searchFilter; }
++ bool IsConfirmed() const { return m_bConfirmed; }
++ bool IsCanceled() const { return m_bCanceled; }
++ void OnSearch();
++
++ protected:
++ void UpdateChannelSpin(void);
++ void UpdateGroupsSpin(void);
++ void UpdateGenreSpin(void);
++ void UpdateDurationSpin(void);
++ void ReadDateTime(const CStdString &strDate, const CStdString &strTime, CDateTime &dateTime) const;
++ void Update();
++
++ bool m_bConfirmed;
++ bool m_bCanceled;
++ EPG::EpgSearchFilter *m_searchFilter;
++ };
++}
+diff --git a/xbmc/pvr/dialogs/GUIDialogPVRRecordingInfo.cpp b/xbmc/pvr/dialogs/GUIDialogPVRRecordingInfo.cpp
+new file mode 100644
+index 0000000..babd22a
+--- /dev/null
++++ b/xbmc/pvr/dialogs/GUIDialogPVRRecordingInfo.cpp
+@@ -0,0 +1,61 @@
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "GUIDialogPVRRecordingInfo.h"
++#include "guilib/GUIWindowManager.h"
++#include "FileItem.h"
++
++using namespace std;
++using namespace PVR;
++
++#define CONTROL_BTN_OK 10
++
++CGUIDialogPVRRecordingInfo::CGUIDialogPVRRecordingInfo(void)
++ : CGUIDialog(WINDOW_DIALOG_PVR_RECORDING_INFO, "DialogPVRRecordingInfo.xml")
++ , m_recordItem(new CFileItem)
++{
++}
++
++bool CGUIDialogPVRRecordingInfo::OnMessage(CGUIMessage& message)
++{
++ if (message.GetMessage() == GUI_MSG_CLICKED)
++ {
++ int iControl = message.GetSenderId();
++
++ if (iControl == CONTROL_BTN_OK)
++ {
++ Close();
++ return true;
++ }
++ }
++
++ return CGUIDialog::OnMessage(message);
++}
++
++void CGUIDialogPVRRecordingInfo::SetRecording(const CFileItem *item)
++{
++ *m_recordItem = *item;
++}
++
++CFileItemPtr CGUIDialogPVRRecordingInfo::GetCurrentListItem(int offset)
++{
++ return m_recordItem;
++}
+diff --git a/xbmc/pvr/dialogs/GUIDialogPVRRecordingInfo.h b/xbmc/pvr/dialogs/GUIDialogPVRRecordingInfo.h
+new file mode 100644
+index 0000000..f9d686d
+--- /dev/null
++++ b/xbmc/pvr/dialogs/GUIDialogPVRRecordingInfo.h
+@@ -0,0 +1,41 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "guilib/GUIDialog.h"
++
++namespace PVR
++{
++ class CGUIDialogPVRRecordingInfo : public CGUIDialog
++ {
++ public:
++ CGUIDialogPVRRecordingInfo(void);
++ virtual ~CGUIDialogPVRRecordingInfo(void) {}
++ virtual bool OnMessage(CGUIMessage& message);
++ virtual bool HasListItems() const { return true; };
++ virtual CFileItemPtr GetCurrentListItem(int offset = 0);
++
++ void SetRecording(const CFileItem *item);
++
++ protected:
++ CFileItemPtr m_recordItem;
++ };
++}
+diff --git a/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp b/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp
+new file mode 100644
+index 0000000..b01b100
+--- /dev/null
++++ b/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp
+@@ -0,0 +1,389 @@
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "GUIDialogPVRTimerSettings.h"
++#include "dialogs/GUIDialogKeyboard.h"
++#include "dialogs/GUIDialogNumeric.h"
++#include "settings/GUISettings.h"
++#include "guilib/LocalizeStrings.h"
++
++#include "pvr/PVRManager.h"
++#include "pvr/timers/PVRTimerInfoTag.h"
++#include "pvr/channels/PVRChannelGroupsContainer.h"
++
++using namespace std;
++using namespace PVR;
++
++#define CONTROL_TMR_ACTIVE 20
++#define CONTROL_TMR_CHNAME_TV 21
++#define CONTROL_TMR_DAY 22
++#define CONTROL_TMR_BEGIN 23
++#define CONTROL_TMR_END 24
++#define CONTROL_TMR_PRIORITY 26
++#define CONTROL_TMR_LIFETIME 27
++#define CONTROL_TMR_FIRST_DAY 28
++#define CONTROL_TMR_NAME 29
++#define CONTROL_TMR_DIR 30
++#define CONTROL_TMR_RADIO 50
++#define CONTROL_TMR_CHNAME_RADIO 51
++
++CGUIDialogPVRTimerSettings::CGUIDialogPVRTimerSettings(void)
++ : CGUIDialogSettings(WINDOW_DIALOG_PVR_TIMER_SETTING, "DialogPVRTimerSettings.xml")
++{
++ m_cancelled = true;
++ m_tmp_day = 11;
++}
++
++void CGUIDialogPVRTimerSettings::AddChannelNames(CFileItemList &channelsList, SETTINGSTRINGS &channelNames, bool bRadio)
++{
++ g_PVRChannelGroups->GetGroupAll(bRadio)->GetMembers(channelsList);
++
++ channelNames.push_back("0 dummy");
++ for (int i = 0; i < channelsList.Size(); i++)
++ {
++ CStdString string;
++ CFileItemPtr item = channelsList[i];
++ const CPVRChannel *channel = item->GetPVRChannelInfoTag();
++ string.Format("%i %s", channel->ChannelNumber(), channel->ChannelName().c_str());
++ channelNames.push_back(string);
++ }
++
++ int iControl = bRadio ? CONTROL_TMR_CHNAME_RADIO : CONTROL_TMR_CHNAME_TV;
++ AddSpin(iControl, 19078, &m_timerItem->GetPVRTimerInfoTag()->m_iChannelNumber, channelNames.size(), channelNames);
++ EnableSettings(iControl, m_timerItem->GetPVRTimerInfoTag()->m_bIsRadio == bRadio);
++}
++
++void CGUIDialogPVRTimerSettings::SetWeekdaySettingFromTimer(const CPVRTimerInfoTag &timer)
++{
++ if (timer.m_bIsRepeating)
++ {
++ if (timer.m_iWeekdays == 0x01)
++ m_tmp_day = 0;
++ else if (timer.m_iWeekdays == 0x02)
++ m_tmp_day = 1;
++ else if (timer.m_iWeekdays == 0x04)
++ m_tmp_day = 2;
++ else if (timer.m_iWeekdays == 0x08)
++ m_tmp_day = 3;
++ else if (timer.m_iWeekdays == 0x10)
++ m_tmp_day = 4;
++ else if (timer.m_iWeekdays == 0x20)
++ m_tmp_day = 5;
++ else if (timer.m_iWeekdays == 0x40)
++ m_tmp_day = 6;
++ else if (timer.m_iWeekdays == 0x1F)
++ m_tmp_day = 7;
++ else if (timer.m_iWeekdays == 0x3F)
++ m_tmp_day = 8;
++ else if (timer.m_iWeekdays == 0x7F)
++ m_tmp_day = 9;
++ else if (timer.m_iWeekdays == 0x60)
++ m_tmp_day = 10;
++ }
++}
++
++void CGUIDialogPVRTimerSettings::SetTimerFromWeekdaySetting(CPVRTimerInfoTag &timer)
++{
++ timer.m_bIsRepeating = true;
++
++ if (m_tmp_day == 0)
++ timer.m_iWeekdays = 0x01;
++ else if (m_tmp_day == 1)
++ timer.m_iWeekdays = 0x02;
++ else if (m_tmp_day == 2)
++ timer.m_iWeekdays = 0x04;
++ else if (m_tmp_day == 3)
++ timer.m_iWeekdays = 0x08;
++ else if (m_tmp_day == 4)
++ timer.m_iWeekdays = 0x10;
++ else if (m_tmp_day == 5)
++ timer.m_iWeekdays = 0x20;
++ else if (m_tmp_day == 6)
++ timer.m_iWeekdays = 0x40;
++ else if (m_tmp_day == 7)
++ timer.m_iWeekdays = 0x1F;
++ else if (m_tmp_day == 8)
++ timer.m_iWeekdays = 0x3F;
++ else if (m_tmp_day == 9)
++ timer.m_iWeekdays = 0x7F;
++ else if (m_tmp_day == 10)
++ timer.m_iWeekdays = 0x60;
++ else
++ timer.m_iWeekdays = 0;
++}
++
++void CGUIDialogPVRTimerSettings::CreateSettings()
++{
++ CPVRTimerInfoTag* tag = m_timerItem->GetPVRTimerInfoTag();
++
++ // clear out any old settings
++ m_settings.clear();
++
++ // create our settings controls
++ m_bTimerActive = tag->IsActive();
++ AddBool(CONTROL_TMR_ACTIVE, 19074, &m_bTimerActive);
++ AddButton(CONTROL_TMR_NAME, 19075, &tag->m_strTitle, true);
++
++ if (tag->SupportsFolders())
++ AddButton(CONTROL_TMR_DIR, 19076, &tag->m_strDirectory, true);
++
++ AddBool(CONTROL_TMR_RADIO, 19077, &tag->m_bIsRadio);
++
++ /// Channel names
++ {
++ // For TV
++ CFileItemList channelslist_tv;
++ SETTINGSTRINGS channelstrings_tv;
++ AddChannelNames(channelslist_tv, channelstrings_tv, false);
++
++ // For Radio
++ CFileItemList channelslist_radio;
++ SETTINGSTRINGS channelstrings_radio;
++ AddChannelNames(channelslist_radio, channelstrings_radio, true);
++ }
++
++ /// Day
++ {
++ SETTINGSTRINGS daystrings;
++ tm time_cur;
++ tm time_tmr;
++
++ for (unsigned int iDayPtr = 19086; iDayPtr <= 19096; iDayPtr++)
++ daystrings.push_back(g_localizeStrings.Get(iDayPtr));
++ CDateTime time = CDateTime::GetCurrentDateTime();
++ CDateTime timestart = tag->StartAsLocalTime();
++
++ /* get diffence of timer in days between today and timer start date */
++ time.GetAsTm(time_cur);
++ timestart.GetAsTm(time_tmr);
++
++ m_tmp_day += time_tmr.tm_yday - time_cur.tm_yday;
++ if (time_tmr.tm_yday - time_cur.tm_yday < 0)
++ m_tmp_day += 365;
++
++ for (int i = 1; i < 365; ++i)
++ {
++ CStdString string = time.GetAsLocalizedDate();
++ daystrings.push_back(string);
++ time += CDateTimeSpan(1, 0, 0, 0);
++ }
++
++ SetWeekdaySettingFromTimer(*tag);
++
++ AddSpin(CONTROL_TMR_DAY, 19079, &m_tmp_day, daystrings.size(), daystrings);
++ }
++
++ AddButton(CONTROL_TMR_BEGIN, 19080, &timerStartTimeStr, true);
++ AddButton(CONTROL_TMR_END, 19081, &timerEndTimeStr, true);
++ AddSpin(CONTROL_TMR_PRIORITY, 19082, &tag->m_iPriority, 0, 99);
++ AddSpin(CONTROL_TMR_LIFETIME, 19083, &tag->m_iLifetime, 0, 365);
++
++ /// First day
++ {
++ SETTINGSTRINGS daystrings;
++ tm time_cur;
++ tm time_tmr;
++
++ CDateTime time = CDateTime::GetCurrentDateTime();
++ CDateTime timestart = tag->FirstDayAsLocalTime();
++
++ /* get diffence of timer in days between today and timer start date */
++ if (time < timestart)
++ {
++ time.GetAsTm(time_cur);
++ timestart.GetAsTm(time_tmr);
++
++ m_tmp_iFirstDay += time_tmr.tm_yday - time_cur.tm_yday + 1;
++ if (time_tmr.tm_yday - time_cur.tm_yday < 0)
++ m_tmp_iFirstDay += 365;
++ }
++
++ daystrings.push_back(g_localizeStrings.Get(19030));
++
++ for (int i = 1; i < 365; ++i)
++ {
++ CStdString string = time.GetAsLocalizedDate();
++ daystrings.push_back(string);
++ time += CDateTimeSpan(1, 0, 0, 0);
++ }
++
++ AddSpin(CONTROL_TMR_FIRST_DAY, 19084, &m_tmp_iFirstDay, daystrings.size(), daystrings);
++
++ if (tag->m_bIsRepeating)
++ EnableSettings(CONTROL_TMR_FIRST_DAY, true);
++ else
++ EnableSettings(CONTROL_TMR_FIRST_DAY, false);
++ }
++}
++
++void CGUIDialogPVRTimerSettings::OnSettingChanged(SettingInfo &setting)
++{
++ CPVRTimerInfoTag* tag = m_timerItem->GetPVRTimerInfoTag();
++
++ if (setting.id == CONTROL_TMR_NAME)
++ {
++ if (CGUIDialogKeyboard::ShowAndGetInput(tag->m_strTitle, g_localizeStrings.Get(19097), false))
++ {
++ UpdateSetting(CONTROL_TMR_NAME);
++ }
++ }
++ if (setting.id == CONTROL_TMR_DIR && CGUIDialogKeyboard::ShowAndGetInput(tag->m_strDirectory, g_localizeStrings.Get(19104), false))
++ UpdateSetting(CONTROL_TMR_DIR);
++ else if (setting.id == CONTROL_TMR_RADIO)
++ {
++ const CPVRChannel* channeltag = NULL;
++ if (!tag->m_bIsRadio)
++ {
++ EnableSettings(CONTROL_TMR_CHNAME_TV, true);
++ EnableSettings(CONTROL_TMR_CHNAME_RADIO, false);
++ channeltag = g_PVRChannelGroups->GetGroupAllTV()->GetByChannelNumber(tag->m_iChannelNumber);
++ }
++ else
++ {
++ EnableSettings(CONTROL_TMR_CHNAME_TV, false);
++ EnableSettings(CONTROL_TMR_CHNAME_RADIO, true);
++ channeltag = g_PVRChannelGroups->GetGroupAllRadio()->GetByChannelNumber(tag->m_iChannelNumber);
++ }
++
++ if (channeltag)
++ {
++ tag->m_iClientChannelUid = channeltag->UniqueID();
++ tag->m_iClientId = channeltag->ClientID();
++ tag->m_bIsRadio = channeltag->IsRadio();
++ tag->m_iChannelNumber = channeltag->ChannelNumber();
++ }
++ }
++ else if (setting.id == CONTROL_TMR_CHNAME_TV || setting.id == CONTROL_TMR_CHNAME_RADIO)
++ {
++ const CPVRChannel* channeltag = g_PVRChannelGroups->GetGroupAll(tag->m_bIsRadio)->GetByChannelNumber(tag->m_iChannelNumber);
++
++ if (channeltag)
++ {
++ tag->m_iClientChannelUid = channeltag->UniqueID();
++ tag->m_iClientId = channeltag->ClientID();
++ tag->m_bIsRadio = channeltag->IsRadio();
++ tag->m_iChannelNumber = channeltag->ChannelNumber();
++ }
++ }
++ else if (setting.id == CONTROL_TMR_DAY && m_tmp_day > 10)
++ {
++ CDateTime time = CDateTime::GetCurrentDateTime();
++ CDateTime timestart = timerStartTime;
++ CDateTime timestop = timerEndTime;
++ int m_tmp_diff;
++ tm time_cur;
++ tm time_tmr;
++
++ /* get diffence of timer in days between today and timer start date */
++ time.GetAsTm(time_cur);
++ timestart.GetAsTm(time_tmr);
++
++ m_tmp_diff = time_tmr.tm_yday - time_cur.tm_yday;
++ if (time_tmr.tm_yday - time_cur.tm_yday < 0)
++ m_tmp_diff = 365;
++
++ CDateTime newStart = timestart + CDateTimeSpan(m_tmp_day-11-m_tmp_diff, 0, 0, 0);
++ CDateTime newEnd = timestop + CDateTimeSpan(m_tmp_day-11-m_tmp_diff, 0, 0, 0);
++ tag->SetStartFromLocalTime(newStart);
++ tag->SetEndFromLocalTime(newEnd);
++
++ EnableSettings(CONTROL_TMR_FIRST_DAY, false);
++
++ tag->m_bIsRepeating = false;
++ tag->m_iWeekdays = 0;
++ }
++ else if (setting.id == CONTROL_TMR_DAY && m_tmp_day <= 10)
++ {
++ EnableSettings(CONTROL_TMR_FIRST_DAY, true);
++ SetTimerFromWeekdaySetting(*tag);
++ }
++ else if (setting.id == CONTROL_TMR_BEGIN)
++ {
++ if (CGUIDialogNumeric::ShowAndGetTime(timerStartTime, g_localizeStrings.Get(14066)))
++ {
++ CDateTime timestart = timerStartTime;
++ int start_day = tag->StartAsLocalTime().GetDay();
++ int start_month = tag->StartAsLocalTime().GetMonth();
++ int start_year = tag->StartAsLocalTime().GetYear();
++ int start_hour = timestart.GetHour();
++ int start_minute = timestart.GetMinute();
++ CDateTime newStart(start_year, start_month, start_day, start_hour, start_minute, 0);
++ tag->SetStartFromLocalTime(newStart);
++
++ timerStartTimeStr = tag->StartAsLocalTime().GetAsLocalizedTime("", false);
++ UpdateSetting(CONTROL_TMR_BEGIN);
++ }
++ }
++ else if (setting.id == CONTROL_TMR_END)
++ {
++ if (CGUIDialogNumeric::ShowAndGetTime(timerEndTime, g_localizeStrings.Get(14066)))
++ {
++ CDateTime timestop = timerEndTime;
++ int start_day = tag->EndAsLocalTime().GetDay();
++ int start_month = tag->EndAsLocalTime().GetMonth();
++ int start_year = tag->EndAsLocalTime().GetYear();
++ int start_hour = timestop.GetHour();
++ int start_minute = timestop.GetMinute();
++ CDateTime newEnd(start_year, start_month, start_day, start_hour, start_minute, 0);
++ tag->SetEndFromLocalTime(newEnd);
++
++ timerEndTimeStr = tag->EndAsLocalTime().GetAsLocalizedTime("", false);
++ UpdateSetting(CONTROL_TMR_END);
++ }
++ }
++ else if (setting.id == CONTROL_TMR_FIRST_DAY && m_tmp_day <= 10)
++ {
++ CDateTime newFirstDay;
++ if (m_tmp_iFirstDay > 0)
++ newFirstDay = CDateTime::GetCurrentDateTime() + CDateTimeSpan(m_tmp_iFirstDay-1, 0, 0, 0);
++
++ tag->SetFirstDayFromLocalTime(newFirstDay);
++ }
++
++ tag->UpdateSummary();
++}
++
++void CGUIDialogPVRTimerSettings::SetTimer(CFileItem *item)
++{
++ m_timerItem = item;
++ m_cancelled = true;
++
++ m_timerItem->GetPVRTimerInfoTag()->StartAsLocalTime().GetAsSystemTime(timerStartTime);
++ m_timerItem->GetPVRTimerInfoTag()->EndAsLocalTime().GetAsSystemTime(timerEndTime);
++ timerStartTimeStr = m_timerItem->GetPVRTimerInfoTag()->StartAsLocalTime().GetAsLocalizedTime("", false);
++ timerEndTimeStr = m_timerItem->GetPVRTimerInfoTag()->EndAsLocalTime().GetAsLocalizedTime("", false);
++
++ m_tmp_iFirstDay = 0;
++ m_tmp_day = 11;
++}
++
++void CGUIDialogPVRTimerSettings::OnOkay()
++{
++ m_cancelled = false;
++ CPVRTimerInfoTag* tag = m_timerItem->GetPVRTimerInfoTag();
++ if (tag->m_strTitle == g_localizeStrings.Get(19056))
++ tag->m_strTitle = g_PVRChannelGroups->GetByUniqueID(tag->m_iClientChannelUid, tag->m_iClientId)->ChannelName();
++
++ if (m_bTimerActive)
++ tag->m_state = PVR_TIMER_STATE_SCHEDULED;
++ else
++ tag->m_state = PVR_TIMER_STATE_CANCELLED;
++}
+diff --git a/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.h b/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.h
+new file mode 100644
+index 0000000..70f3bb2
+--- /dev/null
++++ b/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.h
+@@ -0,0 +1,61 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "XBDateTime.h"
++#include "settings/GUIDialogSettings.h"
++#include "guilib/GUIListItem.h"
++
++class CFileItem;
++
++namespace PVR
++{
++ class CPVRTimerInfoTag;
++
++ class CGUIDialogPVRTimerSettings : public CGUIDialogSettings
++ {
++ public:
++ CGUIDialogPVRTimerSettings(void);
++ virtual ~CGUIDialogPVRTimerSettings(void) {}
++ void SetTimer(CFileItem *item);
++ bool GetOK() { return !m_cancelled; }
++
++ protected:
++ virtual void CreateSettings();
++ virtual void OnSettingChanged(SettingInfo &setting);
++ virtual void OnOkay();
++ virtual void OnCancel() { m_cancelled = true; }
++ virtual void AddChannelNames(CFileItemList &channelsList, SETTINGSTRINGS &channelNames, bool bRadio);
++ virtual void SetWeekdaySettingFromTimer(const CPVRTimerInfoTag &timer);
++ virtual void SetTimerFromWeekdaySetting(CPVRTimerInfoTag &timer);
++
++ SYSTEMTIME timerStartTime;
++ SYSTEMTIME timerEndTime;
++ CStdString timerStartTimeStr;
++ CStdString timerEndTimeStr;
++ int m_tmp_iFirstDay;;
++ int m_tmp_day;
++ bool m_bTimerActive;
++
++ CFileItem *m_timerItem;
++ bool m_cancelled;
++ };
++}
+diff --git a/xbmc/pvr/dialogs/Makefile b/xbmc/pvr/dialogs/Makefile
+new file mode 100644
+index 0000000..675cdbb
+--- /dev/null
++++ b/xbmc/pvr/dialogs/Makefile
+@@ -0,0 +1,15 @@
++SRCS=GUIDialogPVRChannelManager.cpp \
++ GUIDialogPVRChannelsOSD.cpp \
++ GUIDialogPVRCutterOSD.cpp \
++ GUIDialogPVRDirectorOSD.cpp \
++ GUIDialogPVRGroupManager.cpp \
++ GUIDialogPVRGuideInfo.cpp \
++ GUIDialogPVRGuideOSD.cpp \
++ GUIDialogPVRGuideSearch.cpp \
++ GUIDialogPVRRecordingInfo.cpp \
++ GUIDialogPVRTimerSettings.cpp
++
++LIB=pvrdialogs.a
++
++include ../../../Makefile.include
++-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
+diff --git a/xbmc/pvr/recordings/Makefile b/xbmc/pvr/recordings/Makefile
+new file mode 100644
+index 0000000..465f1fb
+--- /dev/null
++++ b/xbmc/pvr/recordings/Makefile
+@@ -0,0 +1,7 @@
++SRCS=PVRRecording.cpp \
++ PVRRecordings.cpp
++
++LIB=pvrrecordings.a
++
++include ../../../Makefile.include
++-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
+diff --git a/xbmc/pvr/recordings/PVRRecording.cpp b/xbmc/pvr/recordings/PVRRecording.cpp
+new file mode 100644
+index 0000000..566f911
+--- /dev/null
++++ b/xbmc/pvr/recordings/PVRRecording.cpp
+@@ -0,0 +1,210 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "dialogs/GUIDialogOK.h"
++#include "pvr/PVRManager.h"
++#include "settings/AdvancedSettings.h"
++#include "PVRRecordings.h"
++#include "pvr/addons/PVRClients.h"
++#include "utils/StringUtils.h"
++
++#include "epg/Epg.h"
++
++using namespace PVR;
++using namespace EPG;
++
++CPVRRecording::CPVRRecording()
++{
++ Reset();
++}
++
++CPVRRecording::CPVRRecording(const PVR_RECORDING &recording, unsigned int iClientId)
++{
++ Reset();
++
++ m_strRecordingId = recording.strRecordingId;
++ m_strTitle = recording.strTitle;
++ m_iClientId = iClientId;
++ m_recordingTime = recording.recordingTime + g_advancedSettings.m_iPVRTimeCorrection;
++ m_duration = CDateTimeSpan(0, 0, recording.iDuration / 60, recording.iDuration % 60);
++ m_iPriority = recording.iPriority;
++ m_iLifetime = recording.iLifetime;
++ m_strDirectory = recording.strDirectory;
++ m_strPlot = recording.strPlot;
++ m_strPlotOutline = recording.strPlotOutline;
++ m_strStreamURL = recording.strStreamURL;
++ m_strChannelName = recording.strChannelName;
++ m_strGenre = CEpg::ConvertGenreIdToString(recording.iGenreType, recording.iGenreSubType);
++ m_defualt_icon = recording.strIconPath;
++ m_fanart_image = recording.strDefFanart;
++
++}
++
++bool CPVRRecording::operator ==(const CPVRRecording& right) const
++{
++ return (this == &right) ||
++ (m_strRecordingId == right.m_strRecordingId &&
++ m_iClientId == right.m_iClientId &&
++ m_strChannelName == right.m_strChannelName &&
++ m_recordingTime == right.m_recordingTime &&
++ m_duration == right.m_duration &&
++ m_strPlotOutline == right.m_strPlotOutline &&
++ m_strPlot == right.m_strPlot &&
++ m_strStreamURL == right.m_strStreamURL &&
++ m_iPriority == right.m_iPriority &&
++ m_iLifetime == right.m_iLifetime &&
++ m_strDirectory == right.m_strDirectory &&
++ m_strFileNameAndPath == right.m_strFileNameAndPath &&
++ m_strTitle == right.m_strTitle);
++}
++
++bool CPVRRecording::operator !=(const CPVRRecording& right) const
++{
++ return !(*this == right);
++}
++
++void CPVRRecording::Reset(void)
++{
++ m_strRecordingId = StringUtils::EmptyString;
++ m_iClientId = 0;
++ m_strChannelName = StringUtils::EmptyString;
++ m_strDirectory = StringUtils::EmptyString;
++ m_strStreamURL = StringUtils::EmptyString;
++ m_iPriority = -1;
++ m_iLifetime = -1;
++ m_strFileNameAndPath = StringUtils::EmptyString;
++ m_defualt_icon = StringUtils::EmptyString;
++ m_fanart_image = StringUtils::EmptyString;
++
++ m_recordingTime.Reset();
++ CVideoInfoTag::Reset();
++}
++
++int CPVRRecording::GetDuration() const
++{
++ return (m_duration.GetDays() * 60*60*24 +
++ m_duration.GetHours() * 60*60 +
++ m_duration.GetMinutes() * 60 +
++ m_duration.GetSeconds());
++}
++
++bool CPVRRecording::Delete(void)
++{
++ PVR_ERROR error;
++ if (!g_PVRClients->DeleteRecording(*this, &error))
++ {
++ DisplayError(error);
++ return false;
++ }
++
++ return true;
++}
++
++bool CPVRRecording::Rename(const CStdString &strNewName)
++{
++ PVR_ERROR error;
++ m_strTitle.Format("%s", strNewName);
++ if (!g_PVRClients->RenameRecording(*this, &error))
++ {
++ DisplayError(error);
++ return false;
++ }
++
++ return true;
++}
++
++void CPVRRecording::DisplayError(PVR_ERROR err) const
++{
++ if (err == PVR_ERROR_SERVER_ERROR)
++ CGUIDialogOK::ShowAndGetInput(19033,19111,19110,0); /* print info dialog "Server error!" */
++ else if (err == PVR_ERROR_NOT_SYNC)
++ CGUIDialogOK::ShowAndGetInput(19033,19108,19110,0); /* print info dialog "Recordings not in sync!" */
++ else if (err == PVR_ERROR_NOT_DELETED)
++ CGUIDialogOK::ShowAndGetInput(19033,19068,19110,0); /* print info dialog "Couldn't delete recording!" */
++ else
++ CGUIDialogOK::ShowAndGetInput(19033,19147,19110,0); /* print info dialog "Unknown error!" */
++
++ return;
++}
++
++void CPVRRecording::Update(const CPVRRecording &tag)
++{
++ m_strRecordingId = tag.m_strRecordingId;
++ m_iClientId = tag.m_iClientId;
++ m_strTitle = tag.m_strTitle;
++ m_recordingTime = tag.m_recordingTime;
++ m_duration = tag.m_duration;
++ m_iPriority = tag.m_iPriority;
++ m_iLifetime = tag.m_iLifetime;
++ m_strDirectory = tag.m_strDirectory;
++ m_strPlot = tag.m_strPlot;
++ m_strPlotOutline = tag.m_strPlotOutline;
++ m_strStreamURL = tag.m_strStreamURL;
++ m_strChannelName = tag.m_strChannelName;
++ m_strGenre = tag.m_strGenre;
++ m_defualt_icon = tag.m_defualt_icon;
++ m_fanart_image = tag.m_fanart_image;
++
++ CStdString strShow;
++ strShow.Format("%s - ", g_localizeStrings.Get(20364).c_str());
++ if (m_strPlotOutline.Left(strShow.size()).Equals(strShow))
++ {
++ CStdString strEpisode = m_strPlotOutline;
++ CStdString strTitle = m_strDirectory;
++
++ int pos = strTitle.ReverseFind('/');
++ strTitle.erase(0, pos + 1);
++ strEpisode.erase(0, strShow.size());
++ m_strTitle.Format("%s - %s", strTitle.c_str(), strEpisode);
++ pos = strEpisode.Find('-');
++ strEpisode.erase(0, pos + 2);
++ m_strPlotOutline = strEpisode;
++ }
++ UpdatePath();
++}
++
++void CPVRRecording::UpdatePath(void)
++{
++ if (!m_strStreamURL.IsEmpty())
++ {
++ m_strFileNameAndPath = m_strStreamURL;
++ }
++ else
++ {
++ CStdString strTitle(m_strTitle);
++ CStdString strDatetime(m_recordingTime.GetAsSaveString());
++ strTitle.Replace('/','-');
++ strTitle.Remove('?');
++
++ if (m_strDirectory != StringUtils::EmptyString)
++ m_strFileNameAndPath.Format("pvr://recordings/%s/%s/%s.pvr", m_strDirectory.c_str(), strDatetime.c_str(), strTitle.c_str());
++ else
++ m_strFileNameAndPath.Format("pvr://recordings/%s/%s.pvr", strDatetime.c_str(), strTitle.c_str());
++ }
++}
++
++const CDateTime &CPVRRecording::RecordingTimeAsLocalTime(void) const
++{
++ static CDateTime tmp;
++ tmp.SetFromUTCDateTime(m_recordingTime);
++
++ return tmp;
++}
+diff --git a/xbmc/pvr/recordings/PVRRecording.h b/xbmc/pvr/recordings/PVRRecording.h
+new file mode 100644
+index 0000000..cbcda61
+--- /dev/null
++++ b/xbmc/pvr/recordings/PVRRecording.h
+@@ -0,0 +1,107 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++/*
++ * DESCRIPTION:
++ *
++ * CPVRRecordingInfoTag is part of the XBMC PVR system to support recording entrys,
++ * stored on a other Backend like VDR or MythTV.
++ *
++ * The recording information tag holds data about name, length, recording time
++ * and so on of recorded stream stored on the backend.
++ *
++ * The filename string is used to by the PVRManager and passed to DVDPlayer
++ * to stream data from the backend to XBMC.
++ *
++ * It is a also CVideoInfoTag and some of his variables must be set!
++ *
++ */
++
++#include "addons/include/xbmc_pvr_types.h"
++#include "video/VideoInfoTag.h"
++#include "XBDateTime.h"
++
++namespace PVR
++{
++ class CPVRRecording : public CVideoInfoTag
++ {
++ public:
++ int m_iClientId; /*!< ID of the backend */
++ CStdString m_strRecordingId; /*!< unique id of the recording on the client */
++ CStdString m_strChannelName; /*!< name of the channel this was recorded from */
++ CDateTimeSpan m_duration; /*!< duration of this recording */
++ int m_iPriority; /*!< priority of this recording */
++ int m_iLifetime; /*!< lifetime of this recording */
++ CStdString m_strStreamURL; /*!< stream URL. if empty use pvr client */
++ CStdString m_strDirectory; /*!< directory of this recording on the client */
++ CStdString m_defualt_icon; /*!< url to locally stored image */
++ CStdString m_fanart_image; /*!< url to locally stored image */
++
++ CPVRRecording(void);
++ CPVRRecording(const PVR_RECORDING &recording, unsigned int iClientId);
++ virtual ~CPVRRecording() {};
++
++ bool operator ==(const CPVRRecording& right) const;
++ bool operator !=(const CPVRRecording& right) const;
++
++ /*!
++ * @brief Reset this tag to it's initial state.
++ */
++ void Reset(void);
++
++ /*!
++ * @brief The duration of this recording in seconds.
++ * @return The duration.
++ */
++ int GetDuration() const;
++
++ /*!
++ * @brief Delete this recording on the client (if supported).
++ * @return True if it was deleted successfully, false otherwise.
++ */
++ bool Delete(void);
++
++ /*!
++ * @brief Rename this recording on the client (if supported).
++ * @param strNewName The new name.
++ * @return True if it was renamed successfully, false otherwise.
++ */
++ bool Rename(const CStdString &strNewName);
++
++ /*!
++ * @brief Update this tag with the contents of the given tag.
++ * @param tag The new tag info.
++ */
++ void Update(const CPVRRecording &tag);
++
++ const CDateTime &RecordingTimeAsUTC(void) const { return m_recordingTime; }
++ const CDateTime &RecordingTimeAsLocalTime(void) const;
++ void SetRecordingTimeFromUTC(CDateTime &recordingTime) { m_recordingTime = recordingTime; }
++ void SetRecordingTimeFromLocalTime(CDateTime &recordingTime) { m_recordingTime = recordingTime.GetAsUTCDateTime(); }
++
++ private:
++ CDateTime m_recordingTime; /*!< start time of the recording */
++
++ void UpdatePath(void);
++ void DisplayError(PVR_ERROR err) const;
++ };
++}
+diff --git a/xbmc/pvr/recordings/PVRRecordings.cpp b/xbmc/pvr/recordings/PVRRecordings.cpp
+new file mode 100644
+index 0000000..2bab01c
+--- /dev/null
++++ b/xbmc/pvr/recordings/PVRRecordings.cpp
+@@ -0,0 +1,355 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "FileItem.h"
++#include "dialogs/GUIDialogOK.h"
++#include "guilib/GUIWindowManager.h"
++#include "guilib/LocalizeStrings.h"
++#include "Util.h"
++#include "URL.h"
++#include "utils/log.h"
++#include "threads/SingleLock.h"
++#include "video/VideoDatabase.h"
++
++#include "utils/URIUtils.h"
++#include "pvr/PVRManager.h"
++#include "pvr/addons/PVRClients.h"
++#include "PVRRecordings.h"
++
++using namespace PVR;
++
++CPVRRecordings::CPVRRecordings(void) :
++ m_bIsUpdating(false),
++ m_strDirectoryHistory("pvr://recordings/")
++{
++ m_thumbLoader.SetNumOfWorkers(1);
++}
++
++void CPVRRecordings::UpdateFromClients(void)
++{
++ CSingleLock lock(m_critSection);
++ Clear();
++ g_PVRClients->GetRecordings(this);
++}
++
++CStdString CPVRRecordings::TrimSlashes(const CStdString &strOrig) const
++{
++ CStdString strReturn(strOrig);
++ while (strReturn.Left(1) == "/")
++ strReturn.erase(0, 1);
++
++ URIUtils::RemoveSlashAtEnd(strReturn);
++
++ return strReturn;
++}
++
++const CStdString CPVRRecordings::GetDirectoryFromPath(const CStdString &strPath, const CStdString &strBase) const
++{
++ CStdString strReturn;
++ CStdString strUsePath = TrimSlashes(strPath);
++ CStdString strUseBase = TrimSlashes(strBase);
++
++ /* strip the base or return an empty value if it doesn't fit or match */
++ if (!strUseBase.IsEmpty())
++ {
++ if (strUsePath.GetLength() <= strUseBase.GetLength() || strUsePath.Left(strUseBase.GetLength()) != strUseBase)
++ return strReturn;
++ strUsePath.erase(0, strUseBase.GetLength());
++ }
++
++ /* check for more occurences */
++ int iDelimiter = strUsePath.Find('/');
++ if (iDelimiter > 0)
++ strReturn = strUsePath.Left(iDelimiter);
++ else
++ strReturn = strUsePath;
++
++ return TrimSlashes(strReturn);
++}
++
++bool CPVRRecordings::IsDirectoryMember(const CStdString &strDirectory, const CStdString &strEntryDirectory, bool bDirectMember /* = true */) const
++{
++ CStdString strUseDirectory = TrimSlashes(strDirectory);
++ CStdString strUseEntryDirectory = TrimSlashes(strEntryDirectory);
++
++ return strUseEntryDirectory.Left(strUseDirectory.GetLength()).Equals(strUseDirectory) &&
++ (!bDirectMember || strUseEntryDirectory.Equals(strUseDirectory));
++}
++
++void CPVRRecordings::GetContents(const CStdString &strDirectory, CFileItemList *results)
++{
++ for (unsigned int iRecordingPtr = 0; iRecordingPtr < size(); iRecordingPtr++)
++ {
++ CPVRRecording *current = at(iRecordingPtr);
++ if (!IsDirectoryMember(strDirectory, current->m_strDirectory, true))
++ continue;
++
++ CFileItemPtr pFileItem(new CFileItem(*current));
++ pFileItem->SetLabel2(current->RecordingTimeAsLocalTime().GetAsLocalizedDateTime(true, false));
++ pFileItem->m_dateTime = current->RecordingTimeAsLocalTime();
++ pFileItem->SetPath(current->m_strFileNameAndPath);
++ CVideoDatabase db;
++ if (db.Open())
++ pFileItem->GetPVRRecordingInfoTag()->m_playCount=db.GetPlayCount(*pFileItem);
++ pFileItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, pFileItem->GetPVRRecordingInfoTag()->m_playCount > 0);
++ results->Add(pFileItem);
++ }
++}
++
++void CPVRRecordings::GetSubDirectories(const CStdString &strBase, CFileItemList *results, bool bAutoSkip /* = true */)
++{
++ CStdString strUseBase = TrimSlashes(strBase);
++
++ for (unsigned int iRecordingPtr = 0; iRecordingPtr < size(); iRecordingPtr++)
++ {
++ CPVRRecording *current = at(iRecordingPtr);
++ const CStdString strCurrent = GetDirectoryFromPath(current->m_strDirectory, strUseBase);
++ if (strCurrent.IsEmpty())
++ continue;
++
++ CStdString strFilePath;
++ if(strUseBase.empty())
++ strFilePath.Format("pvr://recordings/%s/", strCurrent.c_str());
++ else
++ strFilePath.Format("pvr://recordings/%s/%s/", strUseBase.c_str(), strCurrent.c_str());
++
++ if (!results->Contains(strFilePath))
++ {
++ CFileItemPtr pFileItem;
++ pFileItem.reset(new CFileItem(strCurrent, true));
++ pFileItem->SetPath(strFilePath);
++ pFileItem->SetLabel(strCurrent);
++ pFileItem->SetLabelPreformated(true);
++ pFileItem->m_dateTime = current->RecordingTimeAsLocalTime();
++ results->Add(pFileItem);
++ }
++ else
++ {
++ CFileItemPtr pFileItem;
++ pFileItem=results->Get(strFilePath);
++ if (pFileItem->m_dateTime<current->RecordingTimeAsLocalTime())
++ pFileItem->m_dateTime = current->RecordingTimeAsLocalTime();
++ }
++ }
++
++ CFileItemList files;
++ GetContents(strBase, &files);
++
++ if (bAutoSkip && results->Size() == 1 && files.Size() == 0)
++ {
++ CStdString strGetPath;
++ strGetPath.Format("%s/%s/", strUseBase.c_str(), results->Get(0)->GetLabel());
++
++ results->Clear();
++
++ CLog::Log(LOGDEBUG, "CPVRRecordings - %s - '%s' only has 1 subdirectory, selecting that directory ('%s')",
++ __FUNCTION__, strUseBase.c_str(), strGetPath.c_str());
++ GetSubDirectories(strGetPath, results, true);
++ return;
++ }
++
++ results->Append(files);
++
++ if (!strUseBase.IsEmpty())
++ {
++ CStdString strLabel("..");
++ CFileItemPtr pItem(new CFileItem(strLabel));
++ pItem->SetPath(m_strDirectoryHistory);
++ pItem->m_bIsFolder = true;
++ pItem->m_bIsShareOrDrive = false;
++ results->AddFront(pItem, 0);
++ }
++ m_strDirectoryHistory.Format("pvr://recordings/%s", strUseBase.c_str());
++}
++
++int CPVRRecordings::Load(void)
++{
++ Update();
++
++ return size();
++}
++
++void CPVRRecordings::Unload()
++{
++ Clear();
++}
++
++void CPVRRecordings::Update(void)
++{
++ CSingleLock lock(m_critSection);
++ if (m_bIsUpdating)
++ return;
++ m_bIsUpdating = true;
++ lock.Leave();
++
++ CLog::Log(LOGDEBUG, "CPVRRecordings - %s - updating recordings", __FUNCTION__);
++ UpdateFromClients();
++
++ lock.Enter();
++ m_bIsUpdating = false;
++ SetChanged();
++ lock.Leave();
++
++ NotifyObservers("recordings-reset");
++}
++
++int CPVRRecordings::GetNumRecordings()
++{
++ CSingleLock lock(m_critSection);
++ return size();
++}
++
++int CPVRRecordings::GetRecordings(CFileItemList* results)
++{
++ CSingleLock lock(m_critSection);
++
++ for (unsigned int iRecordingPtr = 0; iRecordingPtr < size(); iRecordingPtr++)
++ {
++ CFileItemPtr pFileItem(new CFileItem(*at(iRecordingPtr)));
++ results->Add(pFileItem);
++ }
++
++ return size();
++}
++
++bool CPVRRecordings::DeleteRecording(const CFileItem &item)
++{
++ if (!item.IsPVRRecording())
++ {
++ CLog::Log(LOGERROR, "CPVRRecordings - %s - cannot delete file: no valid recording tag", __FUNCTION__);
++ return false;
++ }
++
++ CPVRRecording *tag = (CPVRRecording *)item.GetPVRRecordingInfoTag();
++ return tag->Delete();
++}
++
++bool CPVRRecordings::RenameRecording(CFileItem &item, CStdString &strNewName)
++{
++ bool bReturn = false;
++
++ if (!item.IsPVRRecording())
++ {
++ CLog::Log(LOGERROR, "CPVRRecordings - %s - cannot rename file: no valid recording tag", __FUNCTION__);
++ return bReturn;
++ }
++
++ CPVRRecording* tag = item.GetPVRRecordingInfoTag();
++ return tag->Rename(strNewName);
++}
++
++bool CPVRRecordings::GetDirectory(const CStdString& strPath, CFileItemList &items)
++{
++ bool bSuccess(false);
++ CFileItemList files;
++
++ {
++ CSingleLock lock(m_critSection);
++
++ CURL url(strPath);
++ CStdString strFileName = url.GetFileName();
++ URIUtils::RemoveSlashAtEnd(strFileName);
++
++ if (strFileName.Left(10) == "recordings")
++ {
++ strFileName.erase(0, 10);
++ GetSubDirectories(strFileName, &items, true);
++ GetContents(strFileName, &files);
++ bSuccess = true;
++ }
++ }
++
++ if(bSuccess)
++ {
++ for (int i = 0; i < files.Size(); i++)
++ {
++ CFileItemPtr pFileItem = files.Get(i);
++ CFileItemPtr pThumbItem = items.Get(pFileItem->GetPath());
++ if (!pThumbItem->HasThumbnail())
++ m_thumbLoader.LoadItem(pThumbItem.get());
++ }
++ }
++
++ return bSuccess;
++}
++
++CPVRRecording *CPVRRecordings::GetByPath(const CStdString &path)
++{
++ CPVRRecording *tag = NULL;
++ CSingleLock lock(m_critSection);
++
++ CURL url(path);
++ CStdString fileName = url.GetFileName();
++ URIUtils::RemoveSlashAtEnd(fileName);
++
++ if (fileName.Left(11) == "recordings/")
++ {
++ if (fileName.IsEmpty())
++ return tag;
++
++ for (unsigned int iRecordingPtr = 0; iRecordingPtr < size(); iRecordingPtr++)
++ {
++ CPVRRecording *recording = at(iRecordingPtr);
++
++ if(path.Equals(recording->m_strFileNameAndPath))
++ {
++ tag = recording;
++ break;
++ }
++ }
++ }
++
++ return tag;
++}
++
++void CPVRRecordings::Clear()
++{
++ CSingleLock lock(m_critSection);
++
++ for (unsigned int iRecordingPtr = 0; iRecordingPtr < size(); iRecordingPtr++)
++ delete at(iRecordingPtr);
++ erase(begin(), end());
++}
++
++void CPVRRecordings::UpdateEntry(const CPVRRecording &tag)
++{
++ bool bFound = false;
++ CSingleLock lock(m_critSection);
++
++ for (unsigned int iRecordingPtr = 0; iRecordingPtr < size(); iRecordingPtr++)
++ {
++ CPVRRecording *currentTag = at(iRecordingPtr);
++ if (currentTag->m_iClientId == tag.m_iClientId &&
++ currentTag->m_strRecordingId.Equals(tag.m_strRecordingId))
++ {
++ currentTag->Update(tag);
++ bFound = true;
++ break;
++ }
++ }
++
++ if (!bFound)
++ {
++ CPVRRecording *newTag = new CPVRRecording();
++ newTag->Update(tag);
++ push_back(newTag);
++ }
++}
+diff --git a/xbmc/pvr/recordings/PVRRecordings.h b/xbmc/pvr/recordings/PVRRecordings.h
+new file mode 100644
+index 0000000..78480b2
+--- /dev/null
++++ b/xbmc/pvr/recordings/PVRRecordings.h
+@@ -0,0 +1,70 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "PVRRecording.h"
++#include "XBDateTime.h"
++#include "threads/Thread.h"
++#include "utils/Observer.h"
++#include "ThumbLoader.h"
++
++namespace PVR
++{
++ class CPVRRecordings : public std::vector<CPVRRecording *>,
++ public Observable
++ {
++ private:
++ CCriticalSection m_critSection;
++ bool m_bIsUpdating;
++ CStdString m_strDirectoryHistory;
++ CVideoThumbLoader m_thumbLoader;
++
++ virtual void UpdateFromClients(void);
++ virtual CStdString TrimSlashes(const CStdString &strOrig) const;
++ virtual const CStdString GetDirectoryFromPath(const CStdString &strPath, const CStdString &strBase) const;
++ virtual bool IsDirectoryMember(const CStdString &strDirectory, const CStdString &strEntryDirectory, bool bDirectMember = true) const;
++ virtual void GetContents(const CStdString &strDirectory, CFileItemList *results);
++ virtual void GetSubDirectories(const CStdString &strBase, CFileItemList *results, bool bAutoSkip = true);
++
++ public:
++ CPVRRecordings(void);
++ virtual ~CPVRRecordings(void) { Clear(); };
++
++ int Load();
++ void Unload();
++ void Clear();
++ void UpdateEntry(const CPVRRecording &tag);
++ void UpdateFromClient(const CPVRRecording &tag) { UpdateEntry(tag); }
++
++ /**
++ * @brief refresh the recordings list from the clients.
++ */
++ void Update(void);
++
++ int GetNumRecordings();
++ int GetRecordings(CFileItemList* results);
++ bool DeleteRecording(const CFileItem &item);
++ bool RenameRecording(CFileItem &item, CStdString &strNewName);
++
++ bool GetDirectory(const CStdString& strPath, CFileItemList &items);
++ CPVRRecording *GetByPath(const CStdString &path);
++ };
++}
+diff --git a/xbmc/pvr/timers/Makefile b/xbmc/pvr/timers/Makefile
+new file mode 100644
+index 0000000..0af3a3b
+--- /dev/null
++++ b/xbmc/pvr/timers/Makefile
+@@ -0,0 +1,7 @@
++SRCS=PVRTimerInfoTag.cpp \
++ PVRTimers.cpp
++
++LIB=pvrtimers.a
++
++include ../../../Makefile.include
++-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
+diff --git a/xbmc/pvr/timers/PVRTimerInfoTag.cpp b/xbmc/pvr/timers/PVRTimerInfoTag.cpp
+new file mode 100644
+index 0000000..2085e63
+--- /dev/null
++++ b/xbmc/pvr/timers/PVRTimerInfoTag.cpp
+@@ -0,0 +1,628 @@
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "Application.h"
++#include "settings/GUISettings.h"
++#include "dialogs/GUIDialogKaiToast.h"
++#include "dialogs/GUIDialogOK.h"
++#include "dialogs/GUIDialogYesNo.h"
++#include "settings/AdvancedSettings.h"
++#include "utils/log.h"
++#include "utils/StringUtils.h"
++
++#include "PVRTimers.h"
++#include "pvr/PVRManager.h"
++#include "pvr/channels/PVRChannelGroupsContainer.h"
++#include "epg/EpgContainer.h"
++#include "pvr/addons/PVRClients.h"
++
++#include "epg/Epg.h"
++
++using namespace PVR;
++using namespace EPG;
++
++CPVRTimerInfoTag::CPVRTimerInfoTag(void)
++{
++ m_strTitle = StringUtils::EmptyString;
++ m_strDirectory = "/";
++ m_strSummary = StringUtils::EmptyString;
++ m_iClientId = g_PVRClients->GetFirstConnectedClientID();
++ m_iClientIndex = -1;
++ m_iClientChannelUid = -1;
++ m_iPriority = g_guiSettings.GetInt("pvrrecord.defaultpriority");
++ m_iLifetime = g_guiSettings.GetInt("pvrrecord.defaultlifetime");
++ m_bIsRepeating = false;
++ m_iWeekdays = 0;
++ m_strFileNameAndPath = StringUtils::EmptyString;
++ m_iChannelNumber = 0;
++ m_bIsRadio = false;
++ m_iEpgId = -1;
++ m_channel = NULL;
++ m_iMarginStart = g_guiSettings.GetInt("pvrrecord.marginstart");
++ m_iMarginEnd = g_guiSettings.GetInt("pvrrecord.marginend");
++ m_strGenre = StringUtils::EmptyString;
++ m_iGenreType = 0;
++ m_iGenreSubType = 0;
++ m_StartTime = CDateTime::GetUTCDateTime();
++ m_StopTime = m_StartTime;
++ m_state = PVR_TIMER_STATE_SCHEDULED;
++ m_FirstDay.SetValid(false);
++}
++
++CPVRTimerInfoTag::CPVRTimerInfoTag(const PVR_TIMER &timer, CPVRChannel *channel, unsigned int iClientId)
++{
++ m_strTitle = timer.strTitle;
++ m_strDirectory = timer.strDirectory;
++ m_strSummary = StringUtils::EmptyString;
++ m_iClientId = iClientId;
++ m_iClientIndex = timer.iClientIndex;
++ m_iClientChannelUid = channel ? channel->UniqueID() : timer.iClientChannelUid;
++ m_iChannelNumber = channel ? g_PVRChannelGroups->GetGroupAll(channel->IsRadio())->GetChannelNumber(*channel) : 0;
++ m_StartTime = timer.startTime + g_advancedSettings.m_iPVRTimeCorrection;
++ m_StopTime = timer.endTime + g_advancedSettings.m_iPVRTimeCorrection;
++ m_bIsRepeating = timer.bIsRepeating;
++ m_FirstDay = timer.firstDay + g_advancedSettings.m_iPVRTimeCorrection;
++ m_iWeekdays = timer.iWeekdays;
++ m_iPriority = timer.iPriority;
++ m_iLifetime = timer.iLifetime;
++ m_iMarginStart = timer.iMarginStart;
++ m_iMarginEnd = timer.iMarginEnd;
++ m_strGenre = CEpg::ConvertGenreIdToString(timer.iGenreType, timer.iGenreSubType);
++ m_iGenreType = timer.iGenreType;
++ m_iGenreSubType = timer.iGenreSubType;
++ m_iEpgId = -1;
++ m_channel = channel;
++ m_bIsRadio = channel && channel->IsRadio();
++ m_state = timer.state;
++ m_strFileNameAndPath.Format("pvr://client%i/timers/%i", m_iClientId, m_iClientIndex);
++
++ UpdateEpgEvent();
++ UpdateSummary();
++}
++
++bool CPVRTimerInfoTag::operator ==(const CPVRTimerInfoTag& right) const
++{
++ if (this == &right) return true;
++
++ bool bChannelsMatch = true;
++ if (m_channel && right.m_channel)
++ bChannelsMatch = *m_channel == *right.m_channel;
++ else if (!m_channel && right.m_channel)
++ bChannelsMatch = false;
++ else if (m_channel && !right.m_channel)
++ bChannelsMatch = false;
++
++ return (m_iClientIndex == right.m_iClientIndex &&
++ m_strSummary == right.m_strSummary &&
++ m_iClientChannelUid == right.m_iClientChannelUid &&
++ m_bIsRepeating == right.m_bIsRepeating &&
++ m_StartTime == right.m_StartTime &&
++ m_StopTime == right.m_StopTime &&
++ m_FirstDay == right.m_FirstDay &&
++ m_iWeekdays == right.m_iWeekdays &&
++ m_iPriority == right.m_iPriority &&
++ m_iLifetime == right.m_iLifetime &&
++ m_strFileNameAndPath == right.m_strFileNameAndPath &&
++ m_strTitle == right.m_strTitle &&
++ m_strDirectory == right.m_strDirectory &&
++ m_iClientId == right.m_iClientId &&
++ m_iMarginStart == right.m_iMarginStart &&
++ m_iMarginEnd == right.m_iMarginEnd &&
++ m_state == right.m_state &&
++ bChannelsMatch);
++}
++
++CPVRTimerInfoTag &CPVRTimerInfoTag::operator=(const CPVRTimerInfoTag &orig)
++{
++ m_channel = orig.m_channel;
++ m_iClientIndex = orig.m_iClientIndex;
++ m_strSummary = orig.m_strSummary;
++ m_iClientChannelUid = orig.m_iClientChannelUid;
++ m_bIsRepeating = orig.m_bIsRepeating;
++ m_StartTime = orig.m_StartTime;
++ m_StopTime = orig.m_StopTime;
++ m_FirstDay = orig.m_FirstDay;
++ m_iWeekdays = orig.m_iWeekdays;
++ m_iPriority = orig.m_iPriority;
++ m_iLifetime = orig.m_iLifetime;
++ m_strFileNameAndPath = orig.m_strFileNameAndPath;
++ m_strTitle = orig.m_strTitle;
++ m_strDirectory = orig.m_strDirectory;
++ m_iClientId = orig.m_iClientId;
++ m_iMarginStart = orig.m_iMarginStart;
++ m_iMarginEnd = orig.m_iMarginEnd;
++ m_state = orig.m_state;
++ m_iChannelNumber = orig.m_iChannelNumber;
++
++ return *this;
++}
++
++CPVRTimerInfoTag::~CPVRTimerInfoTag(void)
++{
++ CEpgInfoTag *tag = GetEpgInfoTag();
++ if (tag)
++ tag->OnTimerDeleted();
++}
++
++/**
++ * Compare not equal for two CPVRTimerInfoTag
++ */
++bool CPVRTimerInfoTag::operator !=(const CPVRTimerInfoTag& right) const
++{
++ if (this == &right) return false;
++
++ return !(*this == right);
++}
++
++int CPVRTimerInfoTag::Compare(const CPVRTimerInfoTag &timer) const
++{
++ CSingleLock lock(m_critSection);
++ int iTimerDelta = 0;
++ if (StartAsUTC() != timer.StartAsUTC())
++ {
++ CDateTimeSpan timerDelta = StartAsUTC() - timer.StartAsUTC();
++ iTimerDelta = (timerDelta.GetSeconds() + timerDelta.GetMinutes() * 60 + timerDelta.GetHours() * 3600 + timerDelta.GetDays() * 86400);
++ }
++
++ /* if the start times are equal, compare the priority of the timers */
++ return iTimerDelta == 0 ?
++ timer.m_iPriority - m_iPriority :
++ iTimerDelta;
++}
++
++void CPVRTimerInfoTag::UpdateSummary(void)
++{
++ CSingleLock lock(m_critSection);
++ m_strSummary.clear();
++
++ if (!m_bIsRepeating || !m_iWeekdays)
++ {
++ m_strSummary.Format("%s %s %s %s %s",
++ StartAsLocalTime().GetAsLocalizedDate(),
++ g_localizeStrings.Get(19159),
++ StartAsLocalTime().GetAsLocalizedTime(StringUtils::EmptyString, false),
++ g_localizeStrings.Get(19160),
++ EndAsLocalTime().GetAsLocalizedTime(StringUtils::EmptyString, false));
++ }
++ else if (m_FirstDay.IsValid())
++ {
++ m_strSummary.Format("%s-%s-%s-%s-%s-%s-%s %s %s %s %s %s %s",
++ m_iWeekdays & 0x01 ? g_localizeStrings.Get(19149) : "__",
++ m_iWeekdays & 0x02 ? g_localizeStrings.Get(19150) : "__",
++ m_iWeekdays & 0x04 ? g_localizeStrings.Get(19151) : "__",
++ m_iWeekdays & 0x08 ? g_localizeStrings.Get(19152) : "__",
++ m_iWeekdays & 0x10 ? g_localizeStrings.Get(19153) : "__",
++ m_iWeekdays & 0x20 ? g_localizeStrings.Get(19154) : "__",
++ m_iWeekdays & 0x40 ? g_localizeStrings.Get(19155) : "__",
++ g_localizeStrings.Get(19156),
++ FirstDayAsLocalTime().GetAsLocalizedDate(false),
++ g_localizeStrings.Get(19159),
++ StartAsLocalTime().GetAsLocalizedTime(StringUtils::EmptyString, false),
++ g_localizeStrings.Get(19160),
++ EndAsLocalTime().GetAsLocalizedTime(StringUtils::EmptyString, false));
++ }
++ else
++ {
++ m_strSummary.Format("%s-%s-%s-%s-%s-%s-%s %s %s %s %s",
++ m_iWeekdays & 0x01 ? g_localizeStrings.Get(19149) : "__",
++ m_iWeekdays & 0x02 ? g_localizeStrings.Get(19150) : "__",
++ m_iWeekdays & 0x04 ? g_localizeStrings.Get(19151) : "__",
++ m_iWeekdays & 0x08 ? g_localizeStrings.Get(19152) : "__",
++ m_iWeekdays & 0x10 ? g_localizeStrings.Get(19153) : "__",
++ m_iWeekdays & 0x20 ? g_localizeStrings.Get(19154) : "__",
++ m_iWeekdays & 0x40 ? g_localizeStrings.Get(19155) : "__",
++ g_localizeStrings.Get(19159),
++ StartAsLocalTime().GetAsLocalizedTime(StringUtils::EmptyString, false),
++ g_localizeStrings.Get(19160),
++ EndAsLocalTime().GetAsLocalizedTime(StringUtils::EmptyString, false));
++ }
++}
++
++/**
++ * Get the status string of this Timer, is used by the GUIInfoManager
++ */
++const CStdString &CPVRTimerInfoTag::GetStatus() const
++{
++ CSingleLock lock(m_critSection);
++ if (m_strFileNameAndPath == "pvr://timers/add.timer")
++ return g_localizeStrings.Get(19026);
++ else if (m_state == PVR_TIMER_STATE_CANCELLED || m_state == PVR_TIMER_STATE_ABORTED)
++ return g_localizeStrings.Get(13106);
++ else if (m_state == PVR_TIMER_STATE_RECORDING)
++ return g_localizeStrings.Get(19162);
++ return g_localizeStrings.Get(305);
++}
++
++bool CPVRTimerInfoTag::AddToClient(void)
++{
++ CSingleLock lock(m_critSection);
++ UpdateEpgEvent();
++ PVR_ERROR error;
++ if (!g_PVRClients->AddTimer(*this, &error))
++ {
++ DisplayError(error);
++ return false;
++ }
++ else
++ return true;
++}
++
++bool CPVRTimerInfoTag::DeleteFromClient(bool bForce /* = false */)
++{
++ bool bRemoved = false;
++ PVR_ERROR error;
++
++ CSingleLock lock(m_critSection);
++ bRemoved = g_PVRClients->DeleteTimer(*this, bForce, &error);
++ if (!bRemoved && error == PVR_ERROR_RECORDING_RUNNING)
++ {
++ if (CGUIDialogYesNo::ShowAndGetInput(122,0,19122,0))
++ bRemoved = g_PVRClients->DeleteTimer(*this, true, &error);
++ else
++ return false;
++ }
++
++ if (!bRemoved)
++ {
++ DisplayError(error);
++ return false;
++ }
++
++ CEpgInfoTag *epgTag = GetEpgInfoTag();
++ if (epgTag)
++ {
++ epgTag->OnTimerDeleted();
++ m_iEpgId = -1;
++ }
++
++ return true;
++}
++
++bool CPVRTimerInfoTag::RenameOnClient(const CStdString &strNewName)
++{
++ PVR_ERROR error;
++ CSingleLock lock(m_critSection);
++ m_strTitle.Format("%s", strNewName);
++ if (!g_PVRClients->RenameTimer(*this, m_strTitle, &error))
++ {
++ if (error == PVR_ERROR_NOT_IMPLEMENTED)
++ return UpdateOnClient();
++
++ DisplayError(error);
++ return false;
++ }
++
++ return true;
++}
++
++bool CPVRTimerInfoTag::UpdateEntry(const CPVRTimerInfoTag &tag)
++{
++ CSingleLock lock(m_critSection);
++
++ m_iClientId = tag.m_iClientId;
++ m_iClientIndex = tag.m_iClientIndex;
++ m_strTitle = tag.m_strTitle;
++ m_strDirectory = tag.m_strDirectory;
++ m_iClientChannelUid = tag.m_iClientChannelUid;
++ m_StartTime = tag.m_StartTime;
++ m_StopTime = tag.m_StopTime;
++ m_FirstDay = tag.m_FirstDay;
++ m_iPriority = tag.m_iPriority;
++ m_iLifetime = tag.m_iLifetime;
++ m_state = tag.m_state;
++ m_bIsRepeating = tag.m_bIsRepeating;
++ m_iWeekdays = tag.m_iWeekdays;
++ m_iChannelNumber = tag.m_iChannelNumber;
++ m_bIsRadio = tag.m_bIsRadio;
++ m_iMarginStart = tag.m_iMarginStart;
++ m_iMarginEnd = tag.m_iMarginEnd;
++ m_iEpgId = tag.m_iEpgId;
++ m_epgStart = tag.m_epgStart;
++ m_strGenre = tag.m_strGenre;
++ m_iGenreType = tag.m_iGenreType;
++ m_iGenreSubType = tag.m_iGenreSubType;
++ m_strSummary = tag.m_strSummary;
++
++ /* try to find an epg event */
++ UpdateEpgEvent();
++
++ if (m_strSummary.IsEmpty())
++ UpdateSummary();
++
++ return true;
++}
++
++void CPVRTimerInfoTag::UpdateEpgEvent(bool bClear /* = false */)
++{
++ CSingleLock lock(m_critSection);
++ if (bClear)
++ {
++ CEpgInfoTag *epgTag = GetEpgInfoTag();
++ if (epgTag)
++ epgTag->OnTimerDeleted();
++ }
++ else
++ {
++ /* already got an epg event set */
++ if (m_iEpgId != -1)
++ return;
++
++ /* try to get the channel */
++ CPVRChannel *channel = (CPVRChannel *) g_PVRChannelGroups->GetByUniqueID(m_iClientChannelUid, m_iClientId);
++ if (!channel)
++ return;
++
++ /* try to get the EPG table */
++ CEpg *epg = channel->GetEPG();
++ if (!epg)
++ return;
++
++ /* try to set the timer on the epg tag that matches with a 2 minute margin */
++ CEpgInfoTag *epgTag = (CEpgInfoTag *) epg->GetTagBetween(StartAsUTC() - CDateTimeSpan(0, 0, 2, 0), EndAsUTC() + CDateTimeSpan(0, 0, 2, 0));
++ if (!epgTag)
++ epgTag = (CEpgInfoTag *) epg->GetTagAround(StartAsUTC());
++
++ if (epgTag)
++ {
++ m_iEpgId = epgTag->m_iEpgId;
++ m_epgStart = epgTag->StartAsUTC();
++ m_strGenre = epgTag->Genre();
++ m_iGenreType = epgTag->GenreType();
++ m_iGenreSubType = epgTag->GenreSubType();
++ epgTag->SetTimer(this);
++ }
++ }
++}
++
++bool CPVRTimerInfoTag::UpdateOnClient()
++{
++ CSingleLock lock(m_critSection);
++ UpdateEpgEvent();
++ PVR_ERROR error;
++ if (!g_PVRClients->UpdateTimer(*this, &error))
++ {
++ DisplayError(error);
++ return false;
++ }
++ else
++ return true;
++}
++
++void CPVRTimerInfoTag::DisplayError(PVR_ERROR err) const
++{
++ if (err == PVR_ERROR_SERVER_ERROR)
++ CGUIDialogOK::ShowAndGetInput(19033,19111,19110,0); /* print info dialog "Server error!" */
++ else if (err == PVR_ERROR_NOT_SYNC)
++ CGUIDialogOK::ShowAndGetInput(19033,19112,19110,0); /* print info dialog "Timers not in sync!" */
++ else if (err == PVR_ERROR_NOT_SAVED)
++ CGUIDialogOK::ShowAndGetInput(19033,19109,19110,0); /* print info dialog "Couldn't delete timer!" */
++ else if (err == PVR_ERROR_ALREADY_PRESENT)
++ CGUIDialogOK::ShowAndGetInput(19033,19109,0,19067); /* print info dialog */
++ else
++ CGUIDialogOK::ShowAndGetInput(19033,19147,19110,0); /* print info dialog "Unknown error!" */
++}
++
++void CPVRTimerInfoTag::SetEpgInfoTag(CEpgInfoTag *tag)
++{
++ CSingleLock lock(m_critSection);
++ if (tag)
++ {
++ if (m_iEpgId != tag->m_iEpgId || m_epgStart != tag->StartAsUTC())
++ {
++ CLog::Log(LOGINFO, "cPVRTimerInfoTag: timer %s set to epg event %s", m_strTitle.c_str(), tag->Title().c_str());
++ m_iEpgId = tag->m_iEpgId;
++ m_epgStart = tag->StartAsUTC();
++ }
++ }
++ else
++ {
++ if (m_iEpgId != -1)
++ CLog::Log(LOGINFO, "cPVRTimerInfoTag: timer %s set to no epg event", m_strTitle.c_str());
++ m_iEpgId = -1;
++ }
++}
++
++void CPVRTimerInfoTag::OnEpgTagDeleted(void)
++{
++ CSingleLock lock(m_critSection);
++ m_iEpgId = -1;
++}
++
++int CPVRTimerInfoTag::ChannelNumber() const
++{
++ CSingleLock lock(m_critSection);
++ const CPVRChannel *channeltag = g_PVRChannelGroups->GetByUniqueID(m_iClientChannelUid, m_iClientId);
++ if (channeltag)
++ return channeltag->ChannelNumber();
++ else
++ return 0;
++}
++
++CStdString CPVRTimerInfoTag::ChannelName() const
++{
++ CSingleLock lock(m_critSection);
++ const CPVRChannel *channeltag = g_PVRChannelGroups->GetByUniqueID(m_iClientChannelUid, m_iClientId);
++ if (channeltag)
++ return channeltag->ChannelName();
++ else
++ return StringUtils::EmptyString;
++}
++
++CStdString CPVRTimerInfoTag::ChannelIcon() const
++{
++ CSingleLock lock(m_critSection);
++ const CPVRChannel *channeltag = g_PVRChannelGroups->GetByUniqueID(m_iClientChannelUid, m_iClientId);
++ if (channeltag)
++ return channeltag->IconPath();
++ else
++ return StringUtils::EmptyString;
++}
++
++bool CPVRTimerInfoTag::SetDuration(int iDuration)
++{
++ CSingleLock lock(m_critSection);
++ if (m_StartTime.IsValid())
++ {
++ m_StopTime = m_StartTime + CDateTimeSpan(0, iDuration / 60, iDuration % 60, 0);
++ return true;
++ }
++
++ return false;
++}
++
++CPVRTimerInfoTag *CPVRTimerInfoTag::CreateFromEpg(const CEpgInfoTag &tag)
++{
++ /* create a new timer */
++ CPVRTimerInfoTag *newTag = new CPVRTimerInfoTag();
++ if (!newTag)
++ {
++ CLog::Log(LOGERROR, "%s - couldn't create new timer", __FUNCTION__);
++ return NULL;
++ }
++
++ /* check if a valid channel is set */
++ const CPVRChannel *channel = tag.ChannelTag();
++ if (channel == NULL)
++ {
++ CLog::Log(LOGERROR, "%s - no channel set", __FUNCTION__);
++ return NULL;
++ }
++
++ /* check if the epg end date is in the future */
++ if (tag.EndAsLocalTime() < CDateTime::GetCurrentDateTime())
++ {
++ CLog::Log(LOGERROR, "%s - end time is in the past", __FUNCTION__);
++ return NULL;
++ }
++
++ /* set the timer data */
++ CDateTime newStart = tag.StartAsUTC();
++ CDateTime newEnd = tag.EndAsUTC();
++ newTag->m_iClientIndex = -1;
++ newTag->m_strTitle = tag.Title().IsEmpty() ? channel->ChannelName() : tag.Title();
++ newTag->m_iChannelNumber = channel->ChannelNumber();
++ newTag->m_iClientChannelUid = channel->UniqueID();
++ newTag->m_iClientId = channel->ClientID();
++ newTag->m_bIsRadio = channel->IsRadio();
++ newTag->m_iGenreType = tag.GenreType();
++ newTag->m_iGenreSubType = tag.GenreSubType();
++ newTag->SetStartFromUTC(newStart);
++ newTag->SetEndFromUTC(newEnd);
++
++ if (tag.Plot().IsEmpty())
++ {
++ newTag->m_strSummary.Format("%s %s %s %s %s",
++ newTag->StartAsLocalTime().GetAsLocalizedDate(),
++ g_localizeStrings.Get(19159),
++ newTag->StartAsLocalTime().GetAsLocalizedTime(StringUtils::EmptyString, false),
++ g_localizeStrings.Get(19160),
++ newTag->EndAsLocalTime().GetAsLocalizedTime(StringUtils::EmptyString, false));
++ }
++ else
++ {
++ newTag->m_strSummary = tag.Plot();
++ }
++
++ newTag->m_iEpgId = tag.m_iEpgId;
++ newTag->m_epgStart = tag.StartAsUTC();
++
++ /* unused only for reference */
++ newTag->m_strFileNameAndPath = "pvr://timers/new";
++
++ return newTag;
++}
++
++const CDateTime &CPVRTimerInfoTag::StartAsLocalTime(void) const
++{
++ static CDateTime tmp;
++ CSingleLock lock(m_critSection);
++ tmp.SetFromUTCDateTime(m_StartTime);
++
++ return tmp;
++}
++
++const CDateTime &CPVRTimerInfoTag::EndAsLocalTime(void) const
++{
++ static CDateTime tmp;
++ CSingleLock lock(m_critSection);
++ tmp.SetFromUTCDateTime(m_StopTime);
++
++ return tmp;
++}
++
++const CDateTime &CPVRTimerInfoTag::FirstDayAsLocalTime(void) const
++{
++ static CDateTime tmp;
++ CSingleLock lock(m_critSection);
++ tmp.SetFromUTCDateTime(m_FirstDay);
++
++ return tmp;
++}
++
++void CPVRTimerInfoTag::GetNotificationText(CStdString &strText) const
++{
++ CSingleLock lock(m_critSection);
++ switch (m_state)
++ {
++ case PVR_TIMER_STATE_ABORTED:
++ case PVR_TIMER_STATE_CANCELLED:
++ strText.Format("%s: '%s'", g_localizeStrings.Get(19224), m_strTitle.c_str());
++ break;
++ case PVR_TIMER_STATE_SCHEDULED:
++ strText.Format("%s: '%s'", g_localizeStrings.Get(19225), m_strTitle.c_str());
++ break;
++ case PVR_TIMER_STATE_RECORDING:
++ strText.Format("%s: '%s'", g_localizeStrings.Get(19226), m_strTitle.c_str());
++ break;
++ case PVR_TIMER_STATE_COMPLETED:
++ strText.Format("%s: '%s'", g_localizeStrings.Get(19227), m_strTitle.c_str());
++ break;
++ default:
++ break;
++ }
++}
++
++void CPVRTimerInfoTag::QueueNotification(void) const
++{
++ if (g_guiSettings.GetBool("pvrrecord.timernotifications"))
++ {
++ CStdString strMessage;
++ GetNotificationText(strMessage);
++
++ if (!strMessage.IsEmpty())
++ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(19166), strMessage);
++ }
++}
++
++EPG::CEpgInfoTag *CPVRTimerInfoTag::GetEpgInfoTag(void) const
++{
++ CSingleLock lock(m_critSection);
++ CEpg *epg = m_iEpgId != -1 ? g_EpgContainer.GetById(m_iEpgId) : NULL;
++ if (epg)
++ return epg->GetTag(-1, m_epgStart);
++ return NULL;
++}
++
++bool CPVRTimerInfoTag::SupportsFolders() const
++{
++ return g_PVRClients->GetAddonCapabilities(m_iClientId).bSupportsRecordingFolders;
++}
++
+diff --git a/xbmc/pvr/timers/PVRTimerInfoTag.h b/xbmc/pvr/timers/PVRTimerInfoTag.h
+new file mode 100644
+index 0000000..0974e54
+--- /dev/null
++++ b/xbmc/pvr/timers/PVRTimerInfoTag.h
+@@ -0,0 +1,181 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++/*
++ * DESCRIPTION:
++ *
++ * CPVRTimerInfoTag is part of the PVRManager to support sheduled recordings.
++ *
++ * The timer information tag holds data about current programmed timers for
++ * the PVRManager. It is possible to create timers directly based upon
++ * a EPG entry by giving the EPG information tag or as instant timer
++ * on currently tuned channel, or give a blank tag to modify later.
++ *
++ * With exception of the blank one, the tag can easily and unmodified added
++ * by the PVRManager function "bool AddTimer(const CFileItem &item)" to
++ * the backend server.
++ *
++ * The filename inside the tag is for reference only and gives the index
++ * number of the tag reported by the PVR backend and can not be played!
++ *
++ *
++ * USED SETUP VARIABLES:
++ *
++ * ------------- Name -------------|---Type--|-default-|--Description-----
++ * pvrmanager.instantrecordtime = Integer = 180 = Length of a instant timer in minutes
++ * pvrmanager.defaultpriority = Integer = 50 = Default Priority
++ * pvrmanager.defaultlifetime = Integer = 99 = Liftime of the timer in days
++ * pvrmanager.marginstart = Integer = 2 = Minutes to start record earlier
++ * pvrmanager.marginstop = Integer = 10 = Minutes to stop record later
++ *
++ */
++
++#include "XBDateTime.h"
++#include "../addons/include/xbmc_pvr_types.h"
++
++class CFileItem;
++
++namespace EPG
++{
++ class CEpgInfoTag;
++}
++
++namespace PVR
++{
++ class CGUIDialogPVRTimerSettings;
++ class CPVRChannel;
++
++ class CPVRTimerInfoTag
++ {
++ friend class EPG::CEpgInfoTag;
++
++ public:
++ CStdString m_strTitle; /*!< @brief name of this timer */
++ CStdString m_strDirectory; /*!< @brief directory where the recording must be stored */
++ CStdString m_strSummary; /*!< @brief summary string with the time to show inside a GUI list */
++ PVR_TIMER_STATE m_state; /*!< @brief the state of this timer */
++ int m_iClientId; /*!< @brief ID of the backend */
++ int m_iClientIndex; /*!< @brief index number of the tag, given by the backend, -1 for new */
++ int m_iClientChannelUid; /*!< @brief channel uid */
++ int m_iPriority; /*!< @brief priority of the timer */
++ int m_iLifetime; /*!< @brief lifetime of the timer in days */
++ bool m_bIsRepeating; /*!< @brief repeating timer if true, use the m_FirstDay and repeat flags */
++ int m_iWeekdays; /*!< @brief bit based store of weekdays to repeat */
++ CStdString m_strFileNameAndPath; /*!< @brief filename is only for reference */
++ int m_iChannelNumber; /*!< @brief integer value of the channel number */
++ bool m_bIsRadio; /*!< @brief is radio channel if set */
++ const CPVRChannel * m_channel;
++ unsigned int m_iMarginStart; /*!< @brief (optional) if set, the backend starts the recording iMarginStart minutes before startTime. */
++ unsigned int m_iMarginEnd; /*!< @brief (optional) if set, the backend ends the recording iMarginEnd minutes after endTime. */
++ CStdString m_strGenre; /*!< @brief genre of the timer */
++ int m_iGenreType; /*!< @brief genre type of the timer */
++ int m_iGenreSubType; /*!< @brief genre subtype of the timer */
++
++ CPVRTimerInfoTag(void);
++ CPVRTimerInfoTag(const PVR_TIMER &timer, CPVRChannel *channel, unsigned int iClientId);
++ virtual ~CPVRTimerInfoTag(void);
++
++ void Reset();
++
++ bool operator ==(const CPVRTimerInfoTag& right) const;
++ bool operator !=(const CPVRTimerInfoTag& right) const;
++ CPVRTimerInfoTag &operator=(const CPVRTimerInfoTag &orig);
++
++ int Compare(const CPVRTimerInfoTag &timer) const;
++
++ void UpdateSummary(void);
++
++ void DisplayError(PVR_ERROR err) const;
++
++ const CStdString &GetStatus() const;
++
++ bool SetDuration(int iDuration);
++
++ static CPVRTimerInfoTag *CreateFromEpg(const EPG::CEpgInfoTag &tag);
++ void SetEpgInfoTag(EPG::CEpgInfoTag *tag);
++ EPG::CEpgInfoTag *GetEpgInfoTag(void) const;
++
++ int ChannelNumber(void) const;
++ CStdString ChannelName(void) const;
++ CStdString ChannelIcon(void) const;
++
++ bool UpdateEntry(const CPVRTimerInfoTag &tag);
++
++ void UpdateEpgEvent(bool bClear = false);
++
++ bool IsActive(void) const { return m_state == PVR_TIMER_STATE_SCHEDULED || m_state == PVR_TIMER_STATE_RECORDING; }
++ bool IsRecording(void) const { return m_state == PVR_TIMER_STATE_RECORDING; }
++
++ const CDateTime &StartAsUTC(void) const { return m_StartTime; }
++ const CDateTime &StartAsLocalTime(void) const;
++ void SetStartFromUTC(CDateTime &start) { m_StartTime = start; }
++ void SetStartFromLocalTime(CDateTime &start) { m_StartTime = start.GetAsUTCDateTime(); }
++
++ const CDateTime &EndAsUTC(void) const { return m_StopTime; }
++ const CDateTime &EndAsLocalTime(void) const;
++ void SetEndFromUTC(CDateTime &end) { m_StopTime = end; }
++ void SetEndFromLocalTime(CDateTime &end) { m_StopTime = end.GetAsUTCDateTime(); }
++
++ const CDateTime &FirstDayAsUTC(void) const { return m_FirstDay; }
++ const CDateTime &FirstDayAsLocalTime(void) const;
++ void SetFirstDayFromUTC(CDateTime &firstDay) { m_FirstDay = firstDay; }
++ void SetFirstDayFromLocalTime(CDateTime &firstDay) { m_FirstDay = firstDay.GetAsUTCDateTime(); }
++
++ unsigned int MarginStart(void) const { return m_iMarginStart; }
++ void SetMarginStart(unsigned int iMinutes) { m_iMarginStart = iMinutes; }
++
++ unsigned int MarginEnd(void) const { return m_iMarginEnd; }
++ void SetMarginEnd(unsigned int iMinutes) { m_iMarginEnd = iMinutes; }
++
++ bool SupportsFolders() const;
++
++ /*!
++ * @brief Show a notification for this timer in the UI
++ */
++ void QueueNotification(void) const;
++
++ /*!
++ * @brief Get the text for the notification.
++ * @param strText The notification.
++ */
++ void GetNotificationText(CStdString &strText) const;
++
++ /* Client control functions */
++ bool AddToClient();
++ bool DeleteFromClient(bool bForce = false);
++ bool RenameOnClient(const CStdString &strNewName);
++ bool UpdateOnClient();
++
++ protected:
++ /*!
++ * @brief Called by the CEpgInfoTag destructor
++ */
++ virtual void OnEpgTagDeleted(void);
++
++ CCriticalSection m_critSection;
++ int m_iEpgId; /*!< the id of the epg table or -1 if none */
++ CDateTime m_epgStart; /*!< the start time of the epg tag */
++ CDateTime m_StartTime; /*!< start time */
++ CDateTime m_StopTime; /*!< stop time */
++ CDateTime m_FirstDay; /*!< if it is a repeating timer the first date it starts */
++ };
++}
+diff --git a/xbmc/pvr/timers/PVRTimers.cpp b/xbmc/pvr/timers/PVRTimers.cpp
+new file mode 100644
+index 0000000..3f158f4
+--- /dev/null
++++ b/xbmc/pvr/timers/PVRTimers.cpp
+@@ -0,0 +1,755 @@
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "Application.h"
++#include "FileItem.h"
++#include "settings/GUISettings.h"
++#include "dialogs/GUIDialogKaiToast.h"
++#include "dialogs/GUIDialogOK.h"
++#include "threads/SingleLock.h"
++#include "utils/log.h"
++#include "utils/URIUtils.h"
++#include "utils/StringUtils.h"
++
++#include "PVRTimers.h"
++#include "pvr/PVRManager.h"
++#include "pvr/channels/PVRChannelGroupsContainer.h"
++#include "epg/EpgContainer.h"
++#include "pvr/addons/PVRClients.h"
++
++using namespace std;
++using namespace PVR;
++using namespace EPG;
++
++CPVRTimers::CPVRTimers(void)
++{
++ m_bIsUpdating = false;
++}
++
++CPVRTimers::~CPVRTimers(void)
++{
++ Unload();
++}
++
++int CPVRTimers::Load()
++{
++ Unload();
++ g_EpgContainer.RegisterObserver(this);
++ Update();
++
++ return GetNumTimers();
++}
++
++void CPVRTimers::Unload()
++{
++ CSingleLock lock(m_critSection);
++ CEpgContainer *epg = &g_EpgContainer;
++ if (epg)
++ epg->UnregisterObserver(this);
++
++ for (map<CDateTime, vector<CPVRTimerInfoTag *>* >::iterator it = m_tags.begin(); it != m_tags.end(); it++)
++ {
++ vector<CPVRTimerInfoTag*> *timers = it->second;
++ for (unsigned int iTagPtr = 0; iTagPtr < timers->size(); iTagPtr++)
++ delete timers->at(iTagPtr);
++ delete it->second;
++ }
++ m_tags.clear();
++}
++
++int CPVRTimers::LoadFromClients(void)
++{
++ return g_PVRClients->GetTimers(this);
++}
++
++bool CPVRTimers::Update(void)
++{
++ {
++ CSingleLock lock(m_critSection);
++ if (m_bIsUpdating)
++ return false;
++ m_bIsUpdating = true;
++ }
++
++ CLog::Log(LOGDEBUG, "CPVRTimers - %s - updating timers", __FUNCTION__);
++ CPVRTimers PVRTimers_tmp;
++ PVRTimers_tmp.LoadFromClients();
++
++ return UpdateEntries(&PVRTimers_tmp);
++}
++
++bool CPVRTimers::IsRecording(void)
++{
++ CSingleLock lock(m_critSection);
++
++ for (map<CDateTime, vector<CPVRTimerInfoTag *>* >::iterator it = m_tags.begin(); it != m_tags.end(); it++)
++ {
++ vector<CPVRTimerInfoTag*> *timers = it->second;
++ for (unsigned int iTagPtr = 0; iTagPtr < timers->size(); iTagPtr++)
++ if (timers->at(iTagPtr)->IsRecording())
++ return true;
++ }
++
++ return false;
++}
++
++bool CPVRTimers::UpdateEntries(CPVRTimers *timers)
++{
++ bool bChanged(false);
++ bool bAddedOrDeleted(false);
++ vector<CStdString> timerNotifications;
++
++ CSingleLock lock(m_critSection);
++
++ /* go through the timer list and check for updated or new timers */
++ for (map<CDateTime, vector<CPVRTimerInfoTag *>* >::iterator it = timers->m_tags.begin(); it != timers->m_tags.end(); it++)
++ {
++ vector<CPVRTimerInfoTag*> *entry = it->second;
++ for (unsigned int iTagPtr = 0; iTagPtr < entry->size(); iTagPtr++)
++ {
++ const CPVRTimerInfoTag *timer = entry->at(iTagPtr);
++
++ /* check if this timer is present in this container */
++ CPVRTimerInfoTag *existingTimer = (CPVRTimerInfoTag *) GetByClient(timer->m_iClientId, timer->m_iClientIndex);
++ if (existingTimer)
++ {
++ /* if it's present, update the current tag */
++ bool bStateChanged(existingTimer->m_state != timer->m_state);
++ if (existingTimer->UpdateEntry(*timer))
++ {
++ bChanged = true;
++
++ if (bStateChanged && g_PVRManager.IsStarted())
++ {
++ CStdString strMessage;
++ existingTimer->GetNotificationText(strMessage);
++ timerNotifications.push_back(strMessage);
++ }
++
++ CLog::Log(LOGDEBUG,"PVRTimers - %s - updated timer %d on client %d",
++ __FUNCTION__, timer->m_iClientIndex, timer->m_iClientId);
++ }
++ }
++ else
++ {
++ /* new timer */
++ CPVRTimerInfoTag *newTimer = new CPVRTimerInfoTag;
++ newTimer->UpdateEntry(*timer);
++
++ vector<CPVRTimerInfoTag *>* addEntry = NULL;
++ map<CDateTime, vector<CPVRTimerInfoTag *>* >::iterator itr = m_tags.find(newTimer->StartAsUTC());
++ if (itr == m_tags.end())
++ {
++ addEntry = new vector<CPVRTimerInfoTag *>;
++ m_tags.insert(make_pair(newTimer->StartAsUTC(), addEntry));
++ }
++ else
++ {
++ addEntry = itr->second;
++ }
++
++ addEntry->push_back(newTimer);
++ bChanged = true;
++ bAddedOrDeleted = true;
++
++ if (g_PVRManager.IsStarted())
++ {
++ CStdString strMessage;
++ newTimer->GetNotificationText(strMessage);
++ timerNotifications.push_back(strMessage);
++ }
++
++ CLog::Log(LOGDEBUG,"PVRTimers - %s - added timer %d on client %d",
++ __FUNCTION__, timer->m_iClientIndex, timer->m_iClientId);
++ }
++ }
++ }
++
++ /* check for deleted timers */
++ for (map<CDateTime, vector<CPVRTimerInfoTag *>* >::iterator it = m_tags.begin(); it != m_tags.end(); it != m_tags.end() ? it++ : it)
++ {
++ vector<CPVRTimerInfoTag*> *entry = it->second;
++ for (int iTagPtr = entry->size() - 1; iTagPtr >= 0; iTagPtr--)
++ {
++ CPVRTimerInfoTag *timer = entry->at(iTagPtr);
++ if (!timer)
++ continue;
++
++ if (timers->GetByClient(timer->m_iClientId, timer->m_iClientIndex) == NULL)
++ {
++ /* timer was not found */
++ CLog::Log(LOGDEBUG,"PVRTimers - %s - deleted timer %d on client %d",
++ __FUNCTION__, timer->m_iClientIndex, timer->m_iClientId);
++
++ if (g_PVRManager.IsStarted())
++ {
++ CStdString strMessage;
++ strMessage.Format("%s: '%s'", (timer->EndAsUTC() <= CDateTime::GetCurrentDateTime().GetAsUTCDateTime()) ? g_localizeStrings.Get(19227) : g_localizeStrings.Get(19228), timer->m_strTitle.c_str());
++ timerNotifications.push_back(strMessage);
++ }
++
++ delete entry->at(iTagPtr);
++ entry->erase(entry->begin() + iTagPtr);
++
++ if (entry->size() == 0)
++ m_tags.erase(it++);
++ bChanged = true;
++ bAddedOrDeleted = true;
++ }
++ }
++ }
++
++ m_bIsUpdating = false;
++ if (bChanged)
++ {
++ SetChanged();
++ lock.Leave();
++
++ NotifyObservers(bAddedOrDeleted ? "timers-reset" : "timers", false);
++
++ if (g_guiSettings.GetBool("pvrrecord.timernotifications"))
++ {
++ /* queue notifications */
++ for (unsigned int iNotificationPtr = 0; iNotificationPtr < timerNotifications.size(); iNotificationPtr++)
++ {
++ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info,
++ g_localizeStrings.Get(19166),
++ timerNotifications.at(iNotificationPtr));
++ }
++ }
++ }
++
++ return bChanged;
++}
++
++bool CPVRTimers::UpdateEntry(const CPVRTimerInfoTag &timer)
++{
++ CPVRTimerInfoTag *tag = NULL;
++ CSingleLock lock(m_critSection);
++
++ if ((tag = GetByClient(timer.m_iClientId, timer.m_iClientIndex)) == NULL)
++ {
++ tag = new CPVRTimerInfoTag();
++ vector<CPVRTimerInfoTag *>* addEntry = NULL;
++ map<CDateTime, vector<CPVRTimerInfoTag *>* >::iterator itr = m_tags.find(timer.StartAsUTC());
++ if (itr == m_tags.end())
++ {
++ addEntry = new vector<CPVRTimerInfoTag *>;
++ m_tags.insert(make_pair(timer.StartAsUTC(), addEntry));
++ }
++ else
++ {
++ addEntry = itr->second;
++ }
++ addEntry->push_back(tag);
++ }
++
++ return tag->UpdateEntry(timer);
++}
++
++/********** getters **********/
++
++int CPVRTimers::GetTimers(CFileItemList* results)
++{
++ int iReturn(0);
++ CSingleLock lock(m_critSection);
++ for (map<CDateTime, vector<CPVRTimerInfoTag *>* >::iterator it = m_tags.begin(); it != m_tags.end(); it++)
++ {
++ vector<CPVRTimerInfoTag*> *timers = it->second;
++ for (unsigned int iTagPtr = 0; iTagPtr < timers->size(); iTagPtr++)
++ {
++ CFileItemPtr timer(new CFileItem(*timers->at(iTagPtr)));
++ results->Add(timer);
++ ++iReturn;
++ }
++ }
++
++ return iReturn;
++}
++
++bool CPVRTimers::GetNextActiveTimer(CPVRTimerInfoTag *tag) const
++{
++ CSingleLock lock(m_critSection);
++ for (map<CDateTime, vector<CPVRTimerInfoTag *>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
++ {
++ for (unsigned int iTimerPtr = 0; iTimerPtr < it->second->size(); iTimerPtr++)
++ {
++ CPVRTimerInfoTag *current = it->second->at(iTimerPtr);
++ if (current->IsActive() && !current->IsRecording())
++ {
++ *tag = *current;
++ return true;
++ }
++ }
++ }
++
++ return false;
++}
++
++int CPVRTimers::GetActiveTimers(vector<CPVRTimerInfoTag *> *tags) const
++{
++ int iInitialSize = tags->size();
++ CSingleLock lock(m_critSection);
++
++ for (map<CDateTime, vector<CPVRTimerInfoTag *>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
++ {
++ for (unsigned int iTimerPtr = 0; iTimerPtr < it->second->size(); iTimerPtr++)
++ {
++ if (it->second->at(iTimerPtr)->IsActive())
++ tags->push_back(it->second->at(iTimerPtr));
++ }
++ }
++
++ return tags->size() - iInitialSize;
++}
++
++int CPVRTimers::GetNumActiveTimers(void) const
++{
++ int iReturn(0);
++ CSingleLock lock(m_critSection);
++
++ for (map<CDateTime, vector<CPVRTimerInfoTag *>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
++ {
++ for (unsigned int iTimerPtr = 0; iTimerPtr < it->second->size(); iTimerPtr++)
++ {
++ if (it->second->at(iTimerPtr)->IsActive())
++ ++iReturn;
++ }
++ }
++
++ return iReturn;
++}
++
++int CPVRTimers::GetActiveRecordings(vector<CPVRTimerInfoTag *> *tags) const
++{
++ int iInitialSize = tags->size();
++ CSingleLock lock(m_critSection);
++
++ for (map<CDateTime, vector<CPVRTimerInfoTag *>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
++ {
++ for (unsigned int iTimerPtr = 0; iTimerPtr < it->second->size(); iTimerPtr++)
++ {
++ if (it->second->at(iTimerPtr)->IsRecording())
++ tags->push_back(it->second->at(iTimerPtr));
++ }
++ }
++
++ return tags->size() - iInitialSize;
++}
++
++int CPVRTimers::GetNumActiveRecordings(void) const
++{
++ int iReturn(0);
++ CSingleLock lock(m_critSection);
++
++ for (map<CDateTime, vector<CPVRTimerInfoTag *>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
++ {
++ for (unsigned int iTimerPtr = 0; iTimerPtr < it->second->size(); iTimerPtr++)
++ {
++ if (it->second->at(iTimerPtr)->IsRecording())
++ ++iReturn;
++ }
++ }
++
++ return iReturn;
++}
++
++CPVRTimerInfoTag *CPVRTimers::GetTimer(const CDateTime &start, int iTimer /* = -1 */) const
++{
++ CSingleLock lock(m_critSection);
++ map<CDateTime, vector<CPVRTimerInfoTag *>* >::const_iterator it = m_tags.find(start);
++ if (it != m_tags.end() && it->second->size() > 0)
++ {
++ if (iTimer != -1)
++ {
++ for (unsigned int iTimerPtr = 0; iTimerPtr < it->second->size(); iTimerPtr++)
++ {
++ if (it->second->at(iTimerPtr)->m_iClientIndex == iTimer)
++ return it->second->at(iTimerPtr);
++ }
++ }
++ else
++ {
++ return it->second->at(0);
++ }
++ }
++ return NULL;
++}
++
++int CPVRTimers::GetNumTimers() const
++{
++ int iReturn(0);
++ CSingleLock lock(m_critSection);
++ for (map<CDateTime, vector<CPVRTimerInfoTag *>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
++ iReturn += it->second->size();
++ return iReturn;
++}
++
++bool CPVRTimers::GetDirectory(const CStdString& strPath, CFileItemList &items)
++{
++ CStdString base(strPath);
++ URIUtils::RemoveSlashAtEnd(base);
++
++ CURL url(strPath);
++ CStdString fileName = url.GetFileName();
++ URIUtils::RemoveSlashAtEnd(fileName);
++
++ if (fileName == "timers")
++ {
++ CFileItemPtr item;
++
++ item.reset(new CFileItem(base + "/add.timer", false));
++ item->SetLabel(g_localizeStrings.Get(19026));
++ item->SetLabelPreformated(true);
++ items.Add(item);
++
++ CSingleLock lock(m_critSection);
++ for (map<CDateTime, vector<CPVRTimerInfoTag *>* >::iterator it = m_tags.begin(); it != m_tags.end(); it++)
++ {
++ for (unsigned int iTimerPtr = 0; iTimerPtr < it->second->size(); iTimerPtr++)
++ {
++ item.reset(new CFileItem(*it->second->at(iTimerPtr)));
++ items.Add(item);
++ }
++ }
++
++ return true;
++ }
++ return false;
++}
++
++/********** channel methods **********/
++
++bool CPVRTimers::ChannelHasTimers(const CPVRChannel &channel)
++{
++ CSingleLock lock(m_critSection);
++ for (map<CDateTime, vector<CPVRTimerInfoTag *>* >::iterator it = m_tags.begin(); it != m_tags.end(); it++)
++ {
++ for (unsigned int iTimerPtr = 0; iTimerPtr < it->second->size(); iTimerPtr++)
++ {
++ CPVRTimerInfoTag *timer = it->second->at(iTimerPtr);
++
++ if (timer->ChannelNumber() == channel.ChannelNumber() && timer->m_bIsRadio == channel.IsRadio())
++ return true;
++ }
++ }
++
++ return false;
++}
++
++
++bool CPVRTimers::DeleteTimersOnChannel(const CPVRChannel &channel, bool bDeleteRepeating /* = true */, bool bCurrentlyActiveOnly /* = false */)
++{
++ bool bReturn = false;
++ CSingleLock lock(m_critSection);
++
++ for (map<CDateTime, vector<CPVRTimerInfoTag *>* >::reverse_iterator it = m_tags.rbegin(); it != m_tags.rend(); it++)
++ {
++ for (int iTimerPtr = it->second->size() - 1; iTimerPtr >= 0; iTimerPtr--)
++ {
++ CPVRTimerInfoTag *timer = it->second->at(iTimerPtr);
++
++ if (bCurrentlyActiveOnly &&
++ (CDateTime::GetCurrentDateTime() < timer->StartAsLocalTime() ||
++ CDateTime::GetCurrentDateTime() > timer->EndAsLocalTime()))
++ continue;
++
++ if (!bDeleteRepeating && timer->m_bIsRepeating)
++ continue;
++
++ if (timer->ChannelNumber() == channel.ChannelNumber() && timer->m_bIsRadio == channel.IsRadio())
++ {
++ bReturn = timer->DeleteFromClient(true) || bReturn;
++ it->second->erase(it->second->begin() + iTimerPtr);
++ }
++ }
++ }
++
++ return bReturn;
++}
++
++CPVRTimerInfoTag *CPVRTimers::InstantTimer(CPVRChannel *channel, bool bStartTimer /* = true */)
++{
++ if (!channel)
++ return NULL;
++
++ CEpgInfoTag epgTag;
++ bool bHasEpgNow = channel->GetEPGNow(epgTag);
++ CPVRTimerInfoTag *newTimer = bHasEpgNow ? CPVRTimerInfoTag::CreateFromEpg(epgTag) : NULL;
++ if (!newTimer)
++ {
++ newTimer = new CPVRTimerInfoTag;
++ /* set the timer data */
++ newTimer->m_iClientIndex = -1;
++ newTimer->m_strTitle = channel->ChannelName();
++ newTimer->m_strSummary = g_localizeStrings.Get(19056);
++ newTimer->m_iChannelNumber = channel->ChannelNumber();
++ newTimer->m_iClientChannelUid = channel->UniqueID();
++ newTimer->m_iClientId = channel->ClientID();
++ newTimer->m_bIsRadio = channel->IsRadio();
++
++ /* generate summary string */
++ newTimer->m_strSummary.Format("%s %s %s %s %s",
++ newTimer->StartAsLocalTime().GetAsLocalizedDate(),
++ g_localizeStrings.Get(19159),
++ newTimer->StartAsLocalTime().GetAsLocalizedTime(StringUtils::EmptyString, false),
++ g_localizeStrings.Get(19160),
++ newTimer->EndAsLocalTime().GetAsLocalizedTime(StringUtils::EmptyString, false));
++ }
++
++ CDateTime startTime = CDateTime::GetCurrentDateTime().GetAsUTCDateTime();
++ if(!bHasEpgNow)
++ newTimer->SetStartFromUTC(startTime);
++ newTimer->m_iMarginStart = 0; /* set the start margin to 0 for instant timers */
++
++ int iDuration = g_guiSettings.GetInt("pvrrecord.instantrecordtime");
++ CDateTime endTime = CDateTime::GetUTCDateTime() + CDateTimeSpan(0, 0, iDuration ? iDuration : 120, 0);
++ if(bHasEpgNow)
++ newTimer->SetEndFromUTC(endTime);
++
++ /* unused only for reference */
++ newTimer->m_strFileNameAndPath = "pvr://timers/new";
++
++ if (bStartTimer && !newTimer->AddToClient())
++ {
++ CLog::Log(LOGERROR, "PVRTimers - %s - unable to add an instant timer on the client", __FUNCTION__);
++ delete newTimer;
++ newTimer = NULL;
++ }
++
++ return newTimer;
++}
++
++/********** static methods **********/
++
++bool CPVRTimers::AddTimer(const CFileItem &item)
++{
++ /* Check if a CPVRTimerInfoTag is inside file item */
++ if (!item.IsPVRTimer())
++ {
++ CLog::Log(LOGERROR, "PVRTimers - %s - no TimerInfoTag given", __FUNCTION__);
++ return false;
++ }
++
++ CPVRTimerInfoTag *tag = (CPVRTimerInfoTag *)item.GetPVRTimerInfoTag();
++ if (!tag)
++ return false;
++
++ return AddTimer(*tag);
++}
++
++bool CPVRTimers::AddTimer(CPVRTimerInfoTag &item)
++{
++ if (!g_PVRClients->GetAddonCapabilities(item.m_iClientId).bSupportsTimers)
++ {
++ CGUIDialogOK::ShowAndGetInput(19033,0,19215,0);
++ return false;
++ }
++
++ return item.AddToClient();
++}
++
++bool CPVRTimers::DeleteTimer(const CFileItem &item, bool bForce /* = false */)
++{
++ /* Check if a CPVRTimerInfoTag is inside file item */
++ if (!item.IsPVRTimer())
++ {
++ CLog::Log(LOGERROR, "PVRTimers - %s - no TimerInfoTag given", __FUNCTION__);
++ return false;
++ }
++
++ CPVRTimerInfoTag *tag = (CPVRTimerInfoTag *)item.GetPVRTimerInfoTag();
++ if (!tag)
++ return false;
++
++ return DeleteTimer(*tag, bForce);
++}
++
++bool CPVRTimers::DeleteTimer(CPVRTimerInfoTag &item, bool bForce /* = false */)
++{
++ return item.DeleteFromClient(bForce);
++}
++
++bool CPVRTimers::RenameTimer(CFileItem &item, const CStdString &strNewName)
++{
++ /* Check if a CPVRTimerInfoTag is inside file item */
++ if (!item.IsPVRTimer())
++ {
++ CLog::Log(LOGERROR, "PVRTimers - %s - no TimerInfoTag given", __FUNCTION__);
++ return false;
++ }
++
++ CPVRTimerInfoTag* tag = item.GetPVRTimerInfoTag();
++ if (!tag)
++ return false;
++
++ return RenameTimer(*tag, strNewName);
++}
++
++bool CPVRTimers::RenameTimer(CPVRTimerInfoTag &item, const CStdString &strNewName)
++{
++ return item.RenameOnClient(strNewName);
++}
++
++bool CPVRTimers::UpdateTimer(const CFileItem &item)
++{
++ /* Check if a CPVRTimerInfoTag is inside file item */
++ if (!item.IsPVRTimer())
++ {
++ CLog::Log(LOGERROR, "PVRTimers - %s - no TimerInfoTag given", __FUNCTION__);
++ return false;
++ }
++
++ const CPVRTimerInfoTag* tag = item.GetPVRTimerInfoTag();
++ if (!tag)
++ return false;
++
++ return UpdateTimer((CPVRTimerInfoTag &) *tag);
++}
++
++bool CPVRTimers::UpdateTimer(CPVRTimerInfoTag &item)
++{
++ return item.UpdateOnClient();
++}
++
++CPVRTimerInfoTag *CPVRTimers::GetByClient(int iClientId, int iClientTimerId)
++{
++ CSingleLock lock(m_critSection);
++
++ for (map<CDateTime, vector<CPVRTimerInfoTag *>* >::iterator it = m_tags.begin(); it != m_tags.end(); it++)
++ {
++ for (unsigned int iTimerPtr = 0; iTimerPtr < it->second->size(); iTimerPtr++)
++ {
++ CPVRTimerInfoTag *timer = it->second->at(iTimerPtr);
++ if (timer->m_iClientId == iClientId && timer->m_iClientIndex == iClientTimerId)
++ return timer;
++ }
++ }
++
++ return NULL;
++}
++
++bool CPVRTimers::IsRecordingOnChannel(const CPVRChannel &channel) const
++{
++ CSingleLock lock(m_critSection);
++
++ for (map<CDateTime, vector<CPVRTimerInfoTag *>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
++ {
++ for (unsigned int iTimerPtr = 0; iTimerPtr < it->second->size(); iTimerPtr++)
++ {
++ CPVRTimerInfoTag *timer = it->second->at(iTimerPtr);
++
++ if (timer->IsRecording() && timer->m_iClientChannelUid == channel.UniqueID() && timer->m_iClientId == channel.ClientID())
++ return true;
++ }
++ }
++
++ return false;
++}
++
++CPVRTimerInfoTag *CPVRTimers::GetMatch(const CEpgInfoTag *Epg)
++{
++ CSingleLock lock(m_critSection);
++
++ for (map<CDateTime, vector<CPVRTimerInfoTag *>* >::iterator it = m_tags.begin(); it != m_tags.end(); it++)
++ {
++ for (unsigned int iTimerPtr = 0; iTimerPtr < it->second->size(); iTimerPtr++)
++ {
++ CPVRTimerInfoTag *timer = it->second->at(iTimerPtr);
++
++ if (!Epg || !Epg->GetTable() || !Epg->GetTable()->Channel())
++ continue;
++
++ const CPVRChannel *channel = Epg->GetTable()->Channel();
++ if (timer->ChannelNumber() != channel->ChannelNumber()
++ || timer->m_bIsRadio != channel->IsRadio())
++ continue;
++
++ if (timer->StartAsUTC() > Epg->StartAsUTC() || timer->EndAsUTC() < Epg->EndAsUTC())
++ continue;
++
++ return timer;
++ }
++ }
++ return NULL;
++}
++
++CPVRTimerInfoTag *CPVRTimers::GetMatch(const CFileItem *item)
++{
++ CPVRTimerInfoTag *returnTag = NULL;
++
++ if (item && item->HasEPGInfoTag())
++ returnTag = GetMatch(item->GetEPGInfoTag());
++
++ return returnTag;
++}
++
++void CPVRTimers::Notify(const Observable &obs, const CStdString& msg)
++{
++ if (msg.Equals("epg"))
++ g_PVRManager.TriggerTimersUpdate();
++}
++
++CDateTime CPVRTimers::GetNextEventTime(void) const
++{
++ const CStdString wakeupcmd = g_guiSettings.GetString("pvrpowermanagement.setwakeupcmd", false);
++ const bool dailywakup = g_guiSettings.GetBool("pvrpowermanagement.dailywakeup");
++ const CDateTime now = CDateTime::GetUTCDateTime();
++ const CDateTimeSpan prewakeup(0, 0, g_guiSettings.GetInt("pvrpowermanagement.prewakeup"), 0);
++ const CDateTimeSpan idle(0, 0, g_guiSettings.GetInt("pvrpowermanagement.backendidletime"), 0);
++
++ CDateTime timerwakeuptime;
++ CDateTime dailywakeuptime;
++
++ /* Check next active time */
++ CPVRTimerInfoTag timer;
++ if (GetNextActiveTimer(&timer))
++ {
++ const CDateTime start = timer.StartAsUTC();
++
++ if ((start - idle) > now) {
++ timerwakeuptime = start - prewakeup;
++ } else {
++ timerwakeuptime = now + idle;
++ }
++ }
++
++ /* check daily wake up */
++ if (dailywakup)
++ {
++ dailywakeuptime.SetFromDBTime(g_guiSettings.GetString("pvrpowermanagement.dailywakeuptime", false));
++ dailywakeuptime = dailywakeuptime.GetAsUTCDateTime();
++
++ dailywakeuptime.SetDateTime(
++ now.GetYear(), now.GetMonth(), now.GetDay(),
++ dailywakeuptime.GetHour(), dailywakeuptime.GetMinute(), dailywakeuptime.GetSecond()
++ );
++
++ if ((dailywakeuptime - idle) < now)
++ {
++ const CDateTimeSpan oneDay(1,0,0,0);
++ dailywakeuptime += oneDay;
++ }
++ }
++
++ const CDateTime retVal((dailywakeuptime < timerwakeuptime) ? dailywakeuptime : timerwakeuptime);
++ return retVal;
++}
+diff --git a/xbmc/pvr/timers/PVRTimers.h b/xbmc/pvr/timers/PVRTimers.h
+new file mode 100644
+index 0000000..0c3cd07
+--- /dev/null
++++ b/xbmc/pvr/timers/PVRTimers.h
+@@ -0,0 +1,198 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "PVRTimerInfoTag.h"
++#include "XBDateTime.h"
++#include "addons/include/xbmc_pvr_types.h"
++#include "utils/Observer.h"
++#include "threads/Thread.h"
++
++class CFileItem;
++namespace EPG
++{
++ class CEpgInfoTag;
++}
++
++namespace PVR
++{
++ class CGUIDialogPVRTimerSettings;
++
++ class CPVRTimers : public Observer,
++ public Observable
++ {
++ public:
++ CPVRTimers(void);
++ virtual ~CPVRTimers(void);
++
++ /**
++ * Load the timers from the clients.
++ * Returns the amount of timers that were added.
++ */
++ int Load();
++
++ /**
++ * Clear this timer list.
++ */
++ void Unload();
++
++ /**
++ * @brief refresh the channel list from the clients.
++ */
++ bool Update(void);
++
++ /**
++ * Update a timer entry in this container.
++ */
++ bool UpdateEntry(const CPVRTimerInfoTag &timer);
++ bool UpdateFromClient(const CPVRTimerInfoTag &timer) { return UpdateEntry(timer); }
++
++ /********** getters **********/
++
++ /**
++ * Get all known timers.
++ */
++ int GetTimers(CFileItemList* results);
++
++ /**
++ * The timer that will be active next.
++ * Returns false if there is none.
++ */
++ bool GetNextActiveTimer(CPVRTimerInfoTag *tag) const;
++
++ int GetActiveTimers(std::vector<CPVRTimerInfoTag *> *tags) const;
++
++ int GetActiveRecordings(std::vector<CPVRTimerInfoTag *> *tags) const;
++ /**
++ * The amount of timers in this container.
++ */
++ int GetNumTimers() const;
++
++ int GetNumActiveTimers(void) const;
++
++ int GetNumActiveRecordings(void) const;
++
++ CPVRTimerInfoTag *GetTimer(const CDateTime &start, int iTimer = -1) const;
++
++ /**
++ * Get the directory for a path.
++ */
++ bool GetDirectory(const CStdString& strPath, CFileItemList &items);
++
++ /********** channel methods **********/
++
++ /**
++ * Check if there are any active timers on a channel.
++ */
++ bool ChannelHasTimers(const CPVRChannel &channel);
++
++ /*!
++ * @brief Delete all timers on a channel.
++ * @param channel The channel to delete the timers for.
++ * @param bDeleteRepeating True to delete repeating events too, false otherwise.
++ * @param bCurrentlyActiveOnly True to delete timers that are currently running only.
++ * @return True if timers any were deleted, false otherwise.
++ */
++ bool DeleteTimersOnChannel(const CPVRChannel &channel, bool bDeleteRepeating = true, bool bCurrentlyActiveOnly = false);
++
++ /*!
++ * @brief Create a new instant timer on a channel.
++ * @param channel The channel to create the timer on.
++ * @param bStartTimer True to start the timer instantly, false otherwise.
++ * @return The new timer or NULL if it couldn't be created.
++ */
++ CPVRTimerInfoTag *InstantTimer(CPVRChannel *channel, bool bStartTimer = true);
++
++ /*!
++ * @return Next event time (timer or daily wake up)
++ */
++ CDateTime GetNextEventTime(void) const;
++
++ /********** static methods **********/
++
++ /**
++ * Add a timer to the client.
++ * True if it was sent correctly, false if not.
++ */
++ static bool AddTimer(const CFileItem &item);
++
++ /**
++ * Add a timer to the client.
++ * True if it was sent correctly, false if not.
++ */
++ static bool AddTimer(CPVRTimerInfoTag &item);
++
++ /**
++ * Delete a timer on the client.
++ * True if it was sent correctly, false if not.
++ */
++ static bool DeleteTimer(const CFileItem &item, bool bForce = false);
++
++ /**
++ * Delete a timer on the client.
++ * True if it was sent correctly, false if not.
++ */
++ static bool DeleteTimer(CPVRTimerInfoTag &item, bool bForce = false);
++
++ /**
++ * Rename a timer on the client.
++ * True if it was sent correctly, false if not.
++ */
++ static bool RenameTimer(CFileItem &item, const CStdString &strNewName);
++
++ /**
++ * Rename a timer on the client.
++ * True if it was sent correctly, false if not.
++ */
++ static bool RenameTimer(CPVRTimerInfoTag &item, const CStdString &strNewName);
++
++ /**
++ * Get updated timer information from the client.
++ * True if it was requested correctly, false if not.
++ */
++ static bool UpdateTimer(const CFileItem &item);
++
++ /**
++ * Get updated timer information from the client.
++ * True if it was requested correctly, false if not.
++ */
++ static bool UpdateTimer(CPVRTimerInfoTag &item);
++
++ bool IsRecording(void);
++ bool UpdateEntries(CPVRTimers *timers);
++ CPVRTimerInfoTag *GetByClient(int iClientId, int iClientTimerId);
++ CPVRTimerInfoTag *GetMatch(const EPG::CEpgInfoTag *Epg);
++ CPVRTimerInfoTag *GetMatch(const CFileItem *item);
++ virtual void Notify(const Observable &obs, const CStdString& msg);
++ bool IsRecordingOnChannel(const CPVRChannel &channel) const;
++
++ private:
++ /*!
++ * @brief Add timers to this container.
++ * @return The amount of timers that were added.
++ */
++ int LoadFromClients(void);
++
++ CCriticalSection m_critSection;
++ bool m_bIsUpdating;
++ std::map<CDateTime, std::vector<CPVRTimerInfoTag *>* > m_tags;
++ };
++}
+diff --git a/xbmc/pvr/windows/GUIViewStatePVR.cpp b/xbmc/pvr/windows/GUIViewStatePVR.cpp
+new file mode 100644
+index 0000000..fc7c9dc
+--- /dev/null
++++ b/xbmc/pvr/windows/GUIViewStatePVR.cpp
+@@ -0,0 +1,69 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "GUIViewStatePVR.h"
++#include "GUIWindowPVR.h"
++#include "GUIWindowPVRCommon.h"
++#include "guilib/GUIWindowManager.h"
++#include "settings/GUISettings.h"
++#include "settings/Settings.h"
++
++using namespace PVR;
++
++CGUIViewStatePVR::CGUIViewStatePVR(const CFileItemList& items) :
++ CGUIViewState(items)
++{
++ PVRWindow ActiveView = GetActiveView();
++ if (ActiveView == PVR_WINDOW_RECORDINGS)
++ {
++ if (g_guiSettings.GetBool("filelists.ignorethewhensorting"))
++ AddSortMethod(SORT_METHOD_LABEL_IGNORE_THE, 551, LABEL_MASKS("%L", "%I", "%L", "")); // FileName, Size | Foldername, e
++ else
++ AddSortMethod(SORT_METHOD_LABEL, 551, LABEL_MASKS("%L", "%I", "%L", "")); // FileName, Size | Foldername, empty
++ AddSortMethod(SORT_METHOD_SIZE, 553, LABEL_MASKS("%L", "%I", "%L", "%I")); // FileName, Size | Foldername, Size
++ AddSortMethod(SORT_METHOD_DATE, 552, LABEL_MASKS("%L", "%J", "%L", "%J")); // FileName, Date | Foldername, Date
++ AddSortMethod(SORT_METHOD_FILE, 561, LABEL_MASKS("%L", "%I", "%L", "")); // Filename, Size | FolderName, empty
++ }
++
++ LoadViewState(items.GetPath(), ActiveView == PVR_WINDOW_UNKNOWN ? WINDOW_PVR : WINDOW_PVR + 100 - ActiveView );
++}
++
++PVRWindow CGUIViewStatePVR::GetActiveView()
++{
++ PVRWindow returnWindow = PVR_WINDOW_UNKNOWN;
++
++ int iActiveWindow = g_windowManager.GetActiveWindow();
++ if (iActiveWindow == WINDOW_PVR)
++ {
++ CGUIWindowPVR *pWindow = (CGUIWindowPVR *) g_windowManager.GetWindow(WINDOW_PVR);
++ CGUIWindowPVRCommon *pActiveView = NULL;
++ if (pWindow && (pActiveView = pWindow->GetActiveView()) != NULL)
++ returnWindow = pActiveView->GetWindowId();
++ }
++
++ return returnWindow;
++}
++
++void CGUIViewStatePVR::SaveViewState(void)
++{
++ PVRWindow ActiveView = GetActiveView();
++ SaveViewToDb(m_items.GetPath(), ActiveView == PVR_WINDOW_UNKNOWN ? WINDOW_PVR : WINDOW_PVR + 100 - ActiveView, NULL);
++}
+\ No newline at end of file
+diff --git a/xbmc/pvr/windows/GUIViewStatePVR.h b/xbmc/pvr/windows/GUIViewStatePVR.h
+new file mode 100644
+index 0000000..d0f1c0c
+--- /dev/null
++++ b/xbmc/pvr/windows/GUIViewStatePVR.h
+@@ -0,0 +1,40 @@
++#pragma once
++
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "GUIViewState.h"
++#include "GUIWindowPVRCommon.h"
++
++namespace PVR
++{
++ class CGUIViewStatePVR : public CGUIViewState
++ {
++ public:
++ CGUIViewStatePVR(const CFileItemList& items);
++ virtual ~CGUIViewStatePVR(void) {}
++ virtual PVRWindow GetActiveView(void);
++ protected:
++ virtual bool AutoPlayNextItem(void) { return false; };
++ virtual bool HideParentDirItems(void) { return true; }
++ virtual void SaveViewState(void);
++ };
++}
+diff --git a/xbmc/pvr/windows/GUIWindowPVR.cpp b/xbmc/pvr/windows/GUIWindowPVR.cpp
+new file mode 100644
+index 0000000..b6e1cd5
+--- /dev/null
++++ b/xbmc/pvr/windows/GUIWindowPVR.cpp
+@@ -0,0 +1,288 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "GUIWindowPVR.h"
++
++#include "GUIWindowPVRChannels.h"
++#include "GUIWindowPVRGuide.h"
++#include "GUIWindowPVRRecordings.h"
++#include "GUIWindowPVRSearch.h"
++#include "GUIWindowPVRTimers.h"
++
++#include "pvr/PVRManager.h"
++#include "pvr/addons/PVRClients.h"
++#include "guilib/GUIMessage.h"
++#include "guilib/GUIWindowManager.h"
++#include "dialogs/GUIDialogBusy.h"
++#include "dialogs/GUIDialogKaiToast.h"
++#include "threads/SingleLock.h"
++
++using namespace PVR;
++
++CGUIWindowPVR::CGUIWindowPVR(void) :
++ CGUIMediaWindow(WINDOW_PVR, "MyPVR.xml")
++{
++ m_guideGrid = NULL;
++ m_bViewsCreated = false;
++ m_currentSubwindow = NULL;
++ m_savedSubwindow = NULL;
++}
++
++CGUIWindowPVR::~CGUIWindowPVR(void)
++{
++ Cleanup();
++}
++
++CGUIWindowPVRCommon *CGUIWindowPVR::GetActiveView(void) const
++{
++ CSingleLock lock(m_critSection);
++ if (!m_bViewsCreated)
++ return NULL;
++
++ return m_currentSubwindow;
++}
++
++void CGUIWindowPVR::SetActiveView(CGUIWindowPVRCommon *window)
++{
++ CSingleLock lock(m_critSection);
++ if (!m_bViewsCreated)
++ return;
++
++ m_currentSubwindow = window;
++}
++
++void CGUIWindowPVR::GetContextButtons(int itemNumber, CContextButtons &buttons)
++{
++ CGUIWindowPVRCommon *view = GetActiveView();
++ if (view)
++ view->GetContextButtons(itemNumber, buttons);
++
++ CGUIMediaWindow::GetContextButtons(itemNumber, buttons);
++}
++
++CGUIWindowPVRCommon *CGUIWindowPVR::GetSavedView(void) const
++{
++ CSingleLock lock(m_critSection);
++ if (!m_bViewsCreated)
++ return NULL;
++
++ return m_savedSubwindow;
++}
++
++bool CGUIWindowPVR::OnAction(const CAction &action)
++{
++ CGUIWindowPVRCommon *view = GetActiveView();
++ return (view && view->OnAction(action)) ||
++ CGUIMediaWindow::OnAction(action);
++}
++
++bool CGUIWindowPVR::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
++{
++ CGUIWindowPVRCommon *view = GetActiveView();
++ return (view && view->OnContextButton(itemNumber, button)) ||
++ CGUIMediaWindow::OnContextButton(itemNumber, button);
++}
++
++void CGUIWindowPVR::OnInitWindow(void)
++{
++ if (!g_PVRManager.IsStarted() || !g_PVRClients->HasConnectedClients())
++ {
++ g_windowManager.PreviousWindow();
++ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Warning,
++ g_localizeStrings.Get(19045),
++ g_localizeStrings.Get(19044));
++ return;
++ }
++
++ CreateViews();
++
++ CSingleLock graphicsLock(g_graphicsContext);
++ SET_CONTROL_VISIBLE(CONTROL_LIST_TIMELINE);
++
++ CSingleLock lock(m_critSection);
++ if (m_savedSubwindow)
++ m_savedSubwindow->OnInitWindow();
++ lock.Leave();
++ graphicsLock.Leave();
++
++ CGUIMediaWindow::OnInitWindow();
++}
++
++bool CGUIWindowPVR::OnMessage(CGUIMessage& message)
++{
++ return (OnMessageFocus(message) ||OnMessageClick(message) ||
++ CGUIMediaWindow::OnMessage(message));
++}
++
++void CGUIWindowPVR::OnWindowLoaded(void)
++{
++ CreateViews();
++
++ CGUIMediaWindow::OnWindowLoaded();
++ m_viewControl.Reset();
++ m_viewControl.SetParentWindow(GetID());
++
++ m_viewControl.AddView(GetControl(CONTROL_LIST_CHANNELS_TV));
++ m_viewControl.AddView(GetControl(CONTROL_LIST_CHANNELS_RADIO));
++ m_viewControl.AddView(GetControl(CONTROL_LIST_RECORDINGS));
++ m_viewControl.AddView(GetControl(CONTROL_LIST_TIMERS));
++ m_viewControl.AddView(GetControl(CONTROL_LIST_GUIDE_CHANNEL));
++ m_viewControl.AddView(GetControl(CONTROL_LIST_GUIDE_NOW_NEXT));
++ m_viewControl.AddView(GetControl(CONTROL_LIST_TIMELINE));
++ m_viewControl.AddView(GetControl(CONTROL_LIST_SEARCH));
++}
++
++void CGUIWindowPVR::OnWindowUnload(void)
++{
++ CGUIWindowPVRCommon *view = GetActiveView();
++ if (view)
++ {
++ view->OnWindowUnload();
++ m_savedSubwindow = view;
++ }
++ else
++ {
++ m_savedSubwindow = NULL;
++ }
++
++ m_currentSubwindow = NULL;
++
++ m_viewControl.Reset();
++ CGUIMediaWindow::OnWindowUnload();
++}
++
++void CGUIWindowPVR::SetLabel(int iControl, const CStdString &strLabel)
++{
++ SET_CONTROL_LABEL(iControl, strLabel);
++}
++
++void CGUIWindowPVR::SetLabel(int iControl, int iLabel)
++{
++ SET_CONTROL_LABEL(iControl, iLabel);
++}
++
++void CGUIWindowPVR::UpdateButtons(void)
++{
++ m_windowGuide->UpdateButtons();
++}
++
++bool CGUIWindowPVR::OnMessageFocus(CGUIMessage &message)
++{
++ bool bReturn = false;
++
++ if (message.GetMessage() == GUI_MSG_FOCUSED)
++ {
++ m_windowChannelsRadio->OnMessageFocus(message) ||
++ m_windowChannelsTV->OnMessageFocus(message) ||
++ m_windowGuide->OnMessageFocus(message) ||
++ m_windowRecordings->OnMessageFocus(message) ||
++ m_windowSearch->OnMessageFocus(message) ||
++ m_windowTimers->OnMessageFocus(message);
++
++ m_savedSubwindow = NULL;
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVR::OnMessageClick(CGUIMessage &message)
++{
++ bool bReturn = false;
++
++ if (message.GetMessage() == GUI_MSG_CLICKED)
++ {
++ bReturn = m_windowChannelsRadio->OnClickButton(message) ||
++ m_windowChannelsTV->OnClickButton(message) ||
++ m_windowGuide->OnClickButton(message) ||
++ m_windowRecordings->OnClickButton(message) ||
++ m_windowSearch->OnClickButton(message) ||
++ m_windowTimers->OnClickButton(message) ||
++
++ m_windowChannelsRadio->OnClickList(message) ||
++ m_windowChannelsTV->OnClickList(message) ||
++ m_windowGuide->OnClickList(message) ||
++ m_windowRecordings->OnClickList(message) ||
++ m_windowSearch->OnClickList(message) ||
++ m_windowTimers->OnClickList(message);
++ }
++
++ return bReturn;
++}
++
++void CGUIWindowPVR::CreateViews(void)
++{
++ CSingleLock lock(m_critSection);
++ if (!m_bViewsCreated)
++ {
++ m_bViewsCreated = true;
++
++ m_windowChannelsRadio = new CGUIWindowPVRChannels(this, true);
++ m_windowChannelsTV = new CGUIWindowPVRChannels(this, false);
++ m_windowGuide = new CGUIWindowPVRGuide(this);
++ m_windowRecordings = new CGUIWindowPVRRecordings(this);
++ m_windowSearch = new CGUIWindowPVRSearch(this);
++ m_windowTimers = new CGUIWindowPVRTimers(this);
++ }
++}
++
++void CGUIWindowPVR::Reset(void)
++{
++ CSingleLock graphicsLock(g_graphicsContext);
++ CSingleLock lock(m_critSection);
++
++ Cleanup();
++ CreateViews();
++
++ m_windowChannelsRadio->ResetObservers();
++ m_windowChannelsTV->ResetObservers();
++ m_windowGuide->ResetObservers();
++ m_windowRecordings->ResetObservers();
++ m_windowTimers->ResetObservers();
++
++ m_currentSubwindow = NULL;
++ m_savedSubwindow = NULL;
++ ClearFileItems();
++ FreeResources();
++}
++
++void CGUIWindowPVR::Cleanup(void)
++{
++ if (m_bViewsCreated)
++ {
++ m_windowChannelsRadio->UnregisterObservers();
++ delete m_windowChannelsRadio;
++
++ m_windowChannelsTV->UnregisterObservers();
++ delete m_windowChannelsTV;
++
++ m_windowGuide->UnregisterObservers();
++ delete m_windowGuide;
++
++ m_windowRecordings->UnregisterObservers();
++ delete m_windowRecordings;
++
++ delete m_windowSearch;
++
++ m_windowTimers->UnregisterObservers();
++ delete m_windowTimers;
++ m_bViewsCreated = false;
++ }
++}
+diff --git a/xbmc/pvr/windows/GUIWindowPVR.h b/xbmc/pvr/windows/GUIWindowPVR.h
+new file mode 100644
+index 0000000..6a30e3d
+--- /dev/null
++++ b/xbmc/pvr/windows/GUIWindowPVR.h
+@@ -0,0 +1,89 @@
++#pragma once
++
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "GUIWindowPVRCommon.h"
++#include "epg/GUIEPGGridContainer.h"
++#include "threads/CriticalSection.h"
++
++namespace PVR
++{
++ class CGUIWindowPVRCommon;
++ class CGUIWindowPVRChannels;
++ class CGUIWindowPVRGuide;
++ class CGUIWindowPVRRecordings;
++ class CGUIWindowPVRSearch;
++ class CGUIWindowPVRTimers;
++
++ class CGUIWindowPVR : public CGUIMediaWindow
++ {
++ friend class CGUIWindowPVRCommon;
++ friend class CGUIWindowPVRChannels;
++ friend class CGUIWindowPVRGuide;
++ friend class CGUIWindowPVRRecordings;
++ friend class CGUIWindowPVRSearch;
++ friend class CGUIWindowPVRTimers;
++
++ public:
++ CGUIWindowPVR(void);
++ virtual ~CGUIWindowPVR(void);
++
++ virtual CGUIWindowPVRCommon *GetActiveView(void) const;
++ virtual void SetActiveView(CGUIWindowPVRCommon *window);
++ virtual void GetContextButtons(int itemNumber, CContextButtons &buttons);
++ virtual CGUIWindowPVRCommon *GetSavedView(void) const;
++ virtual bool OnAction(const CAction &action);
++ virtual bool OnContextButton(int itemNumber, CONTEXT_BUTTON button);
++ virtual void OnInitWindow(void);
++ virtual bool OnMessage(CGUIMessage& message);
++ virtual void OnWindowLoaded(void);
++ virtual void OnWindowUnload(void);
++ virtual void Reset(void);
++ virtual void Cleanup(void);
++
++ EPG::CGUIEPGGridContainer *m_guideGrid;
++
++ protected:
++ virtual void SetLabel(int iControl, const CStdString &strLabel);
++ virtual void SetLabel(int iControl, int iLabel);
++ virtual void UpdateButtons(void);
++
++ private:
++ virtual bool OnMessageFocus(CGUIMessage &message);
++ virtual bool OnMessageClick(CGUIMessage &message);
++
++ virtual void CreateViews(void);
++
++ CGUIWindowPVRCommon * m_currentSubwindow;
++ CGUIWindowPVRCommon * m_savedSubwindow;
++
++ CGUIWindowPVRChannels * m_windowChannelsTV;
++ CGUIWindowPVRChannels * m_windowChannelsRadio;
++ CGUIWindowPVRGuide * m_windowGuide;
++ CGUIWindowPVRRecordings *m_windowRecordings;
++ CGUIWindowPVRSearch * m_windowSearch;
++ CGUIWindowPVRTimers * m_windowTimers;
++
++ bool m_bViewsCreated;
++ CCriticalSection m_critSection;
++ };
++}
+diff --git a/xbmc/pvr/windows/GUIWindowPVRChannels.cpp b/xbmc/pvr/windows/GUIWindowPVRChannels.cpp
+new file mode 100644
+index 0000000..1e0ed97
+--- /dev/null
++++ b/xbmc/pvr/windows/GUIWindowPVRChannels.cpp
+@@ -0,0 +1,581 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "GUIWindowPVRChannels.h"
++
++#include "dialogs/GUIDialogFileBrowser.h"
++#include "dialogs/GUIDialogNumeric.h"
++#include "dialogs/GUIDialogKaiToast.h"
++#include "dialogs/GUIDialogOK.h"
++#include "dialogs/GUIDialogYesNo.h"
++#include "dialogs/GUIDialogKeyboard.h"
++#include "guilib/GUIWindowManager.h"
++#include "GUIInfoManager.h"
++#include "pvr/PVRManager.h"
++#include "pvr/channels/PVRChannelGroupsContainer.h"
++#include "pvr/dialogs/GUIDialogPVRGroupManager.h"
++#include "pvr/windows/GUIWindowPVR.h"
++#include "pvr/addons/PVRClients.h"
++#include "pvr/timers/PVRTimers.h"
++#include "epg/EpgContainer.h"
++#include "settings/GUISettings.h"
++#include "settings/Settings.h"
++#include "storage/MediaManager.h"
++#include "utils/log.h"
++#include "threads/SingleLock.h"
++
++using namespace PVR;
++using namespace EPG;
++
++CGUIWindowPVRChannels::CGUIWindowPVRChannels(CGUIWindowPVR *parent, bool bRadio) :
++ CGUIWindowPVRCommon(parent,
++ bRadio ? PVR_WINDOW_CHANNELS_RADIO : PVR_WINDOW_CHANNELS_TV,
++ bRadio ? CONTROL_BTNCHANNELS_RADIO : CONTROL_BTNCHANNELS_TV,
++ bRadio ? CONTROL_LIST_CHANNELS_RADIO: CONTROL_LIST_CHANNELS_TV),
++ CThread("PVR Channel Window")
++{
++ m_bRadio = bRadio;
++ m_selectedGroup = NULL;
++ m_bShowHiddenChannels = false;
++ m_bThreadCreated = false;
++}
++
++CGUIWindowPVRChannels::~CGUIWindowPVRChannels(void)
++{
++ if (m_bThreadCreated)
++ StopThread(true);
++}
++
++void CGUIWindowPVRChannels::ResetObservers(void)
++{
++ CSingleLock lock(m_critSection);
++ g_EpgContainer.RegisterObserver(this);
++ g_PVRTimers->RegisterObserver(this);
++ g_infoManager.RegisterObserver(this);
++}
++
++void CGUIWindowPVRChannels::UnregisterObservers(void)
++{
++ CSingleLock lock(m_critSection);
++ g_EpgContainer.UnregisterObserver(this);
++ if (g_PVRTimers)
++ g_PVRTimers->UnregisterObserver(this);
++ g_infoManager.UnregisterObserver(this);
++}
++
++void CGUIWindowPVRChannels::GetContextButtons(int itemNumber, CContextButtons &buttons) const
++{
++ if (itemNumber < 0 || itemNumber >= m_parent->m_vecItems->Size())
++ return;
++ CFileItemPtr pItem = m_parent->m_vecItems->Get(itemNumber);
++ CPVRChannel *channel = pItem->GetPVRChannelInfoTag();
++
++ if (pItem->GetPath() == "pvr://channels/.add.channel")
++ {
++ /* If yes show only "New Channel" on context menu */
++ buttons.Add(CONTEXT_BUTTON_ADD, 19046); /* add new channel */
++ }
++ else
++ {
++ buttons.Add(CONTEXT_BUTTON_INFO, 19047); /* channel info */
++ buttons.Add(CONTEXT_BUTTON_FIND, 19003); /* find similar program */
++ buttons.Add(CONTEXT_BUTTON_PLAY_ITEM, 19000); /* switch to channel */
++ buttons.Add(CONTEXT_BUTTON_RECORD_ITEM, channel->IsRecording() ? 19256 : 19255); /* start/stop recording on channel */
++ buttons.Add(CONTEXT_BUTTON_SET_THUMB, 20019); /* change icon */
++ buttons.Add(CONTEXT_BUTTON_GROUP_MANAGER, 19048); /* group manager */
++ buttons.Add(CONTEXT_BUTTON_HIDE, m_bShowHiddenChannels ? 19049 : 19054); /* show/hide channel */
++
++ if (m_parent->m_vecItems->Size() > 1 && !m_bShowHiddenChannels)
++ buttons.Add(CONTEXT_BUTTON_MOVE, 116); /* move channel up or down */
++
++ if (m_bShowHiddenChannels || g_PVRChannelGroups->GetGroupAllTV()->GetNumHiddenChannels() > 0)
++ buttons.Add(CONTEXT_BUTTON_SHOW_HIDDEN, m_bShowHiddenChannels ? 19050 : 19051); /* show hidden/visible channels */
++
++ if (g_PVRClients->HasMenuHooks(pItem->GetPVRChannelInfoTag()->ClientID()))
++ buttons.Add(CONTEXT_BUTTON_MENU_HOOKS, 19195); /* PVR client specific action */
++
++ buttons.Add(CONTEXT_BUTTON_FILTER, 19249); /* filter channels */
++ buttons.Add(CONTEXT_BUTTON_UPDATE_EPG, 19251); /* update EPG information */
++ }
++}
++
++bool CGUIWindowPVRChannels::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
++{
++ if (itemNumber < 0 || itemNumber >= (int) m_parent->m_vecItems->Size())
++ return false;
++ CFileItemPtr pItem = m_parent->m_vecItems->Get(itemNumber);
++
++ return OnContextButtonPlay(pItem.get(), button) ||
++ OnContextButtonMove(pItem.get(), button) ||
++ OnContextButtonHide(pItem.get(), button) ||
++ OnContextButtonShowHidden(pItem.get(), button) ||
++ OnContextButtonSetThumb(pItem.get(), button) ||
++ OnContextButtonAdd(pItem.get(), button) ||
++ OnContextButtonInfo(pItem.get(), button) ||
++ OnContextButtonGroupManager(pItem.get(), button) ||
++ OnContextButtonFilter(pItem.get(), button) ||
++ OnContextButtonUpdateEpg(pItem.get(), button) ||
++ OnContextButtonRecord(pItem.get(), button) ||
++ CGUIWindowPVRCommon::OnContextButton(itemNumber, button);
++}
++
++const CPVRChannelGroup *CGUIWindowPVRChannels::SelectedGroup(void)
++{
++ if (!m_selectedGroup)
++ SetSelectedGroup(g_PVRManager.GetPlayingGroup(m_bRadio));
++
++ return m_selectedGroup;
++}
++
++void CGUIWindowPVRChannels::SetSelectedGroup(CPVRChannelGroup *group)
++{
++ if (!group)
++ return;
++
++ if (m_selectedGroup)
++ m_selectedGroup->UnregisterObserver(this);
++ m_selectedGroup = group;
++ m_selectedGroup->RegisterObserver(this);
++ g_PVRManager.SetPlayingGroup(m_selectedGroup);
++}
++
++void CGUIWindowPVRChannels::Notify(const Observable &obs, const CStdString& msg)
++{
++ if (msg.Equals("channelgroup") || msg.Equals("timers-reset") || msg.Equals("timers") || msg.Equals("epg-current-event") || msg.Equals("current-item"))
++ {
++ if (IsVisible())
++ SetInvalid();
++ else
++ m_bUpdateRequired = true;
++ }
++ else if (msg.Equals("channelgroup-reset"))
++ {
++ if (IsVisible())
++ UpdateData();
++ else
++ m_bUpdateRequired = true;
++ }
++}
++
++CPVRChannelGroup *CGUIWindowPVRChannels::SelectNextGroup(void)
++{
++ const CPVRChannelGroup *currentGroup = SelectedGroup();
++ CPVRChannelGroup *nextGroup = currentGroup->GetNextGroup();
++ while (nextGroup && *nextGroup != *currentGroup && nextGroup->Size() == 0)
++ nextGroup = nextGroup->GetNextGroup();
++
++ /* always update so users can reset the list */
++ if (nextGroup)
++ {
++ SetSelectedGroup(nextGroup);
++ UpdateData();
++ }
++
++ return m_selectedGroup;
++}
++
++void CGUIWindowPVRChannels::UpdateData(void)
++{
++ CSingleLock lock(m_critSection);
++ CLog::Log(LOGDEBUG, "CGUIWindowPVRChannels - %s - update window '%s'. set view to %d",
++ __FUNCTION__, GetName(), m_iControlList);
++ m_bUpdateRequired = false;
++
++ g_EpgContainer.RegisterObserver(this);
++ g_PVRTimers->RegisterObserver(this);
++
++ /* lock the graphics context while updating */
++ CSingleLock graphicsLock(g_graphicsContext);
++
++ m_iSelected = m_parent->m_viewControl.GetSelectedItem();
++ m_parent->m_viewControl.Clear();
++ m_parent->m_vecItems->Clear();
++ m_parent->m_viewControl.SetCurrentView(m_iControlList);
++
++ const CPVRChannelGroup *currentGroup = g_PVRManager.GetPlayingGroup(m_bRadio);
++ if (!currentGroup)
++ return;
++
++ CStdString strPath;
++ strPath.Format("pvr://channels/%s/%s/",
++ m_bRadio ? "radio" : "tv",
++ m_bShowHiddenChannels ? ".hidden" : currentGroup->GroupName());
++
++ m_parent->m_vecItems->SetPath(strPath);
++ m_parent->Update(m_parent->m_vecItems->GetPath());
++ m_parent->m_viewControl.SetItems(*m_parent->m_vecItems);
++ if (!SelectPlayingFile())
++ m_parent->m_viewControl.SetSelectedItem(m_iSelected);
++
++ /* empty list */
++ if (m_parent->m_vecItems->Size() == 0)
++ {
++ if (m_bShowHiddenChannels)
++ {
++ /* show the visible channels instead */
++ m_bShowHiddenChannels = false;
++ graphicsLock.Leave();
++ lock.Leave();
++
++ UpdateData();
++ return;
++ }
++ else if (currentGroup->GroupID() > 0)
++ {
++ if (*currentGroup != *SelectNextGroup())
++ return;
++ }
++ }
++
++ m_parent->SetLabel(CONTROL_LABELHEADER, g_localizeStrings.Get(m_bRadio ? 19024 : 19023));
++ if (m_bShowHiddenChannels)
++ m_parent->SetLabel(CONTROL_LABELGROUP, g_localizeStrings.Get(19022));
++ else
++ m_parent->SetLabel(CONTROL_LABELGROUP, currentGroup->GroupName());
++
++ if (!m_bThreadCreated)
++ {
++ m_bThreadCreated = true;
++ Create();
++ SetPriority(-1);
++ }
++}
++
++bool CGUIWindowPVRChannels::OnClickButton(CGUIMessage &message)
++{
++ bool bReturn = false;
++
++ if (IsSelectedButton(message))
++ {
++ bReturn = true;
++ SelectNextGroup();
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRChannels::OnClickList(CGUIMessage &message)
++{
++ bool bReturn = false;
++
++ if (IsSelectedList(message))
++ {
++ bReturn = true;
++ int iAction = message.GetParam1();
++ int iItem = m_parent->m_viewControl.GetSelectedItem();
++
++ if (iItem < 0 || iItem >= (int) m_parent->m_vecItems->Size())
++ return bReturn;
++ CFileItemPtr pItem = m_parent->m_vecItems->Get(iItem);
++
++ /* process actions */
++ if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK || iAction == ACTION_PLAY)
++ ActionPlayChannel(pItem.get());
++ else if (iAction == ACTION_SHOW_INFO)
++ ShowEPGInfo(pItem.get());
++ else if (iAction == ACTION_DELETE_ITEM)
++ ActionDeleteChannel(pItem.get());
++ else if (iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK)
++ m_parent->OnPopupMenu(iItem);
++ else
++ bReturn = false;
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRChannels::OnContextButtonAdd(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_ADD)
++ {
++ CGUIDialogOK::ShowAndGetInput(19033,0,19038,0);
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRChannels::OnContextButtonGroupManager(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_GROUP_MANAGER)
++ {
++ ShowGroupManager();
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRChannels::OnContextButtonHide(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_HIDE)
++ {
++ CPVRChannel *channel = item->GetPVRChannelInfoTag();
++ if (!channel || channel->IsRadio() != m_bRadio)
++ return bReturn;
++
++ CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
++ if (!pDialog)
++ return bReturn;
++
++ pDialog->SetHeading(19039);
++ pDialog->SetLine(0, "");
++ pDialog->SetLine(1, channel->ChannelName());
++ pDialog->SetLine(2, "");
++ pDialog->DoModal();
++
++ if (!pDialog->IsConfirmed())
++ return bReturn;
++
++ g_PVRManager.GetPlayingGroup(m_bRadio)->RemoveFromGroup(*channel);
++ UpdateData();
++
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRChannels::OnContextButtonInfo(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_INFO)
++ {
++ ShowEPGInfo(item);
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRChannels::OnContextButtonMove(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_MOVE)
++ {
++ CPVRChannel *channel = item->GetPVRChannelInfoTag();
++ if (!channel || channel->IsRadio() != m_bRadio)
++ return bReturn;
++
++ CStdString strIndex;
++ strIndex.Format("%i", channel->ChannelNumber());
++ CGUIDialogNumeric::ShowAndGetNumber(strIndex, g_localizeStrings.Get(19052));
++ int newIndex = atoi(strIndex.c_str());
++
++ if (newIndex != channel->ChannelNumber())
++ {
++ g_PVRManager.GetPlayingGroup()->MoveChannel(channel->ChannelNumber(), newIndex);
++ UpdateData();
++ }
++
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRChannels::OnContextButtonPlay(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_PLAY_ITEM)
++ {
++ /* play channel */
++ bReturn = PlayFile(item, g_guiSettings.GetBool("pvrplayback.playminimized"));
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRChannels::OnContextButtonSetThumb(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_SET_THUMB)
++ {
++ if (g_settings.GetCurrentProfile().canWriteSources() && !g_passwordManager.IsProfileLockUnlocked())
++ return bReturn;
++ else if (!g_passwordManager.IsMasterLockUnlocked(true))
++ return bReturn;
++
++ /* setup our thumb list */
++ CFileItemList items;
++ CPVRChannel *channel = item->GetPVRChannelInfoTag();
++
++ if (!channel->IconPath().IsEmpty())
++ {
++ /* add the current thumb, if available */
++ CFileItemPtr current(new CFileItem("thumb://Current", false));
++ current->SetThumbnailImage(channel->IconPath());
++ current->SetLabel(g_localizeStrings.Get(20016));
++ items.Add(current);
++ }
++ else if (item->HasThumbnail())
++ {
++ /* already have a thumb that the share doesn't know about - must be a local one, so we may as well reuse it */
++ CFileItemPtr current(new CFileItem("thumb://Current", false));
++ current->SetThumbnailImage(item->GetThumbnailImage());
++ current->SetLabel(g_localizeStrings.Get(20016));
++ items.Add(current);
++ }
++
++ /* and add a "no thumb" entry as well */
++ CFileItemPtr nothumb(new CFileItem("thumb://None", false));
++ nothumb->SetIconImage(item->GetIconImage());
++ nothumb->SetLabel(g_localizeStrings.Get(20018));
++ items.Add(nothumb);
++
++ CStdString strThumb;
++ VECSOURCES shares;
++ if (g_guiSettings.GetString("pvrmenu.iconpath") != "")
++ {
++ CMediaSource share1;
++ share1.strPath = g_guiSettings.GetString("pvrmenu.iconpath");
++ share1.strName = g_localizeStrings.Get(19018);
++ shares.push_back(share1);
++ }
++ g_mediaManager.GetLocalDrives(shares);
++ if (!CGUIDialogFileBrowser::ShowAndGetImage(items, shares, g_localizeStrings.Get(1030), strThumb))
++ return bReturn;
++
++ if (strThumb != "thumb://Current")
++ {
++ if (strThumb == "thumb://None")
++ strThumb = "";
++
++ channel->SetIconPath(strThumb, true);
++ UpdateData();
++ }
++
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRChannels::OnContextButtonShowHidden(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_SHOW_HIDDEN)
++ {
++ m_bShowHiddenChannels = !m_bShowHiddenChannels;
++ UpdateData();
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRChannels::OnContextButtonFilter(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_FILTER)
++ {
++ CStdString filter = m_parent->GetProperty("filter").asString();
++ CGUIDialogKeyboard::ShowAndGetFilter(filter, false);
++ m_parent->OnFilterItems(filter);
++
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRChannels::OnContextButtonRecord(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn(false);
++ CPVRChannel *channel = item->GetPVRChannelInfoTag();
++
++ if (channel)
++ return g_PVRManager.ToggleRecordingOnChannel(channel->ChannelID());
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRChannels::OnContextButtonUpdateEpg(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_UPDATE_EPG)
++ {
++ CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
++ if (!pDialog)
++ return bReturn;
++
++ CPVRChannel *channel = item->GetPVRChannelInfoTag();
++ pDialog->SetHeading(19251);
++ pDialog->SetLine(0, g_localizeStrings.Get(19252));
++ pDialog->SetLine(1, channel->ChannelName());
++ pDialog->SetLine(2, "");
++ pDialog->DoModal();
++
++ if (!pDialog->IsConfirmed())
++ return bReturn;
++
++ bReturn = UpdateEpgForChannel(item);
++
++ CStdString strMessage;
++ strMessage.Format("%s: '%s'", g_localizeStrings.Get(bReturn ? 19253 : 19254), channel->ChannelName());
++ CGUIDialogKaiToast::QueueNotification(bReturn ? CGUIDialogKaiToast::Info : CGUIDialogKaiToast::Error,
++ g_localizeStrings.Get(19166),
++ strMessage);
++ }
++
++ return bReturn;
++}
++
++void CGUIWindowPVRChannels::ShowGroupManager(void)
++{
++ /* Load group manager dialog */
++ CGUIDialogPVRGroupManager* pDlgInfo = (CGUIDialogPVRGroupManager*)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_GROUP_MANAGER);
++ if (!pDlgInfo)
++ return;
++
++ pDlgInfo->SetRadio(m_bRadio);
++ pDlgInfo->DoModal();
++
++ return;
++}
++
++void CGUIWindowPVRChannels::Process(void)
++{
++ // ugly hack to refresh the progress bars and item contents every 5 seconds
++ int iCount(0);
++ while (!m_bStop)
++ {
++ if (++iCount == 100)
++ {
++ iCount = 0;
++ SetInvalid();
++ }
++ Sleep(50);
++ }
++}
+diff --git a/xbmc/pvr/windows/GUIWindowPVRChannels.h b/xbmc/pvr/windows/GUIWindowPVRChannels.h
+new file mode 100644
+index 0000000..c496e0e
+--- /dev/null
++++ b/xbmc/pvr/windows/GUIWindowPVRChannels.h
+@@ -0,0 +1,75 @@
++#pragma once
++
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "GUIWindowPVRCommon.h"
++#include "utils/Observer.h"
++#include "threads/Thread.h"
++
++namespace PVR
++{
++ class CPVRChannelGroup;
++ class CGUIWindowPVR;
++
++ class CGUIWindowPVRChannels : public CGUIWindowPVRCommon, private Observer, private CThread
++ {
++ friend class CGUIWindowPVR;
++
++ public:
++ CGUIWindowPVRChannels(CGUIWindowPVR *parent, bool bRadio);
++ virtual ~CGUIWindowPVRChannels(void);
++
++ virtual void GetContextButtons(int itemNumber, CContextButtons &buttons) const;
++ virtual bool OnContextButton(int itemNumber, CONTEXT_BUTTON button);
++ virtual const CPVRChannelGroup *SelectedGroup(void);
++ virtual void SetSelectedGroup(CPVRChannelGroup *group);
++ virtual CPVRChannelGroup *SelectNextGroup(void);
++ virtual void UpdateData(void);
++ virtual void Notify(const Observable &obs, const CStdString& msg);
++ virtual void ResetObservers(void);
++ virtual void UnregisterObservers(void);
++
++ private:
++ virtual void Process(void);
++ virtual bool OnClickButton(CGUIMessage &message);
++ virtual bool OnClickList(CGUIMessage &message);
++
++ virtual bool OnContextButtonAdd(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonGroupManager(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonHide(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonInfo(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonMove(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonPlay(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonSetThumb(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonShowHidden(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonFilter(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonUpdateEpg(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonRecord(CFileItem *item, CONTEXT_BUTTON button);
++
++ virtual void ShowGroupManager(void);
++
++ CPVRChannelGroup *m_selectedGroup;
++ bool m_bShowHiddenChannels;
++ bool m_bRadio;
++ bool m_bThreadCreated;
++ };
++}
+diff --git a/xbmc/pvr/windows/GUIWindowPVRCommon.cpp b/xbmc/pvr/windows/GUIWindowPVRCommon.cpp
+new file mode 100644
+index 0000000..a0b5968
+--- /dev/null
++++ b/xbmc/pvr/windows/GUIWindowPVRCommon.cpp
+@@ -0,0 +1,826 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "GUIWindowPVRCommon.h"
++
++#include "Application.h"
++#include "dialogs/GUIDialogOK.h"
++#include "dialogs/GUIDialogYesNo.h"
++#include "filesystem/StackDirectory.h"
++#include "guilib/GUIMessage.h"
++#include "guilib/GUIWindowManager.h"
++#include "pvr/PVRManager.h"
++#include "pvr/channels/PVRChannelGroupsContainer.h"
++#include "pvr/dialogs/GUIDialogPVRGuideInfo.h"
++#include "pvr/dialogs/GUIDialogPVRRecordingInfo.h"
++#include "pvr/dialogs/GUIDialogPVRTimerSettings.h"
++#include "epg/EpgInfoTag.h"
++#include "pvr/timers/PVRTimers.h"
++#include "pvr/addons/PVRClients.h"
++#include "pvr/windows/GUIWindowPVR.h"
++#include "pvr/windows/GUIWindowPVRSearch.h"
++#include "pvr/recordings/PVRRecordings.h"
++#include "settings/GUISettings.h"
++#include "settings/Settings.h"
++#include "utils/log.h"
++#include "utils/URIUtils.h"
++#include "GUIUserMessages.h"
++
++using namespace std;
++using namespace PVR;
++using namespace EPG;
++
++CGUIWindowPVRCommon::CGUIWindowPVRCommon(CGUIWindowPVR *parent, PVRWindow window,
++ unsigned int iControlButton, unsigned int iControlList)
++{
++ m_parent = parent;
++ m_window = window;
++ m_iControlButton = iControlButton;
++ m_iControlList = iControlList;
++ m_bUpdateRequired = false;
++ m_iSelected = 0;
++ m_iSortOrder = SORT_ORDER_ASC;
++ m_iSortMethod = SORT_METHOD_DATE;
++ if( m_parent->GetViewState() )
++ {
++ m_iSortOrder = m_parent->GetViewState()->GetSortOrder();
++ m_iSortMethod = m_parent->GetViewState()->GetSortMethod();
++ }
++}
++
++bool CGUIWindowPVRCommon::operator ==(const CGUIWindowPVRCommon &right) const
++{
++ return (this == &right || m_window == right.m_window);
++}
++
++bool CGUIWindowPVRCommon::operator !=(const CGUIWindowPVRCommon &right) const
++{
++ return !(*this == right);
++}
++
++const char *CGUIWindowPVRCommon::GetName(void) const
++{
++ switch(m_window)
++ {
++ case PVR_WINDOW_EPG:
++ return "epg";
++ case PVR_WINDOW_CHANNELS_RADIO:
++ return "radio";
++ case PVR_WINDOW_CHANNELS_TV:
++ return "tv";
++ case PVR_WINDOW_RECORDINGS:
++ return "recordings";
++ case PVR_WINDOW_SEARCH:
++ return "search";
++ case PVR_WINDOW_TIMERS:
++ return "timers";
++ default:
++ return "unknown";
++ }
++}
++
++bool CGUIWindowPVRCommon::IsVisible(void) const
++{
++ return !g_application.IsPlayingFullScreenVideo() &&
++ g_windowManager.GetActiveWindow() == WINDOW_PVR &&
++ IsActive();
++}
++
++bool CGUIWindowPVRCommon::IsActive(void) const
++{
++ CGUIWindowPVRCommon *window = m_parent->GetActiveView();
++ return (window && *window == *this);
++}
++
++bool CGUIWindowPVRCommon::IsSavedView(void) const
++{
++ CGUIWindowPVRCommon *window = m_parent->GetSavedView();
++ return (window && *window == *this);
++}
++
++bool CGUIWindowPVRCommon::IsSelectedButton(CGUIMessage &message) const
++{
++ return (message.GetSenderId() == (int) m_iControlButton);
++}
++
++bool CGUIWindowPVRCommon::IsSelectedControl(CGUIMessage &message) const
++{
++ return (message.GetControlId() == (int) m_iControlButton);
++}
++
++bool CGUIWindowPVRCommon::IsSelectedList(CGUIMessage &message) const
++{
++ return (message.GetSenderId() == (int) m_iControlList);
++}
++
++void CGUIWindowPVRCommon::SetInvalid()
++{
++ for (int iItemPtr = 0; iItemPtr < m_parent->m_vecItems->Size(); iItemPtr++)
++ m_parent->m_vecItems->Get(iItemPtr)->SetInvalid();
++ m_parent->SetInvalid();
++}
++
++void CGUIWindowPVRCommon::OnInitWindow()
++{
++ m_parent->m_viewControl.SetCurrentView(m_iControlList);
++}
++
++bool CGUIWindowPVRCommon::SelectPlayingFile(void)
++{
++ bool bReturn(false);
++
++ if (g_PVRManager.IsPlaying())
++ {
++ m_parent->m_viewControl.SetSelectedItem(g_application.CurrentFile());
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRCommon::OnMessageFocus(CGUIMessage &message)
++{
++ bool bReturn = false;
++
++ if (message.GetMessage() == GUI_MSG_FOCUSED &&
++ (IsSelectedControl(message) || IsSavedView()))
++ {
++ CLog::Log(LOGDEBUG, "CGUIWindowPVRCommon - %s - focus set to window '%s'", __FUNCTION__, GetName());
++ bool bIsActive = IsActive();
++ m_parent->SetActiveView(this);
++
++ if (!bIsActive || m_bUpdateRequired)
++ UpdateData();
++ else
++ m_iSelected = m_parent->m_viewControl.GetSelectedItem();
++
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++void CGUIWindowPVRCommon::OnWindowUnload(void)
++{
++ m_iSelected = m_parent->m_viewControl.GetSelectedItem();
++}
++
++bool CGUIWindowPVRCommon::OnAction(const CAction &action)
++{
++ bool bReturn = false;
++
++ if (action.GetID() == ACTION_NAV_BACK ||
++ action.GetID() == ACTION_PARENT_DIR)
++ {
++ g_windowManager.PreviousWindow();
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRCommon::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
++{
++ if (itemNumber < 0 || itemNumber >= (int) m_parent->m_vecItems->Size())
++ return false;
++ CFileItemPtr pItem = m_parent->m_vecItems->Get(itemNumber);
++
++ return (OnContextButtonSortAsc(pItem.get(), button) ||
++ OnContextButtonSortBy(pItem.get(), button) ||
++ OnContextButtonSortByChannel(pItem.get(), button) ||
++ OnContextButtonSortByName(pItem.get(), button) ||
++ OnContextButtonSortByDate(pItem.get(), button) ||
++ OnContextButtonFind(pItem.get(), button) ||
++ OnContextButtonMenuHooks(pItem.get(), button));
++}
++
++bool CGUIWindowPVRCommon::OnContextButtonSortByDate(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_SORTBY_DATE)
++ {
++ bReturn = true;
++
++ if (m_iSortMethod != SORT_METHOD_DATE)
++ {
++ m_iSortMethod = SORT_METHOD_DATE;
++ m_iSortOrder = SORT_ORDER_ASC;
++ CGUIMessage message(GUI_MSG_CHANGE_SORT_METHOD, m_parent->GetID(), 0, m_iSortMethod, 0);
++ m_parent->OnMessage(message);
++ }
++ else
++ {
++ m_iSortOrder = m_iSortOrder == SORT_ORDER_ASC ? SORT_ORDER_DESC : SORT_ORDER_ASC;
++ }
++ CGUIMessage message(GUI_MSG_CHANGE_SORT_DIRECTION, m_parent->GetID(), 0, m_iSortOrder, 0);
++ m_parent->OnMessage(message);
++ UpdateData();
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRCommon::OnContextButtonSortByName(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_SORTBY_NAME)
++ {
++ bReturn = true;
++
++ if (m_iSortMethod != SORT_METHOD_LABEL)
++ {
++ m_iSortMethod = SORT_METHOD_LABEL;
++ m_iSortOrder = SORT_ORDER_ASC;
++ CGUIMessage message(GUI_MSG_CHANGE_SORT_METHOD, m_parent->GetID(), 0, m_iSortMethod, 0);
++ m_parent->OnMessage(message);
++ }
++ else
++ {
++ m_iSortOrder = m_iSortOrder == SORT_ORDER_ASC ? SORT_ORDER_DESC : SORT_ORDER_ASC;
++ }
++ CGUIMessage message(GUI_MSG_CHANGE_SORT_DIRECTION, m_parent->GetID(), 0, m_iSortOrder, 0);
++ m_parent->OnMessage(message);
++ UpdateData();
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRCommon::OnContextButtonSortByChannel(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_SORTBY_CHANNEL)
++ {
++ bReturn = true;
++
++ if (m_iSortMethod != SORT_METHOD_CHANNEL)
++ {
++ m_iSortMethod = SORT_METHOD_CHANNEL;
++ m_iSortOrder = SORT_ORDER_ASC;
++ }
++ else
++ {
++ m_iSortOrder = m_iSortOrder == SORT_ORDER_ASC ? SORT_ORDER_DESC : SORT_ORDER_ASC;
++ }
++
++ UpdateData();
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRCommon::OnContextButtonSortAsc(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_SORTASC)
++ {
++ bReturn = true;
++
++ if (m_parent->m_guiState.get())
++ m_parent->m_guiState->SetNextSortOrder();
++ m_parent->UpdateFileList();
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRCommon::OnContextButtonSortBy(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_SORTBY)
++ {
++ bReturn = true;
++
++ if (m_parent->m_guiState.get())
++ m_parent->m_guiState->SetNextSortMethod();
++
++ m_parent->UpdateFileList();
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRCommon::OnContextButtonMenuHooks(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_MENU_HOOKS)
++ {
++ bReturn = true;
++
++ if (item->IsEPG() && item->GetEPGInfoTag()->HasPVRChannel())
++ g_PVRClients->ProcessMenuHooks(item->GetEPGInfoTag()->ChannelTag()->ClientID());
++ else if (item->IsPVRChannel())
++ g_PVRClients->ProcessMenuHooks(item->GetPVRChannelInfoTag()->ClientID());
++ else if (item->IsPVRRecording())
++ g_PVRClients->ProcessMenuHooks(item->GetPVRRecordingInfoTag()->m_iClientId);
++ else if (item->IsPVRTimer())
++ g_PVRClients->ProcessMenuHooks(item->GetPVRTimerInfoTag()->m_iClientId);
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRCommon::ActionDeleteTimer(CFileItem *item)
++{
++ /* check if the timer tag is valid */
++ CPVRTimerInfoTag *timerTag = item->GetPVRTimerInfoTag();
++ if (!timerTag || timerTag->m_iClientIndex < 0)
++ return false;
++
++ /* show a confirmation dialog */
++ CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
++ if (!pDialog)
++ return false;
++ pDialog->SetHeading(122);
++ pDialog->SetLine(0, 19040);
++ pDialog->SetLine(1, "");
++ pDialog->SetLine(2, timerTag->m_strTitle);
++ pDialog->DoModal();
++
++ /* prompt for the user's confirmation */
++ if (!pDialog->IsConfirmed())
++ return false;
++
++ /* delete the timer */
++ return g_PVRTimers->DeleteTimer(*item);
++}
++
++bool CGUIWindowPVRCommon::ShowNewTimerDialog(void)
++{
++ bool bReturn(false);
++
++ CPVRTimerInfoTag *newTimer = new CPVRTimerInfoTag;
++ CFileItem *newItem = new CFileItem(*newTimer);
++ if (ShowTimerSettings(newItem))
++ {
++ /* Add timer to backend */
++ bReturn = g_PVRTimers->AddTimer(*newItem);
++ }
++
++ delete newItem;
++ delete newTimer;
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRCommon::ActionShowTimer(CFileItem *item)
++{
++ bool bReturn = false;
++
++ /* Check if "Add timer..." entry is pressed by OK, if yes
++ create a new timer and open settings dialog, otherwise
++ open settings for selected timer entry */
++ if (item->GetPath() == "pvr://timers/add.timer")
++ {
++ bReturn = ShowNewTimerDialog();
++ }
++ else
++ {
++ if (ShowTimerSettings(item))
++ {
++ /* Update timer on pvr backend */
++ bReturn = g_PVRTimers->UpdateTimer(*item);
++ }
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRCommon::ActionRecord(CFileItem *item)
++{
++ bool bReturn = false;
++
++ CEpgInfoTag *epgTag = item->GetEPGInfoTag();
++ if (!epgTag)
++ return bReturn;
++
++ if (!epgTag->HasPVRChannel())
++ return bReturn;
++
++ if (epgTag->Timer() == NULL)
++ {
++ /* create a confirmation dialog */
++ CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*) g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
++ if (!pDialog)
++ return bReturn;
++
++ pDialog->SetHeading(264);
++ pDialog->SetLine(0, "");
++ pDialog->SetLine(1, epgTag->Title());
++ pDialog->SetLine(2, "");
++ pDialog->DoModal();
++
++ /* prompt for the user's confirmation */
++ if (!pDialog->IsConfirmed())
++ return bReturn;
++
++ CPVRTimerInfoTag *newtimer = CPVRTimerInfoTag::CreateFromEpg(*epgTag);
++ CFileItem *item = new CFileItem(*newtimer);
++
++ bReturn = g_PVRTimers->AddTimer(*item);
++ }
++ else
++ {
++ CGUIDialogOK::ShowAndGetInput(19033,19034,0,0);
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++
++bool CGUIWindowPVRCommon::ActionDeleteRecording(CFileItem *item)
++{
++ bool bReturn = false;
++
++ /* check if the recording tag is valid */
++ CPVRRecording *recTag = (CPVRRecording *) item->GetPVRRecordingInfoTag();
++ if (!recTag || recTag->m_strRecordingId.IsEmpty())
++ return bReturn;
++
++ /* show a confirmation dialog */
++ CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
++ if (!pDialog)
++ return bReturn;
++ pDialog->SetHeading(122);
++ pDialog->SetLine(0, 19043);
++ pDialog->SetLine(1, "");
++ pDialog->SetLine(2, recTag->m_strTitle);
++ pDialog->DoModal();
++
++ /* prompt for the user's confirmation */
++ if (!pDialog->IsConfirmed())
++ return bReturn;
++
++ /* delete the recording */
++ if (g_PVRRecordings->DeleteRecording(*item))
++ {
++ g_PVRManager.TriggerRecordingsUpdate();
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRCommon::ActionPlayChannel(CFileItem *item)
++{
++ bool bReturn = false;
++
++ if (item->GetPath() == "pvr://channels/.add.channel")
++ {
++ /* show "add channel" dialog */
++ CGUIDialogOK::ShowAndGetInput(19033,0,19038,0);
++ bReturn = true;
++ }
++ else
++ {
++ /* open channel */
++ bReturn = PlayFile(item, g_guiSettings.GetBool("pvrplayback.playminimized"));
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRCommon::ActionPlayEpg(CFileItem *item)
++{
++ bool bReturn = false;
++
++ CEpgInfoTag *epgTag = item->GetEPGInfoTag();
++ if (!epgTag)
++ return bReturn;
++
++ const CPVRChannel *channel = epgTag->ChannelTag();
++ if (!channel || channel->ChannelNumber() > 0)
++ return bReturn;
++
++ bReturn = g_application.PlayFile(CFileItem(*channel));
++
++ if (!bReturn)
++ {
++ /* cannot play file */
++ CGUIDialogOK::ShowAndGetInput(19033,0,19035,0);
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRCommon::ActionDeleteChannel(CFileItem *item)
++{
++ CPVRChannel *channel = item->GetPVRChannelInfoTag();
++
++ /* check if the channel tag is valid */
++ if (!channel || channel->ChannelNumber() <= 0)
++ return false;
++
++ /* show a confirmation dialog */
++ CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*) g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
++ if (pDialog)
++ return false;
++ pDialog->SetHeading(19039);
++ pDialog->SetLine(0, "");
++ pDialog->SetLine(1, channel->ChannelName());
++ pDialog->SetLine(2, "");
++ pDialog->DoModal();
++
++ /* prompt for the user's confirmation */
++ if (!pDialog->IsConfirmed())
++ return false;
++
++ g_PVRChannelGroups->GetGroupAll(channel->IsRadio())->RemoveFromGroup(*channel);
++ UpdateData();
++
++ return true;
++}
++
++bool CGUIWindowPVRCommon::UpdateEpgForChannel(CFileItem *item)
++{
++ CPVRChannel *channel = item->GetPVRChannelInfoTag();
++ CEpg *epg = channel->GetEPG();
++ if (!epg)
++ return false;
++
++ epg->ForceUpdate();
++ return true;
++}
++
++bool CGUIWindowPVRCommon::ShowTimerSettings(CFileItem *item)
++{
++ /* Check item is TV timer information tag */
++ if (!item->IsPVRTimer())
++ {
++ CLog::Log(LOGERROR, "CGUIWindowPVRTimers: Can't open timer settings dialog, no timer info tag!");
++ return false;
++ }
++
++ /* Load timer settings dialog */
++ CGUIDialogPVRTimerSettings* pDlgInfo = (CGUIDialogPVRTimerSettings*)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_TIMER_SETTING);
++
++ if (!pDlgInfo)
++ return false;
++
++ /* inform dialog about the file item */
++ pDlgInfo->SetTimer(item);
++
++ /* Open dialog window */
++ pDlgInfo->DoModal();
++
++ /* Get modify flag from window and return it to caller */
++ return pDlgInfo->GetOK();
++}
++
++
++bool CGUIWindowPVRCommon::PlayRecording(CFileItem *item, bool bPlayMinimized /* = false */)
++{
++ if (item->GetPath().Left(17) != "pvr://recordings/")
++ return false;
++
++ CStdString stream = item->GetPVRRecordingInfoTag()->m_strStreamURL;
++ if (stream == "")
++ return false;
++
++ /* Isolate the folder from the filename */
++ size_t found = stream.find_last_of("/");
++ if (found == CStdString::npos)
++ found = stream.find_last_of("\\");
++
++ if (found != CStdString::npos)
++ {
++ /* Check here for asterisk at the begin of the filename */
++ if (stream[found+1] == '*')
++ {
++ /* Create a "stack://" url with all files matching the extension */
++ CStdString ext = URIUtils::GetExtension(stream);
++ CStdString dir = stream.substr(0, found).c_str();
++
++ CFileItemList items;
++ CDirectory::GetDirectory(dir, items);
++ items.Sort(SORT_METHOD_FILE ,SORT_ORDER_ASC);
++
++ vector<int> stack;
++ for (int i = 0; i < items.Size(); ++i)
++ {
++ if (URIUtils::GetExtension(items[i]->GetPath()) == ext)
++ stack.push_back(i);
++ }
++
++ if (stack.size() > 0)
++ {
++ /* If we have a stack change the path of the item to it */
++ CStackDirectory dir;
++ CStdString stackPath = dir.ConstructStackPath(items, stack);
++ item->SetPath(stackPath);
++ }
++ }
++ else
++ {
++ /* If no asterisk is present play only the given stream URL */
++ item->SetPath(stream);
++ }
++ }
++ else
++ {
++ CLog::Log(LOGERROR, "PVRManager - %s - can't open recording: no valid filename", __FUNCTION__);
++ CGUIDialogOK::ShowAndGetInput(19033,0,19036,0);
++ return false;
++ }
++
++ g_application.getApplicationMessenger().PlayFile(*item, false);
++
++ return true;
++}
++
++bool CGUIWindowPVRCommon::PlayFile(CFileItem *item, bool bPlayMinimized /* = false */)
++{
++ if (bPlayMinimized)
++ {
++ if (item->GetPath() == g_application.CurrentFile())
++ {
++ CGUIMessage msg(GUI_MSG_FULLSCREEN, 0, m_parent->GetID());
++ g_windowManager.SendMessage(msg);
++ return true;
++ }
++ else
++ {
++ g_settings.m_bStartVideoWindowed = true;
++ }
++ }
++
++ if (item->GetPath().Left(17) == "pvr://recordings/")
++ {
++ return PlayRecording(item, bPlayMinimized);
++ }
++ else
++ {
++ bool bSwitchSuccessful(false);
++
++ /* try a fast switch */
++ if (item->IsPVRChannel() && (g_PVRManager.IsPlayingTV() || g_PVRManager.IsPlayingRadio()) &&
++ (item->GetPVRChannelInfoTag()->IsRadio() == g_PVRManager.IsPlayingRadio()) && g_application.m_pPlayer)
++ {
++ CPVRChannel* channel = item->GetPVRChannelInfoTag();
++ if (channel->StreamURL().IsEmpty())
++ bSwitchSuccessful = g_application.m_pPlayer->SwitchChannel(*channel);
++ }
++
++ if (!bSwitchSuccessful)
++ {
++ g_application.getApplicationMessenger().PlayFile(*item, false);
++ return true;
++ }
++
++ if (!bSwitchSuccessful)
++ {
++ CGUIDialogOK::ShowAndGetInput(19033,0,19035,0);
++ return false;
++ }
++ }
++
++ return true;
++}
++
++bool CGUIWindowPVRCommon::StartRecordFile(CFileItem *item)
++{
++ if (!item->HasEPGInfoTag())
++ return false;
++
++ CEpgInfoTag *tag = item->GetEPGInfoTag();
++ if (!tag || !tag->HasPVRChannel())
++ return false;
++
++ CPVRTimerInfoTag *timer = g_PVRTimers->GetMatch(item);
++ if (timer)
++ {
++ CGUIDialogOK::ShowAndGetInput(19033,19034,0,0);
++ return false;
++ }
++
++ CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
++ if (!pDialog)
++ return false;
++ pDialog->SetHeading(264);
++ pDialog->SetLine(0, tag->PVRChannelName());
++ pDialog->SetLine(1, "");
++ pDialog->SetLine(2, tag->Title());
++ pDialog->DoModal();
++
++ if (!pDialog->IsConfirmed())
++ return false;
++
++ CPVRTimerInfoTag *newtimer = CPVRTimerInfoTag::CreateFromEpg(*tag);
++ CFileItem *newTimerItem = new CFileItem(*newtimer);
++
++ return g_PVRTimers->AddTimer(*newTimerItem);
++}
++
++bool CGUIWindowPVRCommon::StopRecordFile(CFileItem *item)
++{
++ if (!item->HasEPGInfoTag())
++ return false;
++
++ CEpgInfoTag *tag = item->GetEPGInfoTag();
++ if (!tag || !tag->HasPVRChannel())
++ return false;
++
++ CPVRTimerInfoTag *timer = g_PVRTimers->GetMatch(item);
++ if (!timer || timer->m_bIsRepeating)
++ return false;
++
++ return g_PVRTimers->DeleteTimer(*timer);
++}
++
++void CGUIWindowPVRCommon::ShowEPGInfo(CFileItem *item)
++{
++ CFileItem *tag = NULL;
++ if (item->IsEPG())
++ {
++ tag = new CFileItem(*item);
++ }
++ else if (item->IsPVRChannel())
++ {
++ CEpgInfoTag epgnow;
++ if (!item->GetPVRChannelInfoTag()->GetEPGNow(epgnow))
++ {
++ CGUIDialogOK::ShowAndGetInput(19033,0,19055,0);
++ return;
++ }
++ tag = new CFileItem(epgnow);
++ }
++
++ if (tag)
++ {
++ CGUIDialogPVRGuideInfo* pDlgInfo = (CGUIDialogPVRGuideInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_GUIDE_INFO);
++ if (!pDlgInfo)
++ return;
++
++ pDlgInfo->SetProgInfo(tag);
++ pDlgInfo->DoModal();
++
++ delete tag;
++ }
++}
++
++void CGUIWindowPVRCommon::ShowRecordingInfo(CFileItem *item)
++{
++ if (!item->IsPVRRecording())
++ return;
++
++ CGUIDialogPVRRecordingInfo* pDlgInfo = (CGUIDialogPVRRecordingInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_RECORDING_INFO);
++ if (!pDlgInfo)
++ return;
++
++ pDlgInfo->SetRecording(item);
++ pDlgInfo->DoModal();
++}
++
++bool CGUIWindowPVRCommon::OnContextButtonFind(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_FIND)
++ {
++ bReturn = true;
++ if (m_parent->m_windowSearch)
++ {
++ CEpgInfoTag tag;
++ m_parent->m_windowSearch->m_searchfilter.Reset();
++ if (item->IsEPG())
++ m_parent->m_windowSearch->m_searchfilter.m_strSearchTerm = "\"" + item->GetEPGInfoTag()->Title() + "\"";
++ else if (item->IsPVRChannel() && item->GetPVRChannelInfoTag()->GetEPGNow(tag))
++ m_parent->m_windowSearch->m_searchfilter.m_strSearchTerm = "\"" + tag.Title() + "\"";
++ else if (item->IsPVRRecording())
++ m_parent->m_windowSearch->m_searchfilter.m_strSearchTerm = "\"" + item->GetPVRRecordingInfoTag()->m_strTitle + "\"";
++
++ m_parent->m_windowSearch->m_bSearchConfirmed = true;
++ m_parent->SetLabel(m_iControlButton, 0);
++ m_parent->SetActiveView(m_parent->m_windowSearch);
++ m_parent->m_windowSearch->UpdateData();
++ m_parent->SetLabel(m_iControlList, 0);
++ }
++ }
++
++ return bReturn;
++}
+diff --git a/xbmc/pvr/windows/GUIWindowPVRCommon.h b/xbmc/pvr/windows/GUIWindowPVRCommon.h
+new file mode 100644
+index 0000000..726db9e
+--- /dev/null
++++ b/xbmc/pvr/windows/GUIWindowPVRCommon.h
+@@ -0,0 +1,141 @@
++#pragma once
++
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "FileItem.h"
++#include "windows/GUIMediaWindow.h"
++#include "GUIWindowPVRCommon.h"
++#include "threads/CriticalSection.h"
++
++namespace PVR
++{
++ enum PVRWindow
++ {
++ PVR_WINDOW_UNKNOWN = 0,
++ PVR_WINDOW_EPG = 1,
++ PVR_WINDOW_CHANNELS_TV = 2,
++ PVR_WINDOW_CHANNELS_RADIO = 3,
++ PVR_WINDOW_RECORDINGS = 4,
++ PVR_WINDOW_TIMERS = 5,
++ PVR_WINDOW_SEARCH = 6
++ };
++
++ #define CONTROL_LIST_TIMELINE 10
++ #define CONTROL_LIST_CHANNELS_TV 11
++ #define CONTROL_LIST_CHANNELS_RADIO 12
++ #define CONTROL_LIST_RECORDINGS 13
++ #define CONTROL_LIST_TIMERS 14
++ #define CONTROL_LIST_GUIDE_CHANNEL 15
++ #define CONTROL_LIST_GUIDE_NOW_NEXT 16
++ #define CONTROL_LIST_SEARCH 17
++
++ #define CONTROL_LABELHEADER 29
++ #define CONTROL_LABELGROUP 30
++
++ #define CONTROL_BTNGUIDE 31
++ #define CONTROL_BTNCHANNELS_TV 32
++ #define CONTROL_BTNCHANNELS_RADIO 33
++ #define CONTROL_BTNRECORDINGS 34
++ #define CONTROL_BTNTIMERS 35
++ #define CONTROL_BTNSEARCH 36
++ #define CONTROL_BTNGUIDE_CHANNEL 37
++ #define CONTROL_BTNGUIDE_NOW 38
++ #define CONTROL_BTNGUIDE_NEXT 39
++ #define CONTROL_BTNGUIDE_TIMELINE 40
++
++ class CGUIWindowPVR;
++
++ class CGUIWindowPVRCommon
++ {
++ friend class CGUIWindowPVR;
++
++ public:
++ CGUIWindowPVRCommon(CGUIWindowPVR *parent, PVRWindow window,
++ unsigned int iControlButton, unsigned int iControlList);
++ virtual ~CGUIWindowPVRCommon(void) {};
++
++ bool operator ==(const CGUIWindowPVRCommon &right) const;
++ bool operator !=(const CGUIWindowPVRCommon &right) const;
++
++ virtual const char *GetName(void) const;
++ virtual PVRWindow GetWindowId(void) const { return m_window; }
++ virtual bool IsVisible(void) const;
++ virtual bool IsActive(void) const;
++ virtual bool IsSavedView(void) const;
++ virtual bool IsSelectedButton(CGUIMessage &message) const;
++ virtual bool IsSelectedControl(CGUIMessage &message) const;
++ virtual bool IsSelectedList(CGUIMessage &message) const;
++
++ virtual bool OnAction(const CAction &action);
++ virtual bool OnContextButton(int itemNumber, CONTEXT_BUTTON button);
++
++ virtual void GetContextButtons(int itemNumber, CContextButtons &buttons) const = 0;
++ virtual void UpdateData(void) = 0;
++ virtual void SetInvalid(void);
++
++ virtual void OnInitWindow(void);
++ virtual void OnWindowUnload(void);
++
++ protected:
++ virtual bool SelectPlayingFile(void);
++ virtual bool OnMessageFocus(CGUIMessage &message);
++
++ virtual bool OnClickButton(CGUIMessage &message) = 0;
++ virtual bool OnClickList(CGUIMessage &message) = 0;
++
++ virtual bool ActionDeleteTimer(CFileItem *item);
++ virtual bool ActionShowTimer(CFileItem *item);
++ virtual bool ActionRecord(CFileItem *item);
++ virtual bool ActionDeleteRecording(CFileItem *item);
++ virtual bool ActionPlayChannel(CFileItem *item);
++ virtual bool ActionPlayEpg(CFileItem *item);
++ virtual bool ActionDeleteChannel(CFileItem *item);
++
++ virtual bool PlayRecording(CFileItem *item, bool bPlayMinimized = false);
++ virtual bool PlayFile(CFileItem *item, bool bPlayMinimized = false);
++ virtual bool StartRecordFile(CFileItem *item);
++ virtual bool StopRecordFile(CFileItem *item);
++ virtual void ShowEPGInfo(CFileItem *item);
++ virtual void ShowRecordingInfo(CFileItem *item);
++ virtual bool UpdateEpgForChannel(CFileItem *item);
++ virtual bool ShowTimerSettings(CFileItem *item);
++ virtual bool ShowNewTimerDialog(void);
++
++ virtual bool OnContextButtonMenuHooks(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonSortAsc(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonSortBy(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonSortByDate(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonSortByName(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonSortByChannel(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonFind(CFileItem *item, CONTEXT_BUTTON button);
++
++ CGUIWindowPVR * m_parent;
++ PVRWindow m_window;
++ unsigned int m_iControlButton;
++ unsigned int m_iControlList;
++ bool m_bUpdateRequired;
++ int m_iSelected;
++ SORT_ORDER m_iSortOrder;
++ SORT_METHOD m_iSortMethod;
++ CCriticalSection m_critSection;
++ };
++}
+diff --git a/xbmc/pvr/windows/GUIWindowPVRGuide.cpp b/xbmc/pvr/windows/GUIWindowPVRGuide.cpp
+new file mode 100644
+index 0000000..7277b04
+--- /dev/null
++++ b/xbmc/pvr/windows/GUIWindowPVRGuide.cpp
+@@ -0,0 +1,456 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "GUIWindowPVRGuide.h"
++
++#include "Application.h"
++#include "dialogs/GUIDialogOK.h"
++#include "guilib/GUIWindowManager.h"
++#include "pvr/PVRManager.h"
++#include "pvr/channels/PVRChannelGroupsContainer.h"
++#include "epg/EpgContainer.h"
++#include "pvr/windows/GUIWindowPVR.h"
++#include "settings/AdvancedSettings.h"
++#include "settings/GUISettings.h"
++#include "settings/Settings.h"
++#include "threads/SingleLock.h"
++#include "utils/log.h"
++#include "pvr/addons/PVRClients.h"
++#include "pvr/timers/PVRTimers.h"
++
++using namespace PVR;
++using namespace EPG;
++
++CGUIWindowPVRGuide::CGUIWindowPVRGuide(CGUIWindowPVR *parent) :
++ CGUIWindowPVRCommon(parent, PVR_WINDOW_EPG, CONTROL_BTNGUIDE, CONTROL_LIST_GUIDE_NOW_NEXT),
++ Observer(),
++ m_iGuideView(g_guiSettings.GetInt("epg.defaultguideview"))
++{
++}
++
++void CGUIWindowPVRGuide::UnregisterObservers(void)
++{
++ CSingleLock lock(m_critSection);
++ g_EpgContainer.UnregisterObserver(this);
++}
++
++void CGUIWindowPVRGuide::ResetObservers(void)
++{
++ CSingleLock lock(m_critSection);
++ g_EpgContainer.RegisterObserver(this);
++}
++
++void CGUIWindowPVRGuide::Notify(const Observable &obs, const CStdString& msg)
++{
++ if (msg.Equals("epg"))
++ {
++ /* update the current window if the EPG timeline view is visible */
++ if (IsVisible() && m_iGuideView == GUIDE_VIEW_TIMELINE)
++ UpdateData();
++ else
++ m_bUpdateRequired = true;
++ }
++ else if (msg.Equals("epg-now"))
++ {
++ if (IsVisible() && m_iGuideView != GUIDE_VIEW_TIMELINE)
++ SetInvalid();
++ else
++ m_bUpdateRequired = true;
++ }
++}
++
++void CGUIWindowPVRGuide::GetContextButtons(int itemNumber, CContextButtons &buttons) const
++{
++ if (itemNumber < 0 || itemNumber >= m_parent->m_vecItems->Size())
++ return;
++ CFileItemPtr pItem = m_parent->m_vecItems->Get(itemNumber);
++
++ if (pItem->GetEPGInfoTag()->EndAsLocalTime() > CDateTime::GetCurrentDateTime())
++ {
++ CPVRTimerInfoTag *timer = g_PVRTimers->GetMatch(pItem->GetEPGInfoTag());
++ if (!timer)
++ {
++ if (pItem->GetEPGInfoTag()->StartAsLocalTime() < CDateTime::GetCurrentDateTime())
++ buttons.Add(CONTEXT_BUTTON_START_RECORD, 264); /* record program */
++ else
++ buttons.Add(CONTEXT_BUTTON_START_RECORD, 19061); /* stop recording */
++ }
++ else
++ {
++ if (pItem->GetEPGInfoTag()->StartAsLocalTime() < CDateTime::GetCurrentDateTime())
++ buttons.Add(CONTEXT_BUTTON_STOP_RECORD, 19059);
++ else
++ buttons.Add(CONTEXT_BUTTON_STOP_RECORD, 19060);
++ }
++ }
++
++ buttons.Add(CONTEXT_BUTTON_INFO, 19047); /* epg info */
++ buttons.Add(CONTEXT_BUTTON_PLAY_ITEM, 19000); /* switch channel */
++ buttons.Add(CONTEXT_BUTTON_FIND, 19003); /* find similar program */
++ if (m_iGuideView == GUIDE_VIEW_TIMELINE)
++ {
++ buttons.Add(CONTEXT_BUTTON_BEGIN, 19063); /* go to begin */
++ buttons.Add(CONTEXT_BUTTON_END, 19064); /* go to end */
++ }
++ if (pItem->GetEPGInfoTag()->HasPVRChannel() &&
++ g_PVRClients->HasMenuHooks(pItem->GetEPGInfoTag()->ChannelTag()->ClientID()))
++ buttons.Add(CONTEXT_BUTTON_MENU_HOOKS, 19195); /* PVR client specific action */
++}
++
++
++bool CGUIWindowPVRGuide::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
++{
++ if (itemNumber < 0 || itemNumber >= (int) m_parent->m_vecItems->Size())
++ return false;
++ CFileItemPtr pItem = m_parent->m_vecItems->Get(itemNumber);
++
++ return OnContextButtonPlay(pItem.get(), button) ||
++ OnContextButtonInfo(pItem.get(), button) ||
++ OnContextButtonStartRecord(pItem.get(), button) ||
++ OnContextButtonStopRecord(pItem.get(), button) ||
++ OnContextButtonBegin(pItem.get(), button) ||
++ OnContextButtonEnd(pItem.get(), button) ||
++ CGUIWindowPVRCommon::OnContextButton(itemNumber, button);
++}
++
++void CGUIWindowPVRGuide::UpdateViewChannel(void)
++{
++ CPVRChannel CurrentChannel;
++ bool bGotCurrentChannel = g_PVRManager.GetCurrentChannel(CurrentChannel);
++
++ m_parent->m_guideGrid = NULL;
++ m_parent->m_viewControl.SetCurrentView(CONTROL_LIST_GUIDE_CHANNEL);
++
++ m_parent->SetLabel(m_iControlButton, g_localizeStrings.Get(19222) + ": " + g_localizeStrings.Get(19029));
++ if (bGotCurrentChannel)
++ m_parent->SetLabel(CONTROL_LABELGROUP, CurrentChannel.ChannelName().c_str());
++
++ if (!bGotCurrentChannel || g_PVRManager.GetCurrentEpg(*m_parent->m_vecItems) == 0)
++ {
++ CFileItemPtr item;
++ item.reset(new CFileItem("pvr://guide/" + CurrentChannel.ChannelName() + "/empty.epg", false));
++ item->SetLabel(g_localizeStrings.Get(19028));
++ item->SetLabelPreformated(true);
++ m_parent->m_vecItems->Add(item);
++ }
++ m_parent->m_viewControl.SetItems(*m_parent->m_vecItems);
++}
++
++void CGUIWindowPVRGuide::UpdateViewNow(void)
++{
++ CPVRChannel CurrentChannel;
++ bool bGotCurrentChannel = g_PVRManager.GetCurrentChannel(CurrentChannel);
++ bool bRadio = bGotCurrentChannel ? CurrentChannel.IsRadio() : false;
++
++ m_parent->m_guideGrid = NULL;
++ m_parent->m_viewControl.SetCurrentView(CONTROL_LIST_GUIDE_NOW_NEXT);
++
++ m_parent->SetLabel(m_iControlButton, g_localizeStrings.Get(19222) + ": " + g_localizeStrings.Get(19030));
++ m_parent->SetLabel(CONTROL_LABELGROUP, g_localizeStrings.Get(19030));
++
++ if (g_PVRManager.GetPlayingGroup(bRadio)->GetEPGNow(*m_parent->m_vecItems) == 0)
++ {
++ CFileItemPtr item;
++ item.reset(new CFileItem("pvr://guide/now/empty.epg", false));
++ item->SetLabel(g_localizeStrings.Get(19028));
++ item->SetLabelPreformated(true);
++ m_parent->m_vecItems->Add(item);
++ }
++ m_parent->m_viewControl.SetItems(*m_parent->m_vecItems);
++}
++
++void CGUIWindowPVRGuide::UpdateViewNext(void)
++{
++ CPVRChannel CurrentChannel;
++ bool bGotCurrentChannel = g_PVRManager.GetCurrentChannel(CurrentChannel);
++ bool bRadio = bGotCurrentChannel ? CurrentChannel.IsRadio() : false;
++
++ m_parent->m_guideGrid = NULL;
++ m_parent->m_viewControl.SetCurrentView(CONTROL_LIST_GUIDE_NOW_NEXT);
++
++ m_parent->SetLabel(m_iControlButton, g_localizeStrings.Get(19222) + ": " + g_localizeStrings.Get(19031));
++ m_parent->SetLabel(CONTROL_LABELGROUP, g_localizeStrings.Get(19031));
++
++ if (g_PVRManager.GetPlayingGroup(bRadio)->GetEPGNext(*m_parent->m_vecItems) == 0)
++ {
++ CFileItemPtr item;
++ item.reset(new CFileItem("pvr://guide/next/empty.epg", false));
++ item->SetLabel(g_localizeStrings.Get(19028));
++ item->SetLabelPreformated(true);
++ m_parent->m_vecItems->Add(item);
++ }
++ m_parent->m_viewControl.SetItems(*m_parent->m_vecItems);
++}
++
++void CGUIWindowPVRGuide::UpdateViewTimeline(void)
++{
++ CPVRChannel CurrentChannel;
++ bool bGotCurrentChannel = g_PVRManager.GetCurrentChannel(CurrentChannel);
++ bool bRadio = bGotCurrentChannel ? CurrentChannel.IsRadio() : false;
++ CDateTime gridStart = CDateTime::GetCurrentDateTime();
++ CDateTime firstDate; firstDate.SetFromUTCDateTime(g_EpgContainer.GetFirstEPGDate());
++ CDateTime lastDate; lastDate.SetFromUTCDateTime(g_EpgContainer.GetLastEPGDate());
++ m_parent->m_guideGrid = (CGUIEPGGridContainer*) m_parent->GetControl(CONTROL_LIST_TIMELINE);
++ if (!m_parent->m_guideGrid)
++ return;
++
++ m_parent->SetLabel(m_iControlButton, g_localizeStrings.Get(19222) + ": " + g_localizeStrings.Get(19032));
++ m_parent->SetLabel(CONTROL_LABELGROUP, g_localizeStrings.Get(19032));
++
++ g_PVRManager.GetPlayingGroup(bRadio)->GetEPGAll(*m_parent->m_vecItems);
++ m_parent->m_vecItems->RemoveDiscCache(m_parent->GetID());
++
++ m_parent->m_guideGrid->SetStartEnd(firstDate > gridStart ? firstDate : gridStart, lastDate);
++ m_parent->m_viewControl.SetCurrentView(CONTROL_LIST_TIMELINE);
++ SelectPlayingFile();
++}
++
++bool CGUIWindowPVRGuide::SelectPlayingFile(void)
++{
++ if (m_iGuideView == GUIDE_VIEW_TIMELINE)
++ {
++ if (m_parent->m_guideGrid && g_PVRManager.IsPlaying())
++ m_parent->m_guideGrid->SetChannel(g_application.CurrentFile());
++ return true;
++ }
++ return false;
++}
++
++void CGUIWindowPVRGuide::UpdateData(void)
++{
++ CSingleLock lock(m_critSection);
++ CLog::Log(LOGDEBUG, "CGUIWindowPVRGuide - %s - update window '%s'. set view to %d", __FUNCTION__, GetName(), m_iControlList);
++
++ g_EpgContainer.RegisterObserver(this);
++ m_bUpdateRequired = false;
++
++ /* lock the graphics context while updating */
++ CSingleLock graphicsLock(g_graphicsContext);
++ m_parent->m_viewControl.Clear();
++ m_parent->m_vecItems->Clear();
++
++ if (m_iGuideView == GUIDE_VIEW_CHANNEL)
++ UpdateViewChannel();
++ else if (m_iGuideView == GUIDE_VIEW_NOW)
++ UpdateViewNow();
++ else if (m_iGuideView == GUIDE_VIEW_NEXT)
++ UpdateViewNext();
++ else if (m_iGuideView == GUIDE_VIEW_TIMELINE)
++ UpdateViewTimeline();
++
++ m_parent->SetLabel(CONTROL_LABELHEADER, g_localizeStrings.Get(19222));
++ UpdateButtons();
++}
++
++bool CGUIWindowPVRGuide::IsSelectedButton(CGUIMessage &message) const
++{
++ unsigned int iControl = message.GetSenderId();
++ return (iControl == CONTROL_BTNGUIDE ||
++ iControl == CONTROL_BTNGUIDE_CHANNEL ||
++ iControl == CONTROL_BTNGUIDE_NOW ||
++ iControl == CONTROL_BTNGUIDE_NEXT ||
++ iControl == CONTROL_BTNGUIDE_TIMELINE);
++}
++
++bool CGUIWindowPVRGuide::IsSelectedList(CGUIMessage &message) const
++{
++ return ((message.GetSenderId() == CONTROL_LIST_TIMELINE && m_iGuideView == GUIDE_VIEW_TIMELINE) ||
++ (message.GetSenderId() == CONTROL_LIST_GUIDE_CHANNEL && m_iGuideView == GUIDE_VIEW_CHANNEL) ||
++ (message.GetSenderId() == CONTROL_LIST_GUIDE_NOW_NEXT && (m_iGuideView == GUIDE_VIEW_NOW || m_iGuideView == GUIDE_VIEW_NEXT)));
++}
++
++bool CGUIWindowPVRGuide::OnClickButton(CGUIMessage &message)
++{
++ bool bReturn = false;
++
++ if (IsSelectedButton(message))
++ {
++ unsigned int iControl = message.GetSenderId();
++ bReturn = true;
++
++ if (iControl == CONTROL_BTNGUIDE)
++ {
++ if (++m_iGuideView > GUIDE_VIEW_TIMELINE)
++ m_iGuideView = GUIDE_VIEW_CHANNEL;
++ }
++ else if (iControl == CONTROL_BTNGUIDE_CHANNEL)
++ m_iGuideView = GUIDE_VIEW_CHANNEL;
++ else if (iControl == CONTROL_BTNGUIDE_NOW)
++ m_iGuideView = GUIDE_VIEW_NOW;
++ else if (iControl == CONTROL_BTNGUIDE_NEXT)
++ m_iGuideView = GUIDE_VIEW_NEXT;
++ else if (iControl == CONTROL_BTNGUIDE_TIMELINE)
++ m_iGuideView = GUIDE_VIEW_TIMELINE;
++ else
++ bReturn = false;
++
++ if (bReturn)
++ UpdateData();
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRGuide::OnClickList(CGUIMessage &message)
++{
++ bool bReturn = false;
++
++ if (IsSelectedList(message))
++ {
++ int iAction = message.GetParam1();
++ int iItem = m_parent->m_viewControl.GetSelectedItem();
++
++ /* get the fileitem pointer */
++ if (iItem < 0 || iItem >= (int) m_parent->m_vecItems->Size())
++ return bReturn;
++ CFileItemPtr pItem = m_parent->m_vecItems->Get(iItem);
++
++ /* process actions */
++ bReturn = true;
++ if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK)
++ {
++ if (g_advancedSettings.m_bPVRShowEpgInfoOnEpgItemSelect)
++ ShowEPGInfo(pItem.get());
++ else
++ PlayEpgItem(pItem.get());
++ }
++ else if (iAction == ACTION_SHOW_INFO)
++ ShowEPGInfo(pItem.get());
++ else if (iAction == ACTION_RECORD)
++ ActionRecord(pItem.get());
++ else if (iAction == ACTION_PLAY)
++ ActionPlayEpg(pItem.get());
++ else if (iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK)
++ m_parent->OnPopupMenu(iItem);
++ else
++ bReturn = false;
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRGuide::OnContextButtonBegin(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_BEGIN)
++ {
++ CGUIWindowPVR *pWindow = (CGUIWindowPVR *) g_windowManager.GetWindow(WINDOW_PVR);
++ if (pWindow)
++ pWindow->m_guideGrid->GoToBegin();
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRGuide::OnContextButtonEnd(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_END)
++ {
++ CGUIWindowPVR *pWindow = (CGUIWindowPVR *) g_windowManager.GetWindow(WINDOW_PVR);
++ if (pWindow)
++ pWindow->m_guideGrid->GoToEnd();
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRGuide::OnContextButtonInfo(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_INFO)
++ {
++ ShowEPGInfo(item);
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRGuide::PlayEpgItem(CFileItem *item)
++{
++ const CPVRChannel *channel = !item || !item->HasEPGInfoTag() || !item->GetEPGInfoTag()->HasPVRChannel() ?
++ NULL : item->GetEPGInfoTag()->ChannelTag();
++ if (!channel)
++ return false;
++
++ CLog::Log(LOGDEBUG, "play channel '%s'", channel->ChannelName().c_str());
++ bool bReturn = g_application.PlayFile(CFileItem(*channel));
++ if (!bReturn)
++ CGUIDialogOK::ShowAndGetInput(19033,0,19035,0);
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRGuide::OnContextButtonPlay(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_PLAY_ITEM)
++ {
++ bReturn = PlayEpgItem(item);
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRGuide::OnContextButtonStartRecord(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_START_RECORD)
++ {
++ StartRecordFile(item);
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRGuide::OnContextButtonStopRecord(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_STOP_RECORD)
++ {
++ StopRecordFile(item);
++ bReturn = true;
++ }
++
++ return bReturn;
++}
++
++void CGUIWindowPVRGuide::UpdateButtons(void)
++{
++ if (m_iGuideView == GUIDE_VIEW_CHANNEL)
++ m_parent->SetLabel(m_iControlButton, g_localizeStrings.Get(19222) + ": " + g_localizeStrings.Get(19029));
++ else if (m_iGuideView == GUIDE_VIEW_NOW)
++ m_parent->SetLabel(m_iControlButton, g_localizeStrings.Get(19222) + ": " + g_localizeStrings.Get(19030));
++ else if (m_iGuideView == GUIDE_VIEW_NEXT)
++ m_parent->SetLabel(m_iControlButton, g_localizeStrings.Get(19222) + ": " + g_localizeStrings.Get(19031));
++ else if (m_iGuideView == GUIDE_VIEW_TIMELINE)
++ m_parent->SetLabel(m_iControlButton, g_localizeStrings.Get(19222) + ": " + g_localizeStrings.Get(19032));
++}
+diff --git a/xbmc/pvr/windows/GUIWindowPVRGuide.h b/xbmc/pvr/windows/GUIWindowPVRGuide.h
+new file mode 100644
+index 0000000..f34d0ca
+--- /dev/null
++++ b/xbmc/pvr/windows/GUIWindowPVRGuide.h
+@@ -0,0 +1,72 @@
++#pragma once
++
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "GUIWindowPVRCommon.h"
++#include "epg/GUIEPGGridContainer.h"
++#include "threads/CriticalSection.h"
++#include "utils/Observer.h"
++
++namespace PVR
++{
++ class CGUIWindowPVR;
++
++ class CGUIWindowPVRGuide : public CGUIWindowPVRCommon, public Observer
++ {
++ friend class CGUIWindowPVR;
++
++ public:
++ CGUIWindowPVRGuide(CGUIWindowPVR *parent);
++ virtual ~CGUIWindowPVRGuide(void) {};
++
++ virtual void GetContextButtons(int itemNumber, CContextButtons &buttons) const;
++ virtual bool OnContextButton(int itemNumber, CONTEXT_BUTTON button);
++ virtual void UpdateData(void);
++ virtual void Notify(const Observable &obs, const CStdString& msg);
++ virtual void SetInvalid(void) { UpdateData(); }
++ virtual void UnregisterObservers(void);
++ virtual void ResetObservers(void);
++
++ private:
++ virtual bool SelectPlayingFile(void);
++ virtual bool IsSelectedButton(CGUIMessage &message) const;
++ virtual bool IsSelectedList(CGUIMessage &message) const;
++ virtual bool OnClickButton(CGUIMessage &message);
++ virtual bool OnClickList(CGUIMessage &message);
++ virtual bool PlayEpgItem(CFileItem *item);
++
++ virtual bool OnContextButtonBegin(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonEnd(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonInfo(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonPlay(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonStartRecord(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonStopRecord(CFileItem *item, CONTEXT_BUTTON button);
++
++ virtual void UpdateButtons(void);
++ virtual void UpdateViewChannel(void);
++ virtual void UpdateViewNow(void);
++ virtual void UpdateViewNext(void);
++ virtual void UpdateViewTimeline(void);
++
++ int m_iGuideView;
++ };
++}
+diff --git a/xbmc/pvr/windows/GUIWindowPVRRecordings.cpp b/xbmc/pvr/windows/GUIWindowPVRRecordings.cpp
+new file mode 100644
+index 0000000..da7665f
+--- /dev/null
++++ b/xbmc/pvr/windows/GUIWindowPVRRecordings.cpp
+@@ -0,0 +1,322 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "GUIWindowPVRRecordings.h"
++
++#include "dialogs/GUIDialogKeyboard.h"
++#include "dialogs/GUIDialogYesNo.h"
++#include "guilib/GUIWindowManager.h"
++#include "guilib/LocalizeStrings.h"
++#include "GUIInfoManager.h"
++#include "pvr/PVRManager.h"
++#include "pvr/recordings/PVRRecordings.h"
++#include "pvr/timers/PVRTimers.h"
++#include "pvr/windows/GUIWindowPVR.h"
++#include "utils/log.h"
++#include "utils/StringUtils.h"
++#include "threads/SingleLock.h"
++#include "video/VideoDatabase.h"
++
++using namespace PVR;
++
++CGUIWindowPVRRecordings::CGUIWindowPVRRecordings(CGUIWindowPVR *parent) :
++ CGUIWindowPVRCommon(parent, PVR_WINDOW_RECORDINGS, CONTROL_BTNRECORDINGS, CONTROL_LIST_RECORDINGS)
++{
++ m_strSelectedPath = "pvr://recordings/";
++}
++
++void CGUIWindowPVRRecordings::UnregisterObservers(void)
++{
++ CSingleLock lock(m_critSection);
++ if(g_PVRRecordings)
++ g_PVRRecordings->UnregisterObserver(this);
++ if(g_PVRTimers)
++ g_PVRTimers->UnregisterObserver(this);
++ g_infoManager.UnregisterObserver(this);
++}
++
++void CGUIWindowPVRRecordings::ResetObservers(void)
++{
++ CSingleLock lock(m_critSection);
++ g_PVRRecordings->RegisterObserver(this);
++ g_PVRTimers->RegisterObserver(this);
++ g_infoManager.RegisterObserver(this);
++}
++
++CStdString CGUIWindowPVRRecordings::GetResumeString(CFileItem item)
++{
++ CStdString resumeString;
++ if (item.IsPVRRecording())
++ {
++ CVideoDatabase db;
++ if (db.Open())
++ {
++ CBookmark bookmark;
++ CStdString itemPath(item.GetPVRRecordingInfoTag()->m_strFileNameAndPath);
++ if (db.GetResumeBookMark(itemPath, bookmark) )
++ resumeString.Format(g_localizeStrings.Get(12022).c_str(), StringUtils::SecondsToTimeString(lrint(bookmark.timeInSeconds)).c_str());
++ db.Close();
++ }
++ }
++ return resumeString;
++}
++
++void CGUIWindowPVRRecordings::GetContextButtons(int itemNumber, CContextButtons &buttons) const
++{
++ if (itemNumber < 0 || itemNumber >= m_parent->m_vecItems->Size())
++ return;
++ CFileItemPtr pItem = m_parent->m_vecItems->Get(itemNumber);
++
++ buttons.Add(CONTEXT_BUTTON_INFO, 19053); /* Get Information of this recording */
++ buttons.Add(CONTEXT_BUTTON_FIND, 19003); /* Find similar program */
++ buttons.Add(CONTEXT_BUTTON_PLAY_ITEM, 12021); /* Play this recording */
++ CStdString resumeString = GetResumeString(*pItem);
++ if (!resumeString.IsEmpty())
++ {
++ buttons.Add(CONTEXT_BUTTON_RESUME_ITEM, resumeString);
++ }
++ buttons.Add(CONTEXT_BUTTON_RENAME, 118); /* Rename this recording */
++ buttons.Add(CONTEXT_BUTTON_DELETE, 117); /* Delete this recording */
++ buttons.Add(CONTEXT_BUTTON_SORTBY_NAME, 103); /* sort by name */
++ buttons.Add(CONTEXT_BUTTON_SORTBY_DATE, 104); /* sort by date */
++ // Update sort by button
++//if (m_guiState->GetSortMethod()!=SORT_METHOD_NONE)
++//{
++// CStdString sortLabel;
++// sortLabel.Format(g_localizeStrings.Get(550).c_str(), g_localizeStrings.Get(m_guiState->GetSortMethodLabel()).c_str());
++// buttons.Add(CONTEXT_BUTTON_SORTBY, sortLabel); /* Sort method */
++//
++// if (m_guiState->GetDisplaySortOrder()==SORT_ORDER_ASC)
++// buttons.Add(CONTEXT_BUTTON_SORTASC, 584); /* Sort up or down */
++// else
++// buttons.Add(CONTEXT_BUTTON_SORTASC, 585); /* Sort up or down */
++//}
++}
++
++bool CGUIWindowPVRRecordings::OnAction(const CAction &action)
++{
++ if (action.GetID() == ACTION_PARENT_DIR ||
++ action.GetID() == ACTION_NAV_BACK)
++ {
++ if (m_parent->m_vecItems->GetPath() != "pvr://recordings/")
++ m_parent->GoParentFolder();
++ else
++ g_windowManager.PreviousWindow();
++
++ return true;
++ }
++
++ return CGUIWindowPVRCommon::OnAction(action);
++}
++
++bool CGUIWindowPVRRecordings::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
++{
++ if (itemNumber < 0 || itemNumber >= m_parent->m_vecItems->Size())
++ return false;
++ CFileItemPtr pItem = m_parent->m_vecItems->Get(itemNumber);
++
++ return OnContextButtonPlay(pItem.get(), button) ||
++ OnContextButtonRename(pItem.get(), button) ||
++ OnContextButtonDelete(pItem.get(), button) ||
++ OnContextButtonInfo(pItem.get(), button) ||
++ CGUIWindowPVRCommon::OnContextButton(itemNumber, button);
++}
++
++void CGUIWindowPVRRecordings::OnWindowUnload(void)
++{
++ m_strSelectedPath = m_parent->m_vecItems->GetPath();
++ CGUIWindowPVRCommon::OnWindowUnload();
++}
++
++void CGUIWindowPVRRecordings::UpdateData(void)
++{
++ CSingleLock lock(m_critSection);
++ CLog::Log(LOGDEBUG, "CGUIWindowPVRRecordings - %s - update window '%s'. set view to %d", __FUNCTION__, GetName(), m_iControlList);
++ m_bUpdateRequired = false;
++
++ g_PVRRecordings->RegisterObserver(this);
++ g_PVRTimers->RegisterObserver(this);
++
++ /* lock the graphics context while updating */
++ CSingleLock graphicsLock(g_graphicsContext);
++
++ m_iSelected = m_parent->m_viewControl.GetSelectedItem();
++ m_parent->m_viewControl.Clear();
++ m_parent->m_vecItems->Clear();
++ m_parent->m_viewControl.SetCurrentView(m_iControlList);
++ m_parent->m_vecItems->SetPath("pvr://recordings/");
++ m_parent->Update(m_strSelectedPath);
++ m_parent->m_viewControl.SetItems(*m_parent->m_vecItems);
++ if (!SelectPlayingFile())
++ m_parent->m_viewControl.SetSelectedItem(m_iSelected);
++
++ m_parent->SetLabel(CONTROL_LABELHEADER, g_localizeStrings.Get(19017));
++ m_parent->SetLabel(CONTROL_LABELGROUP, "");
++}
++
++void CGUIWindowPVRRecordings::Notify(const Observable &obs, const CStdString& msg)
++{
++ if (msg.Equals("recordings") || msg.Equals("timers") || msg.Equals("current-item"))
++ {
++ if (IsVisible())
++ SetInvalid();
++ else
++ m_bUpdateRequired = true;
++ }
++ else if (msg.Equals("recordings-reset") || msg.Equals("timers-reset"))
++ {
++ if (IsVisible())
++ UpdateData();
++ else
++ m_bUpdateRequired = true;
++ }
++}
++
++bool CGUIWindowPVRRecordings::OnClickButton(CGUIMessage &message)
++{
++ bool bReturn = false;
++
++ if (IsSelectedButton(message))
++ {
++ bReturn = true;
++ g_PVRManager.TriggerRecordingsUpdate();
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRRecordings::OnClickList(CGUIMessage &message)
++{
++ bool bReturn = false;
++
++ if (IsSelectedList(message))
++ {
++ bReturn = true;
++ int iAction = message.GetParam1();
++ int iItem = m_parent->m_viewControl.GetSelectedItem();
++
++ /* get the fileitem pointer */
++ if (iItem < 0 || iItem >= (int) m_parent->m_vecItems->Size())
++ return bReturn;
++ CFileItemPtr pItem = m_parent->m_vecItems->Get(iItem);
++
++ /* process actions */
++ if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK || iAction == ACTION_PLAY)
++ {
++ int choice = CONTEXT_BUTTON_PLAY_ITEM;
++ CStdString resumeString = GetResumeString(*pItem);
++ if (!resumeString.IsEmpty())
++ {
++ CContextButtons choices;
++ choices.Add(CONTEXT_BUTTON_RESUME_ITEM, resumeString);
++ choices.Add(CONTEXT_BUTTON_PLAY_ITEM, 12021);
++ choice = CGUIDialogContextMenu::ShowAndGetChoice(choices);
++ }
++ if (choice < 0)
++ bReturn = true;
++ else
++ bReturn = OnContextButtonPlay(pItem.get(), (CONTEXT_BUTTON)choice);
++ }
++ else if (iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK)
++ m_parent->OnPopupMenu(iItem);
++ else if (iAction == ACTION_SHOW_INFO)
++ ShowRecordingInfo(pItem.get());
++ else if (iAction == ACTION_DELETE_ITEM)
++ bReturn = ActionDeleteRecording(pItem.get());
++ else
++ bReturn = false;
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRRecordings::OnContextButtonDelete(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_DELETE)
++ {
++ bReturn = false;
++
++ CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
++ if (!pDialog)
++ return bReturn;
++ pDialog->SetHeading(122);
++ pDialog->SetLine(0, 19043);
++ pDialog->SetLine(1, "");
++ pDialog->SetLine(2, item->GetPVRRecordingInfoTag()->m_strTitle);
++ pDialog->DoModal();
++
++ if (!pDialog->IsConfirmed())
++ return bReturn;
++
++ bReturn = g_PVRRecordings->DeleteRecording(*item);
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRRecordings::OnContextButtonInfo(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_INFO)
++ {
++ bReturn = true;
++ ShowRecordingInfo(item);
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRRecordings::OnContextButtonPlay(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if ((button == CONTEXT_BUTTON_PLAY_ITEM) ||
++ (button == CONTEXT_BUTTON_RESUME_ITEM))
++ {
++ item->m_lStartOffset = button == CONTEXT_BUTTON_RESUME_ITEM ? STARTOFFSET_RESUME : 0;
++ bReturn = PlayFile(item, false); /* play recording */
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRRecordings::OnContextButtonRename(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_RENAME)
++ {
++ bReturn = true;
++
++ CPVRRecording *recording = item->GetPVRRecordingInfoTag();
++ CStdString strNewName = recording->m_strTitle;
++ if (CGUIDialogKeyboard::ShowAndGetInput(strNewName, g_localizeStrings.Get(19041), false))
++ {
++ if (g_PVRRecordings->RenameRecording(*item, strNewName))
++ UpdateData();
++ }
++ }
++
++ return bReturn;
++}
+diff --git a/xbmc/pvr/windows/GUIWindowPVRRecordings.h b/xbmc/pvr/windows/GUIWindowPVRRecordings.h
+new file mode 100644
+index 0000000..c31fb6c
+--- /dev/null
++++ b/xbmc/pvr/windows/GUIWindowPVRRecordings.h
+@@ -0,0 +1,62 @@
++#pragma once
++
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "GUIWindowPVRCommon.h"
++#include "utils/Observer.h"
++
++namespace PVR
++{
++ class CGUIWindowPVR;
++
++ class CGUIWindowPVRRecordings : public CGUIWindowPVRCommon, private Observer
++ {
++ friend class CGUIWindowPVR;
++
++ public:
++ CGUIWindowPVRRecordings(CGUIWindowPVR *parent);
++ virtual ~CGUIWindowPVRRecordings(void) {};
++
++ static CStdString GetResumeString(CFileItem item);
++
++ virtual void GetContextButtons(int itemNumber, CContextButtons &buttons) const;
++ virtual bool OnAction(const CAction &action);
++ virtual bool OnContextButton(int itemNumber, CONTEXT_BUTTON button);
++ virtual void OnWindowUnload(void);
++ virtual void UpdateData(void);
++ virtual void Notify(const Observable &obs, const CStdString& msg);
++ virtual void UnregisterObservers(void);
++ virtual void ResetObservers(void);
++
++ private:
++
++ virtual bool OnClickButton(CGUIMessage &message);
++ virtual bool OnClickList(CGUIMessage &message);
++
++ virtual bool OnContextButtonDelete(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonInfo(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonPlay(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonRename(CFileItem *item, CONTEXT_BUTTON button);
++
++ CStdString m_strSelectedPath;
++ };
++}
+diff --git a/xbmc/pvr/windows/GUIWindowPVRSearch.cpp b/xbmc/pvr/windows/GUIWindowPVRSearch.cpp
+new file mode 100644
+index 0000000..3f2cc39
+--- /dev/null
++++ b/xbmc/pvr/windows/GUIWindowPVRSearch.cpp
+@@ -0,0 +1,288 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "GUIWindowPVRSearch.h"
++
++#include "dialogs/GUIDialogOK.h"
++#include "dialogs/GUIDialogProgress.h"
++#include "guilib/GUIWindowManager.h"
++#include "pvr/PVRManager.h"
++#include "pvr/channels/PVRChannelGroupsContainer.h"
++#include "pvr/dialogs/GUIDialogPVRGuideSearch.h"
++#include "epg/EpgContainer.h"
++#include "pvr/recordings/PVRRecordings.h"
++#include "GUIWindowPVR.h"
++#include "utils/log.h"
++#include "pvr/addons/PVRClients.h"
++
++using namespace PVR;
++using namespace EPG;
++
++CGUIWindowPVRSearch::CGUIWindowPVRSearch(CGUIWindowPVR *parent) :
++ CGUIWindowPVRCommon(parent, PVR_WINDOW_SEARCH, CONTROL_BTNSEARCH, CONTROL_LIST_SEARCH),
++ m_bSearchStarted(false),
++ m_bSearchConfirmed(false)
++{
++}
++
++void CGUIWindowPVRSearch::GetContextButtons(int itemNumber, CContextButtons &buttons) const
++{
++ if (itemNumber < 0 || itemNumber >= m_parent->m_vecItems->Size())
++ return;
++ CFileItemPtr pItem = m_parent->m_vecItems->Get(itemNumber);
++
++ if (pItem->GetLabel() != g_localizeStrings.Get(19027))
++ {
++ if (pItem->GetEPGInfoTag()->EndAsLocalTime() > CDateTime::GetCurrentDateTime())
++ {
++ if (!pItem->GetEPGInfoTag()->HasTimer())
++ {
++ if (pItem->GetEPGInfoTag()->StartAsLocalTime() < CDateTime::GetCurrentDateTime())
++ buttons.Add(CONTEXT_BUTTON_START_RECORD, 264); /* RECORD programme */
++ else
++ buttons.Add(CONTEXT_BUTTON_START_RECORD, 19061); /* Create a Timer */
++ }
++ else
++ {
++ if (pItem->GetEPGInfoTag()->StartAsLocalTime() < CDateTime::GetCurrentDateTime())
++ buttons.Add(CONTEXT_BUTTON_STOP_RECORD, 19059); /* Stop recording */
++ else
++ buttons.Add(CONTEXT_BUTTON_STOP_RECORD, 19060); /* Delete Timer */
++ }
++ }
++
++ buttons.Add(CONTEXT_BUTTON_INFO, 19047); /* Epg info button */
++ buttons.Add(CONTEXT_BUTTON_SORTBY_CHANNEL, 19062); /* Sort by channel */
++ buttons.Add(CONTEXT_BUTTON_SORTBY_NAME, 103); /* Sort by Name */
++ buttons.Add(CONTEXT_BUTTON_SORTBY_DATE, 104); /* Sort by Date */
++ buttons.Add(CONTEXT_BUTTON_CLEAR, 19232); /* Clear search results */
++ if (pItem->GetEPGInfoTag()->HasPVRChannel() &&
++ g_PVRClients->HasMenuHooks(pItem->GetEPGInfoTag()->ChannelTag()->ClientID()))
++ buttons.Add(CONTEXT_BUTTON_MENU_HOOKS, 19195); /* PVR client specific action */
++ }
++}
++
++bool CGUIWindowPVRSearch::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
++{
++ if (itemNumber < 0 || itemNumber >= m_parent->m_vecItems->Size())
++ return false;
++ CFileItemPtr pItem = m_parent->m_vecItems->Get(itemNumber);
++
++ return OnContextButtonClear(pItem.get(), button) ||
++ OnContextButtonInfo(pItem.get(), button) ||
++ OnContextButtonStopRecord(pItem.get(), button) ||
++ OnContextButtonStartRecord(pItem.get(), button) ||
++ CGUIWindowPVRCommon::OnContextButton(itemNumber, button);
++}
++
++void CGUIWindowPVRSearch::UpdateData(void)
++{
++ CLog::Log(LOGDEBUG, "CGUIWindowPVRSearch - %s - update window '%s'. set view to %d", __FUNCTION__, GetName(), m_iControlList);
++ m_bUpdateRequired = false;
++
++ /* lock the graphics context while updating */
++ CSingleLock graphicsLock(g_graphicsContext);
++
++ m_iSelected = m_parent->m_viewControl.GetSelectedItem();
++ m_parent->m_viewControl.Clear();
++ m_parent->m_vecItems->Clear();
++ m_parent->m_viewControl.SetCurrentView(m_iControlList);
++
++ if (m_bSearchConfirmed)
++ {
++ CGUIDialogProgress* dlgProgress = (CGUIDialogProgress*)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
++ if (dlgProgress)
++ {
++ dlgProgress->SetHeading(194);
++ dlgProgress->SetLine(0, m_searchfilter.m_strSearchTerm);
++ dlgProgress->SetLine(1, "");
++ dlgProgress->SetLine(2, "");
++ dlgProgress->StartModal();
++ dlgProgress->Progress();
++ }
++
++ // TODO get this from the selected channel group
++ g_EpgContainer.GetEPGSearch(*m_parent->m_vecItems, m_searchfilter);
++ if (dlgProgress)
++ dlgProgress->Close();
++
++ if (m_parent->m_vecItems->Size() == 0)
++ {
++ CGUIDialogOK::ShowAndGetInput(194, 284, 0, 0);
++ m_bSearchConfirmed = false;
++ }
++ }
++
++ if (m_parent->m_vecItems->Size() == 0)
++ {
++ CFileItemPtr item;
++ item.reset(new CFileItem("pvr://guide/searchresults/empty.epg", false));
++ item->SetLabel(g_localizeStrings.Get(19027));
++ item->SetLabelPreformated(true);
++ m_parent->m_vecItems->Add(item);
++ }
++ else
++ {
++ m_parent->m_vecItems->Sort(m_iSortMethod, m_iSortOrder);
++ }
++
++ m_parent->m_viewControl.SetItems(*m_parent->m_vecItems);
++ m_parent->m_viewControl.SetSelectedItem(m_iSelected);
++
++ m_parent->SetLabel(CONTROL_LABELHEADER, g_localizeStrings.Get(283));
++ m_parent->SetLabel(CONTROL_LABELGROUP, "");
++}
++
++bool CGUIWindowPVRSearch::OnClickButton(CGUIMessage &message)
++{
++ bool bReturn = false;
++
++ if (IsSelectedButton(message))
++ {
++ bReturn = true;
++ ShowSearchResults();
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRSearch::OnClickList(CGUIMessage &message)
++{
++ bool bReturn = false;
++
++ if (IsSelectedList(message))
++ {
++ bReturn = true;
++ int iAction = message.GetParam1();
++ int iItem = m_parent->m_viewControl.GetSelectedItem();
++
++ /* get the fileitem pointer */
++ if (iItem < 0 || iItem >= m_parent->m_vecItems->Size())
++ return bReturn;
++ CFileItemPtr pItem = m_parent->m_vecItems->Get(iItem);
++
++ /* process actions */
++ if (iAction == ACTION_SHOW_INFO || iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK)
++ ActionShowSearch(pItem.get());
++ else if (iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK)
++ m_parent->OnPopupMenu(iItem);
++ else if (iAction == ACTION_RECORD)
++ ActionRecord(pItem.get());
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRSearch::OnContextButtonClear(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_CLEAR)
++ {
++ bReturn = true;
++
++ m_bSearchStarted = false;
++ m_bSearchConfirmed = false;
++ m_searchfilter.Reset();
++
++ UpdateData();
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRSearch::OnContextButtonInfo(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_INFO)
++ {
++ bReturn = true;
++
++ ShowEPGInfo(item);
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRSearch::OnContextButtonStartRecord(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_START_RECORD)
++ {
++ bReturn = true;
++
++ StartRecordFile(item);
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRSearch::OnContextButtonStopRecord(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_STOP_RECORD)
++ {
++ bReturn = true;
++
++ StopRecordFile(item);
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRSearch::ActionShowSearch(CFileItem *item)
++{
++ if (item->GetPath() == "pvr://guide/searchresults/empty.epg")
++ ShowSearchResults();
++ else
++ ShowEPGInfo(item);
++
++ return true;
++}
++
++void CGUIWindowPVRSearch::ShowSearchResults()
++{
++ /* Load timer settings dialog */
++ CGUIDialogPVRGuideSearch* pDlgInfo = (CGUIDialogPVRGuideSearch*)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_GUIDE_SEARCH);
++
++ if (!pDlgInfo)
++ return;
++
++ if (!m_bSearchStarted)
++ {
++ m_bSearchStarted = true;
++ m_searchfilter.Reset();
++ }
++
++ pDlgInfo->SetFilterData(&m_searchfilter);
++
++ /* Open dialog window */
++ pDlgInfo->DoModal();
++
++ if (pDlgInfo->IsConfirmed())
++ {
++ m_bSearchConfirmed = true;
++ UpdateData();
++ }
++}
+diff --git a/xbmc/pvr/windows/GUIWindowPVRSearch.h b/xbmc/pvr/windows/GUIWindowPVRSearch.h
+new file mode 100644
+index 0000000..3d6ece4
+--- /dev/null
++++ b/xbmc/pvr/windows/GUIWindowPVRSearch.h
+@@ -0,0 +1,61 @@
++#pragma once
++
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "GUIWindowPVRCommon.h"
++#include "epg/EpgSearchFilter.h"
++
++namespace PVR
++{
++ class CGUIWindowPVR;
++
++ class CGUIWindowPVRSearch : public CGUIWindowPVRCommon
++ {
++ friend class CGUIWindowPVR;
++ friend class CGUIWindowPVRCommon;
++
++ public:
++ CGUIWindowPVRSearch(CGUIWindowPVR *parent);
++ virtual ~CGUIWindowPVRSearch(void) {};
++
++ virtual void GetContextButtons(int itemNumber, CContextButtons &buttons) const;
++ virtual bool OnContextButton(int itemNumber, CONTEXT_BUTTON button);
++ virtual void UpdateData(void);
++
++ private:
++
++ virtual bool OnClickButton(CGUIMessage &message);
++ virtual bool OnClickList(CGUIMessage &message);
++
++ virtual bool OnContextButtonClear(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonInfo(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonStartRecord(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonStopRecord(CFileItem *item, CONTEXT_BUTTON button);
++
++ virtual bool ActionShowSearch(CFileItem *item);
++ virtual void ShowSearchResults();
++
++ bool m_bSearchStarted;
++ bool m_bSearchConfirmed;
++ EPG::EpgSearchFilter m_searchfilter;
++ };
++}
+diff --git a/xbmc/pvr/windows/GUIWindowPVRTimers.cpp b/xbmc/pvr/windows/GUIWindowPVRTimers.cpp
+new file mode 100644
+index 0000000..3c73631
+--- /dev/null
++++ b/xbmc/pvr/windows/GUIWindowPVRTimers.cpp
+@@ -0,0 +1,284 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "GUIWindowPVRTimers.h"
++
++#include "dialogs/GUIDialogKeyboard.h"
++#include "dialogs/GUIDialogOK.h"
++#include "dialogs/GUIDialogYesNo.h"
++#include "guilib/GUIWindowManager.h"
++#include "pvr/PVRManager.h"
++#include "pvr/timers/PVRTimers.h"
++#include "pvr/addons/PVRClients.h"
++#include "GUIWindowPVR.h"
++#include "threads/SingleLock.h"
++
++using namespace PVR;
++
++CGUIWindowPVRTimers::CGUIWindowPVRTimers(CGUIWindowPVR *parent) :
++ CGUIWindowPVRCommon(parent, PVR_WINDOW_TIMERS, CONTROL_BTNTIMERS, CONTROL_LIST_TIMERS)
++{
++}
++
++void CGUIWindowPVRTimers::UnregisterObservers(void)
++{
++ CSingleLock lock(m_critSection);
++ if (g_PVRTimers)
++ g_PVRTimers->UnregisterObserver(this);
++}
++
++void CGUIWindowPVRTimers::ResetObservers(void)
++{
++ CSingleLock lock(m_critSection);
++ g_PVRTimers->RegisterObserver(this);
++}
++
++void CGUIWindowPVRTimers::GetContextButtons(int itemNumber, CContextButtons &buttons) const
++{
++ if (itemNumber < 0 || itemNumber >= m_parent->m_vecItems->Size())
++ return;
++ CFileItemPtr pItem = m_parent->m_vecItems->Get(itemNumber);
++
++ /* Check for a empty file item list, means only a
++ file item with the name "Add timer..." is present */
++ if (pItem->GetPath() == "pvr://timers/add.timer")
++ {
++ buttons.Add(CONTEXT_BUTTON_ADD, 19056); /* new timer */
++ if (m_parent->m_vecItems->Size() > 1)
++ {
++ buttons.Add(CONTEXT_BUTTON_SORTBY_NAME, 103); /* sort by name */
++ buttons.Add(CONTEXT_BUTTON_SORTBY_DATE, 104); /* sort by date */
++ }
++ }
++ else
++ {
++ buttons.Add(CONTEXT_BUTTON_EDIT, 19057); /* edit timer */
++ buttons.Add(CONTEXT_BUTTON_ADD, 19056); /* new timer */
++ buttons.Add(CONTEXT_BUTTON_ACTIVATE, 19058); /* activate/deactivate */
++ buttons.Add(CONTEXT_BUTTON_RENAME, 118); /* rename timer */
++ buttons.Add(CONTEXT_BUTTON_DELETE, 117); /* delete timer */
++ buttons.Add(CONTEXT_BUTTON_SORTBY_NAME, 103); /* sort by name */
++ buttons.Add(CONTEXT_BUTTON_SORTBY_DATE, 104); /* sort by date */
++ if (g_PVRClients->HasMenuHooks(pItem->GetPVRTimerInfoTag()->m_iClientId))
++ buttons.Add(CONTEXT_BUTTON_MENU_HOOKS, 19195); /* PVR client specific action */
++ }
++}
++
++bool CGUIWindowPVRTimers::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
++{
++ if (itemNumber < 0 || itemNumber >= m_parent->m_vecItems->Size())
++ return false;
++ CFileItemPtr pItem = m_parent->m_vecItems->Get(itemNumber);
++
++ return OnContextButtonActivate(pItem.get(), button) ||
++ OnContextButtonAdd(pItem.get(), button) ||
++ OnContextButtonDelete(pItem.get(), button) ||
++ OnContextButtonEdit(pItem.get(), button) ||
++ OnContextButtonRename(pItem.get(), button) ||
++ CGUIWindowPVRCommon::OnContextButton(itemNumber, button);
++}
++
++void CGUIWindowPVRTimers::UpdateData(void)
++{
++ CSingleLock lock(m_critSection);
++ CLog::Log(LOGDEBUG, "CGUIWindowPVRTimers - %s - update window '%s'. set view to %d", __FUNCTION__, GetName(), m_iControlList);
++ m_bUpdateRequired = false;
++
++ g_PVRTimers->RegisterObserver(this);
++
++ /* lock the graphics context while updating */
++ CSingleLock graphicsLock(g_graphicsContext);
++
++ m_iSelected = m_parent->m_viewControl.GetSelectedItem();
++ m_parent->m_viewControl.Clear();
++ m_parent->m_vecItems->Clear();
++ m_parent->m_viewControl.SetCurrentView(m_iControlList);
++ m_parent->m_vecItems->SetPath("pvr://timers/");
++ m_parent->Update(m_parent->m_vecItems->GetPath());
++ m_parent->m_vecItems->Sort(m_iSortMethod, m_iSortOrder);
++ m_parent->m_viewControl.SetItems(*m_parent->m_vecItems);
++ m_parent->m_viewControl.SetSelectedItem(m_iSelected);
++
++ m_parent->SetLabel(CONTROL_LABELHEADER, g_localizeStrings.Get(19025));
++ m_parent->SetLabel(CONTROL_LABELGROUP, "");
++}
++
++bool CGUIWindowPVRTimers::OnClickButton(CGUIMessage &message)
++{
++ bool bReturn = false;
++
++ if (IsSelectedButton(message))
++ {
++ bReturn = true;
++ g_PVRManager.TriggerTimersUpdate();
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRTimers::OnClickList(CGUIMessage &message)
++{
++ bool bReturn = false;
++
++ if (IsSelectedList(message))
++ {
++ bReturn = true;
++ int iAction = message.GetParam1();
++ int iItem = m_parent->m_viewControl.GetSelectedItem();
++
++ /* get the fileitem pointer */
++ if (iItem < 0 || iItem >= m_parent->m_vecItems->Size())
++ return bReturn;
++ CFileItemPtr pItem = m_parent->m_vecItems->Get(iItem);
++
++ /* process actions */
++ if (iAction == ACTION_SHOW_INFO || iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK)
++ ActionShowTimer(pItem.get());
++ else if (iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK)
++ m_parent->OnPopupMenu(iItem);
++ else if (iAction == ACTION_DELETE_ITEM)
++ ActionDeleteTimer(pItem.get());
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRTimers::OnContextButtonActivate(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_ACTIVATE)
++ {
++ bReturn = true;
++ if (!item->HasPVRTimerInfoTag())
++ return bReturn;
++
++ CPVRTimerInfoTag *timer = item->GetPVRTimerInfoTag();
++ int iLabelId;
++ if (timer->IsActive())
++ {
++ timer->m_state = PVR_TIMER_STATE_CANCELLED;
++ iLabelId = 13106;
++ }
++ else
++ {
++ timer->m_state = PVR_TIMER_STATE_SCHEDULED;
++ iLabelId = 305;
++ }
++
++ CGUIDialogOK::ShowAndGetInput(19033, 19040, 0, iLabelId);
++ g_PVRTimers->UpdateTimer(*item);
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRTimers::OnContextButtonAdd(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_ADD)
++ bReturn = ShowNewTimerDialog();
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRTimers::OnContextButtonDelete(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_DELETE)
++ {
++ bReturn = true;
++ if (!item->HasPVRTimerInfoTag())
++ return bReturn;
++
++ CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
++ if (!pDialog)
++ return bReturn;
++ pDialog->SetHeading(122);
++ pDialog->SetLine(0, 19040);
++ pDialog->SetLine(1, "");
++ pDialog->SetLine(2, item->GetPVRTimerInfoTag()->m_strTitle);
++ pDialog->DoModal();
++
++ if (!pDialog->IsConfirmed())
++ return bReturn;
++
++ g_PVRTimers->DeleteTimer(*item);
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRTimers::OnContextButtonEdit(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_EDIT)
++ {
++ bReturn = true;
++ if (!item->HasPVRTimerInfoTag())
++ return bReturn;
++
++ if (ShowTimerSettings(item))
++ g_PVRTimers->UpdateTimer(*item);
++ }
++
++ return bReturn;
++}
++
++bool CGUIWindowPVRTimers::OnContextButtonRename(CFileItem *item, CONTEXT_BUTTON button)
++{
++ bool bReturn = false;
++
++ if (button == CONTEXT_BUTTON_RENAME)
++ {
++ bReturn = true;
++ if (!item->HasPVRTimerInfoTag())
++ return bReturn;
++ CPVRTimerInfoTag *timer = item->GetPVRTimerInfoTag();
++
++ CStdString strNewName(timer->m_strTitle);
++ if (CGUIDialogKeyboard::ShowAndGetInput(strNewName, g_localizeStrings.Get(19042), false))
++ g_PVRTimers->RenameTimer(*item, strNewName);
++ }
++
++ return bReturn;
++}
++
++void CGUIWindowPVRTimers::Notify(const Observable &obs, const CStdString& msg)
++{
++ if (msg.Equals("timers"))
++ {
++ if (IsVisible())
++ SetInvalid();
++ else
++ m_bUpdateRequired = true;
++ }
++ else if (msg.Equals("timers-reset"))
++ {
++ if (IsVisible())
++ UpdateData();
++ else
++ m_bUpdateRequired = true;
++ }
++}
+diff --git a/xbmc/pvr/windows/GUIWindowPVRTimers.h b/xbmc/pvr/windows/GUIWindowPVRTimers.h
+new file mode 100644
+index 0000000..02117ab
+--- /dev/null
++++ b/xbmc/pvr/windows/GUIWindowPVRTimers.h
+@@ -0,0 +1,56 @@
++#pragma once
++
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "GUIWindowPVRCommon.h"
++#include "utils/Observer.h"
++
++namespace PVR
++{
++ class CGUIWindowPVR;
++
++ class CGUIWindowPVRTimers : public CGUIWindowPVRCommon, private Observer
++ {
++ friend class CGUIWindowPVR;
++
++ public:
++ CGUIWindowPVRTimers(CGUIWindowPVR *parent);
++ virtual ~CGUIWindowPVRTimers(void) {};
++
++ virtual void GetContextButtons(int itemNumber, CContextButtons &buttons) const;
++ virtual bool OnContextButton(int itemNumber, CONTEXT_BUTTON button);
++ virtual void UpdateData(void);
++ virtual void Notify(const Observable &obs, const CStdString& msg);
++ virtual void UnregisterObservers(void);
++ virtual void ResetObservers(void);
++
++ private:
++ virtual bool OnClickButton(CGUIMessage &message);
++ virtual bool OnClickList(CGUIMessage &message);
++
++ virtual bool OnContextButtonActivate(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonAdd(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonDelete(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonEdit(CFileItem *item, CONTEXT_BUTTON button);
++ virtual bool OnContextButtonRename(CFileItem *item, CONTEXT_BUTTON button);
++ };
++}
+diff --git a/xbmc/pvr/windows/Makefile b/xbmc/pvr/windows/Makefile
+new file mode 100644
+index 0000000..128eb24
+--- /dev/null
++++ b/xbmc/pvr/windows/Makefile
+@@ -0,0 +1,13 @@
++SRCS=GUIViewStatePVR.cpp \
++ GUIWindowPVR.cpp \
++ GUIWindowPVRChannels.cpp \
++ GUIWindowPVRCommon.cpp \
++ GUIWindowPVRGuide.cpp \
++ GUIWindowPVRRecordings.cpp \
++ GUIWindowPVRSearch.cpp \
++ GUIWindowPVRTimers.cpp
++
++LIB=pvrwindows.a
++
++include ../../../Makefile.include
++-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
+diff --git a/xbmc/pvrclients/Makefile.include.in b/xbmc/pvrclients/Makefile.include.in
+new file mode 100644
+index 0000000..fdacc34
+--- /dev/null
++++ b/xbmc/pvrclients/Makefile.include.in
+@@ -0,0 +1,29 @@
++#
++# Makefile include for PVR AddOns
++#
++
++## INCLUDES
++INCLUDES = -I. -I$(abs_top_srcdir)/xbmc -I$(abs_top_srcdir)/xbmc/cores/dvdplayer/DVDDemuxers -I$(abs_top_srcdir)/xbmc/addons/include
++INCLUDES += -I$(abs_top_srcdir)/xbmc/lib -I$(abs_top_srcdir)/addons/library.xbmc.pvr -I$(abs_top_srcdir)/addons/library.xbmc.addon
++ifneq (@USE_EXTERNAL_FFMPEG@,1)
++ INCLUDES += -I$(abs_top_srcdir)/xbmc/cores/dvdplayer/Codecs/ffmpeg
++endif
++ifeq ($(findstring Darwin,$(shell uname -a)), Darwin)
++ INCLUDES += -I/opt/local/include
++endif
++
++## DEFINES
++DEFINES += -D_LINUX -fPIC -DUSE_DEMUX
++
++## CXXFLAGS
++ifeq ($(findstring Darwin,$(shell uname -a)), Darwin)
++ CXXFLAGS ?= -g -O2 -Wall -Woverloaded-virtual -Wno-parentheses -dynamiclib -single_module -undefined dynamic_lookup
++else
++ CXXFLAGS ?= -g -O2 -Wall -Woverloaded-virtual -Wno-parentheses
++endif
++
++include @abs_top_srcdir@/Makefile.include
++-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
++
++%.pvr: $(OBJS)
++ $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared -g $(OBJS) $(LIBS) $(LIBDIRS) $(SILIB) -o $(LIB)
+diff --git a/xbmc/pvrclients/MediaPortal/Cards.cpp b/xbmc/pvrclients/MediaPortal/Cards.cpp
+new file mode 100644
+index 0000000..c86ae47
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/Cards.cpp
+@@ -0,0 +1,81 @@
++/*
++ * Copyright (C) 2005-2011 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 this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ */
++
++#include <stdlib.h>
++#include "Cards.h"
++#include "uri.h"
++#include "utils.h"
++
++bool CCards::ParseLines(vector<string>& lines)
++{
++ if (lines.size() == 0)
++ return false;
++
++ for (vector<string>::iterator it = lines.begin(); it < lines.end(); it++)
++ {
++ string& data(*it);
++
++ if (data.length() > 0)
++ {
++ vector<string> fields;
++ Card card;
++
++ uri::decode(data);
++ Tokenize(data, fields, "|");
++ // field 0 = idCard
++ // field 1 = devicePath
++ // field 2 = name
++ // field 3 = priority
++ // field 4 = grabEPG
++ // field 5 = lastEpgGrab (2000-01-01 00:00:00 = infinite)
++ // field 6 = recordingFolder
++ // field 7 = idServer
++ // field 8 = enabled
++ // field 9 = camType
++ // field 10 = timeshiftingFolder
++ // field 11 = recordingFormat
++ // field 12 = decryptLimit
++ // field 13 = preload
++ // field 14 = CAM
++ // field 15 = NetProvider
++ // field 16 = stopgraph
++ card.IdCard = atoi(fields[0].c_str());
++ card.DevicePath = fields[1];
++ card.Name = fields[2];
++ card.Priority = atoi(fields[3].c_str());
++ card.GrabEPG = stringtobool(fields[4]);
++ card.LastEpgGrab = DateTimeToTimeT(fields[5].c_str());
++ card.RecordingFolder = fields[6];
++ card.IdServer = atoi(fields[7].c_str());
++ card.Enabled = stringtobool(fields[8]);
++ card.CamType = atoi(fields[9].c_str());
++ card.TimeshiftingFolder = fields[10];
++ card.RecordingFormat = atoi(fields[11].c_str());
++ card.DecryptLimit = atoi(fields[12].c_str());
++ card.Preload = stringtobool(fields[13]);
++ card.CAM = stringtobool(fields[14]);
++ card.NetProvider = atoi(fields[15].c_str());
++ card.StopGraph = stringtobool(fields[16]);
++
++ push_back(card);
++ }
++ }
++
++ return true;
++}
+\ No newline at end of file
+diff --git a/xbmc/pvrclients/MediaPortal/Cards.h b/xbmc/pvrclients/MediaPortal/Cards.h
+new file mode 100644
+index 0000000..a037372
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/Cards.h
+@@ -0,0 +1,63 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2011 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 this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ */
++
++#include <vector>
++#include <string>
++#include <ctime>
++
++using namespace std;
++
++/**
++ * MediaPortal TVServer card settings ("card" table in the database)
++ */
++typedef struct Card
++{
++ int IdCard;
++ string DevicePath;
++ string Name;
++ int Priority;
++ bool GrabEPG;
++ time_t LastEpgGrab;
++ string RecordingFolder;
++ int IdServer;
++ bool Enabled;
++ int CamType;
++ string TimeshiftingFolder;
++ int RecordingFormat;
++ int DecryptLimit;
++ bool Preload;
++ bool CAM;
++ int NetProvider;
++ bool StopGraph;
++} Card;
++
++class CCards: public vector<Card>
++{
++ public:
++
++ /**
++ * \brief Parse the multi-line string response from the TVServerXBMC plugin command "GetCardSettings"
++ * The data is stored in "struct Card" item.
++ *
++ * \param lines Vector with response lines
++ * \return True on success, False on failure
++ */
++ bool ParseLines(vector<string>& lines);
++};
+diff --git a/xbmc/pvrclients/MediaPortal/FileUtils.h b/xbmc/pvrclients/MediaPortal/FileUtils.h
+new file mode 100644
+index 0000000..e530a3d
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/FileUtils.h
+@@ -0,0 +1,10 @@
++#include <string>
++
++namespace OS
++{
++ class CFile
++ {
++ public:
++ static bool Exists(const std::string& strFileName);
++ };
++};
+diff --git a/xbmc/pvrclients/MediaPortal/GenreTable.cpp b/xbmc/pvrclients/MediaPortal/GenreTable.cpp
+new file mode 100644
+index 0000000..124bf53
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/GenreTable.cpp
+@@ -0,0 +1,143 @@
++/*
++ * Copyright (C) 2005-2012 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 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ */
++
++#include "client.h"
++#include "GenreTable.h"
++#include "lib/tinyxml/tinyxml.h"
++
++using namespace ADDON;
++using namespace std;
++
++bool CGenreTable::LoadGenreXML(const std::string &filename)
++{
++ TiXmlDocument xmlDoc;
++ if (!xmlDoc.LoadFile(filename))
++ {
++ XBMC->Log(LOG_DEBUG, "Unable to load %s: %s at line %d", filename.c_str(), xmlDoc.ErrorDesc(), xmlDoc.ErrorRow());
++ return false;
++ }
++
++ XBMC->Log(LOG_DEBUG, "Opened %s to read genre string to type/subtype translation table", filename.c_str());
++
++ TiXmlHandle hDoc(&xmlDoc);
++ TiXmlElement* pElem;
++ TiXmlHandle hRoot(0);
++ string sGenre;
++ const char* sGenreType = NULL;
++ const char* sGenreSubType = NULL;
++ genre_t genre;
++
++ // block: genrestrings
++ pElem = hDoc.FirstChildElement("genrestrings").Element();
++ // should always have a valid root but handle gracefully if it does
++ if (!pElem)
++ {
++ XBMC->Log(LOG_DEBUG, "Could not find <genrestrings> element");
++ return false;
++ }
++
++ //This should hold: pElem->Value() == "genrestrings"
++
++ // save this for later
++ hRoot=TiXmlHandle(pElem);
++
++ // iterate through all genre elements
++ TiXmlElement* pGenreNode = hRoot.FirstChildElement("genre").Element();
++ //This should hold: pGenreNode->Value() == "genre"
++
++ if (!pGenreNode)
++ {
++ XBMC->Log(LOG_DEBUG, "Could not find <genre> element");
++ return false;
++ }
++
++ for (; pGenreNode != NULL; pGenreNode = pGenreNode->NextSiblingElement("genre"))
++ {
++ const char* sGenreString = pGenreNode->GetText();
++
++ if (sGenreString)
++ {
++ sGenreType = pGenreNode->Attribute("type");
++ sGenreSubType = pGenreNode->Attribute("subtype");
++
++ if ((sGenreType) && (strlen(sGenreType) > 2))
++ {
++ if(sscanf(sGenreType + 2, "%x", &genre.type) != 1)
++ genre.type = 0;
++ }
++ else
++ {
++ genre.type = 0;
++ }
++
++ if ((sGenreSubType) && (strlen(sGenreSubType) > 2 ))
++ {
++ if(sscanf(sGenreSubType + 2, "%x", &genre.subtype) != 1)
++ genre.subtype = 0;
++ }
++ else
++ {
++ genre.subtype = 0;
++ }
++
++ if (genre.type > 0)
++ {
++ XBMC->Log(LOG_DEBUG, "Genre '%s' => 0x%x, 0x%x", sGenreString, genre.type, genre.subtype);
++ m_genremap.insert(std::pair<std::string, genre_t>(sGenreString, genre));
++ }
++ }
++ }
++
++ return true;
++}
++
++void CGenreTable::GenreToTypes(string& strGenre, int& genreType, int& genreSubType)
++{
++ // The xmltv plugin from the MediaPortal TV Server can return genre
++ // strings in local language (depending on the external TV guide source).
++ // The only way to solve this at the XMBC side is to transfer the
++ // genre string to XBMC or to let this plugin (or the TVServerXBMC
++ // plugin) translate it into XBMC compatible (numbered) genre types
++ string m_genre = strGenre;
++
++ if(m_genremap.size() > 0 && m_genre.length() > 0)
++ {
++ GenreMap::iterator it;
++
++ std::transform(m_genre.begin(), m_genre.end(), m_genre.begin(), ::tolower);
++
++ it = m_genremap.find(m_genre);
++ if (it != m_genremap.end())
++ {
++ genreType = it->second.type;
++ genreSubType = it->second.subtype;
++ }
++ else
++ {
++ XBMC->Log(LOG_DEBUG, "EPG: No mapping of '%s' to genre type/subtype found.", strGenre.c_str());
++ genreType = EPG_GENRE_USE_STRING;
++ genreSubType = 0;
++ }
++ }
++ else
++ {
++ genreType = 0;
++ genreSubType = 0;
++ }
++}
+diff --git a/xbmc/pvrclients/MediaPortal/GenreTable.h b/xbmc/pvrclients/MediaPortal/GenreTable.h
+new file mode 100644
+index 0000000..c1ea268
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/GenreTable.h
+@@ -0,0 +1,45 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2012 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 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <map>
++#include <string>
++
++typedef struct genre {
++ int type;
++ int subtype;
++} genre_t;
++
++typedef std::map<std::string, genre_t> GenreMap;
++
++class CGenreTable
++{
++public:
++ CGenreTable(const std::string &filename) { LoadGenreXML(filename); };
++ bool LoadGenreXML(const std::string &filename);
++
++ /**
++ * \brief Convert a genre string into a type/subtype combination using the data in the GenreMap
++ * \param strGenre (in)
++ * \param genreType (out)
++ * \param genreSubType (out)
++ */
++ void GenreToTypes(std::string& strGenre, int& genreType, int& genreSubType);
++private:
++ GenreMap m_genremap;
++};
+diff --git a/xbmc/pvrclients/MediaPortal/Makefile.in b/xbmc/pvrclients/MediaPortal/Makefile.in
+new file mode 100644
+index 0000000..a0cf563
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/Makefile.in
+@@ -0,0 +1,33 @@
++#
++# Makefile for the XBMC MediaPortal PVR AddOn
++#
++# See the README for copyright information and
++# how to reach the author.
++#
++
++LIBS = lib/tinyxml/tinyxml.a -ldl
++LIBDIR = @abs_top_srcdir@/addons/pvr.team-mediaportal.tvserver
++LIB = $(LIBDIR)/XBMC_MPTV.pvr
++
++SRCS = channels.cpp \
++ client.cpp \
++ epg.cpp \
++ pvrclient-mediaportal.cpp \
++ recordings.cpp \
++ timers.cpp \
++ Socket.cpp \
++ uri.cpp \
++ utils.cpp \
++ Cards.cpp \
++ GenreTable.cpp
++
++include ../Makefile.include
++
++clean:
++ -rm -f $(OBJS) $(LIB) *.P *~
++ $(MAKE) -C lib/tinyxml clean
++
++
++$(LIB): $(OBJS)
++ $(MAKE) -C lib/tinyxml
++ $(SILENT_CPP) $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared -g $(OBJS) $(LIBS) $(LIBDIRS) $(SILIB) -o $(LIB)
+diff --git a/xbmc/pvrclients/MediaPortal/README b/xbmc/pvrclients/MediaPortal/README
+new file mode 100644
+index 0000000..b3551a5
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/README
+@@ -0,0 +1,162 @@
++XBMC MediaPortal TV-client ('MPTV') PVR Add-on
++----------------------------------------------
++Supported platforms (pvrclient):
++- Windows
++- Linux
++- OSX (should work, not tested by me)
++
++Dependencies:
++- MediaPortal TVServer 1.1.x or 1.2.x. May work also on newer versions
++- TVServerXBMC v1.1.0.100 or higher
++
++THIS IS A PRELIMINARY README AND IS SUBJECT TO CHANGE!!!
++
++Written by: Marcel Groothuis
++
++Project's homepage: http://www.scintilla.utwente.nl/~marcelg/xbmc/
++Latest version available at: project homepage
++
++The MediaPortal TV-Server plugin "TVServerXMBC", status updates, screenshots,
++last-minute patches can be found at:
++
++http://www.scintilla.utwente.nl/~marcelg/xbmc/
++
++This program is free software; you can redistribute 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
++any later version.
++See the file LICENSE.GPL for more information.
++
++----------------------------------------------
++General description:
++
++This is a PVR Add-on for XBMC to access/control the MediaPortal TV-Server
++backend from XBMC. It consists of two plugins: one for XBMC-PVR (1) and one for
++the MediaPortal TV Server (2).
++
++1. PVR client "XBMC_MPTV_win32.pvr" (for Windows):
++ This is the addon in this directory. After building the XBMC solution (and
++ the "pvrclient_mptv") this plugin file should be in addons\pvr\MediaPortal\
++
++The MediaPortal TV Server is written in C#, making it difficult for XBMC to
++control it directly. The TV Server connection of this AddOn depends on a
++special plugin at the TV Server side, called TVServerXBMC.
++
++
++2. TV Server plugin TVServerXBMC.dll/.exe (Windows only):
++ The TVServerXBMC plugin for the TV Server provides a socket interface to
++ XBMC to control the TV Server.
++ You can download it separately from:
++ http://www.scintilla.utwente.nl/~marcelg/xbmc
++ (It is not included by default in the XBMC pvr-testing2 branch, because it
++ will introduce a dependency on Visual C#.)
++ Please read the "readme.txt" file included in the TVServerXBMC zip files
++ for more information.
++
++----------------------------------------------
++Detailed instructions:
++
++(Preliminary...)
++
++1. Install MediaPortal & MediaPortal TV Server (1.1.0)
++ (for older versions, you will need to recompile the TVServerXBMC plugin from source)
++2. Use MediaPortal to make sure that the TV Server is working fine
++3. Download the TVServerXBMC plugin for the TV Server (see my website)
++4. Run the TVServerXBMC.exe (standalone version of the TV Server plugin) and
++ test it (see the readme.txt file included in the TVServerXBMC zip file).
++ Test for example the commands "ListTVChannels", "TimeshiftChannel"
++ (id is the first field returned by "ListTVChannels"). The "TimeshiftChannel"
++ command should return a URL like "rtsp://xxx.xxx.xxx.xx/stream2.0" on a
++ successful timeshift start. You can test this URL in VLC player. This should
++ work before proceeding to the next steps.
++5. Test the rtsp stream in XBMC (you can just use the standard 9.11)
++ Create a playlist file "rtsp-stream.m3u" with the rtsp:// URL as content and
++ start it from inside XBMC.
++ Example contents rtsp-stream.m3u:
++-----
++rtsp://192.168.2.5/steam2.0
++-----
++When this is all working fine, you can finally build XBMC-pvr-testing2:
++
++6. Build the XBMC solution in VC Express (pvr-testing2)
++ (check if XBMC_MPTV_win32.pvr was created succesfully in addons\pvr\MediaPortal)
++
++----------------------------------------------
++MediaPortal PVR-addon settings:
++
++Names are taken from addons/pvr/MediaPortal/settings.xml
++
++host: "Mediaportal Hostname"
++ IP-address of the machine that runs the TVServerXBMC tool
++ Default: 127.0.0.1 (localhost)
++port: "Mediaportal XBMC plugin Port"
++ Port number for the TVServerXBMC.
++ Default: 9596
++ftaonly: "Free-to-air only"
++ Fetch/show only Free-to-air channels from MediaPortal TV
++ Default: false
++useradio: "Include Radio"
++ Fetch also radio channels
++ Default: true
++convertchar: "Character Set Conversion"
++ Enable character conversion to UTF-8.
++ Does nothing. Not yet implemented.
++ Default: false
++timeout: "Connect timeout (s)"
++ Timeout on XBMC<->TVServerXBMC communication. After the selected timeout,
++ XBMC won't wait any longer for an answer from TVServerXBMC and abort the
++ selected action. Bottleneck is the timeshift start for TV channels. This
++ can take a long time, so don't make this
++ value too small.
++ Default: 6
++tvgroup: "Import only TV Channels from group"
++ Allows you to fetch only the TV channels in a specific MediaPortal TVServer
++ group. E.g. you can create a "XBMC" group at the TVServer side that contains
++ only the TV channels that you want to appear at the XBMC side.
++ Default: <empty>
++radiogroup: "Import only Radio Channels from group"
++ Allows you to fetch only the radio channels in a specific MediaPortal TVServer
++ group. E.g. you can create a "XBMC" group at the TVServer side that contains
++ only the radio channels that you want to appear at the XBMC side.
++ Default: <empty>
++resolvertsphostname: "Convert hostname to IP-address"
++ Resolve the TVServer hostname in the rtsp:// streaming URLs to an ip-address
++ at the TVServerXBMC side. May help you with connection problems.
++ Default: true
++readgenre: "EPG: Read genre strings (slow)"
++ Try to translate the EPG genre strings from MediaPortal into XBMC compatible
++ genre id's. However, depending on your EPG source, MediaPortal may return
++ strings in your local language. In this case, you can skip the genre
++ translation via readgenre=false.
++ The current implementation translates only English strings as workaround for
++ the mismatch between XBMC's genre ids and MediaPortals genre strings.
++ Default: false (= don't read and translate the genre strings)
++sleeponrtspurl: "Wait after tuning a channel (ms)"
++ Adds an additional waiting time between the request to start a timeshift for
++ the selected channel and opening the rtsp:// stream in XBMC. You may need this
++ in case XBMC tries to open the returned rtsp:// stream before it is really
++ available. Typical symptom: the channel doesn't play the first time, but it
++ does play the second time.
++ Default: 0 (milliseconds)
++userecordingsdir: "Play recordings directly (no streaming)"
++ By default, the recordings are played via rtsp:// streaming, which is not
++ needed when the TV Server and XBMC are running on the same machine.
++ When you enable this option, XBMC will use the filename of the recording
++ for playback instead of the rtsp::// url.
++ Default: false
++recordingsdir: "Mediaportal recordings directory"
++ The previous setting can also be used on a different pc by sharing the
++ recordings directory over the network. You can use this option to specify
++ where XBMC can find the recordings.
++ Default: <empty>
++----------------------------------------------
++Troubleshooting:
++TODO...
++ You can reach me on the XBMC forum, user: margro or via
++ IRC: #xbmc, #xbmc-pvr user: margro
++----------------------------------------------
++Links:
++
++MediaPortal: http://www.team-mediaportal.com
++TVServer plugin: http://www.scintilla.utwente.nl/~marcelg/xbmc
++
+diff --git a/xbmc/pvrclients/MediaPortal/Socket.cpp b/xbmc/pvrclients/MediaPortal/Socket.cpp
+new file mode 100644
+index 0000000..3da8d94
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/Socket.cpp
+@@ -0,0 +1,755 @@
++/*
++ * Copyright (C) 2005-2011 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 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ */
++#include "libXBMC_addon.h"
++#include "utils.h"
++#include <string>
++#include "os-dependent.h"
++#include "client.h"
++#include "Socket.h"
++
++using namespace std;
++using namespace ADDON;
++using namespace MPTV;
++
++namespace MPTV
++{
++
++/* Master defines for client control */
++#define RECEIVE_TIMEOUT 6 //sec
++
++Socket::Socket(const enum SocketFamily family, const enum SocketDomain domain, const enum SocketType type, const enum SocketProtocol protocol)
++{
++ _sd = INVALID_SOCKET;
++ _family = family;
++ _domain = domain;
++ _type = type;
++ _protocol = protocol;
++ memset (&_sockaddr, 0, sizeof( _sockaddr ) );
++}
++
++
++Socket::Socket()
++{
++ // Default constructor, default settings
++ _sd = INVALID_SOCKET;
++ _family = af_inet;
++ _domain = pf_inet;
++ _type = sock_stream;
++ _protocol = tcp;
++ memset (&_sockaddr, 0, sizeof( _sockaddr ) );
++}
++
++
++Socket::~Socket()
++{
++ close();
++}
++
++bool Socket::setHostname ( const std::string host )
++{
++ if (isalpha(host.c_str()[0]))
++ {
++ // host address is a name
++ struct hostent *he = NULL;
++ if ((he = gethostbyname( host.c_str() )) == 0)
++ {
++ errormessage( getLastError(), "Socket::setHostname");
++ return false;
++ }
++
++ _sockaddr.sin_addr = *((in_addr *) he->h_addr);
++ }
++ else
++ {
++ _sockaddr.sin_addr.s_addr = inet_addr(host.c_str());
++ }
++ return true;
++}
++
++bool Socket::close()
++{
++ if (is_valid())
++ {
++ if (_sd != SOCKET_ERROR)
++#ifdef TARGET_WINDOWS
++ closesocket(_sd);
++#else
++ ::close(_sd);
++#endif
++ _sd = INVALID_SOCKET;
++ osCleanup();
++ return true;
++ }
++ return false;
++}
++
++bool Socket::create()
++{
++ if( is_valid() )
++ {
++ close();
++ }
++
++ if(!osInit())
++ {
++ return false;
++ }
++
++ _sd = socket(_family, _type, _protocol );
++ //0 indicates that the default protocol for the type selected is to be used.
++ //For example, IPPROTO_TCP is chosen for the protocol if the type was set to
++ //SOCK_STREAM and the address family is AF_INET.
++
++ if (_sd == INVALID_SOCKET)
++ {
++ errormessage( getLastError(), "Socket::create" );
++ return false;
++ }
++
++ return true;
++}
++
++
++bool Socket::bind ( const unsigned short port )
++{
++
++ if (!is_valid())
++ {
++ return false;
++ }
++
++ _sockaddr.sin_family = _family;
++ _sockaddr.sin_addr.s_addr = INADDR_ANY; //listen to all
++ _sockaddr.sin_port = htons( port );
++
++ int bind_return = ::bind(_sd, (sockaddr*)(&_sockaddr), sizeof(_sockaddr));
++
++ if ( bind_return == -1 )
++ {
++ errormessage( getLastError(), "Socket::bind" );
++ return false;
++ }
++
++ return true;
++}
++
++
++bool Socket::listen() const
++{
++
++ if (!is_valid())
++ {
++ return false;
++ }
++
++ int listen_return = ::listen (_sd, SOMAXCONN);
++ //This is defined as 5 in winsock.h, and 0x7FFFFFFF in winsock2.h.
++ //linux 128//MAXCONNECTIONS =1
++
++ if (listen_return == -1)
++ {
++ errormessage( getLastError(), "Socket::listen" );
++ return false;
++ }
++
++ return true;
++}
++
++
++bool Socket::accept ( Socket& new_socket ) const
++{
++ if (!is_valid())
++ {
++ return false;
++ }
++
++ socklen_t addr_length = sizeof( _sockaddr );
++ new_socket._sd = ::accept(_sd, const_cast<sockaddr*>( (const sockaddr*) &_sockaddr), &addr_length );
++
++ if (new_socket._sd <= 0)
++ {
++ errormessage( getLastError(), "Socket::accept" );
++ return false;
++ }
++
++ return true;
++}
++
++
++int Socket::send ( const std::string data )
++{
++ if (!is_valid())
++ {
++ return 0;
++ }
++
++ int status = Socket::send( (const char*) data.c_str(), (const unsigned int) data.size());
++
++ return status;
++}
++
++
++int Socket::send ( const char* data, const unsigned int len )
++{
++ fd_set set_w, set_e;
++ struct timeval tv;
++ int result;
++
++ if (!is_valid())
++ {
++ return 0;
++ }
++
++ // fill with new data
++ tv.tv_sec = 0;
++ tv.tv_usec = 0;
++
++ FD_ZERO(&set_w);
++ FD_ZERO(&set_e);
++ FD_SET(_sd, &set_w);
++ FD_SET(_sd, &set_e);
++
++ result = select(FD_SETSIZE, &set_w, NULL, &set_e, &tv);
++
++ if (result < 0)
++ {
++ XBMC->Log(LOG_ERROR, "Socket::send - select failed");
++ _sd = INVALID_SOCKET;
++ return 0;
++ }
++ if (FD_ISSET(_sd, &set_w))
++ {
++ XBMC->Log(LOG_ERROR, "Socket::send - failed to send data");
++ _sd = INVALID_SOCKET;
++ return 0;
++ }
++
++ int status = ::send(_sd, data, len, 0 );
++
++ if (status == -1)
++ {
++ errormessage( getLastError(), "Socket::send");
++ XBMC->Log(LOG_ERROR, "Socket::send - failed to send data");
++ _sd = INVALID_SOCKET;
++ }
++ return status;
++}
++
++
++int Socket::sendto ( const char* data, unsigned int size, bool sendcompletebuffer)
++{
++ int sentbytes = 0;
++ int i;
++
++ do
++ {
++ i = ::sendto(_sd, data, size, 0, (const struct sockaddr*) &_sockaddr, sizeof( _sockaddr ) );
++
++ if (i <= 0)
++ {
++ errormessage( getLastError(), "Socket::sendto");
++ osCleanup();
++ return i;
++ }
++ sentbytes += i;
++ } while ( (sentbytes < (int) size) && (sendcompletebuffer == true));
++
++ return i;
++}
++
++
++int Socket::receive ( std::string& data, unsigned int minpacketsize ) const
++{
++ char * buf = NULL;
++ int status = 0;
++
++ if (!is_valid())
++ {
++ return 0;
++ }
++
++ buf = new char [ minpacketsize + 1 ];
++ memset ( buf, 0, minpacketsize + 1 );
++
++ status = receive( buf, minpacketsize, minpacketsize );
++
++ data = buf;
++
++ delete[] buf;
++ return status;
++}
++
++//Receive until error or \n
++bool Socket::ReadResponse (int &code, vector<string> &lines)
++{
++ fd_set set_r, set_e;
++ timeval timeout;
++ int result;
++ int retries = 6;
++ char buffer[2048];
++ char cont = 0;
++ string line;
++ size_t pos1 = 0, pos2 = 0, pos3 = 0;
++
++ code = 0;
++
++ while (true)
++ {
++ while ((pos1 = line.find("\r\n", pos3)) != std::string::npos)
++ {
++ pos2 = line.find(cont);
++
++ lines.push_back(line.substr(pos2+1, pos1-pos2-1));
++
++ line.erase(0, pos1 + 2);
++ pos3 = 0;
++ return true;
++ }
++
++ // we only need to recheck 1 byte
++ if (line.size() > 0)
++ {
++ pos3 = line.size() - 1;
++ }
++ else
++ {
++ pos3 = 0;
++ }
++
++ if (cont == ' ')
++ {
++ break;
++ }
++
++ timeout.tv_sec = RECEIVE_TIMEOUT;
++ timeout.tv_usec = 0;
++
++ // fill with new data
++ FD_ZERO(&set_r);
++ FD_ZERO(&set_e);
++ FD_SET(_sd, &set_r);
++ FD_SET(_sd, &set_e);
++ result = select(FD_SETSIZE, &set_r, NULL, &set_e, &timeout);
++
++ if (result < 0)
++ {
++ XBMC->Log(LOG_DEBUG, "CVTPTransceiver::ReadResponse - select failed");
++ lines.push_back("ERROR: Select failed");
++ code = 1; //error
++ _sd = INVALID_SOCKET;
++ return false;
++ }
++
++ if (result == 0)
++ {
++ if (retries != 0)
++ {
++ XBMC->Log(LOG_DEBUG, "CVTPTransceiver::ReadResponse - timeout waiting for response, retrying... (%i)", retries);
++ retries--;
++ continue;
++ } else {
++ XBMC->Log(LOG_DEBUG, "CVTPTransceiver::ReadResponse - timeout waiting for response. Failed after 10 retries.");
++ lines.push_back("ERROR: Failed after 10 retries");
++ code = 1; //error
++ _sd = INVALID_SOCKET;
++ return false;
++ }
++ }
++
++ result = recv(_sd, buffer, sizeof(buffer) - 1, 0);
++ if (result < 0)
++ {
++ XBMC->Log(LOG_DEBUG, "CVTPTransceiver::ReadResponse - recv failed");
++ lines.push_back("ERROR: Recv failed");
++ code = 1; //error
++ _sd = INVALID_SOCKET;
++ return false;
++ }
++ buffer[result] = 0;
++
++ line.append(buffer);
++ }
++
++ return true;
++}
++
++int Socket::receive ( std::string& data) const
++{
++ char buf[MAXRECV + 1];
++ int status = 0;
++
++ if ( !is_valid() )
++ {
++ return 0;
++ }
++
++ memset ( buf, 0, MAXRECV + 1 );
++ status = receive( buf, MAXRECV, 0 );
++ data = buf;
++
++ return status;
++}
++
++int Socket::receive ( char* data, const unsigned int buffersize, const unsigned int minpacketsize ) const
++{
++
++ unsigned int receivedsize = 0;
++ int status = 0;
++
++ if ( !is_valid() )
++ {
++ return 0;
++ }
++
++ while ( (receivedsize <= minpacketsize) && (receivedsize < buffersize) )
++ {
++ status = ::recv(_sd, data+receivedsize, (buffersize - receivedsize), 0 );
++
++ if ( status == SOCKET_ERROR )
++ {
++ errormessage( getLastError(), "Socket::receive" );
++ return status;
++ }
++
++ receivedsize += status;
++ }
++
++ return receivedsize;
++}
++
++
++int Socket::recvfrom ( char* data, const int buffersize, const int minpacketsize, struct sockaddr* from, socklen_t* fromlen) const
++{
++ int status = ::recvfrom(_sd, data, buffersize, 0, from, fromlen);
++
++ return status;
++}
++
++
++bool Socket::connect ( const std::string host, const unsigned short port )
++{
++ if ( !is_valid() )
++ {
++ return false;
++ }
++
++ _sockaddr.sin_family = _family;
++ _sockaddr.sin_port = htons ( port );
++
++ if ( !setHostname( host ) )
++ {
++ XBMC->Log(LOG_ERROR, "Socket::setHostname(%s) failed.\n", host.c_str());
++ return false;
++ }
++
++ int status = ::connect ( _sd, reinterpret_cast<sockaddr*>(&_sockaddr), sizeof ( _sockaddr ) );
++
++ if ( status == SOCKET_ERROR )
++ {
++ XBMC->Log(LOG_ERROR, "Socket::connect %s:%u\n", host.c_str(), port);
++ errormessage( getLastError(), "Socket::connect" );
++ return false;
++ }
++
++ return true;
++}
++
++bool Socket::reconnect()
++{
++ if ( _sd != INVALID_SOCKET )
++ {
++ return true;
++ }
++
++ if( !create() )
++ return false;
++
++ int status = ::connect ( _sd, reinterpret_cast<sockaddr*>(&_sockaddr), sizeof ( _sockaddr ) );
++
++ if ( status == SOCKET_ERROR )
++ {
++ errormessage( getLastError(), "Socket::connect" );
++ return false;
++ }
++
++ return true;
++}
++
++bool Socket::is_valid() const
++{
++ return (_sd != INVALID_SOCKET);
++}
++
++#if defined(TARGET_WINDOWS)
++bool Socket::set_non_blocking ( const bool b )
++{
++ u_long iMode;
++
++ if ( b )
++ iMode = 1; // enable non_blocking
++ else
++ iMode = 0; // disable non_blocking
++
++ if (ioctlsocket(_sd, FIONBIO, &iMode) == -1)
++ {
++ XBMC->Log(LOG_ERROR, "Socket::set_non_blocking - Can't set socket condition to: %i", iMode);
++ return false;
++ }
++
++ return true;
++}
++
++void Socket::errormessage( int errnum, const char* functionname) const
++{
++ const char* errmsg = NULL;
++
++ switch (errnum)
++ {
++ case WSANOTINITIALISED:
++ errmsg = "A successful WSAStartup call must occur before using this function.";
++ break;
++ case WSAENETDOWN:
++ errmsg = "The network subsystem or the associated service provider has failed";
++ break;
++ case WSA_NOT_ENOUGH_MEMORY:
++ errmsg = "Insufficient memory available";
++ break;
++ case WSA_INVALID_PARAMETER:
++ errmsg = "One or more parameters are invalid";
++ break;
++ case WSA_OPERATION_ABORTED:
++ errmsg = "Overlapped operation aborted";
++ break;
++ case WSAEINTR:
++ errmsg = "Interrupted function call";
++ break;
++ case WSAEBADF:
++ errmsg = "File handle is not valid";
++ break;
++ case WSAEACCES:
++ errmsg = "Permission denied";
++ break;
++ case WSAEFAULT:
++ errmsg = "Bad address";
++ break;
++ case WSAEINVAL:
++ errmsg = "Invalid argument";
++ break;
++ case WSAENOTSOCK:
++ errmsg = "Socket operation on nonsocket";
++ break;
++ case WSAEDESTADDRREQ:
++ errmsg = "Destination address required";
++ break;
++ case WSAEMSGSIZE:
++ errmsg = "Message too long";
++ break;
++ case WSAEPROTOTYPE:
++ errmsg = "Protocol wrong type for socket";
++ break;
++ case WSAENOPROTOOPT:
++ errmsg = "Bad protocol option";
++ break;
++ case WSAEPFNOSUPPORT:
++ errmsg = "Protocol family not supported";
++ break;
++ case WSAEAFNOSUPPORT:
++ errmsg = "Address family not supported by protocol family";
++ break;
++ case WSAEADDRINUSE:
++ errmsg = "Address already in use";
++ break;
++ case WSAECONNRESET:
++ errmsg = "Connection reset by peer";
++ break;
++ case WSAHOST_NOT_FOUND:
++ errmsg = "Authoritative answer host not found";
++ break;
++ case WSATRY_AGAIN:
++ errmsg = "Nonauthoritative host not found, or server failure";
++ break;
++ case WSAEISCONN:
++ errmsg = "Socket is already connected";
++ break;
++ case WSAETIMEDOUT:
++ errmsg = "Connection timed out";
++ break;
++ case WSAECONNREFUSED:
++ errmsg = "Connection refused";
++ break;
++ case WSANO_DATA:
++ errmsg = "Valid name, no data record of requested type";
++ break;
++ default:
++ errmsg = "WSA Error";
++ }
++ XBMC->Log(LOG_ERROR, "%s: (Winsock error=%i) %s\n", functionname, errnum, errmsg);
++}
++
++int Socket::getLastError() const
++{
++ return WSAGetLastError();
++}
++
++int Socket::win_usage_count = 0; //Declared static in Socket class
++
++bool Socket::osInit()
++{
++ win_usage_count++;
++ // initialize winsock:
++ if (WSAStartup(MAKEWORD(2,2),&_wsaData) != 0)
++ {
++ return false;
++ }
++
++ WORD wVersionRequested = MAKEWORD(2,2);
++
++ // check version
++ if (_wsaData.wVersion != wVersionRequested)
++ {
++ return false;
++ }
++
++ return true;
++}
++
++void Socket::osCleanup()
++{
++ win_usage_count--;
++ if(win_usage_count == 0)
++ {
++ WSACleanup();
++ }
++}
++
++#elif defined TARGET_LINUX
++bool Socket::set_non_blocking ( const bool b )
++{
++ int opts;
++
++ opts = fcntl(_sd, F_GETFL);
++
++ if ( opts < 0 )
++ {
++ return false;
++ }
++
++ if ( b )
++ opts = ( opts | O_NONBLOCK );
++ else
++ opts = ( opts & ~O_NONBLOCK );
++
++ if(fcntl (_sd , F_SETFL, opts) == -1)
++ {
++ XBMC->Log(LOG_ERROR, "Socket::set_non_blocking - Can't set socket flags to: %i", opts);
++ return false;
++ }
++ return true;
++}
++
++void Socket::errormessage( int errnum, const char* functionname) const
++{
++ const char* errmsg = NULL;
++
++ switch ( errnum )
++ {
++ case EAGAIN: //same as EWOULDBLOCK
++ errmsg = "EAGAIN: The socket is marked non-blocking and the requested operation would block";
++ break;
++ case EBADF:
++ errmsg = "EBADF: An invalid descriptor was specified";
++ break;
++ case ECONNRESET:
++ errmsg = "ECONNRESET: Connection reset by peer";
++ break;
++ case EDESTADDRREQ:
++ errmsg = "EDESTADDRREQ: The socket is not in connection mode and no peer address is set";
++ break;
++ case EFAULT:
++ errmsg = "EFAULT: An invalid userspace address was specified for a parameter";
++ break;
++ case EINTR:
++ errmsg = "EINTR: A signal occurred before data was transmitted";
++ break;
++ case EINVAL:
++ errmsg = "EINVAL: Invalid argument passed";
++ break;
++ case ENOTSOCK:
++ errmsg = "ENOTSOCK: The argument is not a valid socket";
++ break;
++ case EMSGSIZE:
++ errmsg = "EMSGSIZE: The socket requires that message be sent atomically, and the size of the message to be sent made this impossible";
++ break;
++ case ENOBUFS:
++ errmsg = "ENOBUFS: The output queue for a network interface was full";
++ break;
++ case ENOMEM:
++ errmsg = "ENOMEM: No memory available";
++ break;
++ case EPIPE:
++ errmsg = "EPIPE: The local end has been shut down on a connection oriented socket";
++ break;
++ case EPROTONOSUPPORT:
++ errmsg = "EPROTONOSUPPORT: The protocol type or the specified protocol is not supported within this domain";
++ break;
++ case EAFNOSUPPORT:
++ errmsg = "EAFNOSUPPORT: The implementation does not support the specified address family";
++ break;
++ case ENFILE:
++ errmsg = "ENFILE: Not enough kernel memory to allocate a new socket structure";
++ break;
++ case EMFILE:
++ errmsg = "EMFILE: Process file table overflow";
++ break;
++ case EACCES:
++ errmsg = "EACCES: Permission to create a socket of the specified type and/or protocol is denied";
++ break;
++ case ECONNREFUSED:
++ errmsg = "ECONNREFUSED: A remote host refused to allow the network connection (typically because it is not running the requested service)";
++ break;
++ case ENOTCONN:
++ errmsg = "ENOTCONN: The socket is associated with a connection-oriented protocol and has not been connected";
++ break;
++ //case E:
++ // errmsg = "";
++ // break;
++ default:
++ break;
++ }
++ XBMC->Log(LOG_ERROR, "%s: (errno=%i) %s\n", functionname, errnum, errmsg);
++}
++
++int Socket::getLastError() const
++{
++ return errno;
++}
++
++bool Socket::osInit()
++{
++ // Not needed for Linux
++ return true;
++}
++
++void Socket::osCleanup()
++{
++ // Not needed for Linux
++}
++#endif //TARGET_WINDOWS || TARGET_LINUX
++
++} //namespace MPTV
+diff --git a/xbmc/pvrclients/MediaPortal/Socket.h b/xbmc/pvrclients/MediaPortal/Socket.h
+new file mode 100644
+index 0000000..efbf5d8
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/Socket.h
+@@ -0,0 +1,302 @@
++/*
++ * Copyright (C) 2005-2011 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 this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ */
++#pragma once
++
++namespace MPTV //Prevent name clash with Live555 Socket
++{
++
++//Include platform specific datatypes, header files, defines and constants:
++#if defined TARGET_WINDOWS
++ #define WIN32_LEAN_AND_MEAN // Enable LEAN_AND_MEAN support
++ #pragma warning(disable:4005) // Disable "warning C4005: '_WINSOCKAPI_' : macro redefinition"
++ #include <winsock2.h>
++ #pragma warning(default:4005)
++ #include <windows.h>
++
++ #ifndef NI_MAXHOST
++ #define NI_MAXHOST 1025
++ #endif
++
++ #ifndef socklen_t
++ typedef int socklen_t;
++ #endif
++ #ifndef ipaddr_t
++ typedef unsigned long ipaddr_t;
++ #endif
++ #ifndef port_t
++ typedef unsigned short port_t;
++ #endif
++#elif defined TARGET_LINUX || defined TARGET_DARWIN
++ #include <sys/types.h> /* for socket,connect */
++ #include <sys/socket.h> /* for socket,connect */
++ #include <sys/un.h> /* for Unix socket */
++ #include <arpa/inet.h> /* for inet_pton */
++ #include <netdb.h> /* for gethostbyname */
++ #include <netinet/in.h> /* for htons */
++ #include <unistd.h> /* for read, write, close */
++ #include <errno.h>
++ #include <fcntl.h>
++
++ typedef int SOCKET;
++ typedef sockaddr SOCKADDR;
++ typedef sockaddr_in SOCKADDR_IN;
++ #define INVALID_SOCKET (-1)
++ #define SOCKET_ERROR (-1)
++#else
++ #error Platform specific socket support is not yet available on this platform!
++#endif
++
++using namespace std;
++
++#include <vector>
++
++#define MAXCONNECTIONS 1 ///< Maximum number of pending connections before "Connection refused"
++#define MAXRECV 1500 ///< Maximum packet size
++
++enum SocketFamily
++{
++ #ifdef CONFIG_SOCKET_IPV6
++ af_inet6 = AF_INET6,
++ af_unspec = AF_UNSPEC, ///< Either INET or INET6
++ #endif
++ af_inet = AF_INET
++};
++
++enum SocketDomain
++{
++ #if defined TARGET_LINUX || defined TARGET_DARWIN
++ pf_unix = PF_UNIX,
++ pf_local = PF_LOCAL,
++ #endif
++ #ifdef CONFIG_SOCKET_IPV6
++ pf_inet6 = PF_INET6,
++ pf_unspec = PF_UNSPEC, //< Either INET or INET6
++ #endif
++ pf_inet = PF_INET
++};
++
++enum SocketType
++{
++ sock_stream = SOCK_STREAM,
++ sock_dgram = SOCK_DGRAM
++};
++
++enum SocketProtocol
++{
++ tcp = IPPROTO_TCP,
++ udp = IPPROTO_UDP
++ #ifdef CONFIG_SOCKET_IPV6
++ , ipv6 = IPPROTO_IPV6
++ #endif
++};
++
++class Socket
++{
++ public:
++
++ /*!
++ * An unconnected socket may be created directly on the local
++ * machine. The socket type (SOCK_STREAM, SOCK_DGRAM) and
++ * protocol may also be specified.
++ * If the socket cannot be created, an exception is thrown.
++ *
++ * \param family Socket family (IPv4 or IPv6)
++ * \param domain The domain parameter specifies a communications domain within which communication will take place;
++ * this selects the protocol family which should be used.
++ * \param type base type and protocol family of the socket.
++ * \param protocol specific protocol to apply.
++ */
++ Socket(const enum SocketFamily family, const enum SocketDomain domain, const enum SocketType type, const enum SocketProtocol protocol = tcp);
++ Socket(void);
++ virtual ~Socket();
++
++ //Socket settings
++
++ /*!
++ * Socket setFamily
++ * \param family Can be af_inet or af_inet6. Default: af_inet
++ */
++ void setFamily(const enum SocketFamily family)
++ {
++ _family = family;
++ };
++
++ /*!
++ * Socket setDomain
++ * \param domain Can be pf_unix, pf_local, pf_inet or pf_inet6. Default: pf_inet
++ */
++ void setDomain(const enum SocketDomain domain)
++ {
++ _domain = domain;
++ };
++
++ /*!
++ * Socket setType
++ * \param type Can be sock_stream or sock_dgram. Default: sock_stream.
++ */
++ void setType(const enum SocketType type)
++ {
++ _type = type;
++ };
++
++ /*!
++ * Socket setProtocol
++ * \param protocol Can be tcp or udp. Default: tcp.
++ */
++ void setProtocol(const enum SocketProtocol protocol)
++ {
++ _protocol = protocol;
++ };
++
++ /*!
++ * Socket setPort
++ * \param port port number for socket communication
++ */
++ void setPort (const unsigned short port)
++ {
++ _sockaddr.sin_port = htons ( port );
++ };
++
++ bool setHostname ( const std::string host );
++
++ // Server initialization
++
++ /*!
++ * Socket create
++ * Create a new socket
++ * \return True if succesful
++ */
++ bool create();
++
++ /*!
++ * Socket close
++ * Close the socket
++ * \return True if succesful
++ */
++ bool close();
++
++ /*!
++ * Socket bind
++ */
++ bool bind ( const unsigned short port );
++ bool listen() const;
++ bool accept ( Socket& socket ) const;
++
++ // Client initialization
++ bool connect ( const std::string host, const unsigned short port );
++
++ bool reconnect();
++
++ // Data Transmission
++
++ /*!
++ * Socket send function
++ *
++ * \param data Reference to a std::string with the data to transmit
++ * \return Number of bytes send or -1 in case of an error
++ */
++ int send ( const std::string data );
++
++ /*!
++ * Socket send function
++ *
++ * \param data Pointer to a character array of size 'size' with the data to transmit
++ * \param size Length of the data to transmit
++ * \return Number of bytes send or -1 in case of an error
++ */
++ int send ( const char* data, const unsigned int size );
++
++ /*!
++ * Socket sendto function
++ *
++ * \param data Reference to a std::string with the data to transmit
++ * \param size Length of the data to transmit
++ * \param sendcompletebuffer If 'true': do not return until the complete buffer is transmitted
++ * \return Number of bytes send or -1 in case of an error
++ */
++ int sendto ( const char* data, unsigned int size, bool sendcompletebuffer = false);
++ // Data Receive
++
++ /*!
++ * Socket receive function
++ *
++ * \param data Reference to a std::string for storage of the received data.
++ * \param minpacketsize The minimum number of bytes that should be received before returning from this function
++ * \return Number of bytes received or SOCKET_ERROR
++ */
++ int receive ( std::string& data, unsigned int minpacketsize ) const;
++
++ /*!
++ * Socket receive function
++ *
++ * \param data Reference to a std::string for storage of the received data.
++ * \return Number of bytes received or SOCKET_ERROR
++ */
++ int receive ( std::string& data ) const;
++
++ /*!
++ * Socket receive function
++ *
++ * \param data Pointer to a character array of size buffersize. Used to store the received data.
++ * \param buffersize Size of the 'data' buffer
++ * \param minpacketsize Specifies the minimum number of bytes that need to be received before returning
++ * \return Number of bytes received or SOCKET_ERROR
++ */
++ int receive ( char* data, const unsigned int buffersize, const unsigned int minpacketsize ) const;
++
++ /*!
++ * Socket recvfrom function
++ *
++ * \param data Pointer to a character array of size buffersize. Used to store the received data.
++ * \param buffersize Size of the 'data' buffer
++ * \param minpacketsize Do not return before at least 'minpacketsize' bytes are in the buffer.
++ * \param from Optional: pointer to a sockaddr struct that will get the address from which the data is received
++ * \param fromlen Optional, only required if 'from' is given: length of from struct
++ * \return Number of bytes received or SOCKET_ERROR
++ */
++ int recvfrom ( char* data, const int buffersize, const int minpacketsize, struct sockaddr* from = NULL, socklen_t* fromlen = NULL) const;
++
++ bool set_non_blocking ( const bool );
++
++ bool ReadResponse (int &code, vector<string> &lines);
++
++ bool is_valid() const;
++
++ private:
++
++ SOCKET _sd; ///< Socket Descriptor
++ SOCKADDR_IN _sockaddr; ///< Socket Address
++
++ enum SocketFamily _family; ///< Socket Address Family
++ enum SocketProtocol _protocol; ///< Socket Protocol
++ enum SocketType _type; ///< Socket Type
++ enum SocketDomain _domain; ///< Socket domain
++
++ #ifdef TARGET_WINDOWS
++ WSADATA _wsaData; ///< Windows Socket data
++ static int win_usage_count; ///< Internal Windows usage counter used to prevent a global WSACleanup when more than one Socket object is used
++ #endif
++
++ void errormessage( int errornum, const char* functionname = NULL) const;
++ int getLastError(void) const;
++ bool osInit();
++ void osCleanup();
++};
++
++} //namespace MPTV
+diff --git a/xbmc/pvrclients/MediaPortal/channels.cpp b/xbmc/pvrclients/MediaPortal/channels.cpp
+new file mode 100644
+index 0000000..b766871
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/channels.cpp
+@@ -0,0 +1,74 @@
++/*
++ * Copyright (C) 2005-2011 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 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ */
++
++#include <vector>
++#include "channels.h"
++#include "utils.h"
++#include <stdlib.h>
++#include <string.h>
++
++cChannel::cChannel()
++{
++ name = "";
++ uid = 0;
++ external_id = 0;
++ iswebstream = false;
++ url = "";
++}
++
++cChannel::~cChannel()
++{
++}
++
++bool cChannel::Parse(const std::string& data)
++{
++ vector<string> fields;
++
++ Tokenize(data, fields, "|");
++
++ if (fields.size() >= 4)
++ {
++ // Expected format:
++ // ListTVChannels, ListRadioChannels
++ // 0 = channel uid
++ // 1 = channel external id/number
++ // 2 = channel name
++ // 3 = isencrypted ("0"/"1")
++ // ListRadioChannels only: (TVServerXBMC >= v1.1.0.100)
++ // 4 = iswebstream
++ // 5 = webstream url
++
++ uid = atoi(fields[0].c_str());
++ external_id = atoi(fields[1].c_str());
++ name = fields[2];
++ encrypted = (strncmp(fields[3].c_str(), "1", 1) == 0);
++
++ if (fields.size() >= 6)
++ {
++ iswebstream = (strncmp(fields[4].c_str(), "1", 1) == 0);
++ url = fields[5].c_str();
++ }
++
++ return true;
++ }
++ else
++ {
++ return false;
++ }
++}
+diff --git a/xbmc/pvrclients/MediaPortal/channels.h b/xbmc/pvrclients/MediaPortal/channels.h
+new file mode 100644
+index 0000000..f127d71
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/channels.h
+@@ -0,0 +1,48 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "libXBMC_pvr.h"
++#include <string>
++
++class cChannel
++{
++private:
++ std::string name;
++ int uid;
++ int external_id;
++ bool encrypted;
++ bool iswebstream;
++ std::string url;
++
++public:
++ cChannel();
++ virtual ~cChannel();
++
++ bool Parse(const std::string& data);
++ const char *Name(void) const { return name.c_str(); }
++ int UID(void) const { return uid; }
++ int ExternalID(void) const { return external_id; }
++ bool Encrypted(void) const { return encrypted; }
++ bool IsWebstream(void) const { return iswebstream; }
++ const char* URL(void) const { return url.c_str(); }
++};
++
+diff --git a/xbmc/pvrclients/MediaPortal/client.cpp b/xbmc/pvrclients/MediaPortal/client.cpp
+new file mode 100644
+index 0000000..193fb25
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/client.cpp
+@@ -0,0 +1,734 @@
++/*
++ * Copyright (C) 2005-2011 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 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ */
++
++#include "client.h"
++#include "xbmc_pvr_dll.h"
++#include "pvrclient-mediaportal.h"
++#include "utils.h"
++
++using namespace std;
++using namespace ADDON;
++
++/* User adjustable settings are saved here.
++ * Default values are defined inside client.h
++ * and exported to the other source files.
++ */
++std::string g_szHostname = DEFAULT_HOST; ///< The Host name or IP of the MediaPortal TV Server
++int g_iPort = DEFAULT_PORT; ///< The TVServerXBMC listening port (default: 9596)
++int g_iConnectTimeout = DEFAULT_TIMEOUT; ///< The Socket connection timeout
++int g_iSleepOnRTSPurl = DEFAULT_SLEEP_RTSP_URL; ///< An optional delay between tuning a channel and opening the corresponding RTSP stream in XBMC (default: 0)
++bool g_bOnlyFTA = DEFAULT_FTA_ONLY; ///< Send only Free-To-Air Channels inside Channel list to XBMC
++bool g_bRadioEnabled = DEFAULT_RADIO; ///< Send also Radio channels list to XBMC
++bool g_bHandleMessages = DEFAULT_HANDLE_MSG; ///< Send VDR's OSD status messages to XBMC OSD
++bool g_bResolveRTSPHostname = DEFAULT_RESOLVE_RTSP_HOSTNAME; ///< Resolve the server hostname in the rtsp URLs to an IP at the TV Server side (default: false)
++bool g_bReadGenre = DEFAULT_READ_GENRE; ///< Read the genre strings from MediaPortal and translate them into XBMC DVB genre id's (only English)
++bool g_bUseRecordingsDir = DEFAULT_USE_REC_DIR; ///< Use a normal directory if true for recordings
++std::string g_szRecordingsDir = DEFAULT_REC_DIR; ///< The path to the recordings directory
++std::string g_szTVGroup = DEFAULT_TVGROUP; ///< Import only TV channels from this TV Server TV group
++std::string g_szRadioGroup = DEFAULT_RADIOGROUP; ///< Import only radio channels from this TV Server radio group
++bool g_bDirectTSFileRead = DEFAULT_DIRECT_TS_FR; ///< Open the Live-TV timeshift buffer directly (skip RTSP streaming)
++
++/* Client member variables */
++ADDON_STATUS m_CurStatus = ADDON_STATUS_UNKNOWN;
++cPVRClientMediaPortal *g_client = NULL;
++bool g_bCreated = false;
++int g_iClientID = -1;
++std::string g_szUserPath = "";
++std::string g_szClientPath = "";
++CHelper_libXBMC_addon *XBMC = NULL;
++CHelper_libXBMC_pvr *PVR = NULL;
++
++extern "C" {
++
++void ADDON_ReadSettings(void);
++
++/***********************************************************
++ * Standard AddOn related public library functions
++ ***********************************************************/
++
++//-- Create -------------------------------------------------------------------
++// Called after loading of the dll, all steps to become Client functional
++// must be performed here.
++//-----------------------------------------------------------------------------
++ADDON_STATUS ADDON_Create(void* hdl, void* props)
++{
++ if (!hdl || !props)
++ return ADDON_STATUS_UNKNOWN;
++
++ PVR_PROPERTIES* pvrprops = (PVR_PROPERTIES*)props;
++
++ XBMC = new CHelper_libXBMC_addon;
++ if (!XBMC->RegisterMe(hdl))
++ {
++ SAFE_DELETE(XBMC);
++ return ADDON_STATUS_UNKNOWN;
++ }
++
++ PVR = new CHelper_libXBMC_pvr;
++ if (!PVR->RegisterMe(hdl))
++ {
++ SAFE_DELETE(PVR);
++ SAFE_DELETE(XBMC);
++ return ADDON_STATUS_UNKNOWN;
++ }
++
++ XBMC->Log(LOG_DEBUG, "Creating MediaPortal PVR-Client (ffmpeg rtsp version)");
++
++ m_CurStatus = ADDON_STATUS_UNKNOWN;
++ g_iClientID = pvrprops->iClientId;
++ g_szUserPath = pvrprops->strUserPath;
++ g_szClientPath = pvrprops->strClientPath;
++
++ ADDON_ReadSettings();
++
++ /* Create connection to MediaPortal XBMC TV client */
++ g_client = new cPVRClientMediaPortal();
++ if (!g_client->Connect())
++ {
++ SAFE_DELETE(g_client);
++ SAFE_DELETE(PVR);
++ SAFE_DELETE(XBMC);
++ m_CurStatus = ADDON_STATUS_LOST_CONNECTION;
++ }
++ else
++ {
++ m_CurStatus = ADDON_STATUS_OK;
++ }
++
++ g_bCreated = true;
++
++ return m_CurStatus;
++}
++
++//-- Destroy ------------------------------------------------------------------
++// Used during destruction of the client, all steps to do clean and safe Create
++// again must be done.
++//-----------------------------------------------------------------------------
++void ADDON_Destroy()
++{
++ if ((g_bCreated) && (g_client))
++ {
++ g_client->Disconnect();
++ SAFE_DELETE(g_client);
++
++ g_bCreated = false;
++ }
++
++ if (PVR)
++ {
++ SAFE_DELETE(PVR);
++ }
++ if (XBMC)
++ {
++ SAFE_DELETE(XBMC);
++ }
++
++ m_CurStatus = ADDON_STATUS_UNKNOWN;
++}
++
++//-- GetStatus ----------------------------------------------------------------
++// Report the current Add-On Status to XBMC
++//-----------------------------------------------------------------------------
++ADDON_STATUS ADDON_GetStatus()
++{
++ return m_CurStatus;
++}
++
++//-- HasSettings --------------------------------------------------------------
++// Report "true", yes this AddOn have settings
++//-----------------------------------------------------------------------------
++bool ADDON_HasSettings()
++{
++ return true;
++}
++
++unsigned int ADDON_GetSettings(ADDON_StructSetting ***sSet)
++{
++ return 0;
++}
++
++void ADDON_ReadSettings(void)
++{
++ /* Read setting "host" from settings.xml */
++ char buffer[1024];
++
++ if (!XBMC)
++ return;
++
++ /* Connection settings */
++ /***********************/
++ if (XBMC->GetSetting("host", &buffer))
++ {
++ g_szHostname = buffer;
++ uri::decode(g_szHostname);
++ }
++ else
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'host' setting, falling back to '127.0.0.1' as default");
++ g_szHostname = DEFAULT_HOST;
++ }
++
++ /* Read setting "port" from settings.xml */
++ if (!XBMC->GetSetting("port", &g_iPort))
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'port' setting, falling back to '9596' as default");
++ g_iPort = DEFAULT_PORT;
++ }
++
++ /* Read setting "timeout" from settings.xml */
++ if (!XBMC->GetSetting("timeout", &g_iConnectTimeout))
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'timeout' setting, falling back to %i seconds as default", DEFAULT_TIMEOUT);
++ g_iConnectTimeout = DEFAULT_TIMEOUT;
++ }
++
++ /* MediaPortal settings */
++ /***********************/
++
++ /* Read setting "ftaonly" from settings.xml */
++ if (!XBMC->GetSetting("ftaonly", &g_bOnlyFTA))
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'ftaonly' setting, falling back to 'false' as default");
++ g_bOnlyFTA = DEFAULT_FTA_ONLY;
++ }
++
++ /* Read setting "useradio" from settings.xml */
++ if (!XBMC->GetSetting("useradio", &g_bRadioEnabled))
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'useradio' setting, falling back to 'true' as default");
++ g_bRadioEnabled = DEFAULT_RADIO;
++ }
++
++ if (!XBMC->GetSetting("tvgroup", &buffer))
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'tvgroup' setting, falling back to '' as default");
++ } else {
++ g_szTVGroup = buffer;
++ }
++
++ if (!XBMC->GetSetting("radiogroup", &buffer))
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'tvgroup' setting, falling back to '' as default");
++ } else {
++ g_szRadioGroup = buffer;
++ }
++
++ /* Read setting "resolvertsphostname" from settings.xml */
++ if (!XBMC->GetSetting("resolvertsphostname", &g_bResolveRTSPHostname))
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'resolvertsphostname' setting, falling back to 'true' as default");
++ g_bResolveRTSPHostname = DEFAULT_RESOLVE_RTSP_HOSTNAME;
++ }
++
++ /* Read setting "readgenre" from settings.xml */
++ if (!XBMC->GetSetting("readgenre", &g_bReadGenre))
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'readgenre' setting, falling back to 'true' as default");
++ g_bReadGenre = DEFAULT_READ_GENRE;
++ }
++
++ /* Read setting "sleeponrtspurl" from settings.xml */
++ if (!XBMC->GetSetting("sleeponrtspurl", &g_iSleepOnRTSPurl))
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'sleeponrtspurl' setting, falling back to %i seconds as default", DEFAULT_SLEEP_RTSP_URL);
++ g_iSleepOnRTSPurl = DEFAULT_SLEEP_RTSP_URL;
++ }
++
++ /* Read setting "userecordingsdir" from settings.xml */
++ if (!XBMC->GetSetting("userecordingsdir", &g_bUseRecordingsDir))
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'userecordingsdir' setting, falling back to 'false' as default");
++ g_bReadGenre = DEFAULT_USE_REC_DIR;
++ }
++
++ if (!XBMC->GetSetting("recordingsdir", &buffer))
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'recordingsdir' setting, falling back to '%s' as default", DEFAULT_REC_DIR);
++ } else {
++ g_szRecordingsDir = buffer;
++ }
++ g_bDirectTSFileRead = false;
++
++ /* Log the current settings for debugging purposes */
++ XBMC->Log(LOG_DEBUG, "settings: host='%s', port=%i, timeout=%i", g_szHostname.c_str(), g_iPort, g_iConnectTimeout);
++ XBMC->Log(LOG_DEBUG, "settings: ftaonly=%i, useradio=%i, tvgroup='%s', radiogroup='%s'", (int) g_bOnlyFTA, (int) g_bRadioEnabled, g_szTVGroup.c_str(), g_szRadioGroup.c_str());
++ XBMC->Log(LOG_DEBUG, "settings: readgenre=%i, sleeponrtspurl=%i", (int) g_bReadGenre, g_iSleepOnRTSPurl);
++ XBMC->Log(LOG_DEBUG, "settings: userecordingsdir=%i, recordingsdir='%s'", (int) g_bUseRecordingsDir, g_szRecordingsDir.c_str());
++ XBMC->Log(LOG_DEBUG, "settings: resolvertsphostname=%i", (int) g_bResolveRTSPHostname);
++}
++
++//-- SetSetting ---------------------------------------------------------------
++// Called everytime a setting is changed by the user and to inform AddOn about
++// new setting and to do required stuff to apply it.
++//-----------------------------------------------------------------------------
++ADDON_STATUS ADDON_SetSetting(const char *settingName, const void *settingValue)
++{
++ string str = settingName;
++
++ // SetSetting can occur when the addon is enabled, but TV support still
++ // disabled. In that case the addon is not loaded, so we should not try
++ // to change its settings.
++ if (!g_bCreated)
++ return ADDON_STATUS_OK;
++
++ if (str == "host")
++ {
++ string tmp_sHostname;
++ XBMC->Log(LOG_INFO, "Changed Setting 'host' from %s to %s", g_szHostname.c_str(), (const char*) settingValue);
++ tmp_sHostname = g_szHostname;
++ g_szHostname = (const char*) settingValue;
++ if (tmp_sHostname != g_szHostname)
++ return ADDON_STATUS_NEED_RESTART;
++ }
++ else if (str == "port")
++ {
++ XBMC->Log(LOG_INFO, "Changed Setting 'port' from %u to %u", g_iPort, *(int*) settingValue);
++ if (g_iPort != *(int*) settingValue)
++ {
++ g_iPort = *(int*) settingValue;
++ return ADDON_STATUS_NEED_RESTART;
++ }
++ }
++ else if (str == "ftaonly")
++ {
++ XBMC->Log(LOG_INFO, "Changed setting 'ftaonly' from %u to %u", g_bOnlyFTA, *(bool*) settingValue);
++ g_bOnlyFTA = *(bool*) settingValue;
++ }
++ else if (str == "useradio")
++ {
++ XBMC->Log(LOG_INFO, "Changed setting 'useradio' from %u to %u", g_bRadioEnabled, *(bool*) settingValue);
++ g_bRadioEnabled = *(bool*) settingValue;
++ }
++ else if (str == "timeout")
++ {
++ XBMC->Log(LOG_INFO, "Changed setting 'timeout' from %u to %u", g_iConnectTimeout, *(int*) settingValue);
++ g_iConnectTimeout = *(int*) settingValue;
++ }
++ else if (str == "tvgroup")
++ {
++ XBMC->Log(LOG_INFO, "Changed setting 'tvgroup' from %s to %s", g_szTVGroup.c_str(), (const char*) settingValue);
++ g_szTVGroup = (const char*) settingValue;
++ }
++ else if (str == "radiogroup")
++ {
++ XBMC->Log(LOG_INFO, "Changed setting 'radiogroup' from %s to %s", g_szRadioGroup.c_str(), (const char*) settingValue);
++ g_szRadioGroup = (const char*) settingValue;
++ }
++ else if (str == "resolvertsphostname")
++ {
++ XBMC->Log(LOG_INFO, "Changed setting 'resolvertsphostname' from %u to %u", g_bResolveRTSPHostname, *(bool*) settingValue);
++ g_bResolveRTSPHostname = *(bool*) settingValue;
++ }
++ else if (str == "readgenre")
++ {
++ XBMC->Log(LOG_INFO, "Changed setting 'readgenre' from %u to %u", g_bReadGenre, *(bool*) settingValue);
++ g_bReadGenre = *(bool*) settingValue;
++ }
++ else if (str == "sleeponrtspurl")
++ {
++ XBMC->Log(LOG_INFO, "Changed setting 'sleeponrtspurl' from %u to %u", g_iSleepOnRTSPurl, *(int*) settingValue);
++ g_iSleepOnRTSPurl = *(int*) settingValue;
++ }
++ else if (str == "userecordingsdir")
++ {
++ XBMC->Log(LOG_INFO, "Changed setting 'userecordingsdir' from %u to %u", g_bUseRecordingsDir, *(bool*) settingValue);
++ g_bUseRecordingsDir = *(bool*) settingValue;
++ }
++ else if (str == "recordingsdir")
++ {
++ XBMC->Log(LOG_INFO, "Changed setting 'recordingsdir' from %s to %s", g_szRecordingsDir.c_str(), (const char*) settingValue);
++ g_szRecordingsDir = (const char*) settingValue;
++ }
++ return ADDON_STATUS_OK;
++}
++
++void ADDON_Stop()
++{
++ ADDON_Destroy();
++}
++
++void ADDON_FreeSettings()
++{
++
++}
++
++/***********************************************************
++ * PVR Client AddOn specific public library functions
++ ***********************************************************/
++
++//-- GetAddonCapabilities ------------------------------------------------------------
++// Tell XBMC our requirements
++//-----------------------------------------------------------------------------
++PVR_ERROR GetAddonCapabilities(PVR_ADDON_CAPABILITIES *pCapabilities)
++{
++ XBMC->Log(LOG_DEBUG, "->GetProperties()");
++
++ pCapabilities->bSupportsTimeshift = false;
++ pCapabilities->bSupportsEPG = true;
++ pCapabilities->bSupportsRecordings = true;
++ pCapabilities->bSupportsTimers = true;
++ pCapabilities->bSupportsTV = true;
++ pCapabilities->bSupportsRadio = g_bRadioEnabled;
++ pCapabilities->bSupportsChannelSettings = true;
++ pCapabilities->bSupportsChannelGroups = true;
++ pCapabilities->bHandlesInputStream = true;
++ pCapabilities->bHandlesDemuxing = false;
++ pCapabilities->bSupportsChannelScan = false;
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++PVR_ERROR GetStreamProperties(PVR_STREAM_PROPERTIES *pProperties)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++//-- GetBackendName -----------------------------------------------------------
++// Return the Name of the Backend
++//-----------------------------------------------------------------------------
++const char * GetBackendName(void)
++{
++ if (g_client)
++ return g_client->GetBackendName();
++ else
++ return "";
++}
++
++//-- GetBackendVersion --------------------------------------------------------
++// Return the Version of the Backend as String
++//-----------------------------------------------------------------------------
++const char * GetBackendVersion(void)
++{
++ if (g_client)
++ return g_client->GetBackendVersion();
++ else
++ return "";
++}
++
++//-- GetConnectionString ------------------------------------------------------
++// Return a String with connection info, if available
++//-----------------------------------------------------------------------------
++const char * GetConnectionString(void)
++{
++ if (g_client)
++ return g_client->GetConnectionString();
++ else
++ return "addon error!";
++}
++
++//-- GetDriveSpace ------------------------------------------------------------
++// Return the Total and Free Drive space on the PVR Backend
++//-----------------------------------------------------------------------------
++PVR_ERROR GetDriveSpace(long long *iTotal, long long *iUsed)
++{
++ if (!g_client)
++ return PVR_ERROR_SERVER_ERROR;
++ else
++ return g_client->GetDriveSpace(iTotal, iUsed);
++}
++
++PVR_ERROR GetBackendTime(time_t *localTime, int *gmtOffset)
++{
++ if (!g_client)
++ return PVR_ERROR_SERVER_ERROR;
++ else
++ return g_client->GetBackendTime(localTime, gmtOffset);
++}
++
++PVR_ERROR DialogChannelScan()
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++PVR_ERROR CallMenuHook(const PVR_MENUHOOK &menuhook)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++
++/*******************************************/
++/** PVR EPG Functions **/
++
++PVR_ERROR GetEPGForChannel(PVR_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd)
++{
++ if (!g_client)
++ return PVR_ERROR_SERVER_ERROR;
++ else
++ return g_client->GetEpg(handle, channel, iStart, iEnd);
++}
++
++
++/*******************************************/
++/** PVR Channel Functions **/
++
++int GetChannelsAmount()
++{
++ if (!g_client)
++ return 0;
++ else
++ return g_client->GetNumChannels();
++}
++
++PVR_ERROR GetChannels(PVR_HANDLE handle, bool bRadio)
++{
++ if (!g_client)
++ return PVR_ERROR_SERVER_ERROR;
++ else
++ return g_client->GetChannels(handle, bRadio);
++}
++
++PVR_ERROR DeleteChannel(const PVR_CHANNEL &channel)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++PVR_ERROR RenameChannel(const PVR_CHANNEL &channel)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++PVR_ERROR DialogChannelSettings(const PVR_CHANNEL &channelinfo)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++PVR_ERROR DialogAddChannel(const PVR_CHANNEL &channelinfo)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++
++/*******************************************/
++/** PVR Channel group Functions **/
++
++int GetChannelGroupsAmount(void)
++{
++ if (!g_client)
++ return 0;
++ else
++ return g_client->GetChannelGroupsAmount();
++}
++
++PVR_ERROR GetChannelGroups(PVR_HANDLE handle, bool bRadio)
++{
++ if (!g_client)
++ return PVR_ERROR_SERVER_ERROR;
++ else
++ return g_client->GetChannelGroups(handle, bRadio);
++}
++
++PVR_ERROR GetChannelGroupMembers(PVR_HANDLE handle, const PVR_CHANNEL_GROUP &group)
++{
++ if (!g_client)
++ return PVR_ERROR_SERVER_ERROR;
++ else
++ return g_client->GetChannelGroupMembers(handle, group);
++}
++
++
++/*******************************************/
++/** PVR Recording Functions **/
++
++int GetRecordingsAmount(void)
++{
++ if (!g_client)
++ return 0;
++ else
++ return g_client->GetNumRecordings();
++}
++
++PVR_ERROR GetRecordings(PVR_HANDLE handle)
++{
++ if (!g_client)
++ return PVR_ERROR_SERVER_ERROR;
++ else
++ return g_client->GetRecordings(handle);
++}
++
++PVR_ERROR DeleteRecording(const PVR_RECORDING &recording)
++{
++ if (!g_client)
++ return PVR_ERROR_SERVER_ERROR;
++ else
++ return g_client->DeleteRecording(recording);
++}
++
++PVR_ERROR RenameRecording(const PVR_RECORDING &recording)
++{
++ if (!g_client)
++ return PVR_ERROR_SERVER_ERROR;
++ else
++ return g_client->RenameRecording(recording);
++}
++
++
++/*******************************************/
++/** PVR Timer Functions **/
++
++int GetTimersAmount(void)
++{
++ if (!g_client)
++ return 0;
++ else
++ return g_client->GetNumTimers();
++}
++
++PVR_ERROR GetTimers(PVR_HANDLE handle)
++{
++ if (!g_client)
++ return PVR_ERROR_SERVER_ERROR;
++ else
++ return g_client->GetTimers(handle);
++}
++
++PVR_ERROR AddTimer(const PVR_TIMER &timer)
++{
++ if (!g_client)
++ return PVR_ERROR_SERVER_ERROR;
++ else
++ return g_client->AddTimer(timer);
++}
++
++PVR_ERROR DeleteTimer(const PVR_TIMER &timer, bool bForceDelete)
++{
++ if (!g_client)
++ return PVR_ERROR_SERVER_ERROR;
++ else
++ return g_client->DeleteTimer(timer, bForceDelete);
++}
++
++PVR_ERROR UpdateTimer(const PVR_TIMER &timer)
++{
++ if (!g_client)
++ return PVR_ERROR_SERVER_ERROR;
++ else
++ return g_client->UpdateTimer(timer);
++}
++
++
++/*******************************************/
++/** PVR Live Stream Functions **/
++
++bool OpenLiveStream(const PVR_CHANNEL &channelinfo)
++{
++ if (!g_client)
++ return false;
++ else
++ return g_client->OpenLiveStream(channelinfo);
++}
++
++void CloseLiveStream()
++{
++ if (g_client)
++ g_client->CloseLiveStream();
++}
++
++int ReadLiveStream(unsigned char *pBuffer, unsigned int iBufferSize)
++{
++ if (!g_client)
++ return 0;
++ else
++ return g_client->ReadLiveStream(pBuffer, iBufferSize);
++}
++
++int GetCurrentClientChannel()
++{
++ if (!g_client)
++ return 0;
++ else
++ return g_client->GetCurrentClientChannel();
++}
++
++bool SwitchChannel(const PVR_CHANNEL &channelinfo)
++{
++ if (!g_client)
++ return false;
++ else
++ return g_client->SwitchChannel(channelinfo);
++}
++
++PVR_ERROR SignalStatus(PVR_SIGNAL_STATUS &signalStatus)
++{
++ if (!g_client)
++ return PVR_ERROR_SERVER_ERROR;
++ else
++ return g_client->SignalStatus(signalStatus);
++}
++
++/*******************************************/
++/** PVR Recording Stream Functions **/
++
++bool OpenRecordedStream(const PVR_RECORDING &recording)
++{
++ if (!g_client)
++ return false;
++ else
++ return g_client->OpenRecordedStream(recording);
++}
++
++void CloseRecordedStream(void)
++{
++ if (g_client)
++ g_client->CloseRecordedStream();
++}
++
++int ReadRecordedStream(unsigned char *pBuffer, unsigned int iBufferSize)
++{
++ if (!g_client)
++ return 0;
++ else
++ return g_client->ReadRecordedStream(pBuffer, iBufferSize);
++}
++
++const char * GetLiveStreamURL(const PVR_CHANNEL &channel)
++{
++ if (!g_client)
++ return "";
++ else
++ return g_client->GetLiveStreamURL(channel);
++}
++
++/** UNUSED API FUNCTIONS */
++PVR_ERROR MoveChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
++DemuxPacket* DemuxRead(void) { return NULL; }
++void DemuxAbort(void) {}
++void DemuxReset(void) {}
++void DemuxFlush(void) {}
++
++long long SeekRecordedStream(long long iPosition, int iWhence) { return -1; }
++long long PositionRecordedStream(void) { return -1; }
++long long LengthRecordedStream(void) { return -1; }
++
++long long SeekLiveStream(long long pos, int whence) { return -1; }
++long long PositionLiveStream(void) { return -1; }
++long long LengthLiveStream(void) { return -1 ; }
++
++} //end extern "C"
+diff --git a/xbmc/pvrclients/MediaPortal/client.h b/xbmc/pvrclients/MediaPortal/client.h
+new file mode 100644
+index 0000000..e22085d
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/client.h
+@@ -0,0 +1,71 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#ifndef CLIENT_H
++#define CLIENT_H
++
++#include "platform/util/StdString.h"
++#include "libXBMC_addon.h"
++#include "libXBMC_pvr.h"
++
++#define DEFAULT_HOST "127.0.0.1"
++#define DEFAULT_PORT 9596
++#define DEFAULT_FTA_ONLY false
++#define DEFAULT_RADIO true
++#define DEFAULT_TIMEOUT 10
++#define DEFAULT_HANDLE_MSG false
++#define DEFAULT_RESOLVE_RTSP_HOSTNAME true
++#define DEFAULT_READ_GENRE false
++#define DEFAULT_SLEEP_RTSP_URL 0
++#define DEFAULT_USE_REC_DIR false
++#define DEFAULT_REC_DIR ""
++#define DEFAULT_TVGROUP ""
++#define DEFAULT_RADIOGROUP ""
++#define DEFAULT_DIRECT_TS_FR false
++
++extern bool g_bCreated; ///< Shows that the Create function was successfully called
++extern int g_iClientID; ///< The PVR client ID used by XBMC for this driver
++extern std::string g_szUserPath; ///< The Path to the user directory inside user profile
++extern std::string g_szClientPath; ///< The Path where this driver is located
++
++/* Client Settings */
++extern std::string g_szHostname;
++extern int g_iPort;
++extern int g_iConnectTimeout;
++extern int g_iSleepOnRTSPurl;
++extern bool g_bOnlyFTA;
++extern bool g_bRadioEnabled;
++extern bool g_bHandleMessages;
++extern bool g_bResolveRTSPHostname;
++extern bool g_bReadGenre;
++extern bool g_bUseRecordingsDir;
++extern bool g_bDirectTSFileRead;
++extern std::string g_szRecordingsDir;
++extern std::string g_szTVGroup;
++extern std::string g_szRadioGroup;
++
++extern ADDON::CHelper_libXBMC_addon *XBMC;
++extern CHelper_libXBMC_pvr *PVR;
++
++extern int g_iTVServerXBMCBuild;
++
++#endif /* CLIENT_H */
+diff --git a/xbmc/pvrclients/MediaPortal/epg.cpp b/xbmc/pvrclients/MediaPortal/epg.cpp
+new file mode 100644
+index 0000000..48c15dc
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/epg.cpp
+@@ -0,0 +1,149 @@
++/*
++ * Copyright (C) 2005-2011 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 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ */
++
++#include <algorithm>
++#include <vector>
++#include <stdio.h>
++
++using namespace std;
++
++#include "epg.h"
++#include "utils.h"
++#include "client.h"
++
++using namespace ADDON;
++
++cEpg::cEpg()
++{
++ m_genretable = NULL;
++ Reset();
++}
++
++cEpg::~cEpg()
++{
++}
++
++void cEpg::Reset()
++{
++ m_genre.clear();
++ m_title.clear();
++ m_shortText.clear();
++ m_description.clear();
++ m_episodePart.clear();
++
++ m_uid = 0;
++ m_StartTime = 0;
++ m_EndTime = 0;
++ m_originalAirDate = 0;
++ m_Duration = 0;
++ m_genre_type = 0;
++ m_genre_subtype = 0;
++ m_seriesNumber = 0;
++ m_episodeNumber = 0;
++ m_starRating = 0;
++ m_parentalRating = 0;
++}
++
++bool cEpg::ParseLine(string& data)
++{
++ try
++ {
++ vector<string> epgfields;
++
++ Tokenize(data, epgfields, "|");
++
++ if( epgfields.size() >= 5 )
++ {
++ //XBMC->Log(LOG_DEBUG, "%s: %s", epgfields[0].c_str(), epgfields[2].c_str());
++ // field 0 = start date + time
++ // field 1 = end date + time
++ // field 2 = title
++ // field 3 = description
++ // field 4 = genre string
++ // field 5 = idProgram (int)
++ // field 6 = idChannel (int)
++ // field 7 = seriesNum (string)
++ // field 8 = episodeNumber (string)
++ // field 9 = episodeName (string)
++ // field 10 = episodePart (string)
++ // field 11 = originalAirDate (date + time)
++ // field 12 = classification (string)
++ // field 13 = starRating (int)
++ // field 14 = parentalRating (int)
++
++ m_StartTime = DateTimeToTimeT(epgfields[0]);
++
++ if(m_StartTime < 0)
++ {
++ XBMC->Log(LOG_ERROR, "cEpg::ParseLine: Unable to convert start time '%s' into date+time", epgfields[0].c_str());
++ return false;
++ }
++
++ m_EndTime = DateTimeToTimeT(epgfields[1]);
++
++ if( m_EndTime < 0)
++ {
++ XBMC->Log(LOG_ERROR, "cEpg::ParseLine: Unable to convert end time '%s' into date+time", epgfields[1].c_str());
++ return false;
++ }
++
++ m_Duration = m_EndTime - m_StartTime;
++
++ m_title = epgfields[2];
++ m_description = epgfields[3];
++ m_shortText = epgfields[2];
++ m_genre = epgfields[4];
++ if (m_genretable) m_genretable->GenreToTypes(m_genre, m_genre_type, m_genre_subtype);
++
++ if( epgfields.size() >= 15 )
++ {
++ // Since TVServerXBMC v1.x.x.104
++ m_uid = (unsigned int) atol(epgfields[5].c_str());
++ m_seriesNumber = atoi(epgfields[7].c_str());
++ m_episodeNumber = atoi(epgfields[8].c_str());
++ m_episodeName = epgfields[9];
++ m_episodePart = epgfields[10];
++ m_starRating = atoi(epgfields[13].c_str());
++ m_parentalRating = atoi(epgfields[14].c_str());
++
++ //originalAirDate
++ m_originalAirDate = DateTimeToTimeT(epgfields[11]);
++
++ if( m_originalAirDate < 0)
++ {
++ XBMC->Log(LOG_ERROR, "cEpg::ParseLine: Unable to convert original air date '%s' into date+time", epgfields[11].c_str());
++ return false;
++ }
++ }
++
++ return true;
++ }
++ }
++ catch(std::exception &e)
++ {
++ XBMC->Log(LOG_ERROR, "Exception '%s' during parse EPG data string.", e.what());
++ }
++
++ return false;
++}
++
++void cEpg::SetGenreTable(CGenreTable* genretable)
++{
++ m_genretable = genretable;
++}
+diff --git a/xbmc/pvrclients/MediaPortal/epg.h b/xbmc/pvrclients/MediaPortal/epg.h
+new file mode 100644
+index 0000000..1a5b318
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/epg.h
+@@ -0,0 +1,82 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#ifndef __EPG_H
++#define __EPG_H
++
++#include <stdlib.h>
++#include <string>
++#include "libXBMC_addon.h"
++#include "libXBMC_pvr.h"
++#include "GenreTable.h"
++
++using namespace std;
++
++class cEpg
++{
++private:
++ unsigned int m_uid;
++ string m_title;
++ string m_shortText;
++ string m_description;
++ time_t m_StartTime;
++ time_t m_EndTime;
++ time_t m_originalAirDate;
++ int m_Duration;
++ string m_genre;
++ int m_genre_type;
++ int m_genre_subtype;
++ int m_episodeNumber;
++ string m_episodePart;
++ string m_episodeName;
++ int m_seriesNumber;
++ int m_starRating;
++ int m_parentalRating;
++ CGenreTable* m_genretable;
++
++public:
++ cEpg();
++ virtual ~cEpg();
++ void Reset();
++
++ bool ParseLine(string& data);
++ int UniqueId(void) const { return m_uid; }
++ time_t StartTime(void) const { return m_StartTime; }
++ time_t EndTime(void) const { return m_EndTime; }
++ time_t Duration(void) const { return m_Duration; }
++ time_t OriginalAirDate(void) const { return m_originalAirDate; }
++ const char *Title(void) const { return m_title.c_str(); }
++ const char *ShortText(void) const { return m_shortText.c_str(); }
++ const char *Description(void) const { return m_description.c_str(); }
++ const char *Genre(void) const { return m_genre.c_str(); }
++ int GenreType(void) const { return m_genre_type; }
++ int GenreSubType(void) const { return m_genre_subtype; }
++ int SeriesNumber(void) const { return m_seriesNumber; };
++ int EpisodeNumber(void) const { return m_episodeNumber; };
++ const char* EpisodeName(void) const { return m_episodeName.c_str(); };
++ const char* EpisodePart(void) const { return m_episodePart.c_str(); };
++ int StarRating(void) const { return m_starRating; };
++ int ParentalRating(void) const { return m_parentalRating; };
++ void SetGenreTable(CGenreTable* genremap);
++};
++
++#endif //__EPG_H
+diff --git a/xbmc/pvrclients/MediaPortal/lib/tinyxml/Makefile b/xbmc/pvrclients/MediaPortal/lib/tinyxml/Makefile
+new file mode 100644
+index 0000000..4156cae
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/lib/tinyxml/Makefile
+@@ -0,0 +1,9 @@
++INCLUDES=-I.
++SRCS=tinystr.cpp \
++ tinyxml.cpp \
++ tinyxmlerror.cpp \
++ tinyxmlparser.cpp
++LIB=tinyxml.a
++
++include ../../../Makefile.include
++-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
+diff --git a/xbmc/pvrclients/MediaPortal/lib/tinyxml/readme.txt b/xbmc/pvrclients/MediaPortal/lib/tinyxml/readme.txt
+new file mode 100644
+index 0000000..390517e
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/lib/tinyxml/readme.txt
+@@ -0,0 +1,8 @@
++XBMC includes a slightly modified version of tinyxml in lib/tinyXML that refers to its
++internal characterset conversion code.
++
++Mediaportal PVR addon uses its own copy of tinyxml to minimize its dependencies on the XBMC codebase.
++
++tinyxml source:
++http://www.grinninglizard.com/tinyxml/
++Used version 2.6.2
+diff --git a/xbmc/pvrclients/MediaPortal/lib/tinyxml/tinystr.cpp b/xbmc/pvrclients/MediaPortal/lib/tinyxml/tinystr.cpp
+new file mode 100644
+index 0000000..0665768
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/lib/tinyxml/tinystr.cpp
+@@ -0,0 +1,111 @@
++/*
++www.sourceforge.net/projects/tinyxml
++
++This software is provided 'as-is', without any express or implied
++warranty. In no event will the authors be held liable for any
++damages arising from the use of this software.
++
++Permission is granted to anyone to use this software for any
++purpose, including commercial applications, and to alter it and
++redistribute it freely, subject to the following restrictions:
++
++1. The origin of this software must not be misrepresented; you must
++not claim that you wrote the original software. If you use this
++software in a product, an acknowledgment in the product documentation
++would be appreciated but is not required.
++
++2. Altered source versions must be plainly marked as such, and
++must not be misrepresented as being the original software.
++
++3. This notice may not be removed or altered from any source
++distribution.
++*/
++
++
++#ifndef TIXML_USE_STL
++
++#include "tinystr.h"
++
++// Error value for find primitive
++const TiXmlString::size_type TiXmlString::npos = static_cast< TiXmlString::size_type >(-1);
++
++
++// Null rep.
++TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } };
++
++
++void TiXmlString::reserve (size_type cap)
++{
++ if (cap > capacity())
++ {
++ TiXmlString tmp;
++ tmp.init(length(), cap);
++ memcpy(tmp.start(), data(), length());
++ swap(tmp);
++ }
++}
++
++
++TiXmlString& TiXmlString::assign(const char* str, size_type len)
++{
++ size_type cap = capacity();
++ if (len > cap || cap > 3*(len + 8))
++ {
++ TiXmlString tmp;
++ tmp.init(len);
++ memcpy(tmp.start(), str, len);
++ swap(tmp);
++ }
++ else
++ {
++ memmove(start(), str, len);
++ set_size(len);
++ }
++ return *this;
++}
++
++
++TiXmlString& TiXmlString::append(const char* str, size_type len)
++{
++ size_type newsize = length() + len;
++ if (newsize > capacity())
++ {
++ reserve (newsize + capacity());
++ }
++ memmove(finish(), str, len);
++ set_size(newsize);
++ return *this;
++}
++
++
++TiXmlString operator + (const TiXmlString & a, const TiXmlString & b)
++{
++ TiXmlString tmp;
++ tmp.reserve(a.length() + b.length());
++ tmp += a;
++ tmp += b;
++ return tmp;
++}
++
++TiXmlString operator + (const TiXmlString & a, const char* b)
++{
++ TiXmlString tmp;
++ TiXmlString::size_type b_len = static_cast<TiXmlString::size_type>( strlen(b) );
++ tmp.reserve(a.length() + b_len);
++ tmp += a;
++ tmp.append(b, b_len);
++ return tmp;
++}
++
++TiXmlString operator + (const char* a, const TiXmlString & b)
++{
++ TiXmlString tmp;
++ TiXmlString::size_type a_len = static_cast<TiXmlString::size_type>( strlen(a) );
++ tmp.reserve(a_len + b.length());
++ tmp.append(a, a_len);
++ tmp += b;
++ return tmp;
++}
++
++
++#endif // TIXML_USE_STL
+diff --git a/xbmc/pvrclients/MediaPortal/lib/tinyxml/tinystr.h b/xbmc/pvrclients/MediaPortal/lib/tinyxml/tinystr.h
+new file mode 100644
+index 0000000..89cca33
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/lib/tinyxml/tinystr.h
+@@ -0,0 +1,305 @@
++/*
++www.sourceforge.net/projects/tinyxml
++
++This software is provided 'as-is', without any express or implied
++warranty. In no event will the authors be held liable for any
++damages arising from the use of this software.
++
++Permission is granted to anyone to use this software for any
++purpose, including commercial applications, and to alter it and
++redistribute it freely, subject to the following restrictions:
++
++1. The origin of this software must not be misrepresented; you must
++not claim that you wrote the original software. If you use this
++software in a product, an acknowledgment in the product documentation
++would be appreciated but is not required.
++
++2. Altered source versions must be plainly marked as such, and
++must not be misrepresented as being the original software.
++
++3. This notice may not be removed or altered from any source
++distribution.
++*/
++
++
++#ifndef TIXML_USE_STL
++
++#ifndef TIXML_STRING_INCLUDED
++#define TIXML_STRING_INCLUDED
++
++#include <assert.h>
++#include <string.h>
++
++/* The support for explicit isn't that universal, and it isn't really
++ required - it is used to check that the TiXmlString class isn't incorrectly
++ used. Be nice to old compilers and macro it here:
++*/
++#if defined(_MSC_VER) && (_MSC_VER >= 1200 )
++ // Microsoft visual studio, version 6 and higher.
++ #define TIXML_EXPLICIT explicit
++#elif defined(__GNUC__) && (__GNUC__ >= 3 )
++ // GCC version 3 and higher.s
++ #define TIXML_EXPLICIT explicit
++#else
++ #define TIXML_EXPLICIT
++#endif
++
++
++/*
++ TiXmlString is an emulation of a subset of the std::string template.
++ Its purpose is to allow compiling TinyXML on compilers with no or poor STL support.
++ Only the member functions relevant to the TinyXML project have been implemented.
++ The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase
++ a string and there's no more room, we allocate a buffer twice as big as we need.
++*/
++class TiXmlString
++{
++ public :
++ // The size type used
++ typedef size_t size_type;
++
++ // Error value for find primitive
++ static const size_type npos; // = -1;
++
++
++ // TiXmlString empty constructor
++ TiXmlString () : rep_(&nullrep_)
++ {
++ }
++
++ // TiXmlString copy constructor
++ TiXmlString ( const TiXmlString & copy) : rep_(0)
++ {
++ init(copy.length());
++ memcpy(start(), copy.data(), length());
++ }
++
++ // TiXmlString constructor, based on a string
++ TIXML_EXPLICIT TiXmlString ( const char * copy) : rep_(0)
++ {
++ init( static_cast<size_type>( strlen(copy) ));
++ memcpy(start(), copy, length());
++ }
++
++ // TiXmlString constructor, based on a string
++ TIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0)
++ {
++ init(len);
++ memcpy(start(), str, len);
++ }
++
++ // TiXmlString destructor
++ ~TiXmlString ()
++ {
++ quit();
++ }
++
++ TiXmlString& operator = (const char * copy)
++ {
++ return assign( copy, (size_type)strlen(copy));
++ }
++
++ TiXmlString& operator = (const TiXmlString & copy)
++ {
++ return assign(copy.start(), copy.length());
++ }
++
++
++ // += operator. Maps to append
++ TiXmlString& operator += (const char * suffix)
++ {
++ return append(suffix, static_cast<size_type>( strlen(suffix) ));
++ }
++
++ // += operator. Maps to append
++ TiXmlString& operator += (char single)
++ {
++ return append(&single, 1);
++ }
++
++ // += operator. Maps to append
++ TiXmlString& operator += (const TiXmlString & suffix)
++ {
++ return append(suffix.data(), suffix.length());
++ }
++
++
++ // Convert a TiXmlString into a null-terminated char *
++ const char * c_str () const { return rep_->str; }
++
++ // Convert a TiXmlString into a char * (need not be null terminated).
++ const char * data () const { return rep_->str; }
++
++ // Return the length of a TiXmlString
++ size_type length () const { return rep_->size; }
++
++ // Alias for length()
++ size_type size () const { return rep_->size; }
++
++ // Checks if a TiXmlString is empty
++ bool empty () const { return rep_->size == 0; }
++
++ // Return capacity of string
++ size_type capacity () const { return rep_->capacity; }
++
++
++ // single char extraction
++ const char& at (size_type index) const
++ {
++ assert( index < length() );
++ return rep_->str[ index ];
++ }
++
++ // [] operator
++ char& operator [] (size_type index) const
++ {
++ assert( index < length() );
++ return rep_->str[ index ];
++ }
++
++ // find a char in a string. Return TiXmlString::npos if not found
++ size_type find (char lookup) const
++ {
++ return find(lookup, 0);
++ }
++
++ // find a char in a string from an offset. Return TiXmlString::npos if not found
++ size_type find (char tofind, size_type offset) const
++ {
++ if (offset >= length()) return npos;
++
++ for (const char* p = c_str() + offset; *p != '\0'; ++p)
++ {
++ if (*p == tofind) return static_cast< size_type >( p - c_str() );
++ }
++ return npos;
++ }
++
++ void clear ()
++ {
++ //Lee:
++ //The original was just too strange, though correct:
++ // TiXmlString().swap(*this);
++ //Instead use the quit & re-init:
++ quit();
++ init(0,0);
++ }
++
++ /* Function to reserve a big amount of data when we know we'll need it. Be aware that this
++ function DOES NOT clear the content of the TiXmlString if any exists.
++ */
++ void reserve (size_type cap);
++
++ TiXmlString& assign (const char* str, size_type len);
++
++ TiXmlString& append (const char* str, size_type len);
++
++ void swap (TiXmlString& other)
++ {
++ Rep* r = rep_;
++ rep_ = other.rep_;
++ other.rep_ = r;
++ }
++
++ private:
++
++ void init(size_type sz) { init(sz, sz); }
++ void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; }
++ char* start() const { return rep_->str; }
++ char* finish() const { return rep_->str + rep_->size; }
++
++ struct Rep
++ {
++ size_type size, capacity;
++ char str[1];
++ };
++
++ void init(size_type sz, size_type cap)
++ {
++ if (cap)
++ {
++ // Lee: the original form:
++ // rep_ = static_cast<Rep*>(operator new(sizeof(Rep) + cap));
++ // doesn't work in some cases of new being overloaded. Switching
++ // to the normal allocation, although use an 'int' for systems
++ // that are overly picky about structure alignment.
++ const size_type bytesNeeded = sizeof(Rep) + cap;
++ const size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int );
++ rep_ = reinterpret_cast<Rep*>( new int[ intsNeeded ] );
++
++ rep_->str[ rep_->size = sz ] = '\0';
++ rep_->capacity = cap;
++ }
++ else
++ {
++ rep_ = &nullrep_;
++ }
++ }
++
++ void quit()
++ {
++ if (rep_ != &nullrep_)
++ {
++ // The rep_ is really an array of ints. (see the allocator, above).
++ // Cast it back before delete, so the compiler won't incorrectly call destructors.
++ delete [] ( reinterpret_cast<int*>( rep_ ) );
++ }
++ }
++
++ Rep * rep_;
++ static Rep nullrep_;
++
++} ;
++
++
++inline bool operator == (const TiXmlString & a, const TiXmlString & b)
++{
++ return ( a.length() == b.length() ) // optimization on some platforms
++ && ( strcmp(a.c_str(), b.c_str()) == 0 ); // actual compare
++}
++inline bool operator < (const TiXmlString & a, const TiXmlString & b)
++{
++ return strcmp(a.c_str(), b.c_str()) < 0;
++}
++
++inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); }
++inline bool operator > (const TiXmlString & a, const TiXmlString & b) { return b < a; }
++inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); }
++inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); }
++
++inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; }
++inline bool operator == (const char* a, const TiXmlString & b) { return b == a; }
++inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); }
++inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); }
++
++TiXmlString operator + (const TiXmlString & a, const TiXmlString & b);
++TiXmlString operator + (const TiXmlString & a, const char* b);
++TiXmlString operator + (const char* a, const TiXmlString & b);
++
++
++/*
++ TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString.
++ Only the operators that we need for TinyXML have been developped.
++*/
++class TiXmlOutStream : public TiXmlString
++{
++public :
++
++ // TiXmlOutStream << operator.
++ TiXmlOutStream & operator << (const TiXmlString & in)
++ {
++ *this += in;
++ return *this;
++ }
++
++ // TiXmlOutStream << operator.
++ TiXmlOutStream & operator << (const char * in)
++ {
++ *this += in;
++ return *this;
++ }
++
++} ;
++
++#endif // TIXML_STRING_INCLUDED
++#endif // TIXML_USE_STL
+diff --git a/xbmc/pvrclients/MediaPortal/lib/tinyxml/tinyxml.cpp b/xbmc/pvrclients/MediaPortal/lib/tinyxml/tinyxml.cpp
+new file mode 100644
+index 0000000..9c161df
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/lib/tinyxml/tinyxml.cpp
+@@ -0,0 +1,1886 @@
++/*
++www.sourceforge.net/projects/tinyxml
++Original code by Lee Thomason (www.grinninglizard.com)
++
++This software is provided 'as-is', without any express or implied
++warranty. In no event will the authors be held liable for any
++damages arising from the use of this software.
++
++Permission is granted to anyone to use this software for any
++purpose, including commercial applications, and to alter it and
++redistribute it freely, subject to the following restrictions:
++
++1. The origin of this software must not be misrepresented; you must
++not claim that you wrote the original software. If you use this
++software in a product, an acknowledgment in the product documentation
++would be appreciated but is not required.
++
++2. Altered source versions must be plainly marked as such, and
++must not be misrepresented as being the original software.
++
++3. This notice may not be removed or altered from any source
++distribution.
++*/
++
++#include <ctype.h>
++
++#ifdef TIXML_USE_STL
++#include <sstream>
++#include <iostream>
++#endif
++
++#include "tinyxml.h"
++
++FILE* TiXmlFOpen( const char* filename, const char* mode );
++
++bool TiXmlBase::condenseWhiteSpace = true;
++
++// Microsoft compiler security
++FILE* TiXmlFOpen( const char* filename, const char* mode )
++{
++ #if defined(_MSC_VER) && (_MSC_VER >= 1400 )
++ FILE* fp = 0;
++ errno_t err = fopen_s( &fp, filename, mode );
++ if ( !err && fp )
++ return fp;
++ return 0;
++ #else
++ return fopen( filename, mode );
++ #endif
++}
++
++void TiXmlBase::EncodeString( const TIXML_STRING& str, TIXML_STRING* outString )
++{
++ int i=0;
++
++ while( i<(int)str.length() )
++ {
++ unsigned char c = (unsigned char) str[i];
++
++ if ( c == '&'
++ && i < ( (int)str.length() - 2 )
++ && str[i+1] == '#'
++ && str[i+2] == 'x' )
++ {
++ // Hexadecimal character reference.
++ // Pass through unchanged.
++ // &#xA9; -- copyright symbol, for example.
++ //
++ // The -1 is a bug fix from Rob Laveaux. It keeps
++ // an overflow from happening if there is no ';'.
++ // There are actually 2 ways to exit this loop -
++ // while fails (error case) and break (semicolon found).
++ // However, there is no mechanism (currently) for
++ // this function to return an error.
++ while ( i<(int)str.length()-1 )
++ {
++ outString->append( str.c_str() + i, 1 );
++ ++i;
++ if ( str[i] == ';' )
++ break;
++ }
++ }
++ else if ( c == '&' )
++ {
++ outString->append( entity[0].str, entity[0].strLength );
++ ++i;
++ }
++ else if ( c == '<' )
++ {
++ outString->append( entity[1].str, entity[1].strLength );
++ ++i;
++ }
++ else if ( c == '>' )
++ {
++ outString->append( entity[2].str, entity[2].strLength );
++ ++i;
++ }
++ else if ( c == '\"' )
++ {
++ outString->append( entity[3].str, entity[3].strLength );
++ ++i;
++ }
++ else if ( c == '\'' )
++ {
++ outString->append( entity[4].str, entity[4].strLength );
++ ++i;
++ }
++ else if ( c < 32 )
++ {
++ // Easy pass at non-alpha/numeric/symbol
++ // Below 32 is symbolic.
++ char buf[ 32 ];
++
++ #if defined(TIXML_SNPRINTF)
++ TIXML_SNPRINTF( buf, sizeof(buf), "&#x%02X;", (unsigned) ( c & 0xff ) );
++ #else
++ sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) );
++ #endif
++
++ //*ME: warning C4267: convert 'size_t' to 'int'
++ //*ME: Int-Cast to make compiler happy ...
++ outString->append( buf, (int)strlen( buf ) );
++ ++i;
++ }
++ else
++ {
++ //char realc = (char) c;
++ //outString->append( &realc, 1 );
++ *outString += (char) c; // somewhat more efficient function call.
++ ++i;
++ }
++ }
++}
++
++
++TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase()
++{
++ parent = 0;
++ type = _type;
++ firstChild = 0;
++ lastChild = 0;
++ prev = 0;
++ next = 0;
++}
++
++
++TiXmlNode::~TiXmlNode()
++{
++ TiXmlNode* node = firstChild;
++ TiXmlNode* temp = 0;
++
++ while ( node )
++ {
++ temp = node;
++ node = node->next;
++ delete temp;
++ }
++}
++
++
++void TiXmlNode::CopyTo( TiXmlNode* target ) const
++{
++ target->SetValue (value.c_str() );
++ target->userData = userData;
++ target->location = location;
++}
++
++
++void TiXmlNode::Clear()
++{
++ TiXmlNode* node = firstChild;
++ TiXmlNode* temp = 0;
++
++ while ( node )
++ {
++ temp = node;
++ node = node->next;
++ delete temp;
++ }
++
++ firstChild = 0;
++ lastChild = 0;
++}
++
++
++TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node )
++{
++ assert( node->parent == 0 || node->parent == this );
++ assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() );
++
++ if ( node->Type() == TiXmlNode::TINYXML_DOCUMENT )
++ {
++ delete node;
++ if ( GetDocument() )
++ GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return 0;
++ }
++
++ node->parent = this;
++
++ node->prev = lastChild;
++ node->next = 0;
++
++ if ( lastChild )
++ lastChild->next = node;
++ else
++ firstChild = node; // it was an empty list.
++
++ lastChild = node;
++ return node;
++}
++
++
++TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis )
++{
++ if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT )
++ {
++ if ( GetDocument() )
++ GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return 0;
++ }
++ TiXmlNode* node = addThis.Clone();
++ if ( !node )
++ return 0;
++
++ return LinkEndChild( node );
++}
++
++
++TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis )
++{
++ if ( !beforeThis || beforeThis->parent != this ) {
++ return 0;
++ }
++ if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT )
++ {
++ if ( GetDocument() )
++ GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return 0;
++ }
++
++ TiXmlNode* node = addThis.Clone();
++ if ( !node )
++ return 0;
++ node->parent = this;
++
++ node->next = beforeThis;
++ node->prev = beforeThis->prev;
++ if ( beforeThis->prev )
++ {
++ beforeThis->prev->next = node;
++ }
++ else
++ {
++ assert( firstChild == beforeThis );
++ firstChild = node;
++ }
++ beforeThis->prev = node;
++ return node;
++}
++
++
++TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis )
++{
++ if ( !afterThis || afterThis->parent != this ) {
++ return 0;
++ }
++ if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT )
++ {
++ if ( GetDocument() )
++ GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return 0;
++ }
++
++ TiXmlNode* node = addThis.Clone();
++ if ( !node )
++ return 0;
++ node->parent = this;
++
++ node->prev = afterThis;
++ node->next = afterThis->next;
++ if ( afterThis->next )
++ {
++ afterThis->next->prev = node;
++ }
++ else
++ {
++ assert( lastChild == afterThis );
++ lastChild = node;
++ }
++ afterThis->next = node;
++ return node;
++}
++
++
++TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis )
++{
++ if ( !replaceThis )
++ return 0;
++
++ if ( replaceThis->parent != this )
++ return 0;
++
++ if ( withThis.ToDocument() ) {
++ // A document can never be a child. Thanks to Noam.
++ TiXmlDocument* document = GetDocument();
++ if ( document )
++ document->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return 0;
++ }
++
++ TiXmlNode* node = withThis.Clone();
++ if ( !node )
++ return 0;
++
++ node->next = replaceThis->next;
++ node->prev = replaceThis->prev;
++
++ if ( replaceThis->next )
++ replaceThis->next->prev = node;
++ else
++ lastChild = node;
++
++ if ( replaceThis->prev )
++ replaceThis->prev->next = node;
++ else
++ firstChild = node;
++
++ delete replaceThis;
++ node->parent = this;
++ return node;
++}
++
++
++bool TiXmlNode::RemoveChild( TiXmlNode* removeThis )
++{
++ if ( !removeThis ) {
++ return false;
++ }
++
++ if ( removeThis->parent != this )
++ {
++ assert( 0 );
++ return false;
++ }
++
++ if ( removeThis->next )
++ removeThis->next->prev = removeThis->prev;
++ else
++ lastChild = removeThis->prev;
++
++ if ( removeThis->prev )
++ removeThis->prev->next = removeThis->next;
++ else
++ firstChild = removeThis->next;
++
++ delete removeThis;
++ return true;
++}
++
++const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const
++{
++ const TiXmlNode* node;
++ for ( node = firstChild; node; node = node->next )
++ {
++ if ( strcmp( node->Value(), _value ) == 0 )
++ return node;
++ }
++ return 0;
++}
++
++
++const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const
++{
++ const TiXmlNode* node;
++ for ( node = lastChild; node; node = node->prev )
++ {
++ if ( strcmp( node->Value(), _value ) == 0 )
++ return node;
++ }
++ return 0;
++}
++
++
++const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const
++{
++ if ( !previous )
++ {
++ return FirstChild();
++ }
++ else
++ {
++ assert( previous->parent == this );
++ return previous->NextSibling();
++ }
++}
++
++
++const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const
++{
++ if ( !previous )
++ {
++ return FirstChild( val );
++ }
++ else
++ {
++ assert( previous->parent == this );
++ return previous->NextSibling( val );
++ }
++}
++
++
++const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const
++{
++ const TiXmlNode* node;
++ for ( node = next; node; node = node->next )
++ {
++ if ( strcmp( node->Value(), _value ) == 0 )
++ return node;
++ }
++ return 0;
++}
++
++
++const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const
++{
++ const TiXmlNode* node;
++ for ( node = prev; node; node = node->prev )
++ {
++ if ( strcmp( node->Value(), _value ) == 0 )
++ return node;
++ }
++ return 0;
++}
++
++
++void TiXmlElement::RemoveAttribute( const char * name )
++{
++ #ifdef TIXML_USE_STL
++ TIXML_STRING str( name );
++ TiXmlAttribute* node = attributeSet.Find( str );
++ #else
++ TiXmlAttribute* node = attributeSet.Find( name );
++ #endif
++ if ( node )
++ {
++ attributeSet.Remove( node );
++ delete node;
++ }
++}
++
++const TiXmlElement* TiXmlNode::FirstChildElement() const
++{
++ const TiXmlNode* node;
++
++ for ( node = FirstChild();
++ node;
++ node = node->NextSibling() )
++ {
++ if ( node->ToElement() )
++ return node->ToElement();
++ }
++ return 0;
++}
++
++
++const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const
++{
++ const TiXmlNode* node;
++
++ for ( node = FirstChild( _value );
++ node;
++ node = node->NextSibling( _value ) )
++ {
++ if ( node->ToElement() )
++ return node->ToElement();
++ }
++ return 0;
++}
++
++
++const TiXmlElement* TiXmlNode::NextSiblingElement() const
++{
++ const TiXmlNode* node;
++
++ for ( node = NextSibling();
++ node;
++ node = node->NextSibling() )
++ {
++ if ( node->ToElement() )
++ return node->ToElement();
++ }
++ return 0;
++}
++
++
++const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const
++{
++ const TiXmlNode* node;
++
++ for ( node = NextSibling( _value );
++ node;
++ node = node->NextSibling( _value ) )
++ {
++ if ( node->ToElement() )
++ return node->ToElement();
++ }
++ return 0;
++}
++
++
++const TiXmlDocument* TiXmlNode::GetDocument() const
++{
++ const TiXmlNode* node;
++
++ for( node = this; node; node = node->parent )
++ {
++ if ( node->ToDocument() )
++ return node->ToDocument();
++ }
++ return 0;
++}
++
++
++TiXmlElement::TiXmlElement (const char * _value)
++ : TiXmlNode( TiXmlNode::TINYXML_ELEMENT )
++{
++ firstChild = lastChild = 0;
++ value = _value;
++}
++
++
++#ifdef TIXML_USE_STL
++TiXmlElement::TiXmlElement( const std::string& _value )
++ : TiXmlNode( TiXmlNode::TINYXML_ELEMENT )
++{
++ firstChild = lastChild = 0;
++ value = _value;
++}
++#endif
++
++
++TiXmlElement::TiXmlElement( const TiXmlElement& copy)
++ : TiXmlNode( TiXmlNode::TINYXML_ELEMENT )
++{
++ firstChild = lastChild = 0;
++ copy.CopyTo( this );
++}
++
++
++TiXmlElement& TiXmlElement::operator=( const TiXmlElement& base )
++{
++ ClearThis();
++ base.CopyTo( this );
++ return *this;
++}
++
++
++TiXmlElement::~TiXmlElement()
++{
++ ClearThis();
++}
++
++
++void TiXmlElement::ClearThis()
++{
++ Clear();
++ while( attributeSet.First() )
++ {
++ TiXmlAttribute* node = attributeSet.First();
++ attributeSet.Remove( node );
++ delete node;
++ }
++}
++
++
++const char* TiXmlElement::Attribute( const char* name ) const
++{
++ const TiXmlAttribute* node = attributeSet.Find( name );
++ if ( node )
++ return node->Value();
++ return 0;
++}
++
++
++#ifdef TIXML_USE_STL
++const std::string* TiXmlElement::Attribute( const std::string& name ) const
++{
++ const TiXmlAttribute* attrib = attributeSet.Find( name );
++ if ( attrib )
++ return &attrib->ValueStr();
++ return 0;
++}
++#endif
++
++
++const char* TiXmlElement::Attribute( const char* name, int* i ) const
++{
++ const TiXmlAttribute* attrib = attributeSet.Find( name );
++ const char* result = 0;
++
++ if ( attrib ) {
++ result = attrib->Value();
++ if ( i ) {
++ attrib->QueryIntValue( i );
++ }
++ }
++ return result;
++}
++
++
++#ifdef TIXML_USE_STL
++const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const
++{
++ const TiXmlAttribute* attrib = attributeSet.Find( name );
++ const std::string* result = 0;
++
++ if ( attrib ) {
++ result = &attrib->ValueStr();
++ if ( i ) {
++ attrib->QueryIntValue( i );
++ }
++ }
++ return result;
++}
++#endif
++
++
++const char* TiXmlElement::Attribute( const char* name, double* d ) const
++{
++ const TiXmlAttribute* attrib = attributeSet.Find( name );
++ const char* result = 0;
++
++ if ( attrib ) {
++ result = attrib->Value();
++ if ( d ) {
++ attrib->QueryDoubleValue( d );
++ }
++ }
++ return result;
++}
++
++
++#ifdef TIXML_USE_STL
++const std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const
++{
++ const TiXmlAttribute* attrib = attributeSet.Find( name );
++ const std::string* result = 0;
++
++ if ( attrib ) {
++ result = &attrib->ValueStr();
++ if ( d ) {
++ attrib->QueryDoubleValue( d );
++ }
++ }
++ return result;
++}
++#endif
++
++
++int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const
++{
++ const TiXmlAttribute* attrib = attributeSet.Find( name );
++ if ( !attrib )
++ return TIXML_NO_ATTRIBUTE;
++ return attrib->QueryIntValue( ival );
++}
++
++
++int TiXmlElement::QueryUnsignedAttribute( const char* name, unsigned* value ) const
++{
++ const TiXmlAttribute* node = attributeSet.Find( name );
++ if ( !node )
++ return TIXML_NO_ATTRIBUTE;
++
++ int ival = 0;
++ int result = node->QueryIntValue( &ival );
++ *value = (unsigned)ival;
++ return result;
++}
++
++
++int TiXmlElement::QueryBoolAttribute( const char* name, bool* bval ) const
++{
++ const TiXmlAttribute* node = attributeSet.Find( name );
++ if ( !node )
++ return TIXML_NO_ATTRIBUTE;
++
++ int result = TIXML_WRONG_TYPE;
++ if ( StringEqual( node->Value(), "true", true, TIXML_ENCODING_UNKNOWN )
++ || StringEqual( node->Value(), "yes", true, TIXML_ENCODING_UNKNOWN )
++ || StringEqual( node->Value(), "1", true, TIXML_ENCODING_UNKNOWN ) )
++ {
++ *bval = true;
++ result = TIXML_SUCCESS;
++ }
++ else if ( StringEqual( node->Value(), "false", true, TIXML_ENCODING_UNKNOWN )
++ || StringEqual( node->Value(), "no", true, TIXML_ENCODING_UNKNOWN )
++ || StringEqual( node->Value(), "0", true, TIXML_ENCODING_UNKNOWN ) )
++ {
++ *bval = false;
++ result = TIXML_SUCCESS;
++ }
++ return result;
++}
++
++
++
++#ifdef TIXML_USE_STL
++int TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const
++{
++ const TiXmlAttribute* attrib = attributeSet.Find( name );
++ if ( !attrib )
++ return TIXML_NO_ATTRIBUTE;
++ return attrib->QueryIntValue( ival );
++}
++#endif
++
++
++int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const
++{
++ const TiXmlAttribute* attrib = attributeSet.Find( name );
++ if ( !attrib )
++ return TIXML_NO_ATTRIBUTE;
++ return attrib->QueryDoubleValue( dval );
++}
++
++
++#ifdef TIXML_USE_STL
++int TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const
++{
++ const TiXmlAttribute* attrib = attributeSet.Find( name );
++ if ( !attrib )
++ return TIXML_NO_ATTRIBUTE;
++ return attrib->QueryDoubleValue( dval );
++}
++#endif
++
++
++void TiXmlElement::SetAttribute( const char * name, int val )
++{
++ TiXmlAttribute* attrib = attributeSet.FindOrCreate( name );
++ if ( attrib ) {
++ attrib->SetIntValue( val );
++ }
++}
++
++
++#ifdef TIXML_USE_STL
++void TiXmlElement::SetAttribute( const std::string& name, int val )
++{
++ TiXmlAttribute* attrib = attributeSet.FindOrCreate( name );
++ if ( attrib ) {
++ attrib->SetIntValue( val );
++ }
++}
++#endif
++
++
++void TiXmlElement::SetDoubleAttribute( const char * name, double val )
++{
++ TiXmlAttribute* attrib = attributeSet.FindOrCreate( name );
++ if ( attrib ) {
++ attrib->SetDoubleValue( val );
++ }
++}
++
++
++#ifdef TIXML_USE_STL
++void TiXmlElement::SetDoubleAttribute( const std::string& name, double val )
++{
++ TiXmlAttribute* attrib = attributeSet.FindOrCreate( name );
++ if ( attrib ) {
++ attrib->SetDoubleValue( val );
++ }
++}
++#endif
++
++
++void TiXmlElement::SetAttribute( const char * cname, const char * cvalue )
++{
++ TiXmlAttribute* attrib = attributeSet.FindOrCreate( cname );
++ if ( attrib ) {
++ attrib->SetValue( cvalue );
++ }
++}
++
++
++#ifdef TIXML_USE_STL
++void TiXmlElement::SetAttribute( const std::string& _name, const std::string& _value )
++{
++ TiXmlAttribute* attrib = attributeSet.FindOrCreate( _name );
++ if ( attrib ) {
++ attrib->SetValue( _value );
++ }
++}
++#endif
++
++
++void TiXmlElement::Print( FILE* cfile, int depth ) const
++{
++ int i;
++ assert( cfile );
++ for ( i=0; i<depth; i++ ) {
++ fprintf( cfile, " " );
++ }
++
++ fprintf( cfile, "<%s", value.c_str() );
++
++ const TiXmlAttribute* attrib;
++ for ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() )
++ {
++ fprintf( cfile, " " );
++ attrib->Print( cfile, depth );
++ }
++
++ // There are 3 different formatting approaches:
++ // 1) An element without children is printed as a <foo /> node
++ // 2) An element with only a text child is printed as <foo> text </foo>
++ // 3) An element with children is printed on multiple lines.
++ TiXmlNode* node;
++ if ( !firstChild )
++ {
++ fprintf( cfile, " />" );
++ }
++ else if ( firstChild == lastChild && firstChild->ToText() )
++ {
++ fprintf( cfile, ">" );
++ firstChild->Print( cfile, depth + 1 );
++ fprintf( cfile, "</%s>", value.c_str() );
++ }
++ else
++ {
++ fprintf( cfile, ">" );
++
++ for ( node = firstChild; node; node=node->NextSibling() )
++ {
++ if ( !node->ToText() )
++ {
++ fprintf( cfile, "\n" );
++ }
++ node->Print( cfile, depth+1 );
++ }
++ fprintf( cfile, "\n" );
++ for( i=0; i<depth; ++i ) {
++ fprintf( cfile, " " );
++ }
++ fprintf( cfile, "</%s>", value.c_str() );
++ }
++}
++
++
++void TiXmlElement::CopyTo( TiXmlElement* target ) const
++{
++ // superclass:
++ TiXmlNode::CopyTo( target );
++
++ // Element class:
++ // Clone the attributes, then clone the children.
++ const TiXmlAttribute* attribute = 0;
++ for( attribute = attributeSet.First();
++ attribute;
++ attribute = attribute->Next() )
++ {
++ target->SetAttribute( attribute->Name(), attribute->Value() );
++ }
++
++ TiXmlNode* node = 0;
++ for ( node = firstChild; node; node = node->NextSibling() )
++ {
++ target->LinkEndChild( node->Clone() );
++ }
++}
++
++bool TiXmlElement::Accept( TiXmlVisitor* visitor ) const
++{
++ if ( visitor->VisitEnter( *this, attributeSet.First() ) )
++ {
++ for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() )
++ {
++ if ( !node->Accept( visitor ) )
++ break;
++ }
++ }
++ return visitor->VisitExit( *this );
++}
++
++
++TiXmlNode* TiXmlElement::Clone() const
++{
++ TiXmlElement* clone = new TiXmlElement( Value() );
++ if ( !clone )
++ return 0;
++
++ CopyTo( clone );
++ return clone;
++}
++
++
++const char* TiXmlElement::GetText() const
++{
++ const TiXmlNode* child = this->FirstChild();
++ if ( child ) {
++ const TiXmlText* childText = child->ToText();
++ if ( childText ) {
++ return childText->Value();
++ }
++ }
++ return 0;
++}
++
++
++TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT )
++{
++ tabsize = 4;
++ useMicrosoftBOM = false;
++ ClearError();
++}
++
++TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT )
++{
++ tabsize = 4;
++ useMicrosoftBOM = false;
++ value = documentName;
++ ClearError();
++}
++
++
++#ifdef TIXML_USE_STL
++TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT )
++{
++ tabsize = 4;
++ useMicrosoftBOM = false;
++ value = documentName;
++ ClearError();
++}
++#endif
++
++
++TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT )
++{
++ copy.CopyTo( this );
++}
++
++
++TiXmlDocument& TiXmlDocument::operator=( const TiXmlDocument& copy )
++{
++ Clear();
++ copy.CopyTo( this );
++ return *this;
++}
++
++
++bool TiXmlDocument::LoadFile( TiXmlEncoding encoding )
++{
++ return LoadFile( Value(), encoding );
++}
++
++
++bool TiXmlDocument::SaveFile() const
++{
++ return SaveFile( Value() );
++}
++
++bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding )
++{
++ TIXML_STRING filename( _filename );
++ value = filename;
++
++ // reading in binary mode so that tinyxml can normalize the EOL
++ FILE* file = TiXmlFOpen( value.c_str (), "rb" );
++
++ if ( file )
++ {
++ bool result = LoadFile( file, encoding );
++ fclose( file );
++ return result;
++ }
++ else
++ {
++ SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return false;
++ }
++}
++
++bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding )
++{
++ if ( !file )
++ {
++ SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return false;
++ }
++
++ // Delete the existing data:
++ Clear();
++ location.Clear();
++
++ // Get the file size, so we can pre-allocate the string. HUGE speed impact.
++ long length = 0;
++ fseek( file, 0, SEEK_END );
++ length = ftell( file );
++ fseek( file, 0, SEEK_SET );
++
++ // Strange case, but good to handle up front.
++ if ( length <= 0 )
++ {
++ SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return false;
++ }
++
++ // Subtle bug here. TinyXml did use fgets. But from the XML spec:
++ // 2.11 End-of-Line Handling
++ // <snip>
++ // <quote>
++ // ...the XML processor MUST behave as if it normalized all line breaks in external
++ // parsed entities (including the document entity) on input, before parsing, by translating
++ // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to
++ // a single #xA character.
++ // </quote>
++ //
++ // It is not clear fgets does that, and certainly isn't clear it works cross platform.
++ // Generally, you expect fgets to translate from the convention of the OS to the c/unix
++ // convention, and not work generally.
++
++ /*
++ while( fgets( buf, sizeof(buf), file ) )
++ {
++ data += buf;
++ }
++ */
++
++ char* buf = new char[ length+1 ];
++ buf[0] = 0;
++
++ if ( fread( buf, length, 1, file ) != 1 ) {
++ delete [] buf;
++ SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return false;
++ }
++
++ // Process the buffer in place to normalize new lines. (See comment above.)
++ // Copies from the 'p' to 'q' pointer, where p can advance faster if
++ // a newline-carriage return is hit.
++ //
++ // Wikipedia:
++ // Systems based on ASCII or a compatible character set use either LF (Line feed, '\n', 0x0A, 10 in decimal) or
++ // CR (Carriage return, '\r', 0x0D, 13 in decimal) individually, or CR followed by LF (CR+LF, 0x0D 0x0A)...
++ // * LF: Multics, Unix and Unix-like systems (GNU/Linux, AIX, Xenix, Mac OS X, FreeBSD, etc.), BeOS, Amiga, RISC OS, and others
++ // * CR+LF: DEC RT-11 and most other early non-Unix, non-IBM OSes, CP/M, MP/M, DOS, OS/2, Microsoft Windows, Symbian OS
++ // * CR: Commodore 8-bit machines, Apple II family, Mac OS up to version 9 and OS-9
++
++ const char* p = buf; // the read head
++ char* q = buf; // the write head
++ const char CR = 0x0d;
++ const char LF = 0x0a;
++
++ buf[length] = 0;
++ while( *p ) {
++ assert( p < (buf+length) );
++ assert( q <= (buf+length) );
++ assert( q <= p );
++
++ if ( *p == CR ) {
++ *q++ = LF;
++ p++;
++ if ( *p == LF ) { // check for CR+LF (and skip LF)
++ p++;
++ }
++ }
++ else {
++ *q++ = *p++;
++ }
++ }
++ assert( q <= (buf+length) );
++ *q = 0;
++
++ Parse( buf, 0, encoding );
++
++ delete [] buf;
++ return !Error();
++}
++
++
++bool TiXmlDocument::SaveFile( const char * filename ) const
++{
++ // The old c stuff lives on...
++ FILE* fp = TiXmlFOpen( filename, "w" );
++ if ( fp )
++ {
++ bool result = SaveFile( fp );
++ fclose( fp );
++ return result;
++ }
++ return false;
++}
++
++
++bool TiXmlDocument::SaveFile( FILE* fp ) const
++{
++ if ( useMicrosoftBOM )
++ {
++ const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
++ const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
++ const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
++
++ fputc( TIXML_UTF_LEAD_0, fp );
++ fputc( TIXML_UTF_LEAD_1, fp );
++ fputc( TIXML_UTF_LEAD_2, fp );
++ }
++ Print( fp, 0 );
++ return (ferror(fp) == 0);
++}
++
++
++void TiXmlDocument::CopyTo( TiXmlDocument* target ) const
++{
++ TiXmlNode::CopyTo( target );
++
++ target->error = error;
++ target->errorId = errorId;
++ target->errorDesc = errorDesc;
++ target->tabsize = tabsize;
++ target->errorLocation = errorLocation;
++ target->useMicrosoftBOM = useMicrosoftBOM;
++
++ TiXmlNode* node = 0;
++ for ( node = firstChild; node; node = node->NextSibling() )
++ {
++ target->LinkEndChild( node->Clone() );
++ }
++}
++
++
++TiXmlNode* TiXmlDocument::Clone() const
++{
++ TiXmlDocument* clone = new TiXmlDocument();
++ if ( !clone )
++ return 0;
++
++ CopyTo( clone );
++ return clone;
++}
++
++
++void TiXmlDocument::Print( FILE* cfile, int depth ) const
++{
++ assert( cfile );
++ for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() )
++ {
++ node->Print( cfile, depth );
++ fprintf( cfile, "\n" );
++ }
++}
++
++
++bool TiXmlDocument::Accept( TiXmlVisitor* visitor ) const
++{
++ if ( visitor->VisitEnter( *this ) )
++ {
++ for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() )
++ {
++ if ( !node->Accept( visitor ) )
++ break;
++ }
++ }
++ return visitor->VisitExit( *this );
++}
++
++
++const TiXmlAttribute* TiXmlAttribute::Next() const
++{
++ // We are using knowledge of the sentinel. The sentinel
++ // have a value or name.
++ if ( next->value.empty() && next->name.empty() )
++ return 0;
++ return next;
++}
++
++/*
++TiXmlAttribute* TiXmlAttribute::Next()
++{
++ // We are using knowledge of the sentinel. The sentinel
++ // have a value or name.
++ if ( next->value.empty() && next->name.empty() )
++ return 0;
++ return next;
++}
++*/
++
++const TiXmlAttribute* TiXmlAttribute::Previous() const
++{
++ // We are using knowledge of the sentinel. The sentinel
++ // have a value or name.
++ if ( prev->value.empty() && prev->name.empty() )
++ return 0;
++ return prev;
++}
++
++/*
++TiXmlAttribute* TiXmlAttribute::Previous()
++{
++ // We are using knowledge of the sentinel. The sentinel
++ // have a value or name.
++ if ( prev->value.empty() && prev->name.empty() )
++ return 0;
++ return prev;
++}
++*/
++
++void TiXmlAttribute::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const
++{
++ TIXML_STRING n, v;
++
++ EncodeString( name, &n );
++ EncodeString( value, &v );
++
++ if (value.find ('\"') == TIXML_STRING::npos) {
++ if ( cfile ) {
++ fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() );
++ }
++ if ( str ) {
++ (*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\"";
++ }
++ }
++ else {
++ if ( cfile ) {
++ fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() );
++ }
++ if ( str ) {
++ (*str) += n; (*str) += "='"; (*str) += v; (*str) += "'";
++ }
++ }
++}
++
++
++int TiXmlAttribute::QueryIntValue( int* ival ) const
++{
++ if ( TIXML_SSCANF( value.c_str(), "%d", ival ) == 1 )
++ return TIXML_SUCCESS;
++ return TIXML_WRONG_TYPE;
++}
++
++int TiXmlAttribute::QueryDoubleValue( double* dval ) const
++{
++ if ( TIXML_SSCANF( value.c_str(), "%lf", dval ) == 1 )
++ return TIXML_SUCCESS;
++ return TIXML_WRONG_TYPE;
++}
++
++void TiXmlAttribute::SetIntValue( int _value )
++{
++ char buf [64];
++ #if defined(TIXML_SNPRINTF)
++ TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value);
++ #else
++ sprintf (buf, "%d", _value);
++ #endif
++ SetValue (buf);
++}
++
++void TiXmlAttribute::SetDoubleValue( double _value )
++{
++ char buf [256];
++ #if defined(TIXML_SNPRINTF)
++ TIXML_SNPRINTF( buf, sizeof(buf), "%g", _value);
++ #else
++ sprintf (buf, "%g", _value);
++ #endif
++ SetValue (buf);
++}
++
++int TiXmlAttribute::IntValue() const
++{
++ return atoi (value.c_str ());
++}
++
++double TiXmlAttribute::DoubleValue() const
++{
++ return atof (value.c_str ());
++}
++
++
++TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT )
++{
++ copy.CopyTo( this );
++}
++
++
++TiXmlComment& TiXmlComment::operator=( const TiXmlComment& base )
++{
++ Clear();
++ base.CopyTo( this );
++ return *this;
++}
++
++
++void TiXmlComment::Print( FILE* cfile, int depth ) const
++{
++ assert( cfile );
++ for ( int i=0; i<depth; i++ )
++ {
++ fprintf( cfile, " " );
++ }
++ fprintf( cfile, "<!--%s-->", value.c_str() );
++}
++
++
++void TiXmlComment::CopyTo( TiXmlComment* target ) const
++{
++ TiXmlNode::CopyTo( target );
++}
++
++
++bool TiXmlComment::Accept( TiXmlVisitor* visitor ) const
++{
++ return visitor->Visit( *this );
++}
++
++
++TiXmlNode* TiXmlComment::Clone() const
++{
++ TiXmlComment* clone = new TiXmlComment();
++
++ if ( !clone )
++ return 0;
++
++ CopyTo( clone );
++ return clone;
++}
++
++
++void TiXmlText::Print( FILE* cfile, int depth ) const
++{
++ assert( cfile );
++ if ( cdata )
++ {
++ int i;
++ fprintf( cfile, "\n" );
++ for ( i=0; i<depth; i++ ) {
++ fprintf( cfile, " " );
++ }
++ fprintf( cfile, "<![CDATA[%s]]>\n", value.c_str() ); // unformatted output
++ }
++ else
++ {
++ TIXML_STRING buffer;
++ EncodeString( value, &buffer );
++ fprintf( cfile, "%s", buffer.c_str() );
++ }
++}
++
++
++void TiXmlText::CopyTo( TiXmlText* target ) const
++{
++ TiXmlNode::CopyTo( target );
++ target->cdata = cdata;
++}
++
++
++bool TiXmlText::Accept( TiXmlVisitor* visitor ) const
++{
++ return visitor->Visit( *this );
++}
++
++
++TiXmlNode* TiXmlText::Clone() const
++{
++ TiXmlText* clone = 0;
++ clone = new TiXmlText( "" );
++
++ if ( !clone )
++ return 0;
++
++ CopyTo( clone );
++ return clone;
++}
++
++
++TiXmlDeclaration::TiXmlDeclaration( const char * _version,
++ const char * _encoding,
++ const char * _standalone )
++ : TiXmlNode( TiXmlNode::TINYXML_DECLARATION )
++{
++ version = _version;
++ encoding = _encoding;
++ standalone = _standalone;
++}
++
++
++#ifdef TIXML_USE_STL
++TiXmlDeclaration::TiXmlDeclaration( const std::string& _version,
++ const std::string& _encoding,
++ const std::string& _standalone )
++ : TiXmlNode( TiXmlNode::TINYXML_DECLARATION )
++{
++ version = _version;
++ encoding = _encoding;
++ standalone = _standalone;
++}
++#endif
++
++
++TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy )
++ : TiXmlNode( TiXmlNode::TINYXML_DECLARATION )
++{
++ copy.CopyTo( this );
++}
++
++
++TiXmlDeclaration& TiXmlDeclaration::operator=( const TiXmlDeclaration& copy )
++{
++ Clear();
++ copy.CopyTo( this );
++ return *this;
++}
++
++
++void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const
++{
++ if ( cfile ) fprintf( cfile, "<?xml " );
++ if ( str ) (*str) += "<?xml ";
++
++ if ( !version.empty() ) {
++ if ( cfile ) fprintf (cfile, "version=\"%s\" ", version.c_str ());
++ if ( str ) { (*str) += "version=\""; (*str) += version; (*str) += "\" "; }
++ }
++ if ( !encoding.empty() ) {
++ if ( cfile ) fprintf (cfile, "encoding=\"%s\" ", encoding.c_str ());
++ if ( str ) { (*str) += "encoding=\""; (*str) += encoding; (*str) += "\" "; }
++ }
++ if ( !standalone.empty() ) {
++ if ( cfile ) fprintf (cfile, "standalone=\"%s\" ", standalone.c_str ());
++ if ( str ) { (*str) += "standalone=\""; (*str) += standalone; (*str) += "\" "; }
++ }
++ if ( cfile ) fprintf( cfile, "?>" );
++ if ( str ) (*str) += "?>";
++}
++
++
++void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const
++{
++ TiXmlNode::CopyTo( target );
++
++ target->version = version;
++ target->encoding = encoding;
++ target->standalone = standalone;
++}
++
++
++bool TiXmlDeclaration::Accept( TiXmlVisitor* visitor ) const
++{
++ return visitor->Visit( *this );
++}
++
++
++TiXmlNode* TiXmlDeclaration::Clone() const
++{
++ TiXmlDeclaration* clone = new TiXmlDeclaration();
++
++ if ( !clone )
++ return 0;
++
++ CopyTo( clone );
++ return clone;
++}
++
++
++void TiXmlUnknown::Print( FILE* cfile, int depth ) const
++{
++ for ( int i=0; i<depth; i++ )
++ fprintf( cfile, " " );
++ fprintf( cfile, "<%s>", value.c_str() );
++}
++
++
++void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const
++{
++ TiXmlNode::CopyTo( target );
++}
++
++
++bool TiXmlUnknown::Accept( TiXmlVisitor* visitor ) const
++{
++ return visitor->Visit( *this );
++}
++
++
++TiXmlNode* TiXmlUnknown::Clone() const
++{
++ TiXmlUnknown* clone = new TiXmlUnknown();
++
++ if ( !clone )
++ return 0;
++
++ CopyTo( clone );
++ return clone;
++}
++
++
++TiXmlAttributeSet::TiXmlAttributeSet()
++{
++ sentinel.next = &sentinel;
++ sentinel.prev = &sentinel;
++}
++
++
++TiXmlAttributeSet::~TiXmlAttributeSet()
++{
++ assert( sentinel.next == &sentinel );
++ assert( sentinel.prev == &sentinel );
++}
++
++
++void TiXmlAttributeSet::Add( TiXmlAttribute* addMe )
++{
++ #ifdef TIXML_USE_STL
++ assert( !Find( TIXML_STRING( addMe->Name() ) ) ); // Shouldn't be multiply adding to the set.
++ #else
++ assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set.
++ #endif
++
++ addMe->next = &sentinel;
++ addMe->prev = sentinel.prev;
++
++ sentinel.prev->next = addMe;
++ sentinel.prev = addMe;
++}
++
++void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe )
++{
++ TiXmlAttribute* node;
++
++ for( node = sentinel.next; node != &sentinel; node = node->next )
++ {
++ if ( node == removeMe )
++ {
++ node->prev->next = node->next;
++ node->next->prev = node->prev;
++ node->next = 0;
++ node->prev = 0;
++ return;
++ }
++ }
++ assert( 0 ); // we tried to remove a non-linked attribute.
++}
++
++
++#ifdef TIXML_USE_STL
++TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const
++{
++ for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next )
++ {
++ if ( node->name == name )
++ return node;
++ }
++ return 0;
++}
++
++TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const std::string& _name )
++{
++ TiXmlAttribute* attrib = Find( _name );
++ if ( !attrib ) {
++ attrib = new TiXmlAttribute();
++ Add( attrib );
++ attrib->SetName( _name );
++ }
++ return attrib;
++}
++#endif
++
++
++TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const
++{
++ for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next )
++ {
++ if ( strcmp( node->name.c_str(), name ) == 0 )
++ return node;
++ }
++ return 0;
++}
++
++
++TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const char* _name )
++{
++ TiXmlAttribute* attrib = Find( _name );
++ if ( !attrib ) {
++ attrib = new TiXmlAttribute();
++ Add( attrib );
++ attrib->SetName( _name );
++ }
++ return attrib;
++}
++
++
++#ifdef TIXML_USE_STL
++std::istream& operator>> (std::istream & in, TiXmlNode & base)
++{
++ TIXML_STRING tag;
++ tag.reserve( 8 * 1000 );
++ base.StreamIn( &in, &tag );
++
++ base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING );
++ return in;
++}
++#endif
++
++
++#ifdef TIXML_USE_STL
++std::ostream& operator<< (std::ostream & out, const TiXmlNode & base)
++{
++ TiXmlPrinter printer;
++ printer.SetStreamPrinting();
++ base.Accept( &printer );
++ out << printer.Str();
++
++ return out;
++}
++
++
++std::string& operator<< (std::string& out, const TiXmlNode& base )
++{
++ TiXmlPrinter printer;
++ printer.SetStreamPrinting();
++ base.Accept( &printer );
++ out.append( printer.Str() );
++
++ return out;
++}
++#endif
++
++
++TiXmlHandle TiXmlHandle::FirstChild() const
++{
++ if ( node )
++ {
++ TiXmlNode* child = node->FirstChild();
++ if ( child )
++ return TiXmlHandle( child );
++ }
++ return TiXmlHandle( 0 );
++}
++
++
++TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const
++{
++ if ( node )
++ {
++ TiXmlNode* child = node->FirstChild( value );
++ if ( child )
++ return TiXmlHandle( child );
++ }
++ return TiXmlHandle( 0 );
++}
++
++
++TiXmlHandle TiXmlHandle::FirstChildElement() const
++{
++ if ( node )
++ {
++ TiXmlElement* child = node->FirstChildElement();
++ if ( child )
++ return TiXmlHandle( child );
++ }
++ return TiXmlHandle( 0 );
++}
++
++
++TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const
++{
++ if ( node )
++ {
++ TiXmlElement* child = node->FirstChildElement( value );
++ if ( child )
++ return TiXmlHandle( child );
++ }
++ return TiXmlHandle( 0 );
++}
++
++
++TiXmlHandle TiXmlHandle::Child( int count ) const
++{
++ if ( node )
++ {
++ int i;
++ TiXmlNode* child = node->FirstChild();
++ for ( i=0;
++ child && i<count;
++ child = child->NextSibling(), ++i )
++ {
++ // nothing
++ }
++ if ( child )
++ return TiXmlHandle( child );
++ }
++ return TiXmlHandle( 0 );
++}
++
++
++TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const
++{
++ if ( node )
++ {
++ int i;
++ TiXmlNode* child = node->FirstChild( value );
++ for ( i=0;
++ child && i<count;
++ child = child->NextSibling( value ), ++i )
++ {
++ // nothing
++ }
++ if ( child )
++ return TiXmlHandle( child );
++ }
++ return TiXmlHandle( 0 );
++}
++
++
++TiXmlHandle TiXmlHandle::ChildElement( int count ) const
++{
++ if ( node )
++ {
++ int i;
++ TiXmlElement* child = node->FirstChildElement();
++ for ( i=0;
++ child && i<count;
++ child = child->NextSiblingElement(), ++i )
++ {
++ // nothing
++ }
++ if ( child )
++ return TiXmlHandle( child );
++ }
++ return TiXmlHandle( 0 );
++}
++
++
++TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const
++{
++ if ( node )
++ {
++ int i;
++ TiXmlElement* child = node->FirstChildElement( value );
++ for ( i=0;
++ child && i<count;
++ child = child->NextSiblingElement( value ), ++i )
++ {
++ // nothing
++ }
++ if ( child )
++ return TiXmlHandle( child );
++ }
++ return TiXmlHandle( 0 );
++}
++
++
++bool TiXmlPrinter::VisitEnter( const TiXmlDocument& )
++{
++ return true;
++}
++
++bool TiXmlPrinter::VisitExit( const TiXmlDocument& )
++{
++ return true;
++}
++
++bool TiXmlPrinter::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute )
++{
++ DoIndent();
++ buffer += "<";
++ buffer += element.Value();
++
++ for( const TiXmlAttribute* attrib = firstAttribute; attrib; attrib = attrib->Next() )
++ {
++ buffer += " ";
++ attrib->Print( 0, 0, &buffer );
++ }
++
++ if ( !element.FirstChild() )
++ {
++ buffer += " />";
++ DoLineBreak();
++ }
++ else
++ {
++ buffer += ">";
++ if ( element.FirstChild()->ToText()
++ && element.LastChild() == element.FirstChild()
++ && element.FirstChild()->ToText()->CDATA() == false )
++ {
++ simpleTextPrint = true;
++ // no DoLineBreak()!
++ }
++ else
++ {
++ DoLineBreak();
++ }
++ }
++ ++depth;
++ return true;
++}
++
++
++bool TiXmlPrinter::VisitExit( const TiXmlElement& element )
++{
++ --depth;
++ if ( !element.FirstChild() )
++ {
++ // nothing.
++ }
++ else
++ {
++ if ( simpleTextPrint )
++ {
++ simpleTextPrint = false;
++ }
++ else
++ {
++ DoIndent();
++ }
++ buffer += "</";
++ buffer += element.Value();
++ buffer += ">";
++ DoLineBreak();
++ }
++ return true;
++}
++
++
++bool TiXmlPrinter::Visit( const TiXmlText& text )
++{
++ if ( text.CDATA() )
++ {
++ DoIndent();
++ buffer += "<![CDATA[";
++ buffer += text.Value();
++ buffer += "]]>";
++ DoLineBreak();
++ }
++ else if ( simpleTextPrint )
++ {
++ TIXML_STRING str;
++ TiXmlBase::EncodeString( text.ValueTStr(), &str );
++ buffer += str;
++ }
++ else
++ {
++ DoIndent();
++ TIXML_STRING str;
++ TiXmlBase::EncodeString( text.ValueTStr(), &str );
++ buffer += str;
++ DoLineBreak();
++ }
++ return true;
++}
++
++
++bool TiXmlPrinter::Visit( const TiXmlDeclaration& declaration )
++{
++ DoIndent();
++ declaration.Print( 0, 0, &buffer );
++ DoLineBreak();
++ return true;
++}
++
++
++bool TiXmlPrinter::Visit( const TiXmlComment& comment )
++{
++ DoIndent();
++ buffer += "<!--";
++ buffer += comment.Value();
++ buffer += "-->";
++ DoLineBreak();
++ return true;
++}
++
++
++bool TiXmlPrinter::Visit( const TiXmlUnknown& unknown )
++{
++ DoIndent();
++ buffer += "<";
++ buffer += unknown.Value();
++ buffer += ">";
++ DoLineBreak();
++ return true;
++}
++
+diff --git a/xbmc/pvrclients/MediaPortal/lib/tinyxml/tinyxml.h b/xbmc/pvrclients/MediaPortal/lib/tinyxml/tinyxml.h
+new file mode 100644
+index 0000000..23694c6
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/lib/tinyxml/tinyxml.h
+@@ -0,0 +1,1807 @@
++/*
++www.sourceforge.net/projects/tinyxml
++Original code by Lee Thomason (www.grinninglizard.com)
++
++This software is provided 'as-is', without any express or implied
++warranty. In no event will the authors be held liable for any
++damages arising from the use of this software.
++
++Permission is granted to anyone to use this software for any
++purpose, including commercial applications, and to alter it and
++redistribute it freely, subject to the following restrictions:
++
++1. The origin of this software must not be misrepresented; you must
++not claim that you wrote the original software. If you use this
++software in a product, an acknowledgment in the product documentation
++would be appreciated but is not required.
++
++2. Altered source versions must be plainly marked as such, and
++must not be misrepresented as being the original software.
++
++3. This notice may not be removed or altered from any source
++distribution.
++*/
++
++
++#ifndef TINYXML_INCLUDED
++#define TINYXML_INCLUDED
++
++#define TIXML_USE_STL
++
++#ifdef _MSC_VER
++#pragma warning( push )
++#pragma warning( disable : 4530 )
++#pragma warning( disable : 4786 )
++#endif
++
++#include <ctype.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <assert.h>
++
++// Help out windows:
++#if defined( _DEBUG ) && !defined( DEBUG )
++#define DEBUG
++#endif
++
++#ifdef TIXML_USE_STL
++ #include <string>
++ #include <iostream>
++ #include <sstream>
++ #define TIXML_STRING std::string
++#else
++ #include "tinystr.h"
++ #define TIXML_STRING TiXmlString
++#endif
++
++// Deprecated library function hell. Compilers want to use the
++// new safe versions. This probably doesn't fully address the problem,
++// but it gets closer. There are too many compilers for me to fully
++// test. If you get compilation troubles, undefine TIXML_SAFE
++#define TIXML_SAFE
++
++#ifdef TIXML_SAFE
++ #if defined(_MSC_VER) && (_MSC_VER >= 1400 )
++ // Microsoft visual studio, version 2005 and higher.
++ #define TIXML_SNPRINTF _snprintf_s
++ #define TIXML_SSCANF sscanf_s
++ #elif defined(_MSC_VER) && (_MSC_VER >= 1200 )
++ // Microsoft visual studio, version 6 and higher.
++ //#pragma message( "Using _sn* functions." )
++ #define TIXML_SNPRINTF _snprintf
++ #define TIXML_SSCANF sscanf
++ #elif defined(__GNUC__) && (__GNUC__ >= 3 )
++ // GCC version 3 and higher.s
++ //#warning( "Using sn* functions." )
++ #define TIXML_SNPRINTF snprintf
++ #define TIXML_SSCANF sscanf
++ #else
++ #define TIXML_SNPRINTF snprintf
++ #define TIXML_SSCANF sscanf
++ #endif
++#endif
++
++class TiXmlDocument;
++class TiXmlElement;
++class TiXmlComment;
++class TiXmlUnknown;
++class TiXmlAttribute;
++class TiXmlText;
++class TiXmlDeclaration;
++class TiXmlParsingData;
++
++const int TIXML_MAJOR_VERSION = 2;
++const int TIXML_MINOR_VERSION = 6;
++const int TIXML_PATCH_VERSION = 2;
++
++/* Internal structure for tracking location of items
++ in the XML file.
++*/
++struct TiXmlCursor
++{
++ TiXmlCursor() { Clear(); }
++ void Clear() { row = col = -1; }
++
++ int row; // 0 based.
++ int col; // 0 based.
++};
++
++
++/**
++ Implements the interface to the "Visitor pattern" (see the Accept() method.)
++ If you call the Accept() method, it requires being passed a TiXmlVisitor
++ class to handle callbacks. For nodes that contain other nodes (Document, Element)
++ you will get called with a VisitEnter/VisitExit pair. Nodes that are always leaves
++ are simply called with Visit().
++
++ If you return 'true' from a Visit method, recursive parsing will continue. If you return
++ false, <b>no children of this node or its sibilings</b> will be Visited.
++
++ All flavors of Visit methods have a default implementation that returns 'true' (continue
++ visiting). You need to only override methods that are interesting to you.
++
++ Generally Accept() is called on the TiXmlDocument, although all nodes suppert Visiting.
++
++ You should never change the document from a callback.
++
++ @sa TiXmlNode::Accept()
++*/
++class TiXmlVisitor
++{
++public:
++ virtual ~TiXmlVisitor() {}
++
++ /// Visit a document.
++ virtual bool VisitEnter( const TiXmlDocument& /*doc*/ ) { return true; }
++ /// Visit a document.
++ virtual bool VisitExit( const TiXmlDocument& /*doc*/ ) { return true; }
++
++ /// Visit an element.
++ virtual bool VisitEnter( const TiXmlElement& /*element*/, const TiXmlAttribute* /*firstAttribute*/ ) { return true; }
++ /// Visit an element.
++ virtual bool VisitExit( const TiXmlElement& /*element*/ ) { return true; }
++
++ /// Visit a declaration
++ virtual bool Visit( const TiXmlDeclaration& /*declaration*/ ) { return true; }
++ /// Visit a text node
++ virtual bool Visit( const TiXmlText& /*text*/ ) { return true; }
++ /// Visit a comment node
++ virtual bool Visit( const TiXmlComment& /*comment*/ ) { return true; }
++ /// Visit an unknown node
++ virtual bool Visit( const TiXmlUnknown& /*unknown*/ ) { return true; }
++};
++
++// Only used by Attribute::Query functions
++enum
++{
++ TIXML_SUCCESS,
++ TIXML_NO_ATTRIBUTE,
++ TIXML_WRONG_TYPE
++};
++
++
++// Used by the parsing routines.
++enum TiXmlEncoding
++{
++ TIXML_ENCODING_UNKNOWN,
++ TIXML_ENCODING_UTF8,
++ TIXML_ENCODING_LEGACY
++};
++
++const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN;
++
++/** TiXmlBase is a base class for every class in TinyXml.
++ It does little except to establish that TinyXml classes
++ can be printed and provide some utility functions.
++
++ In XML, the document and elements can contain
++ other elements and other types of nodes.
++
++ @verbatim
++ A Document can contain: Element (container or leaf)
++ Comment (leaf)
++ Unknown (leaf)
++ Declaration( leaf )
++
++ An Element can contain: Element (container or leaf)
++ Text (leaf)
++ Attributes (not on tree)
++ Comment (leaf)
++ Unknown (leaf)
++
++ A Decleration contains: Attributes (not on tree)
++ @endverbatim
++*/
++class TiXmlBase
++{
++ friend class TiXmlNode;
++ friend class TiXmlElement;
++ friend class TiXmlDocument;
++
++public:
++ TiXmlBase() : userData(0) {}
++ virtual ~TiXmlBase() {}
++
++ /** All TinyXml classes can print themselves to a filestream
++ or the string class (TiXmlString in non-STL mode, std::string
++ in STL mode.) Either or both cfile and str can be null.
++
++ This is a formatted print, and will insert
++ tabs and newlines.
++
++ (For an unformatted stream, use the << operator.)
++ */
++ virtual void Print( FILE* cfile, int depth ) const = 0;
++
++ /** The world does not agree on whether white space should be kept or
++ not. In order to make everyone happy, these global, static functions
++ are provided to set whether or not TinyXml will condense all white space
++ into a single space or not. The default is to condense. Note changing this
++ value is not thread safe.
++ */
++ static void SetCondenseWhiteSpace( bool condense ) { condenseWhiteSpace = condense; }
++
++ /// Return the current white space setting.
++ static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; }
++
++ /** Return the position, in the original source file, of this node or attribute.
++ The row and column are 1-based. (That is the first row and first column is
++ 1,1). If the returns values are 0 or less, then the parser does not have
++ a row and column value.
++
++ Generally, the row and column value will be set when the TiXmlDocument::Load(),
++ TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set
++ when the DOM was created from operator>>.
++
++ The values reflect the initial load. Once the DOM is modified programmatically
++ (by adding or changing nodes and attributes) the new values will NOT update to
++ reflect changes in the document.
++
++ There is a minor performance cost to computing the row and column. Computation
++ can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value.
++
++ @sa TiXmlDocument::SetTabSize()
++ */
++ int Row() const { return location.row + 1; }
++ int Column() const { return location.col + 1; } ///< See Row()
++
++ void SetUserData( void* user ) { userData = user; } ///< Set a pointer to arbitrary user data.
++ void* GetUserData() { return userData; } ///< Get a pointer to arbitrary user data.
++ const void* GetUserData() const { return userData; } ///< Get a pointer to arbitrary user data.
++
++ // Table that returs, for a given lead byte, the total number of bytes
++ // in the UTF-8 sequence.
++ static const int utf8ByteTable[256];
++
++ virtual const char* Parse( const char* p,
++ TiXmlParsingData* data,
++ TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0;
++
++ /** Expands entities in a string. Note this should not contian the tag's '<', '>', etc,
++ or they will be transformed into entities!
++ */
++ static void EncodeString( const TIXML_STRING& str, TIXML_STRING* out );
++
++ enum
++ {
++ TIXML_NO_ERROR = 0,
++ TIXML_ERROR,
++ TIXML_ERROR_OPENING_FILE,
++ TIXML_ERROR_PARSING_ELEMENT,
++ TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME,
++ TIXML_ERROR_READING_ELEMENT_VALUE,
++ TIXML_ERROR_READING_ATTRIBUTES,
++ TIXML_ERROR_PARSING_EMPTY,
++ TIXML_ERROR_READING_END_TAG,
++ TIXML_ERROR_PARSING_UNKNOWN,
++ TIXML_ERROR_PARSING_COMMENT,
++ TIXML_ERROR_PARSING_DECLARATION,
++ TIXML_ERROR_DOCUMENT_EMPTY,
++ TIXML_ERROR_EMBEDDED_NULL,
++ TIXML_ERROR_PARSING_CDATA,
++ TIXML_ERROR_DOCUMENT_TOP_ONLY,
++
++ TIXML_ERROR_STRING_COUNT
++ };
++
++protected:
++
++ static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding );
++
++ inline static bool IsWhiteSpace( char c )
++ {
++ return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' );
++ }
++ inline static bool IsWhiteSpace( int c )
++ {
++ if ( c < 256 )
++ return IsWhiteSpace( (char) c );
++ return false; // Again, only truly correct for English/Latin...but usually works.
++ }
++
++ #ifdef TIXML_USE_STL
++ static bool StreamWhiteSpace( std::istream * in, TIXML_STRING * tag );
++ static bool StreamTo( std::istream * in, int character, TIXML_STRING * tag );
++ #endif
++
++ /* Reads an XML name into the string provided. Returns
++ a pointer just past the last character of the name,
++ or 0 if the function has an error.
++ */
++ static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding );
++
++ /* Reads text. Returns a pointer past the given end tag.
++ Wickedly complex options, but it keeps the (sensitive) code in one place.
++ */
++ static const char* ReadText( const char* in, // where to start
++ TIXML_STRING* text, // the string read
++ bool ignoreWhiteSpace, // whether to keep the white space
++ const char* endTag, // what ends this text
++ bool ignoreCase, // whether to ignore case in the end tag
++ TiXmlEncoding encoding ); // the current encoding
++
++ // If an entity has been found, transform it into a character.
++ static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding );
++
++ // Get a character, while interpreting entities.
++ // The length can be from 0 to 4 bytes.
++ inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding )
++ {
++ assert( p );
++ if ( encoding == TIXML_ENCODING_UTF8 )
++ {
++ *length = utf8ByteTable[ *((const unsigned char*)p) ];
++ assert( *length >= 0 && *length < 5 );
++ }
++ else
++ {
++ *length = 1;
++ }
++
++ if ( *length == 1 )
++ {
++ if ( *p == '&' )
++ return GetEntity( p, _value, length, encoding );
++ *_value = *p;
++ return p+1;
++ }
++ else if ( *length )
++ {
++ //strncpy( _value, p, *length ); // lots of compilers don't like this function (unsafe),
++ // and the null terminator isn't needed
++ for( int i=0; p[i] && i<*length; ++i ) {
++ _value[i] = p[i];
++ }
++ return p + (*length);
++ }
++ else
++ {
++ // Not valid text.
++ return 0;
++ }
++ }
++
++ // Return true if the next characters in the stream are any of the endTag sequences.
++ // Ignore case only works for english, and should only be relied on when comparing
++ // to English words: StringEqual( p, "version", true ) is fine.
++ static bool StringEqual( const char* p,
++ const char* endTag,
++ bool ignoreCase,
++ TiXmlEncoding encoding );
++
++ static const char* errorString[ TIXML_ERROR_STRING_COUNT ];
++
++ TiXmlCursor location;
++
++ /// Field containing a generic user pointer
++ void* userData;
++
++ // None of these methods are reliable for any language except English.
++ // Good for approximation, not great for accuracy.
++ static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding );
++ static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding );
++ inline static int ToLower( int v, TiXmlEncoding encoding )
++ {
++ if ( encoding == TIXML_ENCODING_UTF8 )
++ {
++ if ( v < 128 ) return tolower( v );
++ return v;
++ }
++ else
++ {
++ return tolower( v );
++ }
++ }
++ static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length );
++
++private:
++ TiXmlBase( const TiXmlBase& ); // not implemented.
++ void operator=( const TiXmlBase& base ); // not allowed.
++
++ struct Entity
++ {
++ const char* str;
++ unsigned int strLength;
++ char chr;
++ };
++ enum
++ {
++ NUM_ENTITY = 5,
++ MAX_ENTITY_LENGTH = 6
++
++ };
++ static Entity entity[ NUM_ENTITY ];
++ static bool condenseWhiteSpace;
++};
++
++
++/** The parent class for everything in the Document Object Model.
++ (Except for attributes).
++ Nodes have siblings, a parent, and children. A node can be
++ in a document, or stand on its own. The type of a TiXmlNode
++ can be queried, and it can be cast to its more defined type.
++*/
++class TiXmlNode : public TiXmlBase
++{
++ friend class TiXmlDocument;
++ friend class TiXmlElement;
++
++public:
++ #ifdef TIXML_USE_STL
++
++ /** An input stream operator, for every class. Tolerant of newlines and
++ formatting, but doesn't expect them.
++ */
++ friend std::istream& operator >> (std::istream& in, TiXmlNode& base);
++
++ /** An output stream operator, for every class. Note that this outputs
++ without any newlines or formatting, as opposed to Print(), which
++ includes tabs and new lines.
++
++ The operator<< and operator>> are not completely symmetric. Writing
++ a node to a stream is very well defined. You'll get a nice stream
++ of output, without any extra whitespace or newlines.
++
++ But reading is not as well defined. (As it always is.) If you create
++ a TiXmlElement (for example) and read that from an input stream,
++ the text needs to define an element or junk will result. This is
++ true of all input streams, but it's worth keeping in mind.
++
++ A TiXmlDocument will read nodes until it reads a root element, and
++ all the children of that root element.
++ */
++ friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base);
++
++ /// Appends the XML node or attribute to a std::string.
++ friend std::string& operator<< (std::string& out, const TiXmlNode& base );
++
++ #endif
++
++ /** The types of XML nodes supported by TinyXml. (All the
++ unsupported types are picked up by UNKNOWN.)
++ */
++ enum NodeType
++ {
++ TINYXML_DOCUMENT,
++ TINYXML_ELEMENT,
++ TINYXML_COMMENT,
++ TINYXML_UNKNOWN,
++ TINYXML_TEXT,
++ TINYXML_DECLARATION,
++ TINYXML_TYPECOUNT
++ };
++
++ virtual ~TiXmlNode();
++
++ /** The meaning of 'value' changes for the specific type of
++ TiXmlNode.
++ @verbatim
++ Document: filename of the xml file
++ Element: name of the element
++ Comment: the comment text
++ Unknown: the tag contents
++ Text: the text string
++ @endverbatim
++
++ The subclasses will wrap this function.
++ */
++ const char *Value() const { return value.c_str (); }
++
++ #ifdef TIXML_USE_STL
++ /** Return Value() as a std::string. If you only use STL,
++ this is more efficient than calling Value().
++ Only available in STL mode.
++ */
++ const std::string& ValueStr() const { return value; }
++ #endif
++
++ const TIXML_STRING& ValueTStr() const { return value; }
++
++ /** Changes the value of the node. Defined as:
++ @verbatim
++ Document: filename of the xml file
++ Element: name of the element
++ Comment: the comment text
++ Unknown: the tag contents
++ Text: the text string
++ @endverbatim
++ */
++ void SetValue(const char * _value) { value = _value;}
++
++ #ifdef TIXML_USE_STL
++ /// STL std::string form.
++ void SetValue( const std::string& _value ) { value = _value; }
++ #endif
++
++ /// Delete all the children of this node. Does not affect 'this'.
++ void Clear();
++
++ /// One step up the DOM.
++ TiXmlNode* Parent() { return parent; }
++ const TiXmlNode* Parent() const { return parent; }
++
++ const TiXmlNode* FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children.
++ TiXmlNode* FirstChild() { return firstChild; }
++ const TiXmlNode* FirstChild( const char * value ) const; ///< The first child of this node with the matching 'value'. Will be null if none found.
++ /// The first child of this node with the matching 'value'. Will be null if none found.
++ TiXmlNode* FirstChild( const char * _value ) {
++ // Call through to the const version - safe since nothing is changed. Exiting syntax: cast this to a const (always safe)
++ // call the method, cast the return back to non-const.
++ return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->FirstChild( _value ));
++ }
++ const TiXmlNode* LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children.
++ TiXmlNode* LastChild() { return lastChild; }
++
++ const TiXmlNode* LastChild( const char * value ) const; /// The last child of this node matching 'value'. Will be null if there are no children.
++ TiXmlNode* LastChild( const char * _value ) {
++ return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->LastChild( _value ));
++ }
++
++ #ifdef TIXML_USE_STL
++ const TiXmlNode* FirstChild( const std::string& _value ) const { return FirstChild (_value.c_str ()); } ///< STL std::string form.
++ TiXmlNode* FirstChild( const std::string& _value ) { return FirstChild (_value.c_str ()); } ///< STL std::string form.
++ const TiXmlNode* LastChild( const std::string& _value ) const { return LastChild (_value.c_str ()); } ///< STL std::string form.
++ TiXmlNode* LastChild( const std::string& _value ) { return LastChild (_value.c_str ()); } ///< STL std::string form.
++ #endif
++
++ /** An alternate way to walk the children of a node.
++ One way to iterate over nodes is:
++ @verbatim
++ for( child = parent->FirstChild(); child; child = child->NextSibling() )
++ @endverbatim
++
++ IterateChildren does the same thing with the syntax:
++ @verbatim
++ child = 0;
++ while( child = parent->IterateChildren( child ) )
++ @endverbatim
++
++ IterateChildren takes the previous child as input and finds
++ the next one. If the previous child is null, it returns the
++ first. IterateChildren will return null when done.
++ */
++ const TiXmlNode* IterateChildren( const TiXmlNode* previous ) const;
++ TiXmlNode* IterateChildren( const TiXmlNode* previous ) {
++ return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( previous ) );
++ }
++
++ /// This flavor of IterateChildren searches for children with a particular 'value'
++ const TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const;
++ TiXmlNode* IterateChildren( const char * _value, const TiXmlNode* previous ) {
++ return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( _value, previous ) );
++ }
++
++ #ifdef TIXML_USE_STL
++ const TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form.
++ TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form.
++ #endif
++
++ /** Add a new node related to this. Adds a child past the LastChild.
++ Returns a pointer to the new object or NULL if an error occured.
++ */
++ TiXmlNode* InsertEndChild( const TiXmlNode& addThis );
++
++
++ /** Add a new node related to this. Adds a child past the LastChild.
++
++ NOTE: the node to be added is passed by pointer, and will be
++ henceforth owned (and deleted) by tinyXml. This method is efficient
++ and avoids an extra copy, but should be used with care as it
++ uses a different memory model than the other insert functions.
++
++ @sa InsertEndChild
++ */
++ TiXmlNode* LinkEndChild( TiXmlNode* addThis );
++
++ /** Add a new node related to this. Adds a child before the specified child.
++ Returns a pointer to the new object or NULL if an error occured.
++ */
++ TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis );
++
++ /** Add a new node related to this. Adds a child after the specified child.
++ Returns a pointer to the new object or NULL if an error occured.
++ */
++ TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis );
++
++ /** Replace a child of this node.
++ Returns a pointer to the new object or NULL if an error occured.
++ */
++ TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis );
++
++ /// Delete a child of this node.
++ bool RemoveChild( TiXmlNode* removeThis );
++
++ /// Navigate to a sibling node.
++ const TiXmlNode* PreviousSibling() const { return prev; }
++ TiXmlNode* PreviousSibling() { return prev; }
++
++ /// Navigate to a sibling node.
++ const TiXmlNode* PreviousSibling( const char * ) const;
++ TiXmlNode* PreviousSibling( const char *_prev ) {
++ return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->PreviousSibling( _prev ) );
++ }
++
++ #ifdef TIXML_USE_STL
++ const TiXmlNode* PreviousSibling( const std::string& _value ) const { return PreviousSibling (_value.c_str ()); } ///< STL std::string form.
++ TiXmlNode* PreviousSibling( const std::string& _value ) { return PreviousSibling (_value.c_str ()); } ///< STL std::string form.
++ const TiXmlNode* NextSibling( const std::string& _value) const { return NextSibling (_value.c_str ()); } ///< STL std::string form.
++ TiXmlNode* NextSibling( const std::string& _value) { return NextSibling (_value.c_str ()); } ///< STL std::string form.
++ #endif
++
++ /// Navigate to a sibling node.
++ const TiXmlNode* NextSibling() const { return next; }
++ TiXmlNode* NextSibling() { return next; }
++
++ /// Navigate to a sibling node with the given 'value'.
++ const TiXmlNode* NextSibling( const char * ) const;
++ TiXmlNode* NextSibling( const char* _next ) {
++ return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->NextSibling( _next ) );
++ }
++
++ /** Convenience function to get through elements.
++ Calls NextSibling and ToElement. Will skip all non-Element
++ nodes. Returns 0 if there is not another element.
++ */
++ const TiXmlElement* NextSiblingElement() const;
++ TiXmlElement* NextSiblingElement() {
++ return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement() );
++ }
++
++ /** Convenience function to get through elements.
++ Calls NextSibling and ToElement. Will skip all non-Element
++ nodes. Returns 0 if there is not another element.
++ */
++ const TiXmlElement* NextSiblingElement( const char * ) const;
++ TiXmlElement* NextSiblingElement( const char *_next ) {
++ return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement( _next ) );
++ }
++
++ #ifdef TIXML_USE_STL
++ const TiXmlElement* NextSiblingElement( const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form.
++ TiXmlElement* NextSiblingElement( const std::string& _value) { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form.
++ #endif
++
++ /// Convenience function to get through elements.
++ const TiXmlElement* FirstChildElement() const;
++ TiXmlElement* FirstChildElement() {
++ return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement() );
++ }
++
++ /// Convenience function to get through elements.
++ const TiXmlElement* FirstChildElement( const char * _value ) const;
++ TiXmlElement* FirstChildElement( const char * _value ) {
++ return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement( _value ) );
++ }
++
++ #ifdef TIXML_USE_STL
++ const TiXmlElement* FirstChildElement( const std::string& _value ) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form.
++ TiXmlElement* FirstChildElement( const std::string& _value ) { return FirstChildElement (_value.c_str ()); } ///< STL std::string form.
++ #endif
++
++ /** Query the type (as an enumerated value, above) of this node.
++ The possible types are: TINYXML_DOCUMENT, TINYXML_ELEMENT, TINYXML_COMMENT,
++ TINYXML_UNKNOWN, TINYXML_TEXT, and TINYXML_DECLARATION.
++ */
++ int Type() const { return type; }
++
++ /** Return a pointer to the Document this node lives in.
++ Returns null if not in a document.
++ */
++ const TiXmlDocument* GetDocument() const;
++ TiXmlDocument* GetDocument() {
++ return const_cast< TiXmlDocument* >( (const_cast< const TiXmlNode* >(this))->GetDocument() );
++ }
++
++ /// Returns true if this node has no children.
++ bool NoChildren() const { return !firstChild; }
++
++ virtual const TiXmlDocument* ToDocument() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
++ virtual const TiXmlElement* ToElement() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
++ virtual const TiXmlComment* ToComment() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
++ virtual const TiXmlUnknown* ToUnknown() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
++ virtual const TiXmlText* ToText() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
++ virtual const TiXmlDeclaration* ToDeclaration() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
++
++ virtual TiXmlDocument* ToDocument() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
++ virtual TiXmlElement* ToElement() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
++ virtual TiXmlComment* ToComment() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
++ virtual TiXmlUnknown* ToUnknown() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
++ virtual TiXmlText* ToText() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
++ virtual TiXmlDeclaration* ToDeclaration() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
++
++ /** Create an exact duplicate of this node and return it. The memory must be deleted
++ by the caller.
++ */
++ virtual TiXmlNode* Clone() const = 0;
++
++ /** Accept a hierchical visit the nodes in the TinyXML DOM. Every node in the
++ XML tree will be conditionally visited and the host will be called back
++ via the TiXmlVisitor interface.
++
++ This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse
++ the XML for the callbacks, so the performance of TinyXML is unchanged by using this
++ interface versus any other.)
++
++ The interface has been based on ideas from:
++
++ - http://www.saxproject.org/
++ - http://c2.com/cgi/wiki?HierarchicalVisitorPattern
++
++ Which are both good references for "visiting".
++
++ An example of using Accept():
++ @verbatim
++ TiXmlPrinter printer;
++ tinyxmlDoc.Accept( &printer );
++ const char* xmlcstr = printer.CStr();
++ @endverbatim
++ */
++ virtual bool Accept( TiXmlVisitor* visitor ) const = 0;
++
++protected:
++ TiXmlNode( NodeType _type );
++
++ // Copy to the allocated object. Shared functionality between Clone, Copy constructor,
++ // and the assignment operator.
++ void CopyTo( TiXmlNode* target ) const;
++
++ #ifdef TIXML_USE_STL
++ // The real work of the input operator.
++ virtual void StreamIn( std::istream* in, TIXML_STRING* tag ) = 0;
++ #endif
++
++ // Figure out what is at *p, and parse it. Returns null if it is not an xml node.
++ TiXmlNode* Identify( const char* start, TiXmlEncoding encoding );
++
++ TiXmlNode* parent;
++ NodeType type;
++
++ TiXmlNode* firstChild;
++ TiXmlNode* lastChild;
++
++ TIXML_STRING value;
++
++ TiXmlNode* prev;
++ TiXmlNode* next;
++
++private:
++ TiXmlNode( const TiXmlNode& ); // not implemented.
++ void operator=( const TiXmlNode& base ); // not allowed.
++};
++
++
++/** An attribute is a name-value pair. Elements have an arbitrary
++ number of attributes, each with a unique name.
++
++ @note The attributes are not TiXmlNodes, since they are not
++ part of the tinyXML document object model. There are other
++ suggested ways to look at this problem.
++*/
++class TiXmlAttribute : public TiXmlBase
++{
++ friend class TiXmlAttributeSet;
++
++public:
++ /// Construct an empty attribute.
++ TiXmlAttribute() : TiXmlBase()
++ {
++ document = 0;
++ prev = next = 0;
++ }
++
++ #ifdef TIXML_USE_STL
++ /// std::string constructor.
++ TiXmlAttribute( const std::string& _name, const std::string& _value )
++ {
++ name = _name;
++ value = _value;
++ document = 0;
++ prev = next = 0;
++ }
++ #endif
++
++ /// Construct an attribute with a name and value.
++ TiXmlAttribute( const char * _name, const char * _value )
++ {
++ name = _name;
++ value = _value;
++ document = 0;
++ prev = next = 0;
++ }
++
++ const char* Name() const { return name.c_str(); } ///< Return the name of this attribute.
++ const char* Value() const { return value.c_str(); } ///< Return the value of this attribute.
++ #ifdef TIXML_USE_STL
++ const std::string& ValueStr() const { return value; } ///< Return the value of this attribute.
++ #endif
++ int IntValue() const; ///< Return the value of this attribute, converted to an integer.
++ double DoubleValue() const; ///< Return the value of this attribute, converted to a double.
++
++ // Get the tinyxml string representation
++ const TIXML_STRING& NameTStr() const { return name; }
++
++ /** QueryIntValue examines the value string. It is an alternative to the
++ IntValue() method with richer error checking.
++ If the value is an integer, it is stored in 'value' and
++ the call returns TIXML_SUCCESS. If it is not
++ an integer, it returns TIXML_WRONG_TYPE.
++
++ A specialized but useful call. Note that for success it returns 0,
++ which is the opposite of almost all other TinyXml calls.
++ */
++ int QueryIntValue( int* _value ) const;
++ /// QueryDoubleValue examines the value string. See QueryIntValue().
++ int QueryDoubleValue( double* _value ) const;
++
++ void SetName( const char* _name ) { name = _name; } ///< Set the name of this attribute.
++ void SetValue( const char* _value ) { value = _value; } ///< Set the value.
++
++ void SetIntValue( int _value ); ///< Set the value from an integer.
++ void SetDoubleValue( double _value ); ///< Set the value from a double.
++
++ #ifdef TIXML_USE_STL
++ /// STL std::string form.
++ void SetName( const std::string& _name ) { name = _name; }
++ /// STL std::string form.
++ void SetValue( const std::string& _value ) { value = _value; }
++ #endif
++
++ /// Get the next sibling attribute in the DOM. Returns null at end.
++ const TiXmlAttribute* Next() const;
++ TiXmlAttribute* Next() {
++ return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Next() );
++ }
++
++ /// Get the previous sibling attribute in the DOM. Returns null at beginning.
++ const TiXmlAttribute* Previous() const;
++ TiXmlAttribute* Previous() {
++ return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Previous() );
++ }
++
++ bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; }
++ bool operator<( const TiXmlAttribute& rhs ) const { return name < rhs.name; }
++ bool operator>( const TiXmlAttribute& rhs ) const { return name > rhs.name; }
++
++ /* Attribute parsing starts: first letter of the name
++ returns: the next char after the value end quote
++ */
++ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
++
++ // Prints this Attribute to a FILE stream.
++ virtual void Print( FILE* cfile, int depth ) const {
++ Print( cfile, depth, 0 );
++ }
++ void Print( FILE* cfile, int depth, TIXML_STRING* str ) const;
++
++ // [internal use]
++ // Set the document pointer so the attribute can report errors.
++ void SetDocument( TiXmlDocument* doc ) { document = doc; }
++
++private:
++ TiXmlAttribute( const TiXmlAttribute& ); // not implemented.
++ void operator=( const TiXmlAttribute& base ); // not allowed.
++
++ TiXmlDocument* document; // A pointer back to a document, for error reporting.
++ TIXML_STRING name;
++ TIXML_STRING value;
++ TiXmlAttribute* prev;
++ TiXmlAttribute* next;
++};
++
++
++/* A class used to manage a group of attributes.
++ It is only used internally, both by the ELEMENT and the DECLARATION.
++
++ The set can be changed transparent to the Element and Declaration
++ classes that use it, but NOT transparent to the Attribute
++ which has to implement a next() and previous() method. Which makes
++ it a bit problematic and prevents the use of STL.
++
++ This version is implemented with circular lists because:
++ - I like circular lists
++ - it demonstrates some independence from the (typical) doubly linked list.
++*/
++class TiXmlAttributeSet
++{
++public:
++ TiXmlAttributeSet();
++ ~TiXmlAttributeSet();
++
++ void Add( TiXmlAttribute* attribute );
++ void Remove( TiXmlAttribute* attribute );
++
++ const TiXmlAttribute* First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; }
++ TiXmlAttribute* First() { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; }
++ const TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; }
++ TiXmlAttribute* Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; }
++
++ TiXmlAttribute* Find( const char* _name ) const;
++ TiXmlAttribute* FindOrCreate( const char* _name );
++
++# ifdef TIXML_USE_STL
++ TiXmlAttribute* Find( const std::string& _name ) const;
++ TiXmlAttribute* FindOrCreate( const std::string& _name );
++# endif
++
++
++private:
++ //*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element),
++ //*ME: this class must be also use a hidden/disabled copy-constructor !!!
++ TiXmlAttributeSet( const TiXmlAttributeSet& ); // not allowed
++ void operator=( const TiXmlAttributeSet& ); // not allowed (as TiXmlAttribute)
++
++ TiXmlAttribute sentinel;
++};
++
++
++/** The element is a container class. It has a value, the element name,
++ and can contain other elements, text, comments, and unknowns.
++ Elements also contain an arbitrary number of attributes.
++*/
++class TiXmlElement : public TiXmlNode
++{
++public:
++ /// Construct an element.
++ TiXmlElement (const char * in_value);
++
++ #ifdef TIXML_USE_STL
++ /// std::string constructor.
++ TiXmlElement( const std::string& _value );
++ #endif
++
++ TiXmlElement( const TiXmlElement& );
++
++ TiXmlElement& operator=( const TiXmlElement& base );
++
++ virtual ~TiXmlElement();
++
++ /** Given an attribute name, Attribute() returns the value
++ for the attribute of that name, or null if none exists.
++ */
++ const char* Attribute( const char* name ) const;
++
++ /** Given an attribute name, Attribute() returns the value
++ for the attribute of that name, or null if none exists.
++ If the attribute exists and can be converted to an integer,
++ the integer value will be put in the return 'i', if 'i'
++ is non-null.
++ */
++ const char* Attribute( const char* name, int* i ) const;
++
++ /** Given an attribute name, Attribute() returns the value
++ for the attribute of that name, or null if none exists.
++ If the attribute exists and can be converted to an double,
++ the double value will be put in the return 'd', if 'd'
++ is non-null.
++ */
++ const char* Attribute( const char* name, double* d ) const;
++
++ /** QueryIntAttribute examines the attribute - it is an alternative to the
++ Attribute() method with richer error checking.
++ If the attribute is an integer, it is stored in 'value' and
++ the call returns TIXML_SUCCESS. If it is not
++ an integer, it returns TIXML_WRONG_TYPE. If the attribute
++ does not exist, then TIXML_NO_ATTRIBUTE is returned.
++ */
++ int QueryIntAttribute( const char* name, int* _value ) const;
++ /// QueryUnsignedAttribute examines the attribute - see QueryIntAttribute().
++ int QueryUnsignedAttribute( const char* name, unsigned* _value ) const;
++ /** QueryBoolAttribute examines the attribute - see QueryIntAttribute().
++ Note that '1', 'true', or 'yes' are considered true, while '0', 'false'
++ and 'no' are considered false.
++ */
++ int QueryBoolAttribute( const char* name, bool* _value ) const;
++ /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute().
++ int QueryDoubleAttribute( const char* name, double* _value ) const;
++ /// QueryFloatAttribute examines the attribute - see QueryIntAttribute().
++ int QueryFloatAttribute( const char* name, float* _value ) const {
++ double d;
++ int result = QueryDoubleAttribute( name, &d );
++ if ( result == TIXML_SUCCESS ) {
++ *_value = (float)d;
++ }
++ return result;
++ }
++
++ #ifdef TIXML_USE_STL
++ /// QueryStringAttribute examines the attribute - see QueryIntAttribute().
++ int QueryStringAttribute( const char* name, std::string* _value ) const {
++ const char* cstr = Attribute( name );
++ if ( cstr ) {
++ *_value = std::string( cstr );
++ return TIXML_SUCCESS;
++ }
++ return TIXML_NO_ATTRIBUTE;
++ }
++
++ /** Template form of the attribute query which will try to read the
++ attribute into the specified type. Very easy, very powerful, but
++ be careful to make sure to call this with the correct type.
++
++ NOTE: This method doesn't work correctly for 'string' types that contain spaces.
++
++ @return TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE
++ */
++ template< typename T > int QueryValueAttribute( const std::string& name, T* outValue ) const
++ {
++ const TiXmlAttribute* node = attributeSet.Find( name );
++ if ( !node )
++ return TIXML_NO_ATTRIBUTE;
++
++ std::stringstream sstream( node->ValueStr() );
++ sstream >> *outValue;
++ if ( !sstream.fail() )
++ return TIXML_SUCCESS;
++ return TIXML_WRONG_TYPE;
++ }
++
++ int QueryValueAttribute( const std::string& name, std::string* outValue ) const
++ {
++ const TiXmlAttribute* node = attributeSet.Find( name );
++ if ( !node )
++ return TIXML_NO_ATTRIBUTE;
++ *outValue = node->ValueStr();
++ return TIXML_SUCCESS;
++ }
++ #endif
++
++ /** Sets an attribute of name to a given value. The attribute
++ will be created if it does not exist, or changed if it does.
++ */
++ void SetAttribute( const char* name, const char * _value );
++
++ #ifdef TIXML_USE_STL
++ const std::string* Attribute( const std::string& name ) const;
++ const std::string* Attribute( const std::string& name, int* i ) const;
++ const std::string* Attribute( const std::string& name, double* d ) const;
++ int QueryIntAttribute( const std::string& name, int* _value ) const;
++ int QueryDoubleAttribute( const std::string& name, double* _value ) const;
++
++ /// STL std::string form.
++ void SetAttribute( const std::string& name, const std::string& _value );
++ ///< STL std::string form.
++ void SetAttribute( const std::string& name, int _value );
++ ///< STL std::string form.
++ void SetDoubleAttribute( const std::string& name, double value );
++ #endif
++
++ /** Sets an attribute of name to a given value. The attribute
++ will be created if it does not exist, or changed if it does.
++ */
++ void SetAttribute( const char * name, int value );
++
++ /** Sets an attribute of name to a given value. The attribute
++ will be created if it does not exist, or changed if it does.
++ */
++ void SetDoubleAttribute( const char * name, double value );
++
++ /** Deletes an attribute with the given name.
++ */
++ void RemoveAttribute( const char * name );
++ #ifdef TIXML_USE_STL
++ void RemoveAttribute( const std::string& name ) { RemoveAttribute (name.c_str ()); } ///< STL std::string form.
++ #endif
++
++ const TiXmlAttribute* FirstAttribute() const { return attributeSet.First(); } ///< Access the first attribute in this element.
++ TiXmlAttribute* FirstAttribute() { return attributeSet.First(); }
++ const TiXmlAttribute* LastAttribute() const { return attributeSet.Last(); } ///< Access the last attribute in this element.
++ TiXmlAttribute* LastAttribute() { return attributeSet.Last(); }
++
++ /** Convenience function for easy access to the text inside an element. Although easy
++ and concise, GetText() is limited compared to getting the TiXmlText child
++ and accessing it directly.
++
++ If the first child of 'this' is a TiXmlText, the GetText()
++ returns the character string of the Text node, else null is returned.
++
++ This is a convenient method for getting the text of simple contained text:
++ @verbatim
++ <foo>This is text</foo>
++ const char* str = fooElement->GetText();
++ @endverbatim
++
++ 'str' will be a pointer to "This is text".
++
++ Note that this function can be misleading. If the element foo was created from
++ this XML:
++ @verbatim
++ <foo><b>This is text</b></foo>
++ @endverbatim
++
++ then the value of str would be null. The first child node isn't a text node, it is
++ another element. From this XML:
++ @verbatim
++ <foo>This is <b>text</b></foo>
++ @endverbatim
++ GetText() will return "This is ".
++
++ WARNING: GetText() accesses a child node - don't become confused with the
++ similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are
++ safe type casts on the referenced node.
++ */
++ const char* GetText() const;
++
++ /// Creates a new Element and returns it - the returned element is a copy.
++ virtual TiXmlNode* Clone() const;
++ // Print the Element to a FILE stream.
++ virtual void Print( FILE* cfile, int depth ) const;
++
++ /* Attribtue parsing starts: next char past '<'
++ returns: next char past '>'
++ */
++ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
++
++ virtual const TiXmlElement* ToElement() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
++ virtual TiXmlElement* ToElement() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
++
++ /** Walk the XML tree visiting this node and all of its children.
++ */
++ virtual bool Accept( TiXmlVisitor* visitor ) const;
++
++protected:
++
++ void CopyTo( TiXmlElement* target ) const;
++ void ClearThis(); // like clear, but initializes 'this' object as well
++
++ // Used to be public [internal use]
++ #ifdef TIXML_USE_STL
++ virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
++ #endif
++ /* [internal use]
++ Reads the "value" of the element -- another element, or text.
++ This should terminate with the current end tag.
++ */
++ const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding );
++
++private:
++ TiXmlAttributeSet attributeSet;
++};
++
++
++/** An XML comment.
++*/
++class TiXmlComment : public TiXmlNode
++{
++public:
++ /// Constructs an empty comment.
++ TiXmlComment() : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) {}
++ /// Construct a comment from text.
++ TiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) {
++ SetValue( _value );
++ }
++ TiXmlComment( const TiXmlComment& );
++ TiXmlComment& operator=( const TiXmlComment& base );
++
++ virtual ~TiXmlComment() {}
++
++ /// Returns a copy of this Comment.
++ virtual TiXmlNode* Clone() const;
++ // Write this Comment to a FILE stream.
++ virtual void Print( FILE* cfile, int depth ) const;
++
++ /* Attribtue parsing starts: at the ! of the !--
++ returns: next char past '>'
++ */
++ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
++
++ virtual const TiXmlComment* ToComment() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
++ virtual TiXmlComment* ToComment() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
++
++ /** Walk the XML tree visiting this node and all of its children.
++ */
++ virtual bool Accept( TiXmlVisitor* visitor ) const;
++
++protected:
++ void CopyTo( TiXmlComment* target ) const;
++
++ // used to be public
++ #ifdef TIXML_USE_STL
++ virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
++ #endif
++// virtual void StreamOut( TIXML_OSTREAM * out ) const;
++
++private:
++
++};
++
++
++/** XML text. A text node can have 2 ways to output the next. "normal" output
++ and CDATA. It will default to the mode it was parsed from the XML file and
++ you generally want to leave it alone, but you can change the output mode with
++ SetCDATA() and query it with CDATA().
++*/
++class TiXmlText : public TiXmlNode
++{
++ friend class TiXmlElement;
++public:
++ /** Constructor for text element. By default, it is treated as
++ normal, encoded text. If you want it be output as a CDATA text
++ element, set the parameter _cdata to 'true'
++ */
++ TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT)
++ {
++ SetValue( initValue );
++ cdata = false;
++ }
++ virtual ~TiXmlText() {}
++
++ #ifdef TIXML_USE_STL
++ /// Constructor.
++ TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT)
++ {
++ SetValue( initValue );
++ cdata = false;
++ }
++ #endif
++
++ TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TINYXML_TEXT ) { copy.CopyTo( this ); }
++ TiXmlText& operator=( const TiXmlText& base ) { base.CopyTo( this ); return *this; }
++
++ // Write this text object to a FILE stream.
++ virtual void Print( FILE* cfile, int depth ) const;
++
++ /// Queries whether this represents text using a CDATA section.
++ bool CDATA() const { return cdata; }
++ /// Turns on or off a CDATA representation of text.
++ void SetCDATA( bool _cdata ) { cdata = _cdata; }
++
++ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
++
++ virtual const TiXmlText* ToText() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
++ virtual TiXmlText* ToText() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
++
++ /** Walk the XML tree visiting this node and all of its children.
++ */
++ virtual bool Accept( TiXmlVisitor* content ) const;
++
++protected :
++ /// [internal use] Creates a new Element and returns it.
++ virtual TiXmlNode* Clone() const;
++ void CopyTo( TiXmlText* target ) const;
++
++ bool Blank() const; // returns true if all white space and new lines
++ // [internal use]
++ #ifdef TIXML_USE_STL
++ virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
++ #endif
++
++private:
++ bool cdata; // true if this should be input and output as a CDATA style text element
++};
++
++
++/** In correct XML the declaration is the first entry in the file.
++ @verbatim
++ <?xml version="1.0" standalone="yes"?>
++ @endverbatim
++
++ TinyXml will happily read or write files without a declaration,
++ however. There are 3 possible attributes to the declaration:
++ version, encoding, and standalone.
++
++ Note: In this version of the code, the attributes are
++ handled as special cases, not generic attributes, simply
++ because there can only be at most 3 and they are always the same.
++*/
++class TiXmlDeclaration : public TiXmlNode
++{
++public:
++ /// Construct an empty declaration.
++ TiXmlDeclaration() : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) {}
++
++#ifdef TIXML_USE_STL
++ /// Constructor.
++ TiXmlDeclaration( const std::string& _version,
++ const std::string& _encoding,
++ const std::string& _standalone );
++#endif
++
++ /// Construct.
++ TiXmlDeclaration( const char* _version,
++ const char* _encoding,
++ const char* _standalone );
++
++ TiXmlDeclaration( const TiXmlDeclaration& copy );
++ TiXmlDeclaration& operator=( const TiXmlDeclaration& copy );
++
++ virtual ~TiXmlDeclaration() {}
++
++ /// Version. Will return an empty string if none was found.
++ const char *Version() const { return version.c_str (); }
++ /// Encoding. Will return an empty string if none was found.
++ const char *Encoding() const { return encoding.c_str (); }
++ /// Is this a standalone document?
++ const char *Standalone() const { return standalone.c_str (); }
++
++ /// Creates a copy of this Declaration and returns it.
++ virtual TiXmlNode* Clone() const;
++ // Print this declaration to a FILE stream.
++ virtual void Print( FILE* cfile, int depth, TIXML_STRING* str ) const;
++ virtual void Print( FILE* cfile, int depth ) const {
++ Print( cfile, depth, 0 );
++ }
++
++ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
++
++ virtual const TiXmlDeclaration* ToDeclaration() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
++ virtual TiXmlDeclaration* ToDeclaration() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
++
++ /** Walk the XML tree visiting this node and all of its children.
++ */
++ virtual bool Accept( TiXmlVisitor* visitor ) const;
++
++protected:
++ void CopyTo( TiXmlDeclaration* target ) const;
++ // used to be public
++ #ifdef TIXML_USE_STL
++ virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
++ #endif
++
++private:
++
++ TIXML_STRING version;
++ TIXML_STRING encoding;
++ TIXML_STRING standalone;
++};
++
++
++/** Any tag that tinyXml doesn't recognize is saved as an
++ unknown. It is a tag of text, but should not be modified.
++ It will be written back to the XML, unchanged, when the file
++ is saved.
++
++ DTD tags get thrown into TiXmlUnknowns.
++*/
++class TiXmlUnknown : public TiXmlNode
++{
++public:
++ TiXmlUnknown() : TiXmlNode( TiXmlNode::TINYXML_UNKNOWN ) {}
++ virtual ~TiXmlUnknown() {}
++
++ TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::TINYXML_UNKNOWN ) { copy.CopyTo( this ); }
++ TiXmlUnknown& operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); return *this; }
++
++ /// Creates a copy of this Unknown and returns it.
++ virtual TiXmlNode* Clone() const;
++ // Print this Unknown to a FILE stream.
++ virtual void Print( FILE* cfile, int depth ) const;
++
++ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
++
++ virtual const TiXmlUnknown* ToUnknown() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
++ virtual TiXmlUnknown* ToUnknown() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
++
++ /** Walk the XML tree visiting this node and all of its children.
++ */
++ virtual bool Accept( TiXmlVisitor* content ) const;
++
++protected:
++ void CopyTo( TiXmlUnknown* target ) const;
++
++ #ifdef TIXML_USE_STL
++ virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
++ #endif
++
++private:
++
++};
++
++
++/** Always the top level node. A document binds together all the
++ XML pieces. It can be saved, loaded, and printed to the screen.
++ The 'value' of a document node is the xml file name.
++*/
++class TiXmlDocument : public TiXmlNode
++{
++public:
++ /// Create an empty document, that has no name.
++ TiXmlDocument();
++ /// Create a document with a name. The name of the document is also the filename of the xml.
++ TiXmlDocument( const char * documentName );
++
++ #ifdef TIXML_USE_STL
++ /// Constructor.
++ TiXmlDocument( const std::string& documentName );
++ #endif
++
++ TiXmlDocument( const TiXmlDocument& copy );
++ TiXmlDocument& operator=( const TiXmlDocument& copy );
++
++ virtual ~TiXmlDocument() {}
++
++ /** Load a file using the current document value.
++ Returns true if successful. Will delete any existing
++ document data before loading.
++ */
++ bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
++ /// Save a file using the current document value. Returns true if successful.
++ bool SaveFile() const;
++ /// Load a file using the given filename. Returns true if successful.
++ bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
++ /// Save a file using the given filename. Returns true if successful.
++ bool SaveFile( const char * filename ) const;
++ /** Load a file using the given FILE*. Returns true if successful. Note that this method
++ doesn't stream - the entire object pointed at by the FILE*
++ will be interpreted as an XML file. TinyXML doesn't stream in XML from the current
++ file location. Streaming may be added in the future.
++ */
++ bool LoadFile( FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
++ /// Save a file using the given FILE*. Returns true if successful.
++ bool SaveFile( FILE* ) const;
++
++ #ifdef TIXML_USE_STL
++ bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version.
++ {
++ return LoadFile( filename.c_str(), encoding );
++ }
++ bool SaveFile( const std::string& filename ) const ///< STL std::string version.
++ {
++ return SaveFile( filename.c_str() );
++ }
++ #endif
++
++ /** Parse the given null terminated block of xml data. Passing in an encoding to this
++ method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml
++ to use that encoding, regardless of what TinyXml might otherwise try to detect.
++ */
++ virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
++
++ /** Get the root element -- the only top level element -- of the document.
++ In well formed XML, there should only be one. TinyXml is tolerant of
++ multiple elements at the document level.
++ */
++ const TiXmlElement* RootElement() const { return FirstChildElement(); }
++ TiXmlElement* RootElement() { return FirstChildElement(); }
++
++ /** If an error occurs, Error will be set to true. Also,
++ - The ErrorId() will contain the integer identifier of the error (not generally useful)
++ - The ErrorDesc() method will return the name of the error. (very useful)
++ - The ErrorRow() and ErrorCol() will return the location of the error (if known)
++ */
++ bool Error() const { return error; }
++
++ /// Contains a textual (english) description of the error if one occurs.
++ const char * ErrorDesc() const { return errorDesc.c_str (); }
++
++ /** Generally, you probably want the error string ( ErrorDesc() ). But if you
++ prefer the ErrorId, this function will fetch it.
++ */
++ int ErrorId() const { return errorId; }
++
++ /** Returns the location (if known) of the error. The first column is column 1,
++ and the first row is row 1. A value of 0 means the row and column wasn't applicable
++ (memory errors, for example, have no row/column) or the parser lost the error. (An
++ error in the error reporting, in that case.)
++
++ @sa SetTabSize, Row, Column
++ */
++ int ErrorRow() const { return errorLocation.row+1; }
++ int ErrorCol() const { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow()
++
++ /** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol())
++ to report the correct values for row and column. It does not change the output
++ or input in any way.
++
++ By calling this method, with a tab size
++ greater than 0, the row and column of each node and attribute is stored
++ when the file is loaded. Very useful for tracking the DOM back in to
++ the source file.
++
++ The tab size is required for calculating the location of nodes. If not
++ set, the default of 4 is used. The tabsize is set per document. Setting
++ the tabsize to 0 disables row/column tracking.
++
++ Note that row and column tracking is not supported when using operator>>.
++
++ The tab size needs to be enabled before the parse or load. Correct usage:
++ @verbatim
++ TiXmlDocument doc;
++ doc.SetTabSize( 8 );
++ doc.Load( "myfile.xml" );
++ @endverbatim
++
++ @sa Row, Column
++ */
++ void SetTabSize( int _tabsize ) { tabsize = _tabsize; }
++
++ int TabSize() const { return tabsize; }
++
++ /** If you have handled the error, it can be reset with this call. The error
++ state is automatically cleared if you Parse a new XML block.
++ */
++ void ClearError() { error = false;
++ errorId = 0;
++ errorDesc = "";
++ errorLocation.row = errorLocation.col = 0;
++ //errorLocation.last = 0;
++ }
++
++ /** Write the document to standard out using formatted printing ("pretty print"). */
++ void Print() const { Print( stdout, 0 ); }
++
++ /* Write the document to a string using formatted printing ("pretty print"). This
++ will allocate a character array (new char[]) and return it as a pointer. The
++ calling code pust call delete[] on the return char* to avoid a memory leak.
++ */
++ //char* PrintToMemory() const;
++
++ /// Print this Document to a FILE stream.
++ virtual void Print( FILE* cfile, int depth = 0 ) const;
++ // [internal use]
++ void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding );
++
++ virtual const TiXmlDocument* ToDocument() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
++ virtual TiXmlDocument* ToDocument() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
++
++ /** Walk the XML tree visiting this node and all of its children.
++ */
++ virtual bool Accept( TiXmlVisitor* content ) const;
++
++protected :
++ // [internal use]
++ virtual TiXmlNode* Clone() const;
++ #ifdef TIXML_USE_STL
++ virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
++ #endif
++
++private:
++ void CopyTo( TiXmlDocument* target ) const;
++
++ bool error;
++ int errorId;
++ TIXML_STRING errorDesc;
++ int tabsize;
++ TiXmlCursor errorLocation;
++ bool useMicrosoftBOM; // the UTF-8 BOM were found when read. Note this, and try to write.
++};
++
++
++/**
++ A TiXmlHandle is a class that wraps a node pointer with null checks; this is
++ an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml
++ DOM structure. It is a separate utility class.
++
++ Take an example:
++ @verbatim
++ <Document>
++ <Element attributeA = "valueA">
++ <Child attributeB = "value1" />
++ <Child attributeB = "value2" />
++ </Element>
++ <Document>
++ @endverbatim
++
++ Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very
++ easy to write a *lot* of code that looks like:
++
++ @verbatim
++ TiXmlElement* root = document.FirstChildElement( "Document" );
++ if ( root )
++ {
++ TiXmlElement* element = root->FirstChildElement( "Element" );
++ if ( element )
++ {
++ TiXmlElement* child = element->FirstChildElement( "Child" );
++ if ( child )
++ {
++ TiXmlElement* child2 = child->NextSiblingElement( "Child" );
++ if ( child2 )
++ {
++ // Finally do something useful.
++ @endverbatim
++
++ And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity
++ of such code. A TiXmlHandle checks for null pointers so it is perfectly safe
++ and correct to use:
++
++ @verbatim
++ TiXmlHandle docHandle( &document );
++ TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement();
++ if ( child2 )
++ {
++ // do something useful
++ @endverbatim
++
++ Which is MUCH more concise and useful.
++
++ It is also safe to copy handles - internally they are nothing more than node pointers.
++ @verbatim
++ TiXmlHandle handleCopy = handle;
++ @endverbatim
++
++ What they should not be used for is iteration:
++
++ @verbatim
++ int i=0;
++ while ( true )
++ {
++ TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).ToElement();
++ if ( !child )
++ break;
++ // do something
++ ++i;
++ }
++ @endverbatim
++
++ It seems reasonable, but it is in fact two embedded while loops. The Child method is
++ a linear walk to find the element, so this code would iterate much more than it needs
++ to. Instead, prefer:
++
++ @verbatim
++ TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).ToElement();
++
++ for( child; child; child=child->NextSiblingElement() )
++ {
++ // do something
++ }
++ @endverbatim
++*/
++class TiXmlHandle
++{
++public:
++ /// Create a handle from any node (at any depth of the tree.) This can be a null pointer.
++ TiXmlHandle( TiXmlNode* _node ) { this->node = _node; }
++ /// Copy constructor
++ TiXmlHandle( const TiXmlHandle& ref ) { this->node = ref.node; }
++ TiXmlHandle operator=( const TiXmlHandle& ref ) { if ( &ref != this ) this->node = ref.node; return *this; }
++
++ /// Return a handle to the first child node.
++ TiXmlHandle FirstChild() const;
++ /// Return a handle to the first child node with the given name.
++ TiXmlHandle FirstChild( const char * value ) const;
++ /// Return a handle to the first child element.
++ TiXmlHandle FirstChildElement() const;
++ /// Return a handle to the first child element with the given name.
++ TiXmlHandle FirstChildElement( const char * value ) const;
++
++ /** Return a handle to the "index" child with the given name.
++ The first child is 0, the second 1, etc.
++ */
++ TiXmlHandle Child( const char* value, int index ) const;
++ /** Return a handle to the "index" child.
++ The first child is 0, the second 1, etc.
++ */
++ TiXmlHandle Child( int index ) const;
++ /** Return a handle to the "index" child element with the given name.
++ The first child element is 0, the second 1, etc. Note that only TiXmlElements
++ are indexed: other types are not counted.
++ */
++ TiXmlHandle ChildElement( const char* value, int index ) const;
++ /** Return a handle to the "index" child element.
++ The first child element is 0, the second 1, etc. Note that only TiXmlElements
++ are indexed: other types are not counted.
++ */
++ TiXmlHandle ChildElement( int index ) const;
++
++ #ifdef TIXML_USE_STL
++ TiXmlHandle FirstChild( const std::string& _value ) const { return FirstChild( _value.c_str() ); }
++ TiXmlHandle FirstChildElement( const std::string& _value ) const { return FirstChildElement( _value.c_str() ); }
++
++ TiXmlHandle Child( const std::string& _value, int index ) const { return Child( _value.c_str(), index ); }
++ TiXmlHandle ChildElement( const std::string& _value, int index ) const { return ChildElement( _value.c_str(), index ); }
++ #endif
++
++ /** Return the handle as a TiXmlNode. This may return null.
++ */
++ TiXmlNode* ToNode() const { return node; }
++ /** Return the handle as a TiXmlElement. This may return null.
++ */
++ TiXmlElement* ToElement() const { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); }
++ /** Return the handle as a TiXmlText. This may return null.
++ */
++ TiXmlText* ToText() const { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); }
++ /** Return the handle as a TiXmlUnknown. This may return null.
++ */
++ TiXmlUnknown* ToUnknown() const { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); }
++
++ /** @deprecated use ToNode.
++ Return the handle as a TiXmlNode. This may return null.
++ */
++ TiXmlNode* Node() const { return ToNode(); }
++ /** @deprecated use ToElement.
++ Return the handle as a TiXmlElement. This may return null.
++ */
++ TiXmlElement* Element() const { return ToElement(); }
++ /** @deprecated use ToText()
++ Return the handle as a TiXmlText. This may return null.
++ */
++ TiXmlText* Text() const { return ToText(); }
++ /** @deprecated use ToUnknown()
++ Return the handle as a TiXmlUnknown. This may return null.
++ */
++ TiXmlUnknown* Unknown() const { return ToUnknown(); }
++
++private:
++ TiXmlNode* node;
++};
++
++
++/** Print to memory functionality. The TiXmlPrinter is useful when you need to:
++
++ -# Print to memory (especially in non-STL mode)
++ -# Control formatting (line endings, etc.)
++
++ When constructed, the TiXmlPrinter is in its default "pretty printing" mode.
++ Before calling Accept() you can call methods to control the printing
++ of the XML document. After TiXmlNode::Accept() is called, the printed document can
++ be accessed via the CStr(), Str(), and Size() methods.
++
++ TiXmlPrinter uses the Visitor API.
++ @verbatim
++ TiXmlPrinter printer;
++ printer.SetIndent( "\t" );
++
++ doc.Accept( &printer );
++ fprintf( stdout, "%s", printer.CStr() );
++ @endverbatim
++*/
++class TiXmlPrinter : public TiXmlVisitor
++{
++public:
++ TiXmlPrinter() : depth( 0 ), simpleTextPrint( false ),
++ buffer(), indent( " " ), lineBreak( "\n" ) {}
++
++ virtual bool VisitEnter( const TiXmlDocument& doc );
++ virtual bool VisitExit( const TiXmlDocument& doc );
++
++ virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute );
++ virtual bool VisitExit( const TiXmlElement& element );
++
++ virtual bool Visit( const TiXmlDeclaration& declaration );
++ virtual bool Visit( const TiXmlText& text );
++ virtual bool Visit( const TiXmlComment& comment );
++ virtual bool Visit( const TiXmlUnknown& unknown );
++
++ /** Set the indent characters for printing. By default 4 spaces
++ but tab (\t) is also useful, or null/empty string for no indentation.
++ */
++ void SetIndent( const char* _indent ) { indent = _indent ? _indent : "" ; }
++ /// Query the indention string.
++ const char* Indent() { return indent.c_str(); }
++ /** Set the line breaking string. By default set to newline (\n).
++ Some operating systems prefer other characters, or can be
++ set to the null/empty string for no indenation.
++ */
++ void SetLineBreak( const char* _lineBreak ) { lineBreak = _lineBreak ? _lineBreak : ""; }
++ /// Query the current line breaking string.
++ const char* LineBreak() { return lineBreak.c_str(); }
++
++ /** Switch over to "stream printing" which is the most dense formatting without
++ linebreaks. Common when the XML is needed for network transmission.
++ */
++ void SetStreamPrinting() { indent = "";
++ lineBreak = "";
++ }
++ /// Return the result.
++ const char* CStr() { return buffer.c_str(); }
++ /// Return the length of the result string.
++ size_t Size() { return buffer.size(); }
++
++ #ifdef TIXML_USE_STL
++ /// Return the result.
++ const std::string& Str() { return buffer; }
++ #endif
++
++private:
++ void DoIndent() {
++ for( int i=0; i<depth; ++i )
++ buffer += indent;
++ }
++ void DoLineBreak() {
++ buffer += lineBreak;
++ }
++
++ int depth;
++ bool simpleTextPrint;
++ TIXML_STRING buffer;
++ TIXML_STRING indent;
++ TIXML_STRING lineBreak;
++};
++
++
++#ifdef _MSC_VER
++#pragma warning( pop )
++#endif
++
++#endif
+diff --git a/xbmc/pvrclients/MediaPortal/lib/tinyxml/tinyxmlerror.cpp b/xbmc/pvrclients/MediaPortal/lib/tinyxml/tinyxmlerror.cpp
+new file mode 100644
+index 0000000..538c21d
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/lib/tinyxml/tinyxmlerror.cpp
+@@ -0,0 +1,52 @@
++/*
++www.sourceforge.net/projects/tinyxml
++Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com)
++
++This software is provided 'as-is', without any express or implied
++warranty. In no event will the authors be held liable for any
++damages arising from the use of this software.
++
++Permission is granted to anyone to use this software for any
++purpose, including commercial applications, and to alter it and
++redistribute it freely, subject to the following restrictions:
++
++1. The origin of this software must not be misrepresented; you must
++not claim that you wrote the original software. If you use this
++software in a product, an acknowledgment in the product documentation
++would be appreciated but is not required.
++
++2. Altered source versions must be plainly marked as such, and
++must not be misrepresented as being the original software.
++
++3. This notice may not be removed or altered from any source
++distribution.
++*/
++
++#include "tinyxml.h"
++
++// The goal of the seperate error file is to make the first
++// step towards localization. tinyxml (currently) only supports
++// english error messages, but the could now be translated.
++//
++// It also cleans up the code a bit.
++//
++
++const char* TiXmlBase::errorString[ TiXmlBase::TIXML_ERROR_STRING_COUNT ] =
++{
++ "No error",
++ "Error",
++ "Failed to open file",
++ "Error parsing Element.",
++ "Failed to read Element name",
++ "Error reading Element value.",
++ "Error reading Attributes.",
++ "Error: empty tag.",
++ "Error reading end tag.",
++ "Error parsing Unknown.",
++ "Error parsing Comment.",
++ "Error parsing Declaration.",
++ "Error document empty.",
++ "Error null (0) or unexpected EOF found in input stream.",
++ "Error parsing CDATA.",
++ "Error when TiXmlDocument added to document, because TiXmlDocument can only be at the root.",
++};
+diff --git a/xbmc/pvrclients/MediaPortal/lib/tinyxml/tinyxmlparser.cpp b/xbmc/pvrclients/MediaPortal/lib/tinyxml/tinyxmlparser.cpp
+new file mode 100644
+index 0000000..81b7eae
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/lib/tinyxml/tinyxmlparser.cpp
+@@ -0,0 +1,1638 @@
++/*
++www.sourceforge.net/projects/tinyxml
++Original code by Lee Thomason (www.grinninglizard.com)
++
++This software is provided 'as-is', without any express or implied
++warranty. In no event will the authors be held liable for any
++damages arising from the use of this software.
++
++Permission is granted to anyone to use this software for any
++purpose, including commercial applications, and to alter it and
++redistribute it freely, subject to the following restrictions:
++
++1. The origin of this software must not be misrepresented; you must
++not claim that you wrote the original software. If you use this
++software in a product, an acknowledgment in the product documentation
++would be appreciated but is not required.
++
++2. Altered source versions must be plainly marked as such, and
++must not be misrepresented as being the original software.
++
++3. This notice may not be removed or altered from any source
++distribution.
++*/
++
++#include <ctype.h>
++#include <stddef.h>
++
++#include "tinyxml.h"
++
++//#define DEBUG_PARSER
++#if defined( DEBUG_PARSER )
++# if defined( DEBUG ) && defined( _MSC_VER )
++# include <windows.h>
++# define TIXML_LOG OutputDebugString
++# else
++# define TIXML_LOG printf
++# endif
++#endif
++
++// Note tha "PutString" hardcodes the same list. This
++// is less flexible than it appears. Changing the entries
++// or order will break putstring.
++TiXmlBase::Entity TiXmlBase::entity[ TiXmlBase::NUM_ENTITY ] =
++{
++ { "&amp;", 5, '&' },
++ { "&lt;", 4, '<' },
++ { "&gt;", 4, '>' },
++ { "&quot;", 6, '\"' },
++ { "&apos;", 6, '\'' }
++};
++
++// Bunch of unicode info at:
++// http://www.unicode.org/faq/utf_bom.html
++// Including the basic of this table, which determines the #bytes in the
++// sequence from the lead byte. 1 placed for invalid sequences --
++// although the result will be junk, pass it through as much as possible.
++// Beware of the non-characters in UTF-8:
++// ef bb bf (Microsoft "lead bytes")
++// ef bf be
++// ef bf bf
++
++const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
++const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
++const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
++
++const int TiXmlBase::utf8ByteTable[256] =
++{
++ // 0 1 2 3 4 5 6 7 8 9 a b c d e f
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0
++ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte
++ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0
++ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte
++ 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid
++};
++
++
++void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
++{
++ const unsigned long BYTE_MASK = 0xBF;
++ const unsigned long BYTE_MARK = 0x80;
++ const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
++
++ if (input < 0x80)
++ *length = 1;
++ else if ( input < 0x800 )
++ *length = 2;
++ else if ( input < 0x10000 )
++ *length = 3;
++ else if ( input < 0x200000 )
++ *length = 4;
++ else
++ { *length = 0; return; } // This code won't covert this correctly anyway.
++
++ output += *length;
++
++ // Scary scary fall throughs.
++ switch (*length)
++ {
++ case 4:
++ --output;
++ *output = (char)((input | BYTE_MARK) & BYTE_MASK);
++ input >>= 6;
++ case 3:
++ --output;
++ *output = (char)((input | BYTE_MARK) & BYTE_MASK);
++ input >>= 6;
++ case 2:
++ --output;
++ *output = (char)((input | BYTE_MARK) & BYTE_MASK);
++ input >>= 6;
++ case 1:
++ --output;
++ *output = (char)(input | FIRST_BYTE_MARK[*length]);
++ }
++}
++
++
++/*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding /*encoding*/ )
++{
++ // This will only work for low-ascii, everything else is assumed to be a valid
++ // letter. I'm not sure this is the best approach, but it is quite tricky trying
++ // to figure out alhabetical vs. not across encoding. So take a very
++ // conservative approach.
++
++// if ( encoding == TIXML_ENCODING_UTF8 )
++// {
++ if ( anyByte < 127 )
++ return isalpha( anyByte );
++ else
++ return 1; // What else to do? The unicode set is huge...get the english ones right.
++// }
++// else
++// {
++// return isalpha( anyByte );
++// }
++}
++
++
++/*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding /*encoding*/ )
++{
++ // This will only work for low-ascii, everything else is assumed to be a valid
++ // letter. I'm not sure this is the best approach, but it is quite tricky trying
++ // to figure out alhabetical vs. not across encoding. So take a very
++ // conservative approach.
++
++// if ( encoding == TIXML_ENCODING_UTF8 )
++// {
++ if ( anyByte < 127 )
++ return isalnum( anyByte );
++ else
++ return 1; // What else to do? The unicode set is huge...get the english ones right.
++// }
++// else
++// {
++// return isalnum( anyByte );
++// }
++}
++
++
++class TiXmlParsingData
++{
++ friend class TiXmlDocument;
++ public:
++ void Stamp( const char* now, TiXmlEncoding encoding );
++
++ const TiXmlCursor& Cursor() const { return cursor; }
++
++ private:
++ // Only used by the document!
++ TiXmlParsingData( const char* start, int _tabsize, int row, int col )
++ {
++ assert( start );
++ stamp = start;
++ tabsize = _tabsize;
++ cursor.row = row;
++ cursor.col = col;
++ }
++
++ TiXmlCursor cursor;
++ const char* stamp;
++ int tabsize;
++};
++
++
++void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding )
++{
++ assert( now );
++
++ // Do nothing if the tabsize is 0.
++ if ( tabsize < 1 )
++ {
++ return;
++ }
++
++ // Get the current row, column.
++ int row = cursor.row;
++ int col = cursor.col;
++ const char* p = stamp;
++ assert( p );
++
++ while ( p < now )
++ {
++ // Treat p as unsigned, so we have a happy compiler.
++ const unsigned char* pU = (const unsigned char*)p;
++
++ // Code contributed by Fletcher Dunn: (modified by lee)
++ switch (*pU) {
++ case 0:
++ // We *should* never get here, but in case we do, don't
++ // advance past the terminating null character, ever
++ return;
++
++ case '\r':
++ // bump down to the next line
++ ++row;
++ col = 0;
++ // Eat the character
++ ++p;
++
++ // Check for \r\n sequence, and treat this as a single character
++ if (*p == '\n') {
++ ++p;
++ }
++ break;
++
++ case '\n':
++ // bump down to the next line
++ ++row;
++ col = 0;
++
++ // Eat the character
++ ++p;
++
++ // Check for \n\r sequence, and treat this as a single
++ // character. (Yes, this bizarre thing does occur still
++ // on some arcane platforms...)
++ if (*p == '\r') {
++ ++p;
++ }
++ break;
++
++ case '\t':
++ // Eat the character
++ ++p;
++
++ // Skip to next tab stop
++ col = (col / tabsize + 1) * tabsize;
++ break;
++
++ case TIXML_UTF_LEAD_0:
++ if ( encoding == TIXML_ENCODING_UTF8 )
++ {
++ if ( *(p+1) && *(p+2) )
++ {
++ // In these cases, don't advance the column. These are
++ // 0-width spaces.
++ if ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 )
++ p += 3;
++ else if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU )
++ p += 3;
++ else if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU )
++ p += 3;
++ else
++ { p +=3; ++col; } // A normal character.
++ }
++ }
++ else
++ {
++ ++p;
++ ++col;
++ }
++ break;
++
++ default:
++ if ( encoding == TIXML_ENCODING_UTF8 )
++ {
++ // Eat the 1 to 4 byte utf8 character.
++ int step = TiXmlBase::utf8ByteTable[*((const unsigned char*)p)];
++ if ( step == 0 )
++ step = 1; // Error case from bad encoding, but handle gracefully.
++ p += step;
++
++ // Just advance one column, of course.
++ ++col;
++ }
++ else
++ {
++ ++p;
++ ++col;
++ }
++ break;
++ }
++ }
++ cursor.row = row;
++ cursor.col = col;
++ assert( cursor.row >= -1 );
++ assert( cursor.col >= -1 );
++ stamp = p;
++ assert( stamp );
++}
++
++
++const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding )
++{
++ if ( !p || !*p )
++ {
++ return 0;
++ }
++ if ( encoding == TIXML_ENCODING_UTF8 )
++ {
++ while ( *p )
++ {
++ const unsigned char* pU = (const unsigned char*)p;
++
++ // Skip the stupid Microsoft UTF-8 Byte order marks
++ if ( *(pU+0)==TIXML_UTF_LEAD_0
++ && *(pU+1)==TIXML_UTF_LEAD_1
++ && *(pU+2)==TIXML_UTF_LEAD_2 )
++ {
++ p += 3;
++ continue;
++ }
++ else if(*(pU+0)==TIXML_UTF_LEAD_0
++ && *(pU+1)==0xbfU
++ && *(pU+2)==0xbeU )
++ {
++ p += 3;
++ continue;
++ }
++ else if(*(pU+0)==TIXML_UTF_LEAD_0
++ && *(pU+1)==0xbfU
++ && *(pU+2)==0xbfU )
++ {
++ p += 3;
++ continue;
++ }
++
++ if ( IsWhiteSpace( *p ) ) // Still using old rules for white space.
++ ++p;
++ else
++ break;
++ }
++ }
++ else
++ {
++ while ( *p && IsWhiteSpace( *p ) )
++ ++p;
++ }
++
++ return p;
++}
++
++#ifdef TIXML_USE_STL
++/*static*/ bool TiXmlBase::StreamWhiteSpace( std::istream * in, TIXML_STRING * tag )
++{
++ for( ;; )
++ {
++ if ( !in->good() ) return false;
++
++ int c = in->peek();
++ // At this scope, we can't get to a document. So fail silently.
++ if ( !IsWhiteSpace( c ) || c <= 0 )
++ return true;
++
++ *tag += (char) in->get();
++ }
++}
++
++/*static*/ bool TiXmlBase::StreamTo( std::istream * in, int character, TIXML_STRING * tag )
++{
++ //assert( character > 0 && character < 128 ); // else it won't work in utf-8
++ while ( in->good() )
++ {
++ int c = in->peek();
++ if ( c == character )
++ return true;
++ if ( c <= 0 ) // Silent failure: can't get document at this scope
++ return false;
++
++ in->get();
++ *tag += (char) c;
++ }
++ return false;
++}
++#endif
++
++// One of TinyXML's more performance demanding functions. Try to keep the memory overhead down. The
++// "assign" optimization removes over 10% of the execution time.
++//
++const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding )
++{
++ // Oddly, not supported on some comilers,
++ //name->clear();
++ // So use this:
++ *name = "";
++ assert( p );
++
++ // Names start with letters or underscores.
++ // Of course, in unicode, tinyxml has no idea what a letter *is*. The
++ // algorithm is generous.
++ //
++ // After that, they can be letters, underscores, numbers,
++ // hyphens, or colons. (Colons are valid ony for namespaces,
++ // but tinyxml can't tell namespaces from names.)
++ if ( p && *p
++ && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) )
++ {
++ const char* start = p;
++ while( p && *p
++ && ( IsAlphaNum( (unsigned char ) *p, encoding )
++ || *p == '_'
++ || *p == '-'
++ || *p == '.'
++ || *p == ':' ) )
++ {
++ //(*name) += *p; // expensive
++ ++p;
++ }
++ if ( p-start > 0 ) {
++ name->assign( start, p-start );
++ }
++ return p;
++ }
++ return 0;
++}
++
++const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding )
++{
++ // Presume an entity, and pull it out.
++ TIXML_STRING ent;
++ int i;
++ *length = 0;
++
++ if ( *(p+1) && *(p+1) == '#' && *(p+2) )
++ {
++ unsigned long ucs = 0;
++ ptrdiff_t delta = 0;
++ unsigned mult = 1;
++
++ if ( *(p+2) == 'x' )
++ {
++ // Hexadecimal.
++ if ( !*(p+3) ) return 0;
++
++ const char* q = p+3;
++ q = strchr( q, ';' );
++
++ if ( !q || !*q ) return 0;
++
++ delta = q-p;
++ --q;
++
++ while ( *q != 'x' )
++ {
++ if ( *q >= '0' && *q <= '9' )
++ ucs += mult * (*q - '0');
++ else if ( *q >= 'a' && *q <= 'f' )
++ ucs += mult * (*q - 'a' + 10);
++ else if ( *q >= 'A' && *q <= 'F' )
++ ucs += mult * (*q - 'A' + 10 );
++ else
++ return 0;
++ mult *= 16;
++ --q;
++ }
++ }
++ else
++ {
++ // Decimal.
++ if ( !*(p+2) ) return 0;
++
++ const char* q = p+2;
++ q = strchr( q, ';' );
++
++ if ( !q || !*q ) return 0;
++
++ delta = q-p;
++ --q;
++
++ while ( *q != '#' )
++ {
++ if ( *q >= '0' && *q <= '9' )
++ ucs += mult * (*q - '0');
++ else
++ return 0;
++ mult *= 10;
++ --q;
++ }
++ }
++ if ( encoding == TIXML_ENCODING_UTF8 )
++ {
++ // convert the UCS to UTF-8
++ ConvertUTF32ToUTF8( ucs, value, length );
++ }
++ else
++ {
++ *value = (char)ucs;
++ *length = 1;
++ }
++ return p + delta + 1;
++ }
++
++ // Now try to match it.
++ for( i=0; i<NUM_ENTITY; ++i )
++ {
++ if ( strncmp( entity[i].str, p, entity[i].strLength ) == 0 )
++ {
++ assert( strlen( entity[i].str ) == entity[i].strLength );
++ *value = entity[i].chr;
++ *length = 1;
++ return ( p + entity[i].strLength );
++ }
++ }
++
++ // So it wasn't an entity, its unrecognized, or something like that.
++ *value = *p; // Don't put back the last one, since we return it!
++ //*length = 1; // Leave unrecognized entities - this doesn't really work.
++ // Just writes strange XML.
++ return p+1;
++}
++
++
++bool TiXmlBase::StringEqual( const char* p,
++ const char* tag,
++ bool ignoreCase,
++ TiXmlEncoding encoding )
++{
++ assert( p );
++ assert( tag );
++ if ( !p || !*p )
++ {
++ assert( 0 );
++ return false;
++ }
++
++ const char* q = p;
++
++ if ( ignoreCase )
++ {
++ while ( *q && *tag && ToLower( *q, encoding ) == ToLower( *tag, encoding ) )
++ {
++ ++q;
++ ++tag;
++ }
++
++ if ( *tag == 0 )
++ return true;
++ }
++ else
++ {
++ while ( *q && *tag && *q == *tag )
++ {
++ ++q;
++ ++tag;
++ }
++
++ if ( *tag == 0 ) // Have we found the end of the tag, and everything equal?
++ return true;
++ }
++ return false;
++}
++
++const char* TiXmlBase::ReadText( const char* p,
++ TIXML_STRING * text,
++ bool trimWhiteSpace,
++ const char* endTag,
++ bool caseInsensitive,
++ TiXmlEncoding encoding )
++{
++ *text = "";
++ if ( !trimWhiteSpace // certain tags always keep whitespace
++ || !condenseWhiteSpace ) // if true, whitespace is always kept
++ {
++ // Keep all the white space.
++ while ( p && *p
++ && !StringEqual( p, endTag, caseInsensitive, encoding )
++ )
++ {
++ int len;
++ char cArr[4] = { 0, 0, 0, 0 };
++ p = GetChar( p, cArr, &len, encoding );
++ text->append( cArr, len );
++ }
++ }
++ else
++ {
++ bool whitespace = false;
++
++ // Remove leading white space:
++ p = SkipWhiteSpace( p, encoding );
++ while ( p && *p
++ && !StringEqual( p, endTag, caseInsensitive, encoding ) )
++ {
++ if ( *p == '\r' || *p == '\n' )
++ {
++ whitespace = true;
++ ++p;
++ }
++ else if ( IsWhiteSpace( *p ) )
++ {
++ whitespace = true;
++ ++p;
++ }
++ else
++ {
++ // If we've found whitespace, add it before the
++ // new character. Any whitespace just becomes a space.
++ if ( whitespace )
++ {
++ (*text) += ' ';
++ whitespace = false;
++ }
++ int len;
++ char cArr[4] = { 0, 0, 0, 0 };
++ p = GetChar( p, cArr, &len, encoding );
++ if ( len == 1 )
++ (*text) += cArr[0]; // more efficient
++ else
++ text->append( cArr, len );
++ }
++ }
++ }
++ if ( p && *p )
++ p += strlen( endTag );
++ return ( p && *p ) ? p : 0;
++}
++
++#ifdef TIXML_USE_STL
++
++void TiXmlDocument::StreamIn( std::istream * in, TIXML_STRING * tag )
++{
++ // The basic issue with a document is that we don't know what we're
++ // streaming. Read something presumed to be a tag (and hope), then
++ // identify it, and call the appropriate stream method on the tag.
++ //
++ // This "pre-streaming" will never read the closing ">" so the
++ // sub-tag can orient itself.
++
++ if ( !StreamTo( in, '<', tag ) )
++ {
++ SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return;
++ }
++
++ while ( in->good() )
++ {
++ int tagIndex = (int) tag->length();
++ while ( in->good() && in->peek() != '>' )
++ {
++ int c = in->get();
++ if ( c <= 0 )
++ {
++ SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++ break;
++ }
++ (*tag) += (char) c;
++ }
++
++ if ( in->good() )
++ {
++ // We now have something we presume to be a node of
++ // some sort. Identify it, and call the node to
++ // continue streaming.
++ TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING );
++
++ if ( node )
++ {
++ node->StreamIn( in, tag );
++ bool isElement = node->ToElement() != 0;
++ delete node;
++ node = 0;
++
++ // If this is the root element, we're done. Parsing will be
++ // done by the >> operator.
++ if ( isElement )
++ {
++ return;
++ }
++ }
++ else
++ {
++ SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return;
++ }
++ }
++ }
++ // We should have returned sooner.
++ SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN );
++}
++
++#endif
++
++const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding )
++{
++ ClearError();
++
++ // Parse away, at the document level. Since a document
++ // contains nothing but other tags, most of what happens
++ // here is skipping white space.
++ if ( !p || !*p )
++ {
++ SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return 0;
++ }
++
++ // Note that, for a document, this needs to come
++ // before the while space skip, so that parsing
++ // starts from the pointer we are given.
++ location.Clear();
++ if ( prevData )
++ {
++ location.row = prevData->cursor.row;
++ location.col = prevData->cursor.col;
++ }
++ else
++ {
++ location.row = 0;
++ location.col = 0;
++ }
++ TiXmlParsingData data( p, TabSize(), location.row, location.col );
++ location = data.Cursor();
++
++ if ( encoding == TIXML_ENCODING_UNKNOWN )
++ {
++ // Check for the Microsoft UTF-8 lead bytes.
++ const unsigned char* pU = (const unsigned char*)p;
++ if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0
++ && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1
++ && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 )
++ {
++ encoding = TIXML_ENCODING_UTF8;
++ useMicrosoftBOM = true;
++ }
++ }
++
++ p = SkipWhiteSpace( p, encoding );
++ if ( !p )
++ {
++ SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return 0;
++ }
++
++ while ( p && *p )
++ {
++ TiXmlNode* node = Identify( p, encoding );
++ if ( node )
++ {
++ p = node->Parse( p, &data, encoding );
++ LinkEndChild( node );
++ }
++ else
++ {
++ break;
++ }
++
++ // Did we get encoding info?
++ if ( encoding == TIXML_ENCODING_UNKNOWN
++ && node->ToDeclaration() )
++ {
++ TiXmlDeclaration* dec = node->ToDeclaration();
++ const char* enc = dec->Encoding();
++ assert( enc );
++
++ if ( *enc == 0 )
++ encoding = TIXML_ENCODING_UTF8;
++ else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) )
++ encoding = TIXML_ENCODING_UTF8;
++ else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) )
++ encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice
++ else
++ encoding = TIXML_ENCODING_LEGACY;
++ }
++
++ p = SkipWhiteSpace( p, encoding );
++ }
++
++ // Was this empty?
++ if ( !firstChild ) {
++ SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding );
++ return 0;
++ }
++
++ // All is well.
++ return p;
++}
++
++void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding )
++{
++ // The first error in a chain is more accurate - don't set again!
++ if ( error )
++ return;
++
++ assert( err > 0 && err < TIXML_ERROR_STRING_COUNT );
++ error = true;
++ errorId = err;
++ errorDesc = errorString[ errorId ];
++
++ errorLocation.Clear();
++ if ( pError && data )
++ {
++ data->Stamp( pError, encoding );
++ errorLocation = data->Cursor();
++ }
++}
++
++
++TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding )
++{
++ TiXmlNode* returnNode = 0;
++
++ p = SkipWhiteSpace( p, encoding );
++ if( !p || !*p || *p != '<' )
++ {
++ return 0;
++ }
++
++ p = SkipWhiteSpace( p, encoding );
++
++ if ( !p || !*p )
++ {
++ return 0;
++ }
++
++ // What is this thing?
++ // - Elements start with a letter or underscore, but xml is reserved.
++ // - Comments: <!--
++ // - Decleration: <?xml
++ // - Everthing else is unknown to tinyxml.
++ //
++
++ const char* xmlHeader = { "<?xml" };
++ const char* commentHeader = { "<!--" };
++ const char* dtdHeader = { "<!" };
++ const char* cdataHeader = { "<![CDATA[" };
++
++ if ( StringEqual( p, xmlHeader, true, encoding ) )
++ {
++ #ifdef DEBUG_PARSER
++ TIXML_LOG( "XML parsing Declaration\n" );
++ #endif
++ returnNode = new TiXmlDeclaration();
++ }
++ else if ( StringEqual( p, commentHeader, false, encoding ) )
++ {
++ #ifdef DEBUG_PARSER
++ TIXML_LOG( "XML parsing Comment\n" );
++ #endif
++ returnNode = new TiXmlComment();
++ }
++ else if ( StringEqual( p, cdataHeader, false, encoding ) )
++ {
++ #ifdef DEBUG_PARSER
++ TIXML_LOG( "XML parsing CDATA\n" );
++ #endif
++ TiXmlText* text = new TiXmlText( "" );
++ text->SetCDATA( true );
++ returnNode = text;
++ }
++ else if ( StringEqual( p, dtdHeader, false, encoding ) )
++ {
++ #ifdef DEBUG_PARSER
++ TIXML_LOG( "XML parsing Unknown(1)\n" );
++ #endif
++ returnNode = new TiXmlUnknown();
++ }
++ else if ( IsAlpha( *(p+1), encoding )
++ || *(p+1) == '_' )
++ {
++ #ifdef DEBUG_PARSER
++ TIXML_LOG( "XML parsing Element\n" );
++ #endif
++ returnNode = new TiXmlElement( "" );
++ }
++ else
++ {
++ #ifdef DEBUG_PARSER
++ TIXML_LOG( "XML parsing Unknown(2)\n" );
++ #endif
++ returnNode = new TiXmlUnknown();
++ }
++
++ if ( returnNode )
++ {
++ // Set the parent, so it can report errors
++ returnNode->parent = this;
++ }
++ return returnNode;
++}
++
++#ifdef TIXML_USE_STL
++
++void TiXmlElement::StreamIn (std::istream * in, TIXML_STRING * tag)
++{
++ // We're called with some amount of pre-parsing. That is, some of "this"
++ // element is in "tag". Go ahead and stream to the closing ">"
++ while( in->good() )
++ {
++ int c = in->get();
++ if ( c <= 0 )
++ {
++ TiXmlDocument* document = GetDocument();
++ if ( document )
++ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return;
++ }
++ (*tag) += (char) c ;
++
++ if ( c == '>' )
++ break;
++ }
++
++ if ( tag->length() < 3 ) return;
++
++ // Okay...if we are a "/>" tag, then we're done. We've read a complete tag.
++ // If not, identify and stream.
++
++ if ( tag->at( tag->length() - 1 ) == '>'
++ && tag->at( tag->length() - 2 ) == '/' )
++ {
++ // All good!
++ return;
++ }
++ else if ( tag->at( tag->length() - 1 ) == '>' )
++ {
++ // There is more. Could be:
++ // text
++ // cdata text (which looks like another node)
++ // closing tag
++ // another node.
++ for ( ;; )
++ {
++ StreamWhiteSpace( in, tag );
++
++ // Do we have text?
++ if ( in->good() && in->peek() != '<' )
++ {
++ // Yep, text.
++ TiXmlText text( "" );
++ text.StreamIn( in, tag );
++
++ // What follows text is a closing tag or another node.
++ // Go around again and figure it out.
++ continue;
++ }
++
++ // We now have either a closing tag...or another node.
++ // We should be at a "<", regardless.
++ if ( !in->good() ) return;
++ assert( in->peek() == '<' );
++ int tagIndex = (int) tag->length();
++
++ bool closingTag = false;
++ bool firstCharFound = false;
++
++ for( ;; )
++ {
++ if ( !in->good() )
++ return;
++
++ int c = in->peek();
++ if ( c <= 0 )
++ {
++ TiXmlDocument* document = GetDocument();
++ if ( document )
++ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return;
++ }
++
++ if ( c == '>' )
++ break;
++
++ *tag += (char) c;
++ in->get();
++
++ // Early out if we find the CDATA id.
++ if ( c == '[' && tag->size() >= 9 )
++ {
++ size_t len = tag->size();
++ const char* start = tag->c_str() + len - 9;
++ if ( strcmp( start, "<![CDATA[" ) == 0 ) {
++ assert( !closingTag );
++ break;
++ }
++ }
++
++ if ( !firstCharFound && c != '<' && !IsWhiteSpace( c ) )
++ {
++ firstCharFound = true;
++ if ( c == '/' )
++ closingTag = true;
++ }
++ }
++ // If it was a closing tag, then read in the closing '>' to clean up the input stream.
++ // If it was not, the streaming will be done by the tag.
++ if ( closingTag )
++ {
++ if ( !in->good() )
++ return;
++
++ int c = in->get();
++ if ( c <= 0 )
++ {
++ TiXmlDocument* document = GetDocument();
++ if ( document )
++ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return;
++ }
++ assert( c == '>' );
++ *tag += (char) c;
++
++ // We are done, once we've found our closing tag.
++ return;
++ }
++ else
++ {
++ // If not a closing tag, id it, and stream.
++ const char* tagloc = tag->c_str() + tagIndex;
++ TiXmlNode* node = Identify( tagloc, TIXML_DEFAULT_ENCODING );
++ if ( !node )
++ return;
++ node->StreamIn( in, tag );
++ delete node;
++ node = 0;
++
++ // No return: go around from the beginning: text, closing tag, or node.
++ }
++ }
++ }
++}
++#endif
++
++const char* TiXmlElement::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
++{
++ p = SkipWhiteSpace( p, encoding );
++ TiXmlDocument* document = GetDocument();
++
++ if ( !p || !*p )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, 0, 0, encoding );
++ return 0;
++ }
++
++ if ( data )
++ {
++ data->Stamp( p, encoding );
++ location = data->Cursor();
++ }
++
++ if ( *p != '<' )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, p, data, encoding );
++ return 0;
++ }
++
++ p = SkipWhiteSpace( p+1, encoding );
++
++ // Read the name.
++ const char* pErr = p;
++
++ p = ReadName( p, &value, encoding );
++ if ( !p || !*p )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, pErr, data, encoding );
++ return 0;
++ }
++
++ TIXML_STRING endTag ("</");
++ endTag += value;
++
++ // Check for and read attributes. Also look for an empty
++ // tag or an end tag.
++ while ( p && *p )
++ {
++ pErr = p;
++ p = SkipWhiteSpace( p, encoding );
++ if ( !p || !*p )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding );
++ return 0;
++ }
++ if ( *p == '/' )
++ {
++ ++p;
++ // Empty tag.
++ if ( *p != '>' )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_PARSING_EMPTY, p, data, encoding );
++ return 0;
++ }
++ return (p+1);
++ }
++ else if ( *p == '>' )
++ {
++ // Done with attributes (if there were any.)
++ // Read the value -- which can include other
++ // elements -- read the end tag, and return.
++ ++p;
++ p = ReadValue( p, data, encoding ); // Note this is an Element method, and will set the error if one happens.
++ if ( !p || !*p ) {
++ // We were looking for the end tag, but found nothing.
++ // Fix for [ 1663758 ] Failure to report error on bad XML
++ if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
++ return 0;
++ }
++
++ // We should find the end tag now
++ // note that:
++ // </foo > and
++ // </foo>
++ // are both valid end tags.
++ if ( StringEqual( p, endTag.c_str(), false, encoding ) )
++ {
++ p += endTag.length();
++ p = SkipWhiteSpace( p, encoding );
++ if ( p && *p && *p == '>' ) {
++ ++p;
++ return p;
++ }
++ if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
++ return 0;
++ }
++ else
++ {
++ if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
++ return 0;
++ }
++ }
++ else
++ {
++ // Try to read an attribute:
++ TiXmlAttribute* attrib = new TiXmlAttribute();
++ if ( !attrib )
++ {
++ return 0;
++ }
++
++ attrib->SetDocument( document );
++ pErr = p;
++ p = attrib->Parse( p, data, encoding );
++
++ if ( !p || !*p )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding );
++ delete attrib;
++ return 0;
++ }
++
++ // Handle the strange case of double attributes:
++ #ifdef TIXML_USE_STL
++ TiXmlAttribute* node = attributeSet.Find( attrib->NameTStr() );
++ #else
++ TiXmlAttribute* node = attributeSet.Find( attrib->Name() );
++ #endif
++ if ( node )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding );
++ delete attrib;
++ return 0;
++ }
++
++ attributeSet.Add( attrib );
++ }
++ }
++ return p;
++}
++
++
++const char* TiXmlElement::ReadValue( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
++{
++ TiXmlDocument* document = GetDocument();
++
++ // Read in text and elements in any order.
++ const char* pWithWhiteSpace = p;
++ p = SkipWhiteSpace( p, encoding );
++
++ while ( p && *p )
++ {
++ if ( *p != '<' )
++ {
++ // Take what we have, make a text element.
++ TiXmlText* textNode = new TiXmlText( "" );
++
++ if ( !textNode )
++ {
++ return 0;
++ }
++
++ if ( TiXmlBase::IsWhiteSpaceCondensed() )
++ {
++ p = textNode->Parse( p, data, encoding );
++ }
++ else
++ {
++ // Special case: we want to keep the white space
++ // so that leading spaces aren't removed.
++ p = textNode->Parse( pWithWhiteSpace, data, encoding );
++ }
++
++ if ( !textNode->Blank() )
++ LinkEndChild( textNode );
++ else
++ delete textNode;
++ }
++ else
++ {
++ // We hit a '<'
++ // Have we hit a new element or an end tag? This could also be
++ // a TiXmlText in the "CDATA" style.
++ if ( StringEqual( p, "</", false, encoding ) )
++ {
++ return p;
++ }
++ else
++ {
++ TiXmlNode* node = Identify( p, encoding );
++ if ( node )
++ {
++ p = node->Parse( p, data, encoding );
++ LinkEndChild( node );
++ }
++ else
++ {
++ return 0;
++ }
++ }
++ }
++ pWithWhiteSpace = p;
++ p = SkipWhiteSpace( p, encoding );
++ }
++
++ if ( !p )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_READING_ELEMENT_VALUE, 0, 0, encoding );
++ }
++ return p;
++}
++
++
++#ifdef TIXML_USE_STL
++void TiXmlUnknown::StreamIn( std::istream * in, TIXML_STRING * tag )
++{
++ while ( in->good() )
++ {
++ int c = in->get();
++ if ( c <= 0 )
++ {
++ TiXmlDocument* document = GetDocument();
++ if ( document )
++ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return;
++ }
++ (*tag) += (char) c;
++
++ if ( c == '>' )
++ {
++ // All is well.
++ return;
++ }
++ }
++}
++#endif
++
++
++const char* TiXmlUnknown::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
++{
++ TiXmlDocument* document = GetDocument();
++ p = SkipWhiteSpace( p, encoding );
++
++ if ( data )
++ {
++ data->Stamp( p, encoding );
++ location = data->Cursor();
++ }
++ if ( !p || !*p || *p != '<' )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_PARSING_UNKNOWN, p, data, encoding );
++ return 0;
++ }
++ ++p;
++ value = "";
++
++ while ( p && *p && *p != '>' )
++ {
++ value += *p;
++ ++p;
++ }
++
++ if ( !p )
++ {
++ if ( document )
++ document->SetError( TIXML_ERROR_PARSING_UNKNOWN, 0, 0, encoding );
++ }
++ if ( p && *p == '>' )
++ return p+1;
++ return p;
++}
++
++#ifdef TIXML_USE_STL
++void TiXmlComment::StreamIn( std::istream * in, TIXML_STRING * tag )
++{
++ while ( in->good() )
++ {
++ int c = in->get();
++ if ( c <= 0 )
++ {
++ TiXmlDocument* document = GetDocument();
++ if ( document )
++ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return;
++ }
++
++ (*tag) += (char) c;
++
++ if ( c == '>'
++ && tag->at( tag->length() - 2 ) == '-'
++ && tag->at( tag->length() - 3 ) == '-' )
++ {
++ // All is well.
++ return;
++ }
++ }
++}
++#endif
++
++
++const char* TiXmlComment::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
++{
++ TiXmlDocument* document = GetDocument();
++ value = "";
++
++ p = SkipWhiteSpace( p, encoding );
++
++ if ( data )
++ {
++ data->Stamp( p, encoding );
++ location = data->Cursor();
++ }
++ const char* startTag = "<!--";
++ const char* endTag = "-->";
++
++ if ( !StringEqual( p, startTag, false, encoding ) )
++ {
++ if ( document )
++ document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding );
++ return 0;
++ }
++ p += strlen( startTag );
++
++ // [ 1475201 ] TinyXML parses entities in comments
++ // Oops - ReadText doesn't work, because we don't want to parse the entities.
++ // p = ReadText( p, &value, false, endTag, false, encoding );
++ //
++ // from the XML spec:
++ /*
++ [Definition: Comments may appear anywhere in a document outside other markup; in addition,
++ they may appear within the document type declaration at places allowed by the grammar.
++ They are not part of the document's character data; an XML processor MAY, but need not,
++ make it possible for an application to retrieve the text of comments. For compatibility,
++ the string "--" (double-hyphen) MUST NOT occur within comments.] Parameter entity
++ references MUST NOT be recognized within comments.
++
++ An example of a comment:
++
++ <!-- declarations for <head> & <body> -->
++ */
++
++ value = "";
++ // Keep all the white space.
++ while ( p && *p && !StringEqual( p, endTag, false, encoding ) )
++ {
++ value.append( p, 1 );
++ ++p;
++ }
++ if ( p && *p )
++ p += strlen( endTag );
++
++ return p;
++}
++
++
++const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
++{
++ p = SkipWhiteSpace( p, encoding );
++ if ( !p || !*p ) return 0;
++
++ if ( data )
++ {
++ data->Stamp( p, encoding );
++ location = data->Cursor();
++ }
++ // Read the name, the '=' and the value.
++ const char* pErr = p;
++ p = ReadName( p, &name, encoding );
++ if ( !p || !*p )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding );
++ return 0;
++ }
++ p = SkipWhiteSpace( p, encoding );
++ if ( !p || !*p || *p != '=' )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding );
++ return 0;
++ }
++
++ ++p; // skip '='
++ p = SkipWhiteSpace( p, encoding );
++ if ( !p || !*p )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding );
++ return 0;
++ }
++
++ const char* end;
++ const char SINGLE_QUOTE = '\'';
++ const char DOUBLE_QUOTE = '\"';
++
++ if ( *p == SINGLE_QUOTE )
++ {
++ ++p;
++ end = "\'"; // single quote in string
++ p = ReadText( p, &value, false, end, false, encoding );
++ }
++ else if ( *p == DOUBLE_QUOTE )
++ {
++ ++p;
++ end = "\""; // double quote in string
++ p = ReadText( p, &value, false, end, false, encoding );
++ }
++ else
++ {
++ // All attribute values should be in single or double quotes.
++ // But this is such a common error that the parser will try
++ // its best, even without them.
++ value = "";
++ while ( p && *p // existence
++ && !IsWhiteSpace( *p ) // whitespace
++ && *p != '/' && *p != '>' ) // tag end
++ {
++ if ( *p == SINGLE_QUOTE || *p == DOUBLE_QUOTE ) {
++ // [ 1451649 ] Attribute values with trailing quotes not handled correctly
++ // We did not have an opening quote but seem to have a
++ // closing one. Give up and throw an error.
++ if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding );
++ return 0;
++ }
++ value += *p;
++ ++p;
++ }
++ }
++ return p;
++}
++
++#ifdef TIXML_USE_STL
++void TiXmlText::StreamIn( std::istream * in, TIXML_STRING * tag )
++{
++ while ( in->good() )
++ {
++ int c = in->peek();
++ if ( !cdata && (c == '<' ) )
++ {
++ return;
++ }
++ if ( c <= 0 )
++ {
++ TiXmlDocument* document = GetDocument();
++ if ( document )
++ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return;
++ }
++
++ (*tag) += (char) c;
++ in->get(); // "commits" the peek made above
++
++ if ( cdata && c == '>' && tag->size() >= 3 ) {
++ size_t len = tag->size();
++ if ( (*tag)[len-2] == ']' && (*tag)[len-3] == ']' ) {
++ // terminator of cdata.
++ return;
++ }
++ }
++ }
++}
++#endif
++
++const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
++{
++ value = "";
++ TiXmlDocument* document = GetDocument();
++
++ if ( data )
++ {
++ data->Stamp( p, encoding );
++ location = data->Cursor();
++ }
++
++ const char* const startTag = "<![CDATA[";
++ const char* const endTag = "]]>";
++
++ if ( cdata || StringEqual( p, startTag, false, encoding ) )
++ {
++ cdata = true;
++
++ if ( !StringEqual( p, startTag, false, encoding ) )
++ {
++ if ( document )
++ document->SetError( TIXML_ERROR_PARSING_CDATA, p, data, encoding );
++ return 0;
++ }
++ p += strlen( startTag );
++
++ // Keep all the white space, ignore the encoding, etc.
++ while ( p && *p
++ && !StringEqual( p, endTag, false, encoding )
++ )
++ {
++ value += *p;
++ ++p;
++ }
++
++ TIXML_STRING dummy;
++ p = ReadText( p, &dummy, false, endTag, false, encoding );
++ return p;
++ }
++ else
++ {
++ bool ignoreWhite = true;
++
++ const char* end = "<";
++ p = ReadText( p, &value, ignoreWhite, end, false, encoding );
++ if ( p && *p )
++ return p-1; // don't truncate the '<'
++ return 0;
++ }
++}
++
++#ifdef TIXML_USE_STL
++void TiXmlDeclaration::StreamIn( std::istream * in, TIXML_STRING * tag )
++{
++ while ( in->good() )
++ {
++ int c = in->get();
++ if ( c <= 0 )
++ {
++ TiXmlDocument* document = GetDocument();
++ if ( document )
++ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return;
++ }
++ (*tag) += (char) c;
++
++ if ( c == '>' )
++ {
++ // All is well.
++ return;
++ }
++ }
++}
++#endif
++
++const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding )
++{
++ p = SkipWhiteSpace( p, _encoding );
++ // Find the beginning, find the end, and look for
++ // the stuff in-between.
++ TiXmlDocument* document = GetDocument();
++ if ( !p || !*p || !StringEqual( p, "<?xml", true, _encoding ) )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding );
++ return 0;
++ }
++ if ( data )
++ {
++ data->Stamp( p, _encoding );
++ location = data->Cursor();
++ }
++ p += 5;
++
++ version = "";
++ encoding = "";
++ standalone = "";
++
++ while ( p && *p )
++ {
++ if ( *p == '>' )
++ {
++ ++p;
++ return p;
++ }
++
++ p = SkipWhiteSpace( p, _encoding );
++ if ( StringEqual( p, "version", true, _encoding ) )
++ {
++ TiXmlAttribute attrib;
++ p = attrib.Parse( p, data, _encoding );
++ version = attrib.Value();
++ }
++ else if ( StringEqual( p, "encoding", true, _encoding ) )
++ {
++ TiXmlAttribute attrib;
++ p = attrib.Parse( p, data, _encoding );
++ encoding = attrib.Value();
++ }
++ else if ( StringEqual( p, "standalone", true, _encoding ) )
++ {
++ TiXmlAttribute attrib;
++ p = attrib.Parse( p, data, _encoding );
++ standalone = attrib.Value();
++ }
++ else
++ {
++ // Read over whatever it is.
++ while( p && *p && *p != '>' && !IsWhiteSpace( *p ) )
++ ++p;
++ }
++ }
++ return 0;
++}
++
++bool TiXmlText::Blank() const
++{
++ for ( unsigned i=0; i<value.length(); i++ )
++ if ( !IsWhiteSpace( value[i] ) )
++ return false;
++ return true;
++}
++
+diff --git a/xbmc/pvrclients/MediaPortal/os-dependent.h b/xbmc/pvrclients/MediaPortal/os-dependent.h
+new file mode 100644
+index 0000000..dfeabf3
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/os-dependent.h
+@@ -0,0 +1,37 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2011 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 this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ */
++
++#if defined(TARGET_WINDOWS)
++#define NOMINMAX // don't define min() and max() to prevent a clash with std::min() and std::max
++#endif
++
++#include "platform/os.h"
++
++#if defined(TARGET_WINDOWS)
++# include "windows/os_windows.h"
++#else
++# include "posix/os_posix.h"
++#endif
++
++#if defined(TARGET_DARWIN)
++# ifndef PTHREAD_MUTEX_RECURSIVE_NP
++# define PTHREAD_MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE
++# endif
++#endif
+diff --git a/xbmc/pvrclients/MediaPortal/posix/os_posix.h b/xbmc/pvrclients/MediaPortal/posix/os_posix.h
+new file mode 100644
+index 0000000..02d1cb7
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/posix/os_posix.h
+@@ -0,0 +1,30 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2010 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 this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ */
++
++#ifndef PVRCLIENT_MEDIAPORTAL_OS_POSIX_H
++#define PVRCLIENT_MEDIAPORTAL_OS_POSIX_H
++
++typedef pthread_mutex_t criticalsection_t;
++typedef unsigned char byte;
++
++/* Platform dependent path separator */
++#define PATH_SEPARATOR_CHAR '/'
++
++#endif
+diff --git a/xbmc/pvrclients/MediaPortal/project/VS2010Express/XBMC_MPTV.vcxproj b/xbmc/pvrclients/MediaPortal/project/VS2010Express/XBMC_MPTV.vcxproj
+new file mode 100644
+index 0000000..69ef87f
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/project/VS2010Express/XBMC_MPTV.vcxproj
+@@ -0,0 +1,167 @@
++<?xml version="1.0" encoding="utf-8"?>
++<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
++ <ItemGroup Label="ProjectConfigurations">
++ <ProjectConfiguration Include="Debug|Win32">
++ <Configuration>Debug</Configuration>
++ <Platform>Win32</Platform>
++ </ProjectConfiguration>
++ <ProjectConfiguration Include="Release|Win32">
++ <Configuration>Release</Configuration>
++ <Platform>Win32</Platform>
++ </ProjectConfiguration>
++ </ItemGroup>
++ <PropertyGroup Label="Globals">
++ <ProjectName>pvrclient_mptv</ProjectName>
++ <ProjectGuid>{74C9642E-1988-48DC-8404-11717C355378}</ProjectGuid>
++ <RootNamespace>XBMC_MPTV</RootNamespace>
++ <Keyword>Win32Proj</Keyword>
++ </PropertyGroup>
++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
++ <ConfigurationType>DynamicLibrary</ConfigurationType>
++ <CharacterSet>MultiByte</CharacterSet>
++ </PropertyGroup>
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
++ <ConfigurationType>DynamicLibrary</ConfigurationType>
++ <CharacterSet>MultiByte</CharacterSet>
++ </PropertyGroup>
++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
++ <ImportGroup Label="ExtensionSettings">
++ </ImportGroup>
++ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
++ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
++ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
++ </ImportGroup>
++ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
++ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
++ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
++ </ImportGroup>
++ <PropertyGroup Label="UserMacros" />
++ <PropertyGroup>
++ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
++ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\..\..\..\addons\pvr.team-mediaportal.tvserver\</OutDir>
++ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Debug\</IntDir>
++ <IgnoreImportLibrary Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</IgnoreImportLibrary>
++ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
++ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PostBuildEventUseInBuild>
++ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\..\..\..\addons\pvr.team-mediaportal.tvserver\</OutDir>
++ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Release\</IntDir>
++ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</LinkIncremental>
++ <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">XBMC_MPTV_win32</TargetName>
++ <TargetExt Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.pvr</TargetExt>
++ <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">XBMC_MPTV_win32</TargetName>
++ <TargetExt Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.pvr</TargetExt>
++ <IncludePath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)\..\..\xbmc\;$(SolutionDir)\..\..\lib\;$(IncludePath);..\..\</IncludePath>
++ <IncludePath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)\..\..\xbmc\;$(SolutionDir)\..\..\lib\;$(IncludePath);..\..\</IncludePath>
++ </PropertyGroup>
++ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
++ <ClCompile>
++ <Optimization>Disabled</Optimization>
++ <AdditionalIncludeDirectories>..\..\..\..\..\addons\library.xbmc.addon;..\..\..\..\..\addons\library.xbmc.pvr;..\..\..\..\addons\include;.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
++ <PreprocessorDefinitions>_WIN32;_DEBUG;_WINDOWS;MPTV_EXPORTS;TARGET_WINDOWS;_WINSOCKAPI_;_USE_32BIT_TIME_T;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++ <MinimalRebuild>true</MinimalRebuild>
++ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
++ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
++ <PrecompiledHeader>
++ </PrecompiledHeader>
++ <WarningLevel>Level3</WarningLevel>
++ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
++ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
++ </ClCompile>
++ <Link>
++ <AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
++ <OutputFile>..\..\..\..\..\addons\pvr.team-mediaportal.tvserver\XBMC_MPTV_win32.pvr</OutputFile>
++ <IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
++ <GenerateDebugInformation>true</GenerateDebugInformation>
++ <ProgramDatabaseFile>$(OutDir)XBMC_MPTV.pdb</ProgramDatabaseFile>
++ <SubSystem>Windows</SubSystem>
++ <RandomizedBaseAddress>false</RandomizedBaseAddress>
++ <DataExecutionPrevention>
++ </DataExecutionPrevention>
++ <ImportLibrary>$(OutDir)XBMC_MPTV.lib</ImportLibrary>
++ <TargetMachine>MachineX86</TargetMachine>
++ </Link>
++ <PostBuildEvent>
++ <Command>
++ </Command>
++ </PostBuildEvent>
++ </ItemDefinitionGroup>
++ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
++ <ClCompile>
++ <Optimization>Disabled</Optimization>
++ <AdditionalIncludeDirectories>..\..\..\..\..\addons\library.xbmc.addon;..\..\..\..\..\addons\library.xbmc.pvr;..\..\..\..\addons\include;.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
++ <PreprocessorDefinitions>_WIN32;_WINDOWS;MPTV_EXPORTS;TARGET_WINDOWS;_WINSOCKAPI_;_USE_32BIT_TIME_T;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++ <MinimalRebuild>true</MinimalRebuild>
++ <BasicRuntimeChecks>Default</BasicRuntimeChecks>
++ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
++ <PrecompiledHeader>
++ </PrecompiledHeader>
++ <WarningLevel>TurnOffAllWarnings</WarningLevel>
++ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
++ </ClCompile>
++ <Link>
++ <AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
++ <OutputFile>..\..\..\..\..\addons\pvr.team-mediaportal.tvserver\XBMC_MPTV_win32.pvr</OutputFile>
++ <IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
++ <GenerateDebugInformation>false</GenerateDebugInformation>
++ <ProgramDatabaseFile>$(OutDir)XBMC_MPTV.pdb</ProgramDatabaseFile>
++ <SubSystem>Windows</SubSystem>
++ <OptimizeReferences>
++ </OptimizeReferences>
++ <EnableCOMDATFolding>
++ </EnableCOMDATFolding>
++ <RandomizedBaseAddress>false</RandomizedBaseAddress>
++ <DataExecutionPrevention>
++ </DataExecutionPrevention>
++ <ImportLibrary>$(OutDir)XBMC_MPTV.lib</ImportLibrary>
++ <TargetMachine>MachineX86</TargetMachine>
++ </Link>
++ </ItemDefinitionGroup>
++ <ItemGroup>
++ <ClCompile Include="..\..\Cards.cpp" />
++ <ClCompile Include="..\..\channels.cpp" />
++ <ClCompile Include="..\..\client.cpp" />
++ <ClCompile Include="..\..\..\..\..\addons\library.xbmc.addon\dlfcn-win32.cpp" />
++ <ClCompile Include="..\..\..\..\..\lib\platform\windows\os-threads.cpp" />
++ <ClCompile Include="..\..\epg.cpp" />
++ <ClCompile Include="..\..\GenreTable.cpp" />
++ <ClCompile Include="..\..\lib\tinyxml\tinystr.cpp" />
++ <ClCompile Include="..\..\lib\tinyxml\tinyxml.cpp" />
++ <ClCompile Include="..\..\lib\tinyxml\tinyxmlerror.cpp" />
++ <ClCompile Include="..\..\lib\tinyxml\tinyxmlparser.cpp" />
++ <ClCompile Include="..\..\pvrclient-mediaportal.cpp" />
++ <ClCompile Include="..\..\recordings.cpp" />
++ <ClCompile Include="..\..\Socket.cpp" />
++ <ClCompile Include="..\..\timers.cpp" />
++ <ClCompile Include="..\..\uri.cpp" />
++ <ClCompile Include="..\..\utils.cpp" />
++ <ClCompile Include="..\..\windows\FileUtils.cpp" />
++ <ClCompile Include="..\..\windows\WindowsUtils.cpp" />
++ </ItemGroup>
++ <ItemGroup>
++ <ClInclude Include="..\..\Cards.h" />
++ <ClInclude Include="..\..\channels.h" />
++ <ClInclude Include="..\..\client.h" />
++ <ClInclude Include="..\..\..\..\..\addons\library.xbmc.addon\dlfcn-win32.h" />
++ <ClInclude Include="..\..\epg.h" />
++ <ClInclude Include="..\..\FileUtils.h" />
++ <ClInclude Include="..\..\lib\tinyxml\tinystr.h" />
++ <ClInclude Include="..\..\lib\tinyxml\tinyxml.h" />
++ <ClInclude Include="..\..\os-dependent.h" />
++ <ClInclude Include="..\..\posix\os_posix.h" />
++ <ClInclude Include="..\..\pvrclient-mediaportal.h" />
++ <ClInclude Include="..\..\recordings.h" />
++ <ClInclude Include="..\..\Socket.h" />
++ <ClInclude Include="..\..\timers.h" />
++ <ClInclude Include="..\..\uri.h" />
++ <ClInclude Include="..\..\utils.h" />
++ <ClInclude Include="..\..\windows\os_windows.h" />
++ <ClInclude Include="..\..\windows\WindowsUtils.h" />
++ </ItemGroup>
++ <ItemGroup>
++ <None Include="..\..\README" />
++ </ItemGroup>
++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
++ <ImportGroup Label="ExtensionTargets">
++ </ImportGroup>
++</Project>
+diff --git a/xbmc/pvrclients/MediaPortal/project/VS2010Express/XBMC_MPTV.vcxproj.filters b/xbmc/pvrclients/MediaPortal/project/VS2010Express/XBMC_MPTV.vcxproj.filters
+new file mode 100644
+index 0000000..35c3024
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/project/VS2010Express/XBMC_MPTV.vcxproj.filters
+@@ -0,0 +1,143 @@
++<?xml version="1.0" encoding="utf-8"?>
++<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
++ <ItemGroup>
++ <Filter Include="Source Files">
++ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
++ <Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
++ </Filter>
++ <Filter Include="Header Files">
++ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
++ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
++ </Filter>
++ <Filter Include="Source Files\tinyXML">
++ <UniqueIdentifier>{a365b5cd-6962-4e53-9c35-55934619a180}</UniqueIdentifier>
++ </Filter>
++ <Filter Include="Header Files\tinyXML">
++ <UniqueIdentifier>{4a9cd83d-78e2-4957-864f-e37d61e2975e}</UniqueIdentifier>
++ </Filter>
++ <Filter Include="Header Files\windows">
++ <UniqueIdentifier>{cdeb5df5-f5b2-482d-9cb4-9e13a05233bd}</UniqueIdentifier>
++ </Filter>
++ <Filter Include="Source Files\windows">
++ <UniqueIdentifier>{a514608f-59ee-4524-b1bc-2ca2ad6dde70}</UniqueIdentifier>
++ </Filter>
++ </ItemGroup>
++ <ItemGroup>
++ <ClCompile Include="..\..\channels.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\client.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\..\..\..\addons\library.xbmc.addon\dlfcn-win32.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\..\..\..\lib\platform\windows\os-threads.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\epg.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\pvrclient-mediaportal.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\recordings.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\Socket.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\timers.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\utils.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\uri.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\Cards.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\lib\tinyxml\tinystr.cpp">
++ <Filter>Source Files\tinyXML</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\lib\tinyxml\tinyxml.cpp">
++ <Filter>Source Files\tinyXML</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\lib\tinyxml\tinyxmlerror.cpp">
++ <Filter>Source Files\tinyXML</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\lib\tinyxml\tinyxmlparser.cpp">
++ <Filter>Source Files\tinyXML</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\windows\FileUtils.cpp">
++ <Filter>Source Files\windows</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\windows\WindowsUtils.cpp">
++ <Filter>Source Files\windows</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\GenreTable.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ </ItemGroup>
++ <ItemGroup>
++ <ClInclude Include="..\..\channels.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\client.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\..\..\..\addons\library.xbmc.addon\dlfcn-win32.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\epg.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\pvrclient-mediaportal.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\recordings.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\Socket.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\timers.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\utils.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\uri.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\Cards.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\lib\tinyxml\tinystr.h">
++ <Filter>Header Files\tinyXML</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\lib\tinyxml\tinyxml.h">
++ <Filter>Header Files\tinyXML</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\os-dependent.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\windows\os_windows.h">
++ <Filter>Header Files\windows</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\posix\os_posix.h">
++ <Filter>Header Files\windows</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\windows\WindowsUtils.h">
++ <Filter>Header Files\windows</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\FileUtils.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ </ItemGroup>
++ <ItemGroup>
++ <None Include="..\..\README" />
++ </ItemGroup>
++</Project>
+diff --git a/xbmc/pvrclients/MediaPortal/pvrclient-mediaportal.cpp b/xbmc/pvrclients/MediaPortal/pvrclient-mediaportal.cpp
+new file mode 100644
+index 0000000..029f45f
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/pvrclient-mediaportal.cpp
+@@ -0,0 +1,1452 @@
++/*
++ * Copyright (C) 2005-2011 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 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ */
++
++#include <ctime>
++#include <stdio.h>
++#include <stdlib.h>
++
++#include "os-dependent.h"
++#include "platform/util/timeutils.h"
++
++#include "client.h"
++#include "timers.h"
++#include "channels.h"
++#include "recordings.h"
++#include "epg.h"
++#include "utils.h"
++#include "pvrclient-mediaportal.h"
++
++#ifdef TARGET_WINDOWS
++#include "FileUtils.h"
++#endif
++
++using namespace std;
++using namespace ADDON;
++
++/* Globals */
++int g_iTVServerXBMCBuild = 0;
++
++/* PVR client version (don't forget to update also the addon.xml and the Changelog.txt files) */
++#define PVRCLIENT_MEDIAPORTAL_VERSION_STRING "1.2.2.111"
++
++/* TVServerXBMC plugin supported versions */
++#define TVSERVERXBMC_MIN_VERSION_STRING "1.1.0.70"
++#define TVSERVERXBMC_MIN_VERSION_BUILD 70
++#define TVSERVERXBMC_RECOMMENDED_VERSION_STRING "1.1.x.109 or 1.2.2.111"
++#define TVSERVERXBMC_RECOMMENDED_VERSION_BUILD 110
++
++/************************************************************/
++/** Class interface */
++
++cPVRClientMediaPortal::cPVRClientMediaPortal()
++{
++ m_iCurrentChannel = -1;
++ m_iCurrentCard = 0;
++ m_tcpclient = new MPTV::Socket(MPTV::af_inet, MPTV::pf_inet, MPTV::sock_stream, MPTV::tcp);
++ m_bConnected = false;
++ m_bStop = true;
++ m_bTimeShiftStarted = false;
++ m_BackendUTCoffset = 0;
++ m_BackendTime = 0;
++ m_bStop = true;
++ m_genretable = NULL;
++ m_iLastRecordingUpdate = 0;
++}
++
++cPVRClientMediaPortal::~cPVRClientMediaPortal()
++{
++ XBMC->Log(LOG_DEBUG, "->~cPVRClientMediaPortal()");
++ if (m_bConnected)
++ Disconnect();
++ SAFE_DELETE(m_tcpclient);
++ SAFE_DELETE(m_genretable);
++}
++
++string cPVRClientMediaPortal::SendCommand(string command)
++{
++ int code;
++ vector<string> lines;
++ PLATFORM::CLockObject critsec(m_mutex);
++
++ if ( !m_tcpclient->send(command) )
++ {
++ if ( !m_tcpclient->is_valid() )
++ {
++ // Connection lost, try to reconnect
++ if ( Connect() )
++ {
++ // Resend the command
++ if (!m_tcpclient->send(command))
++ {
++ XBMC->Log(LOG_ERROR, "SendCommand('%s') failed.", command.c_str());
++ return "";
++ }
++ }
++ }
++ }
++
++ string response;
++ if ( !m_tcpclient->ReadResponse(code, lines) )
++ {
++ XBMC->Log(LOG_ERROR, "SendCommand - Failed with code: %d (%s)", code, lines[lines.size()-1].c_str());
++ }
++
++ return lines[lines.size()-1].c_str();
++}
++
++bool cPVRClientMediaPortal::SendCommand2(string command, int& code, vector<string>& lines)
++{
++ PLATFORM::CLockObject critsec(m_mutex);
++
++ if ( !m_tcpclient->send(command) )
++ {
++ if ( !m_tcpclient->is_valid() )
++ {
++ // Connection lost, try to reconnect
++ if ( Connect() )
++ {
++ // Resend the command
++ if (!m_tcpclient->send(command))
++ {
++ XBMC->Log(LOG_ERROR, "SendCommand2('%s') failed.", command.c_str());
++ return false;
++ }
++ }
++ }
++ }
++
++ if (!m_tcpclient->ReadResponse(code, lines))
++ {
++ XBMC->Log(LOG_ERROR, "SendCommand - Failed with code: %d (%s)", code, lines[lines.size()-1].c_str());
++ return false;
++ }
++ else
++ {
++ string result = lines[lines.size()-1];
++ lines.clear();
++
++ Tokenize(result, lines, ",");
++
++ return true;
++ }
++}
++
++bool cPVRClientMediaPortal::Connect()
++{
++ string result;
++
++ /* Open Connection to MediaPortal Backend TV Server via the XBMC TV Server plugin */
++ XBMC->Log(LOG_INFO, "Mediaportal pvr addon " PVRCLIENT_MEDIAPORTAL_VERSION_STRING " connecting to %s:%i", g_szHostname.c_str(), g_iPort);
++
++ if (!m_tcpclient->create())
++ {
++ XBMC->Log(LOG_ERROR, "Could not connect create socket");
++ return false;
++ }
++
++ if (!m_tcpclient->connect(g_szHostname, g_iPort))
++ {
++ XBMC->Log(LOG_ERROR, "Could not connect to MPTV backend");
++ return false;
++ }
++
++ m_tcpclient->set_non_blocking(1);
++ XBMC->Log(LOG_INFO, "Connected to %s:%i", g_szHostname.c_str(), g_iPort);
++
++ result = SendCommand("PVRclientXBMC:0-1\n");
++
++ if (result.length() == 0)
++ return false;
++
++ if(result.find("Unexpected protocol") != std::string::npos)
++ {
++ XBMC->Log(LOG_ERROR, "TVServer does not accept protocol: PVRclientXBMC:0-1");
++ return false;
++ }
++ else
++ {
++ vector<string> fields;
++ int major = 0, minor = 0, revision = 0;
++ int count = 0;
++
++ // Check the version of the TVServerXBMC plugin:
++ Tokenize(result, fields, "|");
++ if(fields.size() == 2)
++ {
++ // Ok, this TVServerXBMC version answers with a version string
++ count = sscanf(fields[1].c_str(), "%d.%d.%d.%d", &major, &minor, &revision, &g_iTVServerXBMCBuild);
++ if( count < 4 )
++ {
++ XBMC->Log(LOG_ERROR, "Could not parse the TVServerXBMC version string '%s'", fields[1].c_str());
++ return false;
++ }
++
++ // Check for the minimal requirement: 1.1.0.70
++ if( g_iTVServerXBMCBuild < TVSERVERXBMC_MIN_VERSION_BUILD ) //major < 1 || minor < 1 || revision < 0 || build < 70
++ {
++ XBMC->Log(LOG_ERROR, "Your TVServerXBMC version '%s' is too old. Please upgrade to '%s' or higher!", fields[1].c_str(), TVSERVERXBMC_MIN_VERSION_STRING);
++ XBMC->QueueNotification(QUEUE_ERROR, XBMC->GetLocalizedString(30050), fields[1].c_str(), TVSERVERXBMC_MIN_VERSION_STRING);
++ return false;
++ }
++ else
++ {
++ XBMC->Log(LOG_INFO, "Your TVServerXBMC version is '%s'", fields[1].c_str());
++
++ // Advice to upgrade:
++ if( g_iTVServerXBMCBuild < TVSERVERXBMC_RECOMMENDED_VERSION_BUILD )
++ {
++ XBMC->Log(LOG_INFO, "It is adviced to upgrade your TVServerXBMC version '%s' to '%s' or higher!", fields[1].c_str(), TVSERVERXBMC_RECOMMENDED_VERSION_STRING);
++ }
++ }
++ }
++ else
++ {
++ XBMC->Log(LOG_ERROR, "Your TVServerXBMC version is too old. Please upgrade to '%s' or higher!", TVSERVERXBMC_MIN_VERSION_STRING);
++ XBMC->QueueNotification(QUEUE_ERROR, XBMC->GetLocalizedString(30051), TVSERVERXBMC_MIN_VERSION_STRING);
++ return false;
++ }
++ }
++
++ /* Store connection string */
++ char buffer[512];
++ snprintf(buffer, 512, "%s:%i", g_szHostname.c_str(), g_iPort);
++ m_ConnectionString = buffer;
++
++ /* Retrieve card settings (needed for Live TV and recordings folders) */
++ if ( g_iTVServerXBMCBuild >= 106 )
++ {
++ int code;
++ vector<string> lines;
++
++ if ( SendCommand2("GetCardSettings\n", code, lines) )
++ {
++ m_cCards.ParseLines(lines);
++ }
++ }
++
++ m_bConnected = true;
++
++ // Read the genre string to type/subtype translation file:
++ if(g_bReadGenre)
++ {
++ string sGenreFile = g_szClientPath + PATH_SEPARATOR_CHAR + "resources" + PATH_SEPARATOR_CHAR + "genre_translation.xml";
++ m_genretable = new CGenreTable(sGenreFile);
++ }
++
++ return true;
++}
++
++void cPVRClientMediaPortal::Disconnect()
++{
++ string result;
++
++ XBMC->Log(LOG_INFO, "Disconnect");
++
++ if (m_tcpclient->is_valid() && m_bTimeShiftStarted)
++ {
++ result = SendCommand("IsTimeshifting:\n");
++
++ if (result.find("True") != std::string::npos )
++ {
++ result = SendCommand("StopTimeshift:\n");
++ }
++ }
++
++ m_bStop = true;
++
++ m_tcpclient->close();
++
++ m_bConnected = false;
++}
++
++/* IsUp()
++ * \brief Check whether we still have a connection with the TVServer. If not, try
++ * to reconnect
++ * \return True when a connection is available, False when even a reconnect failed
++ */
++bool cPVRClientMediaPortal::IsUp()
++{
++ if(!m_tcpclient->is_valid())
++ {
++ if(!Connect())
++ {
++ return false;
++ }
++ }
++ return true;
++}
++
++void* cPVRClientMediaPortal::Process(void*)
++{
++ XBMC->Log(LOG_DEBUG, "->Process() Not yet implemented");
++ return NULL;
++}
++
++
++/************************************************************/
++/** General handling */
++
++// Used among others for the server name string in the "Recordings" view
++const char* cPVRClientMediaPortal::GetBackendName(void)
++{
++ if (!m_tcpclient->is_valid())
++ {
++ return g_szHostname.c_str();
++ }
++
++ XBMC->Log(LOG_DEBUG, "->GetBackendName()");
++
++ if (m_BackendName.length() == 0)
++ {
++ m_BackendName = "MediaPortal TV-server (";
++ m_BackendName += SendCommand("GetBackendName:\n");
++ m_BackendName += ")";
++ }
++
++ return m_BackendName.c_str();
++}
++
++const char* cPVRClientMediaPortal::GetBackendVersion(void)
++{
++ if (!IsUp())
++ return "0.0";
++
++ if(m_BackendVersion.length() == 0)
++ {
++ m_BackendVersion = SendCommand("GetVersion:\n");
++ }
++
++ XBMC->Log(LOG_DEBUG, "GetBackendVersion: %s", m_BackendVersion.c_str());
++
++ return m_BackendVersion.c_str();
++}
++
++const char* cPVRClientMediaPortal::GetConnectionString(void)
++{
++ XBMC->Log(LOG_DEBUG, "GetConnectionString: %s", m_ConnectionString.c_str());
++ return m_ConnectionString.c_str();
++}
++
++PVR_ERROR cPVRClientMediaPortal::GetDriveSpace(long long *iTotal, long long *iUsed)
++{
++ string result;
++ vector<string> fields;
++
++ *iTotal = 0;
++ *iUsed = 0;
++
++ if (!IsUp())
++ return PVR_ERROR_SERVER_ERROR;
++
++ if ( g_iTVServerXBMCBuild >= 100)
++ {
++ result = SendCommand("GetDriveSpace:\n");
++
++ Tokenize(result, fields, "|");
++
++ if(fields.size() >= 2)
++ {
++ *iTotal = (long long) atoi(fields[0].c_str());
++ *iUsed = (long long) atoi(fields[1].c_str());
++ }
++ }
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++PVR_ERROR cPVRClientMediaPortal::GetBackendTime(time_t *localTime, int *gmtOffset)
++{
++ string result;
++ vector<string> fields;
++ int year = 0, month = 0, day = 0;
++ int hour = 0, minute = 0, second = 0;
++ int count = 0;
++ struct tm timeinfo;
++
++ if (!IsUp())
++ return PVR_ERROR_SERVER_ERROR;
++
++ result = SendCommand("GetTime:\n");
++
++ if (result.length() == 0)
++ return PVR_ERROR_SERVER_ERROR;
++
++ Tokenize(result, fields, "|");
++
++ if(fields.size() >= 3)
++ {
++ //[0] date + time TV Server
++ //[1] UTC offset hours
++ //[2] UTC offset minutes
++ //From CPVREpg::CPVREpg(): Expected PVREpg GMT offset is in seconds
++ m_BackendUTCoffset = ((atoi(fields[1].c_str()) * 60) + atoi(fields[2].c_str())) * 60;
++
++ count = sscanf(fields[0].c_str(), "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &minute, &second);
++
++ if(count == 6)
++ {
++ //timeinfo = *localtime ( &rawtime );
++ XBMC->Log(LOG_DEBUG, "GetMPTVTime: time from MP TV Server: %d-%d-%d %d:%d:%d, offset %d seconds", year, month, day, hour, minute, second, m_BackendUTCoffset );
++ timeinfo.tm_hour = hour;
++ timeinfo.tm_min = minute;
++ timeinfo.tm_sec = second;
++ timeinfo.tm_year = year - 1900;
++ timeinfo.tm_mon = month - 1;
++ timeinfo.tm_mday = day;
++ timeinfo.tm_isdst = -1; //Actively determines whether DST is in effect from the specified time and the local time zone.
++ // Make the other fields empty:
++ timeinfo.tm_wday = 0;
++ timeinfo.tm_yday = 0;
++
++ m_BackendTime = mktime(&timeinfo);
++
++ if(m_BackendTime < 0)
++ {
++ XBMC->Log(LOG_DEBUG, "GetMPTVTime: Unable to convert string '%s' into date+time", fields[0].c_str());
++ return PVR_ERROR_SERVER_ERROR;
++ }
++
++ XBMC->Log(LOG_DEBUG, "GetMPTVTime: localtime %s", asctime(localtime(&m_BackendTime)));
++ XBMC->Log(LOG_DEBUG, "GetMPTVTime: gmtime %s", asctime(gmtime(&m_BackendTime)));
++
++ *localTime = m_BackendTime;
++ *gmtOffset = m_BackendUTCoffset;
++ return PVR_ERROR_NO_ERROR;
++ }
++ else
++ {
++ return PVR_ERROR_SERVER_ERROR;
++ }
++ }
++ else
++ return PVR_ERROR_SERVER_ERROR;
++}
++
++/************************************************************/
++/** EPG handling */
++
++PVR_ERROR cPVRClientMediaPortal::GetEpg(PVR_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd)
++{
++ vector<string> lines;
++ char command[256];
++ string result;
++ cEpg epg;
++ EPG_TAG broadcast;
++ struct tm starttime;
++ struct tm endtime;
++
++ starttime = *gmtime( &iStart );
++ endtime = *gmtime( &iEnd );
++
++ if (!IsUp())
++ return PVR_ERROR_SERVER_ERROR;
++
++ if (g_iTVServerXBMCBuild >= 104)
++ {
++ // Request (extended) EPG data for the given period
++ snprintf(command, 256, "GetEPG:%i|%04d-%02d-%02dT%02d:%02d:%02d.0Z|%04d-%02d-%02dT%02d:%02d:%02d.0Z\n",
++ channel.iUniqueId, //Channel id
++ starttime.tm_year + 1900, starttime.tm_mon + 1, starttime.tm_mday, //Start date [2..4]
++ starttime.tm_hour, starttime.tm_min, starttime.tm_sec, //Start time [5..7]
++ endtime.tm_year + 1900, endtime.tm_mon + 1, endtime.tm_mday, //End date [8..10]
++ endtime.tm_hour, endtime.tm_min, endtime.tm_sec); //End time [11..13]
++ }
++ else
++ {
++ // This version does not yet return all EPG fields
++ snprintf(command, 256, "GetEPG:%i\n", channel.iUniqueId);
++ }
++
++ result = SendCommand(command);
++
++ if(result.compare(0,5, "ERROR") != 0)
++ {
++ if( result.length() != 0)
++ {
++ memset(&broadcast, 0, sizeof(EPG_TAG));
++ epg.SetGenreTable(m_genretable);
++
++ Tokenize(result, lines, ",");
++
++ XBMC->Log(LOG_DEBUG, "Found %i EPG items for channel %i\n", lines.size(), channel.iUniqueId);
++
++ for (vector<string>::iterator it = lines.begin(); it < lines.end(); it++)
++ {
++ string& data(*it);
++
++ if( data.length() > 0)
++ {
++ uri::decode(data);
++
++ bool isEnd = epg.ParseLine(data);
++
++ if (isEnd && epg.StartTime() != 0)
++ {
++ broadcast.iUniqueBroadcastId = epg.UniqueId();
++ broadcast.strTitle = epg.Title();
++ broadcast.iChannelNumber = channel.iChannelNumber;
++ broadcast.startTime = epg.StartTime();
++ broadcast.endTime = epg.EndTime();
++ broadcast.strPlotOutline = epg.ShortText();
++ broadcast.strPlot = epg.Description();
++ broadcast.strIconPath = "";
++ broadcast.iGenreType = epg.GenreType();
++ broadcast.iGenreSubType = epg.GenreSubType();
++ broadcast.strGenreDescription = epg.Genre();
++ broadcast.firstAired = epg.OriginalAirDate();
++ broadcast.iParentalRating = epg.ParentalRating();
++ broadcast.iStarRating = epg.StarRating();
++ broadcast.bNotify = false;
++ broadcast.iSeriesNumber = epg.SeriesNumber();
++ broadcast.iEpisodeNumber = epg.EpisodeNumber();
++ broadcast.iEpisodePartNumber = atoi(epg.EpisodePart());
++ broadcast.strEpisodeName = epg.EpisodeName();
++
++ PVR->TransferEpgEntry(handle, &broadcast);
++ }
++ epg.Reset();
++ }
++ }
++ }
++ else
++ {
++ XBMC->Log(LOG_DEBUG, "No EPG items found for channel %i", channel.iUniqueId);
++ }
++ }
++ else
++ {
++ XBMC->Log(LOG_DEBUG, "RequestEPGForChannel(%i) %s", channel.iUniqueId, result.c_str());
++ }
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++
++/************************************************************/
++/** Channel handling */
++
++int cPVRClientMediaPortal::GetNumChannels(void)
++{
++ string result;
++ //CStdString command;
++
++ if (!IsUp())
++ return -1;
++
++ //command.Format("GetChannelCount:%s\n", g_sTVGroup.c_str());
++ // Get the total channel count (radio+tv)
++ // It is only used to check whether XBMC should request the channel list
++ result = SendCommand("GetChannelCount:\n");
++
++ return atol(result.c_str());
++}
++
++PVR_ERROR cPVRClientMediaPortal::GetChannels(PVR_HANDLE handle, bool bRadio)
++{
++ vector<string> lines;
++ CStdString command;
++ int code;
++ PVR_CHANNEL tag;
++ CStdString stream;
++
++ if (!IsUp())
++ return PVR_ERROR_SERVER_ERROR;
++
++ if(bRadio)
++ {
++ if(!g_bRadioEnabled)
++ {
++ XBMC->Log(LOG_INFO, "Fetching radio channels is disabled.");
++ return PVR_ERROR_NO_ERROR;
++ }
++
++ if (g_szRadioGroup.length() > 0)
++ {
++ XBMC->Log(LOG_DEBUG, "GetChannels(radio) for radio group: '%s'", g_szRadioGroup.c_str());
++ command.Format("ListRadioChannels:%s\n", uri::encode(uri::PATH_TRAITS, g_szRadioGroup).c_str());
++ }
++ else
++ {
++ XBMC->Log(LOG_DEBUG, "GetChannels(radio) all channels");
++ command = "ListRadioChannels\n";
++ }
++ }
++ else
++ {
++ if (g_szTVGroup.length() > 0)
++ {
++ XBMC->Log(LOG_DEBUG, "GetChannels(tv) for TV group: '%s'", g_szTVGroup.c_str());
++ command.Format("ListTVChannels:%s\n", uri::encode(uri::PATH_TRAITS, g_szTVGroup).c_str());
++ }
++ else
++ {
++ XBMC->Log(LOG_DEBUG, "GetChannels(tv) all channels");
++ command = "ListTVChannels\n";
++ }
++ }
++
++ if( !SendCommand2(command.c_str(), code, lines) )
++ return PVR_ERROR_SERVER_ERROR;
++
++#ifdef TARGET_WINDOWS
++ bool bCheckForThumbs = false;
++
++ /* Check if we can find the MediaPortal channel logo folders on this machine */
++ std::string strIconName;
++ std::string strThumbPath;
++ std::string strProgramData;
++
++ if (OS::GetEnvironmentVariable("PROGRAMDATA", strProgramData) == true)
++ strThumbPath = strProgramData + "\\Team MediaPortal\\MediaPortal\\Thumbs\\";
++ else
++ {
++ if (OS::Version() >= OS::WindowsVista)
++ {
++ /* Windows Vista/7/Server 2008 */
++ strThumbPath = "C:\\ProgramData\\Team MediaPortal\\MediaPortal\\Thumbs\\";
++ }
++ else
++ {
++ /* Windows XP */
++ if (OS::GetEnvironmentVariable("ALLUSERSPROFILE", strProgramData) == true)
++ strThumbPath = strProgramData + "\\Application Data\\Team MediaPortal\\MediaPortal\\thumbs\\";
++ else
++ strThumbPath = "C:\\Documents and Settings\\All Users\\Application Data\\Team MediaPortal\\MediaPortal\\thumbs\\";
++ }
++ }
++
++ if (bRadio)
++ strThumbPath += "Radio\\";
++ else
++ strThumbPath += "TV\\logos\\";
++
++ bCheckForThumbs = OS::CFile::Exists(strThumbPath);
++#endif // TARGET_WINDOWS
++
++ memset(&tag, 0, sizeof(PVR_CHANNEL));
++
++ for (vector<string>::iterator it = lines.begin(); it < lines.end(); it++)
++ {
++ string& data(*it);
++
++ if (data.length() == 0)
++ {
++ if(bRadio)
++ XBMC->Log(LOG_DEBUG, "TVServer returned no data. Empty/non existing radio group '%s'?", g_szRadioGroup.c_str());
++ else
++ XBMC->Log(LOG_DEBUG, "TVServer returned no data. Empty/non existing tv group '%s'?", g_szTVGroup.c_str());
++ break;
++ }
++
++ uri::decode(data);
++
++ cChannel channel;
++ if( channel.Parse(data) )
++ {
++ tag.iUniqueId = channel.UID();
++ tag.iChannelNumber = g_iTVServerXBMCBuild >= 102 ? channel.ExternalID() : channel.UID();
++ tag.strChannelName = channel.Name();
++#ifdef TARGET_WINDOWS
++ if (bCheckForThumbs)
++ {
++ strIconName = strThumbPath + ToThumbFileName(channel.Name()) + ".png";
++ if ( OS::CFile::Exists(strIconName) )
++ {
++ tag.strIconPath = strIconName.c_str();
++ }
++ else
++ {
++ tag.strIconPath = "";
++ }
++ }
++#else
++ tag.strIconPath = "";
++#endif
++ tag.iEncryptionSystem = channel.Encrypted();
++ tag.bIsRadio = bRadio; //TODO:(channel.Vpid() == 0) && (channel.Apid(0) != 0) ? true : false;
++ tag.bIsHidden = false;
++
++ if(channel.IsWebstream())
++ {
++ tag.strStreamURL = channel.URL();
++ }
++ else
++ {
++ //Use GetLiveStreamURL to fetch an rtsp stream
++ if(bRadio)
++ stream.Format("pvr://stream/radio/%i.ts", tag.iUniqueId);
++ else
++ stream.Format("pvr://stream/tv/%i.ts", tag.iUniqueId);
++ tag.strStreamURL = stream.c_str();
++ }
++ tag.strInputFormat = "";
++
++ if( (!g_bOnlyFTA) || (tag.iEncryptionSystem==0))
++ {
++ PVR->TransferChannelEntry(handle, &tag);
++ }
++ }
++ }
++
++ //pthread_mutex_unlock(&m_critSection);
++ return PVR_ERROR_NO_ERROR;
++}
++
++/************************************************************/
++/** Channel group handling **/
++
++int cPVRClientMediaPortal::GetChannelGroupsAmount(void)
++{
++ // Not directly possible at the moment
++ XBMC->Log(LOG_DEBUG, "GetChannelGroupsAmount: TODO");
++
++ if (!IsUp())
++ return PVR_ERROR_SERVER_ERROR;
++
++ // just tell XBMC that we have groups
++ return 1;
++ //return -1; // not implemented
++}
++
++PVR_ERROR cPVRClientMediaPortal::GetChannelGroups(PVR_HANDLE handle, bool bRadio)
++{
++ vector<string> lines;
++ int code;
++ PVR_CHANNEL_GROUP tag;
++
++ if (!IsUp())
++ return PVR_ERROR_SERVER_ERROR;
++
++ if(bRadio)
++ {
++ if (g_bRadioEnabled)
++ {
++ XBMC->Log(LOG_DEBUG, "GetChannelGroups for radio");
++ if (!SendCommand2("ListRadioGroups\n", code, lines))
++ return PVR_ERROR_SERVER_ERROR;
++ }
++ else
++ {
++ XBMC->Log(LOG_DEBUG, "Skipping GetChannelGroups for radio. Radio support is disabled.");
++ return PVR_ERROR_NO_ERROR;
++ }
++ }
++ else
++ {
++ XBMC->Log(LOG_DEBUG, "GetChannelGroups for TV");
++ if (!SendCommand2("ListGroups\n", code, lines))
++ return PVR_ERROR_SERVER_ERROR;
++ }
++
++ memset(&tag, 0, sizeof(PVR_CHANNEL_GROUP));
++
++ for (vector<string>::iterator it = lines.begin(); it < lines.end(); it++)
++ {
++ string& data(*it);
++
++ if (data.length() == 0)
++ {
++ XBMC->Log(LOG_DEBUG, "TVServer returned no data. No %s groups found?", ((bRadio) ? "radio" : "tv"));
++ break;
++ }
++
++ uri::decode(data);
++
++ tag.bIsRadio = bRadio;
++ tag.strGroupName = data.c_str();
++ XBMC->Log(LOG_DEBUG, "Adding %s group: %s", ((bRadio) ? "radio" : "tv"), tag.strGroupName);
++ PVR->TransferChannelGroup(handle, &tag);
++ }
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++PVR_ERROR cPVRClientMediaPortal::GetChannelGroupMembers(PVR_HANDLE handle, const PVR_CHANNEL_GROUP &group)
++{
++ //TODO: code below is similar to GetChannels code. Refactor and combine...
++ vector<string> lines;
++ CStdString command;
++ int code;
++ PVR_CHANNEL_GROUP_MEMBER tag;
++
++ if (!IsUp())
++ return PVR_ERROR_SERVER_ERROR;
++
++ if(group.bIsRadio)
++ {
++ if (g_bRadioEnabled)
++ {
++ XBMC->Log(LOG_DEBUG, "GetChannelGroupMembers: for radio group '%s'", group.strGroupName);
++ command.Format("ListRadioChannels:%s\n", uri::encode(uri::PATH_TRAITS, group.strGroupName).c_str());
++ }
++ else
++ {
++ XBMC->Log(LOG_DEBUG, "Skipping GetChannelGroupMembers for radio. Radio support is disabled.");
++ return PVR_ERROR_NO_ERROR;
++ }
++ }
++ else
++ {
++ XBMC->Log(LOG_DEBUG, "GetChannelGroupMembers: for tv group '%s'", group.strGroupName);
++ command.Format("ListTVChannels:%s\n", uri::encode(uri::PATH_TRAITS, group.strGroupName).c_str());
++ }
++
++ if (!SendCommand2(command.c_str(), code, lines))
++ return PVR_ERROR_SERVER_ERROR;
++
++ memset(&tag, 0, sizeof(PVR_CHANNEL_GROUP_MEMBER));
++
++ for (vector<string>::iterator it = lines.begin(); it < lines.end(); it++)
++ {
++ string& data(*it);
++
++ if (data.length() == 0)
++ {
++ if(group.bIsRadio)
++ XBMC->Log(LOG_DEBUG, "TVServer returned no data. Empty/non existing radio group '%s'?", g_szRadioGroup.c_str());
++ else
++ XBMC->Log(LOG_DEBUG, "TVServer returned no data. Empty/non existing tv group '%s'?", g_szTVGroup.c_str());
++ break;
++ }
++
++ uri::decode(data);
++
++ cChannel channel;
++ if( channel.Parse(data) )
++ {
++ tag.iChannelUniqueId = channel.UID();
++ tag.iChannelNumber = g_iTVServerXBMCBuild >= 102 ? channel.ExternalID() : channel.UID();
++ tag.strGroupName = group.strGroupName;
++
++
++ // Don't add encrypted channels when FTA only option is turned on
++ if( (!g_bOnlyFTA) || (channel.Encrypted()==false))
++ {
++ XBMC->Log(LOG_DEBUG, "GetChannelGroupMembers: add channel %s to group '%s' (Backend channel uid=%d, channelnr=%d)",
++ channel.Name(), group.strGroupName, tag.iChannelUniqueId, tag.iChannelNumber);
++ PVR->TransferChannelGroupMember(handle, &tag);
++ }
++ }
++ }
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++/************************************************************/
++/** Record handling **/
++
++int cPVRClientMediaPortal::GetNumRecordings(void)
++{
++ string result;
++
++ if (!IsUp())
++ return PVR_ERROR_SERVER_ERROR;
++
++ result = SendCommand("GetRecordingCount:\n");
++
++ return atol(result.c_str());
++}
++
++PVR_ERROR cPVRClientMediaPortal::GetRecordings(PVR_HANDLE handle)
++{
++ vector<string> lines;
++ string result;
++ PVR_RECORDING tag;
++
++ if (!IsUp())
++ return PVR_ERROR_SERVER_ERROR;
++
++ if(g_bResolveRTSPHostname == false)
++ {
++ result = SendCommand("ListRecordings:False\n");
++ }
++ else
++ {
++ result = SendCommand("ListRecordings\n");
++ }
++
++ if( result.length() == 0 )
++ {
++ XBMC->Log(LOG_DEBUG, "Backend returned no recordings" );
++ return PVR_ERROR_NO_ERROR;
++ }
++
++ Tokenize(result, lines, ",");
++
++ memset(&tag, 0, sizeof(PVR_RECORDING));
++
++ for (vector<string>::iterator it = lines.begin(); it != lines.end(); it++)
++ {
++ string& data(*it);
++ uri::decode(data);
++
++ XBMC->Log(LOG_DEBUG, "RECORDING: %s", data.c_str() );
++
++ CStdString strRecordingId;
++ CStdString strDirectory;
++ cRecording recording;
++
++ recording.SetCardSettings(&m_cCards);
++ recording.SetGenreTable(m_genretable);
++
++ if (recording.ParseLine(data))
++ {
++ strRecordingId.Format("%i", recording.Index());
++
++ tag.strRecordingId = strRecordingId.c_str();
++ tag.strTitle = recording.Title();
++ tag.strPlotOutline = g_iTVServerXBMCBuild >= 105 ? recording.EpisodeName() : tag.strTitle;
++ tag.strPlot = recording.Description();
++ tag.strChannelName = recording.ChannelName();
++ tag.recordingTime = recording.StartTime();
++ tag.iDuration = (int) recording.Duration();
++ tag.iPriority = 0; // only available for schedules, not for recordings
++ tag.iLifetime = recording.Lifetime();
++ tag.iGenreType = recording.GenreType();
++ tag.iGenreSubType = recording.GenreSubType();
++
++ strDirectory = recording.Directory();
++ strDirectory.Replace("\\", " - "); // XBMC supports only 1 sublevel below Recordings, so flatten the MediaPortal directory structure
++ tag.strDirectory = strDirectory.c_str(); // used in XBMC as directory structure below "Recordings"
++
++ if (g_bUseRecordingsDir == true)
++ {
++ // Replace path by given path in g_szRecordingsDir
++ if (g_szRecordingsDir.length() > 0)
++ {
++ recording.SetDirectory(g_szRecordingsDir);
++ tag.strStreamURL = recording.FilePath();
++ }
++ else
++ {
++ tag.strStreamURL = recording.FilePath();
++ }
++ }
++ else
++ {
++ // Use rtsp url
++ tag.strStreamURL = recording.Stream();
++ }
++ PVR->TransferRecordingEntry(handle, &tag);
++ }
++ }
++
++ m_iLastRecordingUpdate = PLATFORM::GetTimeMs();
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++PVR_ERROR cPVRClientMediaPortal::DeleteRecording(const PVR_RECORDING &recording)
++{
++ char command[256];
++ string result;
++
++ if (!IsUp())
++ return PVR_ERROR_SERVER_ERROR;
++
++ snprintf(command, 256, "DeleteRecordedTV:%s\n", recording.strRecordingId);
++
++ result = SendCommand(command);
++
++ if(result.find("True") == string::npos)
++ {
++ return PVR_ERROR_NOT_DELETED;
++ }
++
++ // Although XBMC initiates the deletion of this recording, we still have to trigger XBMC to update its
++ // recordings list to remove the recording at the XBMC side
++ PVR->TriggerRecordingUpdate();
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++PVR_ERROR cPVRClientMediaPortal::RenameRecording(const PVR_RECORDING &recording)
++{
++ char command[512];
++ string result;
++
++ if (!IsUp())
++ return PVR_ERROR_SERVER_ERROR;
++
++ snprintf(command, 512, "UpdateRecording:%s|%s\n",
++ recording.strRecordingId,
++ uri::encode(uri::PATH_TRAITS, recording.strTitle).c_str());
++
++ result = SendCommand(command);
++
++ if(result.find("True") == string::npos)
++ {
++ XBMC->Log(LOG_DEBUG, "RenameRecording(%s) to %s [failed]", recording.strRecordingId, recording.strTitle);
++ return PVR_ERROR_NOT_DELETED;
++ }
++ XBMC->Log(LOG_DEBUG, "RenameRecording(%s) to %s [done]", recording.strRecordingId, recording.strTitle);
++
++ // Although XBMC initiates the rename of this recording, we still have to trigger XBMC to update its
++ // recordings list to see the renamed recording at the XBMC side
++ PVR->TriggerRecordingUpdate();
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++
++/************************************************************/
++/** Timer handling */
++
++int cPVRClientMediaPortal::GetNumTimers(void)
++{
++ string result;
++
++ if (!IsUp())
++ return PVR_ERROR_SERVER_ERROR;
++
++ result = SendCommand("GetScheduleCount:\n");
++
++ return atol(result.c_str());
++}
++
++PVR_ERROR cPVRClientMediaPortal::GetTimers(PVR_HANDLE handle)
++{
++ vector<string> lines;
++ string result;
++ PVR_TIMER tag;
++
++ if (!IsUp())
++ return PVR_ERROR_SERVER_ERROR;
++
++ result = SendCommand("ListSchedules:\n");
++
++ if (result.length() > 0)
++ {
++ Tokenize(result, lines, ",");
++
++ memset(&tag, 0, sizeof(PVR_TIMER));
++
++ for (vector<string>::iterator it = lines.begin(); it != lines.end(); it++)
++ {
++ string& data(*it);
++ uri::decode(data);
++
++ XBMC->Log(LOG_DEBUG, "SCHEDULED: %s", data.c_str() );
++
++ cTimer timer;
++
++ if(timer.ParseLine(data.c_str()) == true)
++ {
++ timer.GetPVRtimerinfo(tag);
++ PVR->TransferTimerEntry(handle, &tag);
++ }
++ }
++ }
++
++ if ( PLATFORM::GetTimeMs() > m_iLastRecordingUpdate + 15000)
++ {
++ PVR->TriggerRecordingUpdate();
++ }
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++PVR_ERROR cPVRClientMediaPortal::GetTimerInfo(unsigned int timernumber, PVR_TIMER &timerinfo)
++{
++ string result;
++ char command[256];
++
++ XBMC->Log(LOG_DEBUG, "->GetTimerInfo(%i)", timernumber);
++
++ if (!IsUp())
++ return PVR_ERROR_SERVER_ERROR;
++
++ snprintf(command, 256, "GetScheduleInfo:%i\n", timernumber);
++
++ result = SendCommand(command);
++
++ if (result.length() == 0)
++ return PVR_ERROR_SERVER_ERROR;
++
++ cTimer timer;
++ if( timer.ParseLine(result.c_str()) == false )
++ {
++ XBMC->Log(LOG_DEBUG, "GetTimerInfo(%i) parsing server response failed. Response: %s", timernumber, result.c_str());
++ return PVR_ERROR_SERVER_ERROR;
++ }
++
++ timer.GetPVRtimerinfo(timerinfo);
++ return PVR_ERROR_NO_ERROR;
++}
++
++PVR_ERROR cPVRClientMediaPortal::AddTimer(const PVR_TIMER &timerinfo)
++{
++ string result;
++
++#ifdef _TIME32_T_DEFINED
++ XBMC->Log(LOG_DEBUG, "->AddTimer Channel: %i, starttime: %i endtime: %i program: %s", timerinfo.iClientChannelUid, timerinfo.startTime, timerinfo.endTime, timerinfo.strTitle);
++#else
++ XBMC->Log(LOG_DEBUG, "->AddTimer Channel: %i, 64 bit times not yet supported!", timerinfo.iClientChannelUid);
++#endif
++
++ if (!IsUp())
++ return PVR_ERROR_SERVER_ERROR;
++
++ cTimer timer(timerinfo);
++
++ result = SendCommand(timer.AddScheduleCommand());
++
++ if(result.find("True") == string::npos)
++ {
++ XBMC->Log(LOG_DEBUG, "AddTimer for channel: %i [failed]", timerinfo.iClientChannelUid);
++ return PVR_ERROR_NOT_SAVED;
++ }
++ XBMC->Log(LOG_DEBUG, "AddTimer for channel: %i [done]", timerinfo.iClientChannelUid);
++
++ // Although XBMC adds this timer, we still have to trigger XBMC to update its timer list to
++ // see this new timer at the XBMC side
++ PVR->TriggerTimerUpdate();
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++PVR_ERROR cPVRClientMediaPortal::DeleteTimer(const PVR_TIMER &timer, bool bForceDelete)
++{
++ char command[256];
++ string result;
++
++ if (!IsUp())
++ return PVR_ERROR_SERVER_ERROR;
++
++ snprintf(command, 256, "DeleteSchedule:%i\n",timer.iClientIndex);
++
++ XBMC->Log(LOG_DEBUG, "DeleteTimer: About to delete MediaPortal schedule index=%i", timer.iClientIndex);
++ result = SendCommand(command);
++
++ if(result.find("True") == string::npos)
++ {
++ XBMC->Log(LOG_DEBUG, "DeleteTimer %i [failed]", timer.iClientIndex);
++ return PVR_ERROR_NOT_DELETED;
++ }
++ XBMC->Log(LOG_DEBUG, "DeleteTimer %i [done]", timer.iClientIndex);
++
++ // Although XBMC deletes this timer, we still have to trigger XBMC to update its timer list to
++ // remove the timer from the XBMC list
++ PVR->TriggerTimerUpdate();
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++PVR_ERROR cPVRClientMediaPortal::UpdateTimer(const PVR_TIMER &timerinfo)
++{
++ string result;
++
++#ifdef _TIME32_T_DEFINED
++ XBMC->Log(LOG_DEBUG, "->UpdateTimer Index: %i Channel: %i, starttime: %i endtime: %i program: %s", timerinfo.iClientIndex, timerinfo.iClientChannelUid, timerinfo.startTime, timerinfo.endTime, timerinfo.strTitle);
++#else
++ XBMC->Log(LOG_DEBUG, "->UpdateTimer Channel: %i, 64 bit times not yet supported!", timerinfo.iClientChannelUid);
++#endif
++
++ if (!IsUp())
++ return PVR_ERROR_SERVER_ERROR;
++
++ cTimer timer(timerinfo);
++
++ result = SendCommand(timer.UpdateScheduleCommand());
++ if(result.find("True") == string::npos)
++ {
++ XBMC->Log(LOG_DEBUG, "UpdateTimer for channel: %i [failed]", timerinfo.iClientChannelUid);
++ return PVR_ERROR_NOT_SAVED;
++ }
++ XBMC->Log(LOG_DEBUG, "UpdateTimer for channel: %i [done]", timerinfo.iClientChannelUid);
++
++ // Although XBMC changes this timer, we still have to trigger XBMC to update its timer list to
++ // see the timer changes at the XBMC side
++ PVR->TriggerTimerUpdate();
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++
++/************************************************************/
++/** Live stream handling */
++
++// The MediaPortal TV Server uses rtsp streams which XBMC can handle directly
++// via the dvdplayer (ffmpeg) so we don't need to open the streams in this
++// pvr addon.
++// However, we still need to request the stream URL for the channel we want
++// to watch as it is not known on beforehand.
++// Most of the times it is the same URL for each selected channel. Only the
++// stream itself changes. Example URL: rtsp://tvserverhost/stream2.0
++// The number 2.0 may change when the tvserver is streaming multiple tv channels
++// at the same time.
++bool cPVRClientMediaPortal::OpenLiveStream(const PVR_CHANNEL &channelinfo)
++{
++ string result;
++ char command[256] = "";
++ const char* sResolveRTSPHostname = booltostring(g_bResolveRTSPHostname);
++ vector<string> timeshiftfields;
++
++ XBMC->Log(LOG_DEBUG, "->OpenLiveStream(uid=%i)", channelinfo.iUniqueId);
++ if (!IsUp())
++ {
++ m_iCurrentChannel = -1;
++ return false;
++ }
++
++ if (((int)channelinfo.iUniqueId) == m_iCurrentChannel)
++ return true;
++ else
++ m_iCurrentChannel = -1; // make sure that it is not a valid channel nr in case it will fail lateron
++
++ // Start the timeshift
++ if (g_iTVServerXBMCBuild>=90)
++ {
++ // Use the optimized TimeshiftChannel call (don't stop a running timeshift)
++ snprintf(command, 256, "TimeshiftChannel:%i|%s|False\n", channelinfo.iUniqueId, sResolveRTSPHostname);
++ }
++ else
++ {
++ // Closing existing timeshift streams will be done in the MediaPortal TV
++ // Server plugin, so we can request the new channel stream directly without
++ // stopping the existing stream
++ snprintf(command, 256, "TimeshiftChannel:%i|%s\n", channelinfo.iUniqueId, sResolveRTSPHostname);
++ }
++ result = SendCommand(command);
++
++ if (result.find("ERROR") != std::string::npos || result.length() == 0)
++ {
++ XBMC->Log(LOG_ERROR, "Could not start the timeshift for channel uid=%i. %s", channelinfo.iUniqueId, result.c_str());
++ if (g_iTVServerXBMCBuild>=109)
++ {
++ int tvresult;
++
++ Tokenize(result, timeshiftfields, "|");
++ //[0] = string error message
++ //[1] = TvResult (optional field. SendCommand can also return a timeout)
++
++ if(timeshiftfields.size()>1)
++ {
++ //For TVServer 1.2.1:
++ //enum TvResult
++ //{
++ // Succeeded = 0, (this is not an error)
++ // AllCardsBusy = 1,
++ // ChannelIsScrambled = 2,
++ // NoVideoAudioDetected = 3,
++ // NoSignalDetected = 4,
++ // UnknownError = 5,
++ // UnableToStartGraph = 6,
++ // UnknownChannel = 7,
++ // NoTuningDetails = 8,
++ // ChannelNotMappedToAnyCard = 9,
++ // CardIsDisabled = 10,
++ // ConnectionToSlaveFailed = 11,
++ // NotTheOwner = 12,
++ // GraphBuildingFailed = 13,
++ // SWEncoderMissing = 14,
++ // NoFreeDiskSpace = 15,
++ // NoPmtFound = 16,
++ //};
++
++ tvresult = atoi(timeshiftfields[1].c_str());
++ // Display one of the localized error messages 30060-30075
++ XBMC->QueueNotification(QUEUE_ERROR, XBMC->GetLocalizedString(30059 + (int) tvresult));
++ }
++ else
++ {
++ XBMC->QueueNotification(QUEUE_ERROR, result.c_str());
++ }
++ }
++ else
++ {
++ if (result.find("[ERROR]: TVServer answer: ") != std::string::npos)
++ {
++ //Skip first part: "[ERROR]: TVServer answer: "
++ XBMC->QueueNotification(QUEUE_ERROR, "TVServer: %s", result.substr(26).c_str());
++ }
++ else
++ {
++ //Skip first part: "[ERROR]: "
++ XBMC->QueueNotification(QUEUE_ERROR, result.substr(7).c_str());
++ }
++ }
++ m_iCurrentChannel = -1;
++ return false;
++ }
++ else
++ {
++ Tokenize(result, timeshiftfields, "|");
++
++ //[0] = rtsp url
++ //[1] = original (unresolved) rtsp url
++ //[2] = timeshift buffer filename
++ //[3] = card id (TVServerXBMC build >= 106)
++ //[4] = tsbuffer pos (TVServerXBMC build >= 110)
++ //[5] = tsbuffer file nr (TVServerXBMC build >= 110)
++
++ m_PlaybackURL = timeshiftfields[0];
++ XBMC->Log(LOG_INFO, "Channel stream URL: %s, timeshift buffer: %s", m_PlaybackURL.c_str(), timeshiftfields[2].c_str());
++
++ if (g_iSleepOnRTSPurl > 0)
++ {
++ XBMC->Log(LOG_DEBUG, "Sleeping %i ms before opening stream: %s", g_iSleepOnRTSPurl, timeshiftfields[0].c_str());
++ usleep(g_iSleepOnRTSPurl * 1000);
++ }
++
++ // Check the returned stream URL. When the URL is an rtsp stream, we need
++ // to close it again after watching to stop the timeshift.
++ // A radio web stream (added to the TV Server) will return the web stream
++ // URL without starting a timeshift.
++ if(timeshiftfields[0].compare(0,4, "rtsp") == 0)
++ {
++ m_bTimeShiftStarted = true;
++ }
++
++ // at this point everything is ready for playback
++ m_iCurrentChannel = (int) channelinfo.iUniqueId;
++ if (g_iTVServerXBMCBuild>=106)
++ {
++ m_iCurrentCard = atoi(timeshiftfields[3].c_str());
++ }
++ }
++ return true;
++}
++
++int cPVRClientMediaPortal::ReadLiveStream(unsigned char *pBuffer, unsigned int iBufferSize)
++{
++ return 0;
++}
++
++void cPVRClientMediaPortal::CloseLiveStream(void)
++{
++ string result;
++
++ if (!IsUp())
++ return;
++
++ if (m_bTimeShiftStarted)
++ {
++ result = SendCommand("StopTimeshift:\n");
++ XBMC->Log(LOG_INFO, "CloseLiveStream: %s", result.c_str());
++ m_bTimeShiftStarted = false;
++ m_iCurrentChannel = -1;
++ m_iCurrentCard = 0;
++ }
++ else
++ {
++ XBMC->Log(LOG_DEBUG, "CloseLiveStream: Nothing to do.");
++ }
++}
++
++
++bool cPVRClientMediaPortal::SwitchChannel(const PVR_CHANNEL &channel)
++{
++ if (((int)channel.iUniqueId) == m_iCurrentChannel)
++ return true;
++
++ XBMC->Log(LOG_DEBUG, "SwitchChannel(uid=%i) ffmpeg rtsp: nothing to be done here... GetLiveSteamURL() should fetch a new rtsp url from the backend.", channel.iUniqueId);
++
++ return false;
++}
++
++
++int cPVRClientMediaPortal::GetCurrentClientChannel()
++{
++ XBMC->Log(LOG_DEBUG, "GetCurrentClientChannel: uid=%i", m_iCurrentChannel);
++ return m_iCurrentChannel;
++}
++
++PVR_ERROR cPVRClientMediaPortal::SignalStatus(PVR_SIGNAL_STATUS &signalStatus)
++{
++ if (g_iTVServerXBMCBuild < 108 || (m_iCurrentChannel == -1))
++ {
++ // Not yet supported or playing webstream
++ return PVR_ERROR_NO_ERROR;
++ }
++
++ vector<string> lines;
++ string result;
++
++ result = SendCommand("GetSignalQuality\n");
++
++ if (result.length() > 0)
++ {
++ int signallevel = 0;
++ int signalquality = 0;
++
++ if (sscanf(result.c_str(),"%i|%i", &signallevel, &signalquality) == 2)
++ {
++ signalStatus.iSignal = (int) (signallevel * 655.35); // 100% is 0xFFFF 65535
++ signalStatus.iSNR = (int) (signalquality * 655.35); // 100% is 0xFFFF 65535
++ signalStatus.iBER = 0;
++ strncpy(signalStatus.strAdapterStatus, "timeshifting", 1023); // hardcoded for now...
++ // TODO: fetch the name of the correct card and not just the first one...
++ strncpy(signalStatus.strAdapterName, m_cCards[m_iCurrentCard].Name.c_str(), 1023); //Size buffer is 1024 in xbmc_pvr_types.h
++ }
++ }
++ return PVR_ERROR_NO_ERROR;
++}
++
++
++/************************************************************/
++/** Record stream handling */
++// MediaPortal recordings are also rtsp streams. Main difference here with
++// respect to the live tv streams is that the URLs for the recordings
++// can be requested on beforehand (done in the TVserverXBMC plugin).
++// These URLs are stored in the field PVR_RECORDINGINFO_OLD.stream_url
++bool cPVRClientMediaPortal::OpenRecordedStream(const PVR_RECORDING &recording)
++{
++ XBMC->Log(LOG_DEBUG, "->OpenRecordedStream(index=%s)", recording.strRecordingId);
++ if (!IsUp())
++ return false;
++
++ return false;
++}
++
++void cPVRClientMediaPortal::CloseRecordedStream(void)
++{
++ string result;
++
++ if (!IsUp())
++ return;
++
++}
++
++int cPVRClientMediaPortal::ReadRecordedStream(unsigned char *pBuffer, unsigned int iBufferSize)
++{
++ return -1;
++}
++
++/*
++ * \brief Request the stream URL for live tv/live radio.
++ * The MediaPortal TV Server will try to open the requested channel for
++ * time-shifting and when successful it will start an rtsp:// stream for this
++ * channel and return the URL for this stream.
++ */
++const char* cPVRClientMediaPortal::GetLiveStreamURL(const PVR_CHANNEL &channelinfo)
++{
++ string result;
++
++ XBMC->Log(LOG_DEBUG, "->GetLiveStreamURL(uid=%i)", channelinfo.iUniqueId);
++
++ if (!OpenLiveStream(channelinfo))
++ {
++ return "";
++ }
++ else
++ {
++ return m_PlaybackURL.c_str();
++ }
++}
+diff --git a/xbmc/pvrclients/MediaPortal/pvrclient-mediaportal.h b/xbmc/pvrclients/MediaPortal/pvrclient-mediaportal.h
+new file mode 100644
+index 0000000..d251192
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/pvrclient-mediaportal.h
+@@ -0,0 +1,124 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2011 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 this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ */
++
++#include <vector>
++
++/* Master defines for client control */
++#include "../../addons/include/xbmc_pvr_types.h"
++
++/* Local includes */
++#include "Socket.h"
++#include "Cards.h"
++#include "epg.h"
++#include "platform/threads/mutex.h"
++
++class cPVRClientMediaPortal
++{
++public:
++ /* Class interface */
++ cPVRClientMediaPortal();
++ ~cPVRClientMediaPortal();
++
++ /* VTP Listening Thread */
++ static void* Process(void*);
++
++ /* Server handling */
++ bool Connect();
++ void Disconnect();
++ bool IsUp();
++
++ /* General handling */
++ const char* GetBackendName(void);
++ const char* GetBackendVersion(void);
++ const char* GetConnectionString(void);
++ PVR_ERROR GetDriveSpace(long long *iTotal, long long *iUsed);
++ PVR_ERROR GetBackendTime(time_t *localTime, int *gmtOffset);
++
++ /* EPG handling */
++ PVR_ERROR GetEpg(PVR_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart = NULL, time_t iEnd = NULL);
++
++ /* Channel handling */
++ int GetNumChannels(void);
++ PVR_ERROR GetChannels(PVR_HANDLE handle, bool bRadio);
++
++ /* Channel group handling */
++ int GetChannelGroupsAmount(void);
++ PVR_ERROR GetChannelGroups(PVR_HANDLE handle, bool bRadio);
++ PVR_ERROR GetChannelGroupMembers(PVR_HANDLE handle, const PVR_CHANNEL_GROUP &group);
++
++ /* Record handling **/
++ int GetNumRecordings(void);
++ PVR_ERROR GetRecordings(PVR_HANDLE handle);
++ PVR_ERROR DeleteRecording(const PVR_RECORDING &recording);
++ PVR_ERROR RenameRecording(const PVR_RECORDING &recording);
++
++ /* Timer handling */
++ int GetNumTimers(void);
++ PVR_ERROR GetTimers(PVR_HANDLE handle);
++ PVR_ERROR GetTimerInfo(unsigned int timernumber, PVR_TIMER &timer);
++ PVR_ERROR AddTimer(const PVR_TIMER &timer);
++ PVR_ERROR DeleteTimer(const PVR_TIMER &timer, bool bForceDelete = false);
++ PVR_ERROR UpdateTimer(const PVR_TIMER &timer);
++
++ /* Live stream handling */
++ bool OpenLiveStream(const PVR_CHANNEL &channel);
++ void CloseLiveStream();
++ int ReadLiveStream(unsigned char *pBuffer, unsigned int iBufferSize);
++ int GetCurrentClientChannel();
++ bool SwitchChannel(const PVR_CHANNEL &channel);
++ PVR_ERROR SignalStatus(PVR_SIGNAL_STATUS &signalStatus);
++ const char* GetLiveStreamURL(const PVR_CHANNEL &channel);
++
++ /* Record stream handling */
++ bool OpenRecordedStream(const PVR_RECORDING &recording);
++ void CloseRecordedStream(void);
++ int ReadRecordedStream(unsigned char *pBuffer, unsigned int iBufferSize);
++ long long SeekRecordedStream(long long iPosition, int iWhence = SEEK_SET);
++ long long LengthRecordedStream(void);
++
++protected:
++ MPTV::Socket *m_tcpclient;
++
++private:
++ bool GetChannel(unsigned int number, PVR_CHANNEL &channeldata);
++ bool LoadGenreXML(const std::string &filename);
++
++ int m_iCurrentChannel;
++ int m_iCurrentCard;
++ bool m_bConnected;
++ bool m_bStop;
++ bool m_bTimeShiftStarted;
++ std::string m_ConnectionString;
++ std::string m_PlaybackURL;
++ std::string m_BackendName;
++ std::string m_BackendVersion;
++ time_t m_BackendUTCoffset;
++ time_t m_BackendTime;
++ CCards m_cCards;
++ CGenreTable* m_genretable;
++ PLATFORM::CMutex m_mutex;
++ int64_t m_iLastRecordingUpdate;
++
++ void Close();
++
++ //Used for TV Server communication:
++ std::string SendCommand(std::string command);
++ bool SendCommand2(std::string command, int& code, std::vector<std::string>& lines);
++};
+diff --git a/xbmc/pvrclients/MediaPortal/recordings.cpp b/xbmc/pvrclients/MediaPortal/recordings.cpp
+new file mode 100644
+index 0000000..857cf9f
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/recordings.cpp
+@@ -0,0 +1,289 @@
++/*
++ * Copyright (C) 2005-2011 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 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ */
++
++#include <vector>
++#include <stdio.h>
++
++using namespace std;
++
++#include "recordings.h"
++#include "utils.h"
++#include "timers.h"
++#include "client.h"
++
++using namespace ADDON;
++
++cRecording::cRecording()
++{
++ m_StartTime = 0;
++ m_Duration = 0;
++ m_Index = -1;
++ m_cardSettings = NULL;
++ m_channelID = 0;
++ m_isRecording = false;
++ m_genre_type = 0;
++ m_genre_subtype = 0;
++ m_genretable = NULL;
++}
++
++
++cRecording::~cRecording()
++{
++}
++
++void cRecording::SetCardSettings(CCards* cardSettings)
++{
++ m_cardSettings = cardSettings;
++}
++
++bool cRecording::ParseLine(const std::string& data)
++{
++ time_t endtime;
++ string filePath;
++
++ vector<string> fields;
++
++ Tokenize(data, fields, "|");
++
++ if( fields.size() >= 9 )
++ {
++ //[0] index / mediaportal recording id
++ //[1] start time
++ //[2] end time
++ //[3] channel name
++ //[4] title
++ //[5] description
++ //[6] stream_url (resolved hostname if requested)
++ //[7] filename (we can bypass rtsp streaming when XBMC and the TV server are on the same machine)
++ //[8] keepUntilDate (DateTime)
++ //[9] (optional) original stream_url when resolve hostnames is enabled
++ //[10] keepUntil (int)
++ //[11] episodeName (string)
++ //[12] episodeNumber (string)
++ //[13] episodePart (string)
++ //[14] seriesNumber (string)
++ //[15] scheduleID (int)
++ //[16] genre (string)
++ //[17] idchannel (int)
++ //[18] isrecording (bool)
++
++ m_Index = atoi(fields[0].c_str());
++ m_StartTime = DateTimeToTimeT(fields[1]);
++
++ if (m_StartTime < 0)
++ {
++ XBMC->Log(LOG_ERROR, "%s: Unable to convert start time '%s' into date+time", __FUNCTION__, fields[1].c_str());
++ return false;
++ }
++
++ endtime = DateTimeToTimeT(fields[2]);
++
++ if (endtime < 0)
++ {
++ XBMC->Log(LOG_ERROR, "%s: Unable to convert end time '%s' into date+time", __FUNCTION__, fields[2].c_str());
++ return false;
++ }
++
++ m_Duration = endtime - m_StartTime;
++
++ m_channelName = fields[3];
++ m_title = fields[4];
++ m_description = fields[5];
++ m_stream = fields[6];
++ m_filePath = fields[7];
++
++ // TODO: fill lifetime with data from MP TV Server
++ // From the VDR documentation (VDR is used by Alwinus as basis for the XBMC
++ // PVR framework:
++ // "The lifetime (int) value corresponds to the the number of days (0..99)
++ // a recording made through this timer is guaranteed to remain on disk
++ // before it is automatically removed to free up space for a new recording.
++ // Note that setting this parameter to very high values for all recordings
++ // may soon fill up the entire disk and cause new recordings to fail due to
++ // low disk space. The special value 99 means that this recording will live
++ // forever, and a value of 0 means that this recording can be deleted any
++ // time if a recording with a higher priority needs disk space."
++ m_keepUntilDate = DateTimeToTimeT(fields[8]);
++
++ if (m_keepUntilDate < 0)
++ {
++ // invalid date (or outside time_t boundaries)
++ m_keepUntilDate = cUndefinedDate;
++ }
++
++ if( m_filePath.length() > 0 )
++ {
++ SplitFilePath();
++ }
++ else
++ {
++ m_basePath = "";
++ m_fileName = "";
++ m_directory = "";
++ }
++
++
++ if (fields.size() >= 10) // Since TVServerXBMC 1.0.8.0
++ {
++ m_originalurl = fields[9];
++ }
++ else
++ {
++ m_originalurl = fields[6];
++ }
++
++ if (fields.size() >= 16) // Since TVServerXBMC 1.1.x.105
++ {
++ m_keepUntil = atoi( fields[10].c_str() );
++ m_episodeName = fields[11];
++ m_episodeNumber = fields[12];
++ m_episodePart = fields[13];
++ m_seriesNumber = fields[14];
++ m_scheduleID = atoi( fields[15].c_str() );
++ }
++
++ if (fields.size() >= 19) // Since TVServerXBMC 1.2.x.111
++ {
++ m_genre = fields[16];
++ m_channelID = atoi( fields[17].c_str() );
++ m_isRecording = stringtobool( fields[18].c_str() );
++
++ if (m_genretable) m_genretable->GenreToTypes(m_genre, m_genre_type, m_genre_subtype);
++ }
++ return true;
++ }
++ else
++ {
++ return false;
++ }
++}
++
++
++void cRecording::SetDirectory( string& directory )
++{
++ CStdString tmp;
++ m_basePath = directory;
++ tmp = m_basePath + m_directory + "\\" + m_fileName;
++
++ if( m_basePath.find("smb://") != string::npos )
++ {
++ // Convert to XBMC network share...
++ tmp.Replace("\\","/");
++ }
++
++ m_filePath = tmp;
++}
++
++int cRecording::Lifetime(void) const
++{
++ // margro: the meaning of the XBMC-PVR Lifetime field is undocumented.
++ // Assuming that VDR is the source for this field:
++ // The guaranteed lifetime (in days) of a recording created by this
++ // timer. 0 means that this recording may be automatically deleted
++ // at any time by a new recording with higher priority. 99 means
++ // that this recording will never be automatically deleted. Any
++ // number in the range 1...98 means that this recording may not be
++ // automatically deleted in favour of a new recording, until the
++ // given number of days since the start time of the recording has
++ // passed by
++ KeepMethodType m_keepmethod = (KeepMethodType) m_keepUntil;
++
++ switch (m_keepmethod)
++ {
++ case UntilSpaceNeeded: //until space needed
++ case UntilWatched: //until watched
++ return 0;
++ break;
++ case UntilKeepDate: //until keepdate
++ {
++ double diffseconds = difftime(m_keepUntilDate, m_StartTime);
++ int daysremaining = (int)(diffseconds / cSecsInDay);
++ // Calculate value in the range 1...98, based on m_keepdate
++ if ((daysremaining < MAXLIFETIME) && (daysremaining >= 0))
++ {
++ return daysremaining;
++ }
++ else
++ {
++ // > 98 days => return forever
++ return MAXLIFETIME;
++ }
++ }
++ break;
++ case Forever: //forever
++ return MAXLIFETIME;
++ default:
++ return MAXLIFETIME;
++ }
++}
++
++void cRecording::SplitFilePath(void)
++{
++ size_t found = string::npos;
++
++ // Try to find the base path used for this recording by searching for the
++ // card recording folder name in the the recording file name.
++ if ((m_cardSettings) && (m_cardSettings->size() > 0))
++ {
++ for (CCards::iterator it = m_cardSettings->begin(); it < m_cardSettings->end(); it++)
++ {
++ // Determine whether the first part of the recording file name is shared with this card
++ // Minimal name length of the RecordingFolder should be 3 (drive letter + :\)
++ if (it->RecordingFolder.length() >= 3)
++ {
++ found = m_filePath.find(it->RecordingFolder);
++ if (found != string::npos)
++ {
++ m_basePath = it->RecordingFolder;
++ if (m_basePath.at(m_basePath.length() - 1) != '\\')
++ m_basePath += "\\";
++
++ // Remove the base path
++ m_fileName = m_filePath.substr(it->RecordingFolder.length()+1);
++
++ // Extract subdirectories below the base path
++ size_t found2 = m_fileName.find_last_of("/\\");
++ if (found2 != string::npos)
++ {
++ m_directory = m_fileName.substr(0, found2);
++ m_fileName = m_fileName.substr(found2+1);
++ }
++ else
++ {
++ m_directory = "";
++ }
++
++ break;
++ }
++ }
++ }
++ }
++
++ if (found == string::npos)
++ {
++ m_fileName = m_filePath;
++ m_directory = "";
++ m_basePath = "";
++ }
++}
++
++void cRecording::SetGenreTable(CGenreTable* genretable)
++{
++ m_genretable = genretable;
++}
+diff --git a/xbmc/pvrclients/MediaPortal/recordings.h b/xbmc/pvrclients/MediaPortal/recordings.h
+new file mode 100644
+index 0000000..e3958d2
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/recordings.h
+@@ -0,0 +1,128 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2011 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 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <stdlib.h>
++#include "libXBMC_addon.h"
++#include "libXBMC_pvr.h"
++#include "Cards.h"
++#include "GenreTable.h"
++
++using namespace std;
++
++#define DEFAULTFRAMESPERSECOND 25.0
++#define MAXPRIORITY 99
++#define MAXLIFETIME 99 //Based on VDR addon and VDR documentation. 99=Keep forever, 0=can be deleted at any time, 1..98=days to keep
++
++class cRecording
++{
++private:
++ int m_Index;
++ int m_channelID;
++ string m_channelName;
++ string m_filePath; ///< The full recording path as returned by the backend
++ string m_basePath; ///< The base path shared by all recordings (to be determined from the Card settings)
++ string m_directory; ///< An optional subdirectory below the basePath
++ string m_fileName; ///< The recording filename without path
++ string m_stream;
++ string m_originalurl;
++ time_t m_StartTime;
++ int m_Duration;
++ string m_title; // Title of this event
++ string m_description; // Description of this event
++ string m_episodeName; // Short description of this event (typically the episode name in case of a series)
++ string m_seriesNumber;
++ string m_episodeNumber;
++ string m_episodePart;
++ int m_scheduleID;
++ int m_keepUntil;
++ time_t m_keepUntilDate; ///< MediaPortal keepUntilDate
++ CCards* m_cardSettings; ///< Pointer to the MediaPortal card settings. Will be used to determine the base path of the recordings
++ string m_genre;
++ int m_genre_type;
++ int m_genre_subtype;
++ bool m_isRecording;
++ CGenreTable* m_genretable;
++
++public:
++ cRecording();
++ virtual ~cRecording();
++
++ bool ParseLine(const std::string& data);
++ const char *ChannelName(void) const { return m_channelName.c_str(); }
++ int Index(void) const { return m_Index; }
++ time_t StartTime(void) const { return m_StartTime; }
++ time_t Duration(void) const { return m_Duration; }
++ const char *Title(void) const { return m_title.c_str(); }
++ const char *Description(void) const { return m_description.c_str(); }
++ const char *EpisodeName(void) const { return m_episodeName.c_str(); }
++ const char *SeriesNumber(void) const { return m_seriesNumber.c_str(); }
++ const char *EpisodeNumber(void) const { return m_episodeNumber.c_str(); }
++ const char *EpisodePart(void) const { return m_episodePart.c_str(); }
++ int ScheduleID(void) const { return m_scheduleID; }
++ int Lifetime(void) const;
++
++ /**
++ * \brief Filename of this recording with full path (at server side)
++ */
++ const char *FilePath(void) const { return m_filePath.c_str(); }
++
++ /**
++ * \brief Filename of this recording without full path
++ * \return Filename
++ */
++ const char *FileName(void) const { return m_fileName.c_str(); }
++
++ /**
++ * \brief Directory where this recording is stored (at server side)
++ * \return Filename
++ */
++ const char *Directory(void) const { return m_directory.c_str(); }
++
++ /**
++ * \brief Override the directory where this recording is stored
++ */
++ void SetDirectory( string& directory );
++
++ /**
++ * \brief The RTSP stream URL for this recording (hostname resolved to IP-address)
++ * \return Stream URL
++ */
++ const char *Stream(void) const { return m_stream.c_str(); }
++
++ /**
++ * \brief The RTSP stream URL for this recording (unresolved hostname)
++ * \return Stream URL
++ */
++ const char *OrignalURL(void) const { return m_originalurl.c_str(); }
++
++ /**
++ * \brief Pass a pointer to the MediaPortal card settings to this class
++ * \param the cardSettings
++ */
++ void SetCardSettings(CCards* cardSettings);
++
++ /**
++ * \brief Parse Recording file path and divide it in 3 parts: base path, subdirectory and filename;
++ */
++ void SplitFilePath(void);
++
++ int GenreType(void) const { return m_genre_type; }
++ int GenreSubType(void) const { return m_genre_subtype; }
++ void SetGenreTable(CGenreTable* genremap);
++};
+diff --git a/xbmc/pvrclients/MediaPortal/timers.cpp b/xbmc/pvrclients/MediaPortal/timers.cpp
+new file mode 100644
+index 0000000..8b13810
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/timers.cpp
+@@ -0,0 +1,506 @@
++/*
++ * Copyright (C) 2005-2011 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 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ */
++
++#include <vector>
++#include <stdio.h>
++#include <stdlib.h>
++
++using namespace std;
++
++#include "os-dependent.h" //needed for snprintf
++#include "client.h"
++#include "timers.h"
++#include "utils.h"
++
++using namespace ADDON;
++
++cTimer::cTimer()
++{
++ m_index = -1;
++ m_active = true;
++ m_channel = 0;
++ m_schedtype = Once;
++ m_starttime = 0;
++ m_endtime = 0;
++ m_priority = 0;
++ m_keepmethod = UntilSpaceNeeded;
++ m_keepdate = cUndefinedDate;
++ m_prerecordinterval = -1; // Use MediaPortal setting instead
++ m_postrecordinterval = -1; // Use MediaPortal setting instead
++ m_canceled = cUndefinedDate;
++ m_series = false;
++}
++
++cTimer::cTimer(const PVR_TIMER& timerinfo)
++{
++ m_index = timerinfo.iClientIndex;
++ m_active = (timerinfo.state == PVR_TIMER_STATE_SCHEDULED || timerinfo.state == PVR_TIMER_STATE_RECORDING);
++ if(!m_active)
++ {
++ time(&m_canceled);
++ }
++ else
++ {
++ // Don't know when it was cancelled, so assume that it was canceled now...
++ // backend (TVServerXBMC) will only update the canceled date time when
++ // this schedule was just canceled
++ m_canceled = cUndefinedDate;
++ }
++
++ m_title = timerinfo.strTitle;
++ m_directory = timerinfo.strDirectory;
++ m_channel = timerinfo.iClientChannelUid;
++ m_starttime = timerinfo.startTime;
++ m_endtime = timerinfo.endTime;
++ //m_firstday = timerinfo.firstday;
++ m_isrecording = timerinfo.state == PVR_TIMER_STATE_RECORDING;
++ m_priority = XBMC2MepoPriority(timerinfo.iPriority);
++
++ SetKeepMethod(timerinfo.iLifetime);
++ if(timerinfo.bIsRepeating)
++ {
++ m_schedtype = RepeatFlags2SchedRecType(timerinfo.iWeekdays);
++ } else {
++ m_schedtype = Once;
++ }
++
++ m_prerecordinterval = timerinfo.iMarginStart;
++ m_postrecordinterval = timerinfo.iMarginEnd;
++}
++
++
++cTimer::~cTimer()
++{
++}
++
++/**
++ * @brief Fills the PVR_TIMER struct with information from this timer
++ * @param tag A reference to the PVR_TIMER struct
++ */
++void cTimer::GetPVRtimerinfo(PVR_TIMER &tag)
++{
++ tag.iClientIndex = m_index;
++ if (m_active)
++ tag.state = PVR_TIMER_STATE_SCHEDULED;
++ else if (IsRecording())
++ tag.state = PVR_TIMER_STATE_RECORDING;
++ else
++ tag.state = PVR_TIMER_STATE_CANCELLED;
++ tag.iClientChannelUid = m_channel;
++ tag.strTitle = m_title.c_str();
++ tag.strDirectory = m_directory.c_str();
++ tag.startTime = m_starttime ;
++ tag.endTime = m_endtime ;
++ // From the VDR manual
++ // firstday: The date of the first day when this timer shall start recording
++ // (only available for repeating timers).
++ if(Repeat())
++ {
++ tag.firstDay = m_starttime;
++ } else {
++ tag.firstDay = 0;
++ }
++ tag.iPriority = Priority();
++ tag.iLifetime = GetLifetime();
++ tag.bIsRepeating = Repeat();
++ tag.iWeekdays = RepeatFlags();
++ tag.iMarginStart = m_prerecordinterval * 60;
++ tag.iMarginEnd = m_postrecordinterval * 60;
++ tag.iGenreType = 0;
++ tag.iGenreSubType = 0;
++}
++
++time_t cTimer::StartTime(void) const
++{
++ return m_starttime;
++}
++
++time_t cTimer::EndTime(void) const
++{
++ return m_endtime;
++}
++
++bool cTimer::ParseLine(const char *s)
++{
++ vector<string> schedulefields;
++ string data = s;
++ uri::decode(data);
++
++ Tokenize(data, schedulefields, "|");
++
++ if(schedulefields.size() >= 10)
++ {
++ // field 0 = index
++ // field 1 = start date + time
++ // field 2 = end date + time
++ // field 3 = channel nr
++ // field 4 = channel name
++ // field 5 = program name
++ // field 6 = schedule recording type
++ // field 7 = priority
++ // field 8 = isdone (finished)
++ // field 9 = ismanual
++ // field 10 = directory
++ // field 11 = keepmethod (0=until space needed, 1=until watched, 2=until keepdate, 3=forever) (TVServerXBMC build >= 100)
++ // field 12 = keepdate (2000-01-01 00:00:00 = infinite) (TVServerXBMC build >= 100)
++ // field 13 = preRecordInterval (TVServerXBMC build >= 100)
++ // field 14 = postRecordInterval (TVServerXBMC build >= 100)
++ // field 15 = canceled (TVServerXBMC build >= 100)
++ // field 16 = series (True/False) (TVServerXBMC build >= 100)
++ // field 17 = isrecording (True/False)
++
++ m_index = atoi(schedulefields[0].c_str());
++ m_starttime = DateTimeToTimeT(schedulefields[1]);
++
++ if( m_starttime < 0)
++ return false;
++
++ m_endtime = DateTimeToTimeT(schedulefields[2]);
++
++ if( m_endtime < 0)
++ return false;
++
++ m_channel = atoi(schedulefields[3].c_str());
++ m_title = schedulefields[5];
++
++ m_schedtype = (ScheduleRecordingType) atoi(schedulefields[6].c_str());
++
++ m_priority = atoi(schedulefields[7].c_str());
++ m_done = stringtobool(schedulefields[8]);
++ m_ismanual = stringtobool(schedulefields[9]);
++ m_directory = schedulefields[10];
++
++ if(schedulefields.size() >= 18)
++ {
++ //TVServerXBMC build >= 100
++ m_keepmethod = (KeepMethodType) atoi(schedulefields[11].c_str());
++ m_keepdate = DateTimeToTimeT(schedulefields[12]);
++
++ if( m_keepdate < 0)
++ return false;
++
++ m_prerecordinterval = atoi(schedulefields[13].c_str());
++ m_postrecordinterval = atoi(schedulefields[14].c_str());
++
++ // The DateTime value 2000-01-01 00:00:00 means: active in MediaPortal
++ if(schedulefields[15].compare("2000-01-01 00:00:00Z")==0)
++ {
++ m_canceled = cUndefinedDate;
++ m_active = true;
++ }
++ else
++ {
++ m_canceled = DateTimeToTimeT(schedulefields[15]);
++ m_active = false;
++ }
++
++ m_series = stringtobool(schedulefields[16]);
++ m_isrecording = stringtobool(schedulefields[17]);
++
++ }
++ else
++ {
++ m_keepmethod = UntilSpaceNeeded;
++ m_keepdate = cUndefinedDate;
++ m_prerecordinterval = -1;
++ m_postrecordinterval = -1;
++ m_canceled = cUndefinedDate;
++ m_active = true;
++ m_series = false;
++ m_isrecording = false;
++ }
++
++ return true;
++ }
++ return false;
++}
++
++int cTimer::SchedRecType2RepeatFlags(ScheduleRecordingType schedtype)
++{
++ // margro: the meaning of the XBMC-PVR Weekdays field is undocumented.
++ // Assuming that VDR is the source for this field:
++ // This field contains a bitmask that correcsponds to the days of the week at which this timer runs
++ // It is based on the VDR Day field format "MTWTF--"
++ // The format is a 1 bit for every enabled day and a 0 bit for a disabled day
++ // Thus: WeekDays = "0000 0001" = "M------" (monday only)
++ // "0110 0000" = "-----SS" (saturday and sunday)
++ // "0001 1111" = "MTWTF--" (all weekdays)
++
++ int weekdays = 0;
++
++ switch (schedtype)
++ {
++ case Once:
++ weekdays = 0;
++ break;
++ case Daily:
++ weekdays = 127; // 0111 1111
++ break;
++ case Weekly:
++ {
++ // Not sure what to do with this MediaPortal option...
++ // Assumption: record once a week, on the same day and time
++ // => determine weekday and set the corresponding bit
++ struct tm timeinfo;
++
++ timeinfo = *localtime( &m_starttime );
++
++ int weekday = timeinfo.tm_wday; //days since Sunday [0-6]
++ // bit 0 = monday, need to convert weekday value to bitnumber:
++ if (weekday == 0)
++ weekday = 6; // sunday 0100 0000
++ else
++ weekday--;
++
++ weekdays = 1 << weekday;
++ break;
++ }
++ case EveryTimeOnThisChannel:
++ // Don't know what to do with this MediaPortal option?
++ break;
++ case EveryTimeOnEveryChannel:
++ // Don't know what to do with this MediaPortal option?
++ break;
++ case Weekends:
++ weekdays = 96; // 0110 0000
++ break;
++ case WorkingDays:
++ weekdays = 31; // 0001 1111
++ default:
++ weekdays=0;
++ }
++
++ return weekdays;
++}
++
++ScheduleRecordingType cTimer::RepeatFlags2SchedRecType(int repeatflags)
++{
++ // margro: the meaning of the XBMC-PVR Weekdays field is undocumented.
++ // Assuming that VDR is the source for this field:
++ // This field contains a bitmask that correcsponds to the days of the week at which this timer runs
++ // It is based on the VDR Day field format "MTWTF--"
++ // The format is a 1 bit for every enabled day and a 0 bit for a disabled day
++ // Thus: WeekDays = "0000 0001" = "M------" (monday only)
++ // "0110 0000" = "-----SS" (saturday and sunday)
++ // "0001 1111" = "MTWTF--" (all weekdays)
++
++ switch (repeatflags)
++ {
++ case 0:
++ return Once;
++ break;
++ case 1: //Monday
++ case 2: //Tuesday
++ case 4: //Wednesday
++ case 8: //Thursday
++ case 16: //Friday
++ case 32: //Saturday
++ case 64: //Sunday
++ return Weekly;
++ break;
++ case 31: // 0001 1111
++ return WorkingDays;
++ case 96: // 0110 0000
++ return Weekends;
++ break;
++ case 127: // 0111 1111
++ return Daily;
++ break;
++ default:
++ return Once;
++ }
++
++ return Once;
++}
++
++std::string cTimer::AddScheduleCommand()
++{
++ char command[1024];
++ struct tm starttime;
++ struct tm endtime;
++ struct tm keepdate;
++
++ starttime = *localtime( &m_starttime );
++ XBMC->Log(LOG_DEBUG, "Start time: %s, marginstart: %i min earlier", asctime(&starttime), m_prerecordinterval);
++ endtime = *localtime( &m_endtime );
++ XBMC->Log(LOG_DEBUG, "End time: %s, marginstop: %i min later", asctime(&endtime), m_postrecordinterval);
++ keepdate = *localtime( &m_keepdate );
++
++ if ( g_iTVServerXBMCBuild >= 100)
++ {
++ // Sending separate marginStart, marginStop and schedType is supported
++ snprintf(command, 1023, "AddSchedule:%i|%s|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i\n",
++ m_channel, //Channel number [0]
++ uri::encode(uri::PATH_TRAITS, m_title).c_str(), //Program title [1]
++ starttime.tm_year + 1900, starttime.tm_mon + 1, starttime.tm_mday, //Start date [2..4]
++ starttime.tm_hour, starttime.tm_min, starttime.tm_sec, //Start time [5..7]
++ endtime.tm_year + 1900, endtime.tm_mon + 1, endtime.tm_mday, //End date [8..10]
++ endtime.tm_hour, endtime.tm_min, endtime.tm_sec, //End time [11..13]
++ (int) m_schedtype, m_priority, (int) m_keepmethod, //SchedType, Priority, keepMethod [14..16]
++ keepdate.tm_year + 1900, keepdate.tm_mon + 1, keepdate.tm_mday, //Keepdate [17..19]
++ keepdate.tm_hour, keepdate.tm_min, keepdate.tm_sec, //Keeptime [20..22]
++ m_prerecordinterval, m_postrecordinterval); //Prerecord,postrecord [23,24]
++ }
++ else
++ {
++ // Sending a separate marginStart, marginStop and schedType is not yet supported
++ snprintf(command, 1023, "AddSchedule:%i|%s|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i\n",
++ m_channel, //Channel number
++ uri::encode(uri::PATH_TRAITS, m_title).c_str(), //Program title
++ starttime.tm_year + 1900, starttime.tm_mon + 1, starttime.tm_mday, //Start date
++ starttime.tm_hour, starttime.tm_min, starttime.tm_sec, //Start time
++ endtime.tm_year + 1900, endtime.tm_mon + 1, endtime.tm_mday, //End date
++ endtime.tm_hour, endtime.tm_min, endtime.tm_sec); //End time
++ }
++
++ return command;
++}
++
++std::string cTimer::UpdateScheduleCommand()
++{
++ char command[1024];
++ struct tm starttime;
++ struct tm endtime;
++ struct tm keepdate;
++
++ starttime = *localtime( &m_starttime );
++ XBMC->Log(LOG_DEBUG, "Start time: %s, marginstart: %i min earlier", asctime(&starttime), m_prerecordinterval);
++ endtime = *localtime( &m_endtime );
++ XBMC->Log(LOG_DEBUG, "End time: %s, marginstop: %i min later", asctime(&endtime), m_postrecordinterval);
++ keepdate = *localtime( &m_keepdate );
++
++ if ( g_iTVServerXBMCBuild >= 100)
++ {
++ // Sending separate marginStart, marginStop and schedType is supported
++ snprintf(command, 1024, "UpdateSchedule:%i|%i|%i|%s|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i\n",
++ m_index, //Schedule index [0]
++ m_active, //Active [1]
++ m_channel, //Channel number [2]
++ uri::encode(uri::PATH_TRAITS,m_title).c_str(), //Program title [3]
++ starttime.tm_year + 1900, starttime.tm_mon + 1, starttime.tm_mday, //Start date [4..6]
++ starttime.tm_hour, starttime.tm_min, starttime.tm_sec, //Start time [7..9]
++ endtime.tm_year + 1900, endtime.tm_mon + 1, endtime.tm_mday, //End date [10..12]
++ endtime.tm_hour, endtime.tm_min, endtime.tm_sec, //End time [13..15]
++ (int) m_schedtype, m_priority, (int) m_keepmethod, //SchedType, Priority, keepMethod [16..18]
++ keepdate.tm_year + 1900, keepdate.tm_mon + 1, keepdate.tm_mday, //Keepdate [19..21]
++ keepdate.tm_hour, keepdate.tm_min, keepdate.tm_sec, //Keeptime [22..24]
++ m_prerecordinterval, m_postrecordinterval); //Prerecord,postrecord [25,26]
++ }
++ else
++ {
++ snprintf(command, 1024, "UpdateSchedule:%i|%i|%i|%s|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i\n",
++ m_index, //Schedule index
++ m_active, //Active
++ m_channel, //Channel number
++ uri::encode(uri::PATH_TRAITS,m_title).c_str(), //Program title
++ starttime.tm_year + 1900, starttime.tm_mon + 1, starttime.tm_mday, //Start date
++ starttime.tm_hour, starttime.tm_min, starttime.tm_sec, //Start time
++ endtime.tm_year + 1900, endtime.tm_mon + 1, endtime.tm_mday, //End date
++ endtime.tm_hour, endtime.tm_min, endtime.tm_sec); //End time
++ }
++
++ return command;
++}
++
++
++int cTimer::XBMC2MepoPriority(int xbmcprio)
++{
++ // From XBMC side: 0.99 where 0=lowest and 99=highest priority (like VDR). Default value: 50
++ // Meaning of the MediaPortal field is unknown to me. Default seems to be 0.
++ // TODO: figure out the mapping
++ return 0;
++}
++
++int cTimer::Mepo2XBMCPriority(int mepoprio)
++{
++ return 50; //Default value
++}
++
++
++/*
++ * @brief Convert a XBMC Lifetime value to MediaPortals keepMethod+keepDate settings
++ * @param lifetime the XBMC lifetime value (in days) (following the VDR syntax)
++ * Should be called after setting m_starttime !!
++ */
++void cTimer::SetKeepMethod(int lifetime)
++{
++ // XBMC follows the VDR definition of lifetime
++ // XBMC: 0 means that this recording may be automatically deleted
++ // at any time by a new recording with higher priority
++ // 1-98 means that this recording may not be automatically deleted
++ // in favour of a new recording, until the given number of days
++ // since the start time of the recording has passed by
++ // 99 means that this recording will never be automatically deleted
++ if (lifetime == 0)
++ {
++ m_keepmethod = UntilSpaceNeeded;
++ m_keepdate = cUndefinedDate;
++ }
++ else if (lifetime == 99)
++ {
++ m_keepmethod = Forever;
++ m_keepdate = cUndefinedDate;
++ }
++ else
++ {
++ m_keepmethod = UntilKeepDate;
++ m_keepdate = m_starttime + (lifetime * cSecsInDay);
++ }
++}
++
++int cTimer::GetLifetime(void)
++{
++ // margro: the meaning of the XBMC-PVR Lifetime field is undocumented.
++ // Assuming that VDR is the source for this field:
++ // The guaranteed lifetime (in days) of a recording created by this
++ // timer. 0 means that this recording may be automatically deleted
++ // at any time by a new recording with higher priority. 99 means
++ // that this recording will never be automatically deleted. Any
++ // number in the range 1...98 means that this recording may not be
++ // automatically deleted in favour of a new recording, until the
++ // given number of days since the start time of the recording has
++ // passed by
++ switch (m_keepmethod)
++ {
++ case UntilSpaceNeeded: //until space needed
++ case UntilWatched: //until watched
++ return 0;
++ break;
++ case UntilKeepDate: //until keepdate
++ {
++ double diffseconds = difftime(m_keepdate, m_starttime);
++ int daysremaining = (int)(diffseconds / cSecsInDay);
++ // Calculate value in the range 1...98, based on m_keepdate
++ if (daysremaining < 99)
++ {
++ return daysremaining;
++ }
++ else
++ {
++ // > 98 days => return forever
++ return 99;
++ }
++ }
++ break;
++ case Forever: //forever
++ return 99;
++ default:
++ return 0;
++ }
++}
+diff --git a/xbmc/pvrclients/MediaPortal/timers.h b/xbmc/pvrclients/MediaPortal/timers.h
+new file mode 100644
+index 0000000..c94d70c
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/timers.h
+@@ -0,0 +1,132 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#ifndef __TIMERS_H
++#define __TIMERS_H
++
++#include "libXBMC_pvr.h"
++#include <stdlib.h>
++#include <string>
++#include <ctime>
++
++/* VDR:
++enum eTimerFlags { tfNone = 0x0000,
++ tfActive = 0x0001,
++ tfInstant = 0x0002,
++ tfVps = 0x0004,
++ tfRecording = 0x0008,
++ tfAll = 0xFFFF,
++ };
++*/
++
++// From MediaPortal: TvDatabase.ScheduleRecordingType
++enum ScheduleRecordingType
++{
++ Once = 0,
++ Daily = 1,
++ Weekly = 2,
++ EveryTimeOnThisChannel = 3,
++ EveryTimeOnEveryChannel = 4,
++ Weekends = 5,
++ WorkingDays = 6
++};
++
++enum KeepMethodType
++{
++ UntilSpaceNeeded = 0,
++ UntilWatched = 1,
++ UntilKeepDate = 2,
++ Forever = 3
++};
++
++class cTimer
++{
++ public:
++ cTimer();
++ cTimer(const PVR_TIMER &timerinfo);
++ virtual ~cTimer();
++
++ void GetPVRtimerinfo(PVR_TIMER &tag);
++ int Index(void) const { return m_index; }
++ unsigned int Channel(void) const { return m_channel; }
++ int Priority(void) { return Mepo2XBMCPriority(m_priority); }
++ const char* Title(void) const { return m_title.c_str(); }
++ const char* Dir(void) const { return m_directory.c_str(); }
++ time_t StartTime(void) const;
++ time_t EndTime(void) const;
++ bool ParseLine(const char *s);
++ int PreRecordInterval(void) const { return m_prerecordinterval; }
++ int PostRecordInterval(void) const { return m_postrecordinterval; }
++ int RepeatFlags() { return SchedRecType2RepeatFlags(m_schedtype); };
++ bool Repeat() const { return (m_schedtype == Once ? false : true); };
++ bool Done() const { return m_done; };
++ bool IsManual() const { return m_ismanual; };
++ bool IsActive() const { return !m_canceled; };
++ bool IsRecording() const { return m_isrecording; };
++ ScheduleRecordingType RepeatFlags2SchedRecType(int repeatflags);
++ std::string AddScheduleCommand();
++ std::string UpdateScheduleCommand();
++
++ private:
++ int SchedRecType2RepeatFlags(ScheduleRecordingType schedtype);
++
++ /**
++ * @brief Convert a XBMC Lifetime value to MediaPortals keepMethod+keepDate settings
++ * @param lifetime the XBMC lifetime value (in days) (following the VDR syntax)
++ * Should be called after setting m_starttime !!
++ */
++ void SetKeepMethod(int lifetime);
++ int GetLifetime(void);
++ int XBMC2MepoPriority(int xbmcprio);
++ int Mepo2XBMCPriority(int mepoprio);
++
++ // MediaPortal database fields:
++ int m_index; ///> MediaPortal id_Schedule
++ int m_channel; ///> MediaPortal idChannel
++ ScheduleRecordingType m_schedtype; ///> MediaPortal scheduleType
++ std::string m_title; ///> MediaPortal programName
++ time_t m_starttime; ///> MediaPortal startTime
++ time_t m_endtime; ///> MediaPortal endTime
++ // skipped: maxAirings field
++ int m_priority; ///> MediaPortal priority (not the XBMC one!!!)
++ std::string m_directory; ///> MediaPortal directory
++ // skipped: quality field
++ KeepMethodType m_keepmethod; ///> MediaPortal keepMethod
++ time_t m_keepdate; ///> MediaPortal keepDate
++ int m_prerecordinterval; ///> MediaPortal preRecordInterval
++ int m_postrecordinterval; ///> MediaPortal postRecordInterval
++ time_t m_canceled; ///> MediaPortal canceled (date + time)
++ // skipped: recommendedCard
++ bool m_series; ///> MediaPortal series
++ // skipped: idParentSchedule: not yet supported in XBMC
++
++ // XBMC asks for these fields:
++ bool m_active;
++ bool m_done;
++ bool m_ismanual;
++ bool m_isrecording;
++};
++
++const time_t cUndefinedDate = 946681200; ///> 01-01-2000 00:00:00 in time_t
++const int cSecsInDay = 86400; ///> Amount of seconds in one day
++
++#endif //__TIMERS_H
+diff --git a/xbmc/pvrclients/MediaPortal/uri.cpp b/xbmc/pvrclients/MediaPortal/uri.cpp
+new file mode 100644
+index 0000000..1a42ad1
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/uri.cpp
+@@ -0,0 +1,221 @@
++/*
++ * Copyright (C) 2005-2011 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 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include "uri.h"
++
++namespace uri
++{
++ const char ENCODE_BEGIN_CHAR = '%';
++ const traits SCHEME_TRAITS = {
++ 0, 0, ':',
++ {
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CVA2,CINV,CVA2,CVA2,CINV,
++ CVA2,CVA2,CVA2,CVA2,CVA2,CVA2,CVA2,CVA2, CVA2,CVA2,CEND,CINV,CINV,CINV,CINV,CINV,
++ CINV,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,
++ CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CINV,CINV,CINV,CINV,CINV,
++ CINV,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,
++ CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CINV,CINV,CINV,CINV,CINV, // 127 7F
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ }
++ };
++ const traits AUTHORITY_TRAITS = {
++ "//", 0, 0,
++ {
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CEND,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, // 127 7F
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ }
++ };
++ const traits PATH_TRAITS = {
++ 0, 0, 0,
++ { // '/' is invalid
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CVAL,CINV,CINV,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CINV,
++ CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CINV,CVAL,CINV,CINV,
++ CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,
++ CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CINV,CINV,CINV,CINV,CVAL,
++ CINV,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,
++ CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CINV,CINV,CINV,CVAL,CINV, // 127 7F
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ }
++ };
++ const traits QUERY_TRAITS = {
++ 0, '?', 0,
++ { // '=' and '&' are invalid
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CVAL,CINV,CINV,CVAL,CVAL,CINV,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,
++ CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CINV,CINV,CINV,CVAL,
++ CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,
++ CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CINV,CINV,CINV,CINV,CVAL,
++ CINV,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,
++ CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CINV,CINV,CINV,CVAL,CINV, // 127 7F
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ }
++ };
++ const traits FRAGMENT_TRAITS = {
++ 0, '#', 0,
++ {
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CVAL,CINV,CINV,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,
++ CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CINV,CVAL,CINV,CVAL,
++ CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,
++ CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CINV,CINV,CINV,CINV,CVAL,
++ CINV,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,
++ CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CINV,CINV,CINV,CVAL,CINV, // 127 7F
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
++ }
++ };
++
++ bool parse_hex(const std::string& s, size_t pos, char& chr)
++ {
++ if (s.size() < pos + 2)
++ return false;
++ unsigned int v;
++ unsigned int c = (unsigned int) s[pos];
++ if ('0' <= c && c <= '9')
++ v = (c - '0') << 4;
++ else if ('A' <= c && c <= 'F')
++ v = (10 + (c - 'A')) << 4;
++ else if ('a' <= c && c <= 'f')
++ v = (10 + (c - 'a')) << 4;
++ else
++ return false;
++ c = (unsigned int) s[pos + 1];
++ if ('0' <= c && c <= '9')
++ v += c - '0';
++ else if ('A' <= c && c <= 'F')
++ v += 10 + (c - 'A');
++ else if ('a' <= c && c <= 'f')
++ v += 10 + (c - 'a');
++ else
++ return false;
++ chr = (char) v; // Set output.
++ return true;
++ }
++
++ void append_hex(char v, std::string& s)
++ {
++ unsigned int c = (unsigned char) v & 0xF0;
++ c >>= 4;
++ s.insert(s.end(), (char)((9 < c) ? (c - 10) + 'A' : c + '0'));
++ c = v & 0x0F;
++ s.insert(s.end(), (char)((9 < c) ? (c - 10) + 'A' : c + '0'));
++ }
++
++ std::string encode(const traits& ts, const std::string& comp)
++ {
++ std::string::const_iterator f = comp.begin();
++ std::string::const_iterator anchor = f;
++ std::string s;
++
++ for (; f != comp.end();)
++ {
++ char c = *f;
++ if (ts.char_class[(unsigned char)c] < CVAL || c == ENCODE_BEGIN_CHAR)
++ { // Must encode.
++ s.append(anchor, f); // Catch up to this char.
++ s.append(1, ENCODE_BEGIN_CHAR);
++ append_hex(c, s); // Convert.
++ anchor = ++f;
++ }
++ else
++ {
++ ++f;
++ }
++ }
++ return (anchor == comp.begin()) ? comp : s.append(anchor, comp.end());
++ }
++
++ bool decode(std::string& s)
++ {
++ size_t pos = s.find(ENCODE_BEGIN_CHAR);
++ if (pos == std::string::npos)
++ {
++ // Handle the "99%" case fast.
++ return true;
++ }
++
++ std::string v;
++ for (size_t i = 0;;)
++ {
++ if (pos == std::string::npos)
++ {
++ v.append(s, i, s.size() - i); // Append up to end.
++ break;
++ }
++ v.append(s, i, pos - i); // Append up to char.
++ i = pos + 3; // Skip all 3 chars.
++ char c;
++ if (!parse_hex(s, pos + 1, c))
++ {
++ // Convert hex.
++ return false;
++ }
++ v.insert(v.end(), c); // Append converted hex.
++ pos = s.find(ENCODE_BEGIN_CHAR, i); // Find next
++ }
++ s = v;
++ return true;
++ }
++} //namespace URI
+diff --git a/xbmc/pvrclients/MediaPortal/uri.h b/xbmc/pvrclients/MediaPortal/uri.h
+new file mode 100644
+index 0000000..2757cf0
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/uri.h
+@@ -0,0 +1,71 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2011 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 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ */
++#include <string>
++
++namespace uri
++{
++ /// Char class.
++ enum char_class_e
++ {
++ CINV = -2, ///< invalid
++ CEND = -1, ///< end delimitor
++ CVAL = 0, ///< valid any position
++ CVA2 = 1, ///< valid anywhere but 1st position
++ };
++
++ /// Traits used for parsing and encoding components.
++ struct traits
++ {
++ const char* begin_cstring; ///< begin cstring (or 0 if none)
++ const char begin_char; ///< begin char (or 0 if none)
++ const char end_char; ///< end char (or 0 if none)
++ char char_class[256]; ///< map of char to class
++ };
++
++ /**
++ * \brief Encode the URI (sub) component.
++ * Note that this should be used on the subcomponents before appending to
++ * subdelimiter chars, if any.
++ *
++ * From the RFC: URI producing applications should percent-encode data octets
++ * are specifically allowed by the URI scheme to represent data in that
++ * component. If a reserved character is found in a URI component and
++ * no delimiting role is known for that character, then it must be
++ * interpreted as representing the data octet corresponding to that
++ * character's encoding in US-ASCII.
++ * @see http://tools.ietf.org/html/rfc3986
++ * @see decode std::string encode(const traits& ts, const std::string& comp);
++ */
++ std::string encode(const traits& ts, const std::string& comp);
++ /**
++ * Decode the pct-encoded (hex) sequences, if any, return success.
++ * Does not change string on error.
++ * @see http://tools.ietf.org/html/rfc3986#section-2.1
++ * @see encode
++ * \param s A reference to the std::string to decode
++ */
++ bool decode(std::string& s);
++
++ extern const char ENCODE_BEGIN_CHAR; ///< encode begin char ('\%')
++ extern const traits SCHEME_TRAITS; ///< scheme traits
++ extern const traits AUTHORITY_TRAITS; ///< authority traits
++ extern const traits PATH_TRAITS; ///< path traits
++ extern const traits QUERY_TRAITS; ///< query traits
++ extern const traits FRAGMENT_TRAITS; ///< fragment traits
++}
+diff --git a/xbmc/pvrclients/MediaPortal/utils.cpp b/xbmc/pvrclients/MediaPortal/utils.cpp
+new file mode 100644
+index 0000000..887b98c
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/utils.cpp
+@@ -0,0 +1,133 @@
++/*
++ * Copyright (C) 2005-2011 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 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ */
++#ifdef TARGET_WINDOWS
++#pragma warning(disable:4244) //wchar to char = loss of data
++#endif
++
++#include "utils.h"
++#include <string>
++#include <stdio.h>
++#include "platform/util/StdString.h"
++
++using namespace std;
++
++void Tokenize(const string& str, vector<string>& tokens, const string& delimiters = " ")
++{
++ // Skip delimiters at beginning.
++ //string::size_type lastPos = str.find_first_not_of(delimiters, 0);
++ // Don't skip delimiters at beginning.
++ string::size_type start_pos = 0;
++ // Find first "non-delimiter".
++ string::size_type delim_pos = 0;
++
++ while (string::npos != delim_pos)
++ {
++ delim_pos = str.find_first_of(delimiters, start_pos);
++ // Found a token, add it to the vector.
++ tokens.push_back(str.substr(start_pos, delim_pos - start_pos));
++ start_pos = delim_pos + 1;
++
++ // Find next "non-delimiter"
++ }
++}
++
++
++std::string WStringToString(const std::wstring& s)
++{
++ std::string temp(s.length(), ' ');
++ std::copy(s.begin(), s.end(), temp.begin());
++ return temp;
++}
++
++std::wstring StringToWString(const std::string& s)
++{
++ std::wstring temp(s.length(),L' ');
++ std::copy(s.begin(), s.end(), temp.begin());
++ return temp;
++}
++
++std::string lowercase(const std::string& s)
++{
++ std::string t;
++ for (std::string::const_iterator i = s.begin(); i != s.end(); ++i)
++ {
++ t += tolower(*i);
++ }
++ return t;
++}
++
++bool stringtobool(const std::string& s)
++{
++ std::string temp = lowercase(s);
++
++ if(temp.compare("false") == 0)
++ return false;
++ else if(temp.compare("0") == 0)
++ return false;
++ else
++ return true;
++}
++
++const char* booltostring(const bool b)
++{
++ return (b==true) ? "True" : "False";
++}
++
++time_t DateTimeToTimeT(const std::string& datetime)
++{
++ struct tm timeinfo;
++ int year, month ,day;
++ int hour, minute, second;
++ int count;
++ time_t retval;
++
++ count = sscanf(datetime.c_str(), "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &minute, &second);
++
++ if(count != 6)
++ return -1;
++
++ timeinfo.tm_hour = hour;
++ timeinfo.tm_min = minute;
++ timeinfo.tm_sec = second;
++ timeinfo.tm_year = year - 1900;
++ timeinfo.tm_mon = month - 1;
++ timeinfo.tm_mday = day;
++ // Make the other fields empty:
++ timeinfo.tm_isdst = -1;
++ timeinfo.tm_wday = 0;
++ timeinfo.tm_yday = 0;
++
++ retval = mktime (&timeinfo);
++
++ if(retval < 0)
++ retval = 0;
++
++ return retval;
++}
++
++std::string ToThumbFileName(const char* strChannelName)
++{
++ CStdString strThumbName = strChannelName;
++
++ strThumbName.Replace(":","_");
++ strThumbName.Replace("/","_");
++ strThumbName.Replace("\\","_");
++
++ return strThumbName;
++}
+diff --git a/xbmc/pvrclients/MediaPortal/utils.h b/xbmc/pvrclients/MediaPortal/utils.h
+new file mode 100644
+index 0000000..e6c7aed
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/utils.h
+@@ -0,0 +1,55 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2011 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 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ */
++#include <string>
++#include <vector>
++#include <ctime>
++#include "uri.h"
++
++#ifdef TARGET_WINDOWS
++#include "windows/WindowsUtils.h"
++#endif
++
++using namespace std;
++
++/** Delete macros that make the pointer NULL again */
++#define SAFE_DELETE(p) do { delete (p); (p)=NULL; } while (0)
++#define SAFE_DELETE_ARRAY(p) do { delete[] (p); (p)=NULL; } while (0)
++
++/**
++ * String tokenize
++ * Split string using the given delimiter into a vector of substrings
++ */
++void Tokenize(const string& str, vector<string>& tokens, const string& delimiters);
++
++std::wstring StringToWString(const std::string& s);
++std::string WStringToString(const std::wstring& s);
++std::string lowercase(const std::string& s);
++bool stringtobool(const std::string& s);
++const char* booltostring(const bool b);
++
++/**
++ * @brief Converts a C# DateTime string into a time_t value
++ * Assumes the usage of somedatetimeval.ToString("u") in C#
++ */
++time_t DateTimeToTimeT(const std::string& datetime);
++
++/**
++ * @brief Filters forbidden filename characters from channel name and replaces them with _ )
++ */
++std::string ToThumbFileName(const char* strChannelName);
+diff --git a/xbmc/pvrclients/MediaPortal/windows/FileUtils.cpp b/xbmc/pvrclients/MediaPortal/windows/FileUtils.cpp
+new file mode 100644
+index 0000000..a009918
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/windows/FileUtils.cpp
+@@ -0,0 +1,91 @@
++#include "FileUtils.h"
++#include "os_windows.h"
++#include <string>
++
++namespace OS
++{
++ bool CFile::Exists(const std::string& strFileName)
++ {
++ DWORD dwAttr = GetFileAttributes(strFileName.c_str());
++
++ if(dwAttr == 0xffffffff)
++ {
++ DWORD dwError = GetLastError();
++ if(dwError == ERROR_FILE_NOT_FOUND)
++ {
++ // file not found
++ return false;
++ }
++ else if(dwError == ERROR_PATH_NOT_FOUND)
++ {
++ // path not found
++ return false;
++ }
++ else if(dwError == ERROR_ACCESS_DENIED)
++ {
++ // file or directory exists, but access is denied
++ return false;
++ }
++ else
++ {
++ // some other error has occured
++ return false;
++ }
++ }
++ else
++ {
++ if(dwAttr & FILE_ATTRIBUTE_DIRECTORY)
++ {
++ return true;
++/*
++ // this is a directory
++ if(dwAttr & FILE_ATTRIBUTE_ARCHIVE)
++ // Directory is archive file
++ if(dwAttr & FILE_ATTRIBUTE_COMPRESSED)
++ // Directory is compressed
++ if(dwAttr & FILE_ATTRIBUTE_ENCRYPTED)
++ // Directory is encrypted
++ if(dwAttr & FILE_ATTRIBUTE_HIDDEN)
++ // Directory is hidden
++ if(dwAttr & FILE_ATTRIBUTE_READONLY)
++ // Directory is read-only
++ if(dwAttr & FILE_ATTRIBUTE_REPARSE_POINT)
++ // Directory has an associated reparse point
++ if(dwAttr & FILE_ATTRIBUTE_SYSTEM)
++ // Directory is part or used exclusively by the operating system
++*/
++ }
++ else
++ {
++ return true;
++/*
++ // this is an ordinary file
++ if(dwAttr & FILE_ATTRIBUTE_ARCHIVE)
++ // File is archive file
++ if(dwAttr & FILE_ATTRIBUTE_COMPRESSED)
++ // File is compressed
++ if(dwAttr & FILE_ATTRIBUTE_ENCRYPTED)
++ // File is encrypted
++ if(dwAttr & FILE_ATTRIBUTE_HIDDEN)
++ // File is hidden
++ if(dwAttr & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
++ // File will not be indexed
++ if(dwAttr & FILE_ATTRIBUTE_OFFLINE)
++ // Data of file is not immediately available
++ if(dwAttr & FILE_ATTRIBUTE_READONLY)
++ // File is read-only
++ if(dwAttr & FILE_ATTRIBUTE_REPARSE_POINT)
++ // File has an associated reparse point
++ if(dwAttr & FILE_ATTRIBUTE_SPARSE_FILE)
++ // File is a sparse file
++ if(dwAttr & FILE_ATTRIBUTE_SYSTEM)
++ // File is part or used exclusively by the operating system
++ if(dwAttr & FILE_ATTRIBUTE_TEMPORARY)
++ // File is being used for temporary storage
++*/
++ }
++ }
++
++ return true;
++ }
++}
+\ No newline at end of file
+diff --git a/xbmc/pvrclients/MediaPortal/windows/WindowsUtils.cpp b/xbmc/pvrclients/MediaPortal/windows/WindowsUtils.cpp
+new file mode 100644
+index 0000000..887fea8
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/windows/WindowsUtils.cpp
+@@ -0,0 +1,104 @@
++#include "WindowsUtils.h"
++#include <windows.h>
++#include <stdio.h>
++#include <string>
++
++namespace OS
++{
++ typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO);
++
++ WindowsVersion Version()
++ {
++ OSVERSIONINFOEX osvi;
++ SYSTEM_INFO si;
++ PGNSI GetNativeSystemInfo;
++ BOOL bOsVersionInfoEx;
++
++ ZeroMemory(&si, sizeof(SYSTEM_INFO));
++ ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
++
++ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
++ bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO*) &osvi);
++
++ if(bOsVersionInfoEx == NULL ) return Unknown;
++
++ // Call GetNativeSystemInfo if supported or GetSystemInfo otherwise.
++ GetNativeSystemInfo = (PGNSI) GetProcAddress( GetModuleHandle(TEXT("kernel32.dll")), "GetNativeSystemInfo");
++ if (NULL != GetNativeSystemInfo)
++ GetNativeSystemInfo(&si);
++ else
++ GetSystemInfo(&si);
++
++ if ( VER_PLATFORM_WIN32_NT==osvi.dwPlatformId && osvi.dwMajorVersion > 4 )
++ {
++ // Test for the specific product.
++ if ( osvi.dwMajorVersion == 6 )
++ {
++ if( osvi.dwMinorVersion == 0 )
++ {
++ if( osvi.wProductType == VER_NT_WORKSTATION )
++ return WindowsVista;
++ else
++ return WindowsServer2008;
++ }
++
++ if ( osvi.dwMinorVersion == 1 )
++ {
++ if( osvi.wProductType == VER_NT_WORKSTATION )
++ return Windows7;
++ else
++ return WindowsServer2008R2;
++ }
++ }
++
++ if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2 )
++ {
++ if( GetSystemMetrics(SM_SERVERR2) )
++ return WindowsServer2003R2;
++ else if ( osvi.wSuiteMask & VER_SUITE_STORAGE_SERVER )
++ return WindowsStorageServer2003;
++ else if ( osvi.wSuiteMask & VER_SUITE_WH_SERVER )
++ return WindowsHomeServer;
++ else if( osvi.wProductType == VER_NT_WORKSTATION && si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64)
++ return WindowsXPx64;
++ else
++ return WindowsServer2003;
++ }
++
++ if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1 )
++ {
++ if( osvi.wSuiteMask & VER_SUITE_PERSONAL )
++ return WindowsXPHome;
++ else
++ return WindowsXPPro;
++ }
++
++ if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 )
++ {
++ return Windows2000;
++ }
++ }
++
++ return Unknown;
++ }
++
++ bool GetEnvironmentVariable(const char* strVarName, std::string& strResult)
++ {
++ char strBuffer[4096];
++ DWORD dwRet;
++
++ dwRet = ::GetEnvironmentVariable(strVarName, strBuffer, 4096);
++
++ if(0 == dwRet)
++ {
++ dwRet = GetLastError();
++ if( ERROR_ENVVAR_NOT_FOUND == dwRet )
++ {
++ strResult.clear();
++ return false;
++ }
++ }
++ strResult = strBuffer;
++ return true;
++ }
++}
+diff --git a/xbmc/pvrclients/MediaPortal/windows/WindowsUtils.h b/xbmc/pvrclients/MediaPortal/windows/WindowsUtils.h
+new file mode 100644
+index 0000000..a38b088
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/windows/WindowsUtils.h
+@@ -0,0 +1,27 @@
++#pragma once
++
++#include <string>
++
++namespace OS
++{
++ typedef enum _WindowsVersion
++ {
++ Unknown = 0,
++ Windows2000 = 10,
++ WindowsXPHome = 20,
++ WindowsXPPro = 21,
++ WindowsXPx64 = 22,
++ WindowsVista = 30,
++ WindowsServer2003 = 31,
++ WindowsStorageServer2003 = 32,
++ WindowsHomeServer = 33,
++ WindowsServer2003R2 = 34,
++ Windows7 = 40,
++ WindowsServer2008 = 41,
++ WindowsServer2008R2 = 42
++ } WindowsVersion;
++
++ WindowsVersion Version();
++
++ bool GetEnvironmentVariable(const char* strVarName, std::string& strResult);
++}
+\ No newline at end of file
+diff --git a/xbmc/pvrclients/MediaPortal/windows/os_windows.h b/xbmc/pvrclients/MediaPortal/windows/os_windows.h
+new file mode 100644
+index 0000000..93eb20c
+--- /dev/null
++++ b/xbmc/pvrclients/MediaPortal/windows/os_windows.h
+@@ -0,0 +1,30 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2011 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 this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ */
++
++#ifdef TARGET_WINDOWS
++
++#define WIN32_LEAN_AND_MEAN // Enable LEAN_AND_MEAN support
++#define NOMINMAX // don't define min() and max() to prevent a clash with std::min() and std::max
++#include <windows.h>
++
++/* Platform dependent path separator */
++#define PATH_SEPARATOR_CHAR '\\'
++
++#endif //TARGET_WINDOWS
+diff --git a/xbmc/pvrclients/mythtv-cmyth/Makefile.in b/xbmc/pvrclients/mythtv-cmyth/Makefile.in
+new file mode 100644
+index 0000000..cd7db39
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/Makefile.in
+@@ -0,0 +1,34 @@
++#
++# Makefile for the XBMC MythTV cmyth PVR AddOn
++#
++# See the README for copyright information and
++# how to reach the author.
++#
++
++LIBS = -lboost_system -lboost_filesystem -lboost_regex -ldl
++LIBDIR = @abs_top_srcdir@/addons/pvr.mythtv.cmyth
++LIB = @abs_top_srcdir@/addons/pvr.mythtv.cmyth/XBMC_MythTV_cmyth.pvr
++
++SRCS=client.cpp \
++ cppmyth/MythChannel.cpp \
++ cppmyth/MythConnection.cpp \
++ cppmyth/MythDatabase.cpp \
++ cppmyth/MythEventHandler.cpp \
++ cppmyth/MythFile.cpp \
++ cppmyth/MythProgramInfo.cpp \
++ cppmyth/MythRecorder.cpp \
++ cppmyth/MythSignal.cpp \
++ cppmyth/MythTimer.cpp \
++ cppmyth/MythTimestamp.cpp \
++ cppmyth/MythSGFile.cpp \
++ fileOps.cpp \
++ pvrclient-mythtv.cpp \
++ recordingRules.cpp \
++
++include ../Makefile.include
++
++clean:
++ -rm -f $(OBJS) $(LIB) *.P *~
++
++$(LIB): $(OBJS)
++ $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared -g $(OBJS) $(LIBS) $(LIBDIRS) $(SILIB) -o $(LIB)
+diff --git a/xbmc/pvrclients/mythtv-cmyth/boost_d.txt b/xbmc/pvrclients/mythtv-cmyth/boost_d.txt
+new file mode 100644
+index 0000000..dcd5ed3
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/boost_d.txt
+@@ -0,0 +1,4 @@
++; filename mirror of the file
++boost_1_46_1-headers-win32.7z http://mirrors.xbmc.org/build-deps/win32/
++boost_1_46_1-libs-win32.7z http://mirrors.xbmc.org/build-deps/win32/
++boost_1_46_1-debug-libs-win32.7z http://mirrors.xbmc.org/build-deps/win32/
+diff --git a/xbmc/pvrclients/mythtv-cmyth/boost_win32_dependency.bat b/xbmc/pvrclients/mythtv-cmyth/boost_win32_dependency.bat
+new file mode 100644
+index 0000000..4e3bf74
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/boost_win32_dependency.bat
+@@ -0,0 +1,48 @@
++@ECHO ON
++CD %~dp0
++IF EXIST %CD%\boost GOTO END
++
++SET LOC_PATH=%CD%
++SET FILES=%CD%\boost_d.txt
++
++SET CUR_PATH=%CD%\..\..\..\project\BuildDependencies
++SET WGET=%CUR_PATH%\bin\wget
++SET ZIP=%CUR_PATH%\..\Win32BuildSetup\tools\7z\7za
++SET TMP_PATH=%CD%\tmp
++
++md %TMP_PATH%
++md %CD%
++
++FOR /F "eol=; tokens=1,2" %%f IN (%FILES%) DO (
++echo %%f %%g
++ IF NOT EXIST %%f (
++ %WGET% "%%g/%%f"
++ ) ELSE (
++ echo Already have %%f
++ )
++
++ copy /b "%%f" "%TMP_PATH%"
++ del /F /Q "%%f"
++)
++
++cd %TMP_PATH%
++
++FOR /F "eol=; tokens=1,2" %%f IN (%FILES%) DO (
++ %ZIP% x %%f
++)
++
++FOR /F "tokens=*" %%f IN ('dir /B "*.tar"') DO (
++ %ZIP% x -y %%f
++)
++
++xcopy boost_1_46_1-headers-win32\* "%LOC_PATH%\" /E /Q /I /Y
++xcopy boost_1_46_1-debug-libs-win32\* "%LOC_PATH%\" /E /Q /I /Y
++xcopy boost_1_46_1-libs-win32\* "%LOC_PATH%\" /E /Q /I /Y
++
++cd %LOC_PATH%
++
++rmdir %TMP_PATH% /S /Q
++
++
++:END
++
+diff --git a/xbmc/pvrclients/mythtv-cmyth/client.cpp b/xbmc/pvrclients/mythtv-cmyth/client.cpp
+new file mode 100644
+index 0000000..22879db
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/client.cpp
+@@ -0,0 +1,743 @@
++/*
++ * Copyright (C) 2005-2009 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "client.h"
++#include "xbmc_pvr_dll.h"
++#include "pvrclient-mythtv.h"
++
++using namespace std;
++using namespace ADDON;
++
++#define SEEK_POSSIBLE 0x10 // flag used to check if protocol allows seeks
++
++/* User adjustable settings are saved here.
++ * Default values are defined inside client.h
++ * and exported to the other source files.
++ */
++CStdString g_szHostname = DEFAULT_HOST; ///< The Host name or IP of the mythtv server
++int g_iMythPort = DEFAULT_PORT; ///< The mythtv Port (default is 6543)
++CStdString g_szMythDBuser = DEFAULT_DB_USER; ///< The mythtv sql username (default is mythtv)
++CStdString g_szMythDBpassword = DEFAULT_DB_PASSWORD; ///< The mythtv sql password (default is mythtv)
++CStdString g_szMythDBname = DEFAULT_DB_NAME; ///< The mythtv sql database name (default is mythconverg)
++bool g_bExtraDebug = DEFAULT_EXTRA_DEBUG; ///< Output extensive debug information to the log
++bool g_bLiveTVPriority = DEFAULT_LIVETV_PRIORITY; ///< MythTV Backend setting to allow live TV to move scheduled shows
++int g_iMinMovieLength = DEFAULT_MIN_MOVIE_LENGTH; ///< Minimum length (in minutes) of a recording to be considered to be a movie
++CStdString g_szSeriesRegEx = DEFAULT_SERIES_REGEX; ///< The Regular expression to use to extract the series name (and maybe also episode number)
++CStdString g_szSeriesIdentifier = DEFAULT_SERIES_IDENTIFIER;///< Optional regular expression to use to detect series
++///* Client member variables */
++
++bool m_recordingFirstRead;
++char m_noSignalStreamData[ 6 + 0xffff ];
++long m_noSignalStreamSize = 0;
++long m_noSignalStreamReadPos = 0;
++bool m_bPlayingNoSignal = false;
++int m_iCurrentChannel = 1;
++ADDON_STATUS m_CurStatus = ADDON_STATUS_UNKNOWN;
++bool g_bCreated = false;
++int g_iClientID = -1;
++CStdString g_szUserPath = "";
++CStdString g_szClientPath = "";
++
++PVRClientMythTV *g_client = NULL;
++
++CHelper_libXBMC_addon *XBMC = NULL;
++CHelper_libXBMC_pvr *PVR = NULL;
++CHelper_libXBMC_gui *GUI = NULL;
++CHelper_libcmyth *CMYTH = NULL;
++
++
++extern "C" {
++
++/***********************************************************
++ * Standard AddOn related public library functions
++ ***********************************************************/
++
++ADDON_STATUS ADDON_Create(void* hdl, void* props)
++{
++ if (!props)
++ return ADDON_STATUS_UNKNOWN;
++
++ PVR_PROPERTIES* pvrprops = (PVR_PROPERTIES*)props;
++
++ XBMC = new CHelper_libXBMC_addon;
++ if (!XBMC->RegisterMe(hdl))
++ return ADDON_STATUS_UNKNOWN;
++
++ PVR = new CHelper_libXBMC_pvr;
++ if (!PVR->RegisterMe(hdl))
++ return ADDON_STATUS_UNKNOWN;
++
++ GUI = new CHelper_libXBMC_gui;
++ if (!GUI->RegisterMe(hdl))
++ return ADDON_STATUS_UNKNOWN;
++
++ XBMC->Log(LOG_DEBUG, "Loading cmyth library");
++ CMYTH = new CHelper_libcmyth;
++ if (!CMYTH->RegisterMe(hdl))
++ {
++ XBMC->Log(LOG_ERROR, "Failed to load cmyth library!");
++ return ADDON_STATUS_UNKNOWN;
++ }
++
++ XBMC->Log(LOG_DEBUG, "Creating MythTV cmyth PVR-Client");
++
++ m_CurStatus = ADDON_STATUS_UNKNOWN;
++ g_iClientID = pvrprops->iClientId;
++ g_szUserPath = pvrprops->strUserPath;
++ g_szClientPath = pvrprops->strClientPath;
++
++ /* Read setting "host" from settings.xml */
++ char * buffer;
++ buffer = (char*) malloc (1024);
++ buffer[0] = 0; /* Set the end of string */
++
++ if (XBMC->GetSetting("host", buffer))
++ g_szHostname = buffer;
++ else
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'host' setting, falling back to '%s' as default", DEFAULT_HOST);
++ g_szHostname = DEFAULT_HOST;
++ }
++ buffer[0] = 0;
++
++ /* Read setting "port" from settings.xml */
++ if (!XBMC->GetSetting("port", &g_iMythPort))
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'port' setting, falling back to '%i' as default", DEFAULT_PORT);
++ g_iMythPort = DEFAULT_PORT;
++ }
++
++ /* Read setting "extradebug" from settings.xml */
++ if (!XBMC->GetSetting("extradebug", &g_bExtraDebug))
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'extradebug' setting, falling back to '%b' as default", DEFAULT_EXTRA_DEBUG);
++ g_bExtraDebug = DEFAULT_EXTRA_DEBUG;
++ }
++
++ /* Read setting "db_username" from settings.xml */
++ if (XBMC->GetSetting("db_user", buffer))
++ g_szMythDBuser = buffer;
++ else
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'db_user' setting, falling back to '%s' as default", DEFAULT_DB_USER);
++ g_szMythDBuser = DEFAULT_DB_USER;
++ }
++ buffer[0] = 0;
++
++ /* Read setting "db_password" from settings.xml */
++ if (XBMC->GetSetting("db_password", buffer))
++ g_szMythDBpassword = buffer;
++ else
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'db_password' setting, falling back to '%s' as default", DEFAULT_DB_PASSWORD);
++ g_szMythDBpassword = DEFAULT_DB_PASSWORD;
++ }
++ buffer[0] = 0;
++
++ /* Read setting "db_name" from settings.xml */
++ if (XBMC->GetSetting("db_name", buffer))
++ g_szMythDBname = buffer;
++ else
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'db_name' setting, falling back to '%s' as default", DEFAULT_DB_NAME);
++ g_szMythDBname = DEFAULT_DB_NAME;
++ }
++ buffer[0] = 0;
++
++ /* Read setting "minmovielength" from settings.xml */
++ if (!XBMC->GetSetting("min_movie_length", &g_iMinMovieLength))
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'min_movie_length' setting, falling back to '%i' as default", DEFAULT_MIN_MOVIE_LENGTH);
++ g_iMinMovieLength = DEFAULT_MIN_MOVIE_LENGTH;
++ }
++
++ /* Read setting "db_name" from settings.xml */
++ if (XBMC->GetSetting("series_regex", buffer))
++ g_szSeriesRegEx = buffer;
++ else
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'series_regex' setting, falling back to '%s' as default", DEFAULT_SERIES_REGEX);
++ g_szSeriesRegEx = DEFAULT_SERIES_REGEX;
++ }
++ buffer[0] = 0;
++
++ /* Read setting "db_name" from settings.xml */
++ if (XBMC->GetSetting("series_regex_id", buffer))
++ g_szSeriesIdentifier = buffer;
++ else
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'series_regex_id' setting, falling back to '%s' as default", DEFAULT_SERIES_IDENTIFIER);
++ g_szSeriesIdentifier = DEFAULT_SERIES_IDENTIFIER;
++ }
++ buffer[0] = 0;
++
++ free (buffer);
++
++
++ g_client = new PVRClientMythTV();
++ if (!g_client->Connect())
++ {
++ m_CurStatus = ADDON_STATUS_LOST_CONNECTION;
++ return m_CurStatus;
++ }
++
++ /* Read setting "LiveTV Priority" from backend database */
++ bool savedLiveTVPriority;
++ if(!XBMC->GetSetting("livetv_priority", &savedLiveTVPriority))
++ savedLiveTVPriority = DEFAULT_LIVETV_PRIORITY;
++ g_bLiveTVPriority = g_client->GetLiveTVPriority();
++ if (g_bLiveTVPriority != savedLiveTVPriority)
++ {
++ g_client->SetLiveTVPriority(savedLiveTVPriority);
++ }
++
++ PVR_MENUHOOK recRuleHook;
++ recRuleHook.iHookId=RECORDING_RULES;
++ recRuleHook.iLocalizedStringId=RECORDING_RULES;
++ PVR->AddMenuHook(&recRuleHook);
++
++ m_CurStatus = ADDON_STATUS_OK;
++ g_bCreated = true;
++ return m_CurStatus;
++}
++
++void ADDON_Destroy()
++{
++ if (g_bCreated)
++ {
++ delete g_client;
++ g_client = NULL;
++ g_bCreated = false;
++ }
++
++ if (PVR)
++ {
++ delete(PVR);
++ PVR = NULL;
++ }
++
++ if (XBMC)
++ {
++ delete(XBMC);
++ XBMC = NULL;
++ }
++
++ if (GUI)
++ {
++ delete(GUI);
++ GUI = NULL;
++ }
++
++ if (CMYTH)
++ {
++ delete(CMYTH);
++ CMYTH = NULL;
++ }
++ m_CurStatus = ADDON_STATUS_UNKNOWN;
++}
++
++ADDON_STATUS ADDON_GetStatus()
++{
++ return m_CurStatus;
++}
++
++bool ADDON_HasSettings()
++{
++ return true;
++}
++
++unsigned int ADDON_GetSettings(ADDON_StructSetting ***sSet)
++{
++ return 0;
++}
++
++ADDON_STATUS ADDON_SetSetting(const char *settingName, const void *settingValue)
++{
++ string str = settingName;
++ if (!g_bCreated)
++ return ADDON_STATUS_OK;
++
++ if (str == "host")
++ {
++ string tmp_sHostname;
++ XBMC->Log(LOG_INFO, "Changed Setting 'host' from %s to %s", g_szHostname.c_str(), (const char*) settingValue);
++ tmp_sHostname = g_szHostname;
++ g_szHostname = (const char*) settingValue;
++ if (tmp_sHostname != g_szHostname)
++ return ADDON_STATUS_NEED_RESTART;
++ }
++ else if (str == "port")
++ {
++ XBMC->Log(LOG_INFO, "Changed Setting 'port' from %u to %u", g_iMythPort, *(int*) settingValue);
++ if (g_iMythPort != *(int*) settingValue)
++ {
++ g_iMythPort = *(int*) settingValue;
++ return ADDON_STATUS_NEED_RESTART;
++ }
++ }
++ else if (str == "db_user")
++ {
++ string tmp_sMythDBuser;
++ XBMC->Log(LOG_INFO, "Changed Setting 'db_user' from %s to %s", g_szMythDBuser.c_str(), (const char*) settingValue);
++ tmp_sMythDBuser = g_szMythDBuser;
++ g_szMythDBuser = (const char*) settingValue;
++ if (tmp_sMythDBuser != g_szMythDBuser)
++ return ADDON_STATUS_NEED_RESTART;
++ }
++ else if (str == "db_password")
++ {
++ string tmp_sMythDBpassword;
++ XBMC->Log(LOG_INFO, "Changed Setting 'db_password' from %s to %s", g_szMythDBpassword.c_str(), (const char*) settingValue);
++ tmp_sMythDBpassword = g_szMythDBpassword;
++ g_szMythDBpassword = (const char*) settingValue;
++ if (tmp_sMythDBpassword != g_szMythDBpassword)
++ return ADDON_STATUS_NEED_RESTART;
++ }
++ else if (str == "db_name")
++ {
++ string tmp_sMythDBname;
++ XBMC->Log(LOG_INFO, "Changed Setting 'db_name' from %s to %s", g_szMythDBname.c_str(), (const char*) settingValue);
++ tmp_sMythDBname = g_szMythDBname;
++ g_szMythDBname = (const char*) settingValue;
++ if (tmp_sMythDBname != g_szMythDBname)
++ return ADDON_STATUS_NEED_RESTART;
++ }
++ else if (str == "extradebug")
++ {
++ XBMC->Log(LOG_INFO, "Changed Setting 'extra debug' from %u to %u", g_bExtraDebug, *(bool*) settingValue);
++ if (g_bExtraDebug != *(bool*) settingValue)
++ {
++ g_bExtraDebug = *(bool*) settingValue;
++ return ADDON_STATUS_OK;
++ }
++ }
++ else if (str == "series_regex")
++ {
++ XBMC->Log(LOG_INFO, "Changed Setting 'series_regex' from %s to %s", g_szSeriesRegEx.c_str(), (const char*) settingValue);
++ g_szSeriesRegEx = (const char*) settingValue;
++ return ADDON_STATUS_OK;
++ }
++ else if (str == "series_regex_id")
++ {
++ XBMC->Log(LOG_INFO, "Changed Setting 'series_regex_id' from %s to %s", g_szSeriesIdentifier.c_str(), (const char*) settingValue);
++ g_szSeriesIdentifier = (const char*) settingValue;
++ return ADDON_STATUS_OK;
++ }
++ else if (str == "min_movie_length")
++ {
++ XBMC->Log(LOG_INFO, "Changed Setting 'min_movie_length' from %u to %u", g_iMinMovieLength, *(int*) settingValue);
++ g_iMinMovieLength = *(int*) settingValue;
++ return ADDON_STATUS_OK;
++ }
++ else if (str == "livetv_priority")
++ {
++ XBMC->Log(LOG_INFO, "Changed Setting 'extra debug' from %u to %u", g_bLiveTVPriority, *(bool*) settingValue);
++ if (g_bLiveTVPriority != *(bool*) settingValue && m_CurStatus != ADDON_STATUS_LOST_CONNECTION)
++ {
++ g_bLiveTVPriority = *(bool*) settingValue;
++ g_client->SetLiveTVPriority(g_bLiveTVPriority);
++ return ADDON_STATUS_OK;
++ }
++ }
++ return ADDON_STATUS_OK;
++}
++
++void ADDON_Stop()
++{
++ //ADDON_Destroy();
++}
++
++void ADDON_FreeSettings()
++{
++ return;
++}
++
++
++/***********************************************************
++ * PVR Client AddOn specific public library functions
++ ***********************************************************/
++
++PVR_ERROR GetAddonCapabilities(PVR_ADDON_CAPABILITIES *pCapabilities)
++{
++ pCapabilities->bSupportsTimeshift = true;
++ pCapabilities->bSupportsEPG = true;
++ pCapabilities->bSupportsRecordings = true;
++ pCapabilities->bSupportsTimers = true;
++ pCapabilities->bSupportsTV = true;
++ pCapabilities->bSupportsRadio = true;
++ pCapabilities->bSupportsChannelSettings = false;
++ pCapabilities->bSupportsChannelGroups = true;
++ pCapabilities->bHandlesInputStream = true;
++ pCapabilities->bHandlesDemuxing = false;
++ pCapabilities->bSupportsChannelScan = false;
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++PVR_ERROR GetStreamProperties(PVR_STREAM_PROPERTIES* props)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++const char * GetBackendName()
++{
++ return g_client->GetBackendName();
++}
++
++const char * GetBackendVersion()
++{
++ return g_client->GetBackendVersion();
++}
++
++const char * GetConnectionString()
++{
++ return g_client->GetConnectionString();
++}
++
++PVR_ERROR GetDriveSpace(long long *iTotal, long long *iUsed)
++{
++ return g_client->GetDriveSpace(iTotal,iUsed)?PVR_ERROR_NO_ERROR:PVR_ERROR_UNKNOWN;
++}
++
++PVR_ERROR DialogChannelScan()
++{
++ return PVR_ERROR_NOT_POSSIBLE;
++}
++
++PVR_ERROR CallMenuHook(const PVR_MENUHOOK &menuhook)
++{
++if (g_client == NULL)
++ return PVR_ERROR_SERVER_ERROR;
++
++return g_client->CallMenuHook(menuhook);
++}
++
++/*******************************************/
++/** PVR EPG Functions **/
++
++PVR_ERROR GetEPGForChannel(PVR_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd)
++{
++ if (g_client == NULL)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return g_client->GetEPGForChannel(handle, channel, iStart, iEnd);
++}
++
++/*******************************************/
++/** PVR Channel Functions **/
++
++int GetChannelsAmount()
++{
++ if (g_client == NULL)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return g_client->GetNumChannels();
++}
++
++PVR_ERROR GetChannels(PVR_HANDLE handle, bool bRadio)
++{
++ if (g_client == NULL)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return g_client->GetChannels(handle, bRadio);
++}
++
++PVR_ERROR DeleteChannel(const PVR_CHANNEL &channel)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++PVR_ERROR RenameChannel(const PVR_CHANNEL &channel)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++PVR_ERROR MoveChannel(const PVR_CHANNEL &channel)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++PVR_ERROR DialogChannelSettings(const PVR_CHANNEL &channel)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++PVR_ERROR DialogAddChannel(const PVR_CHANNEL &channel)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++
++/*******************************************/
++/** PVR Recording Functions **/
++
++int GetRecordingsAmount(void)
++{
++ if (g_client == NULL)
++ return 0;
++
++ return g_client->GetRecordingsAmount();
++}
++
++PVR_ERROR GetRecordings(PVR_HANDLE handle)
++{
++ if (g_client == NULL)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return g_client->GetRecordings(handle);
++}
++
++PVR_ERROR DeleteRecording(const PVR_RECORDING &recording)
++{
++ if (g_client == NULL)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return g_client->DeleteRecording(recording);
++}
++
++PVR_ERROR RenameRecording(const PVR_RECORDING &recording)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++/*******************************************/
++/** PVR Timer Functions **/
++
++int GetTimersAmount(void)
++{
++ if (g_client == NULL)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return g_client->GetTimersAmount();
++}
++
++PVR_ERROR GetTimers(PVR_HANDLE handle)
++{
++ if (g_client == NULL)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return g_client->GetTimers(handle);
++}
++
++PVR_ERROR AddTimer(const PVR_TIMER &timer)
++{
++ if (g_client == NULL)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return g_client->AddTimer(timer);
++}
++
++PVR_ERROR DeleteTimer(const PVR_TIMER &timer, bool bForceDelete)
++{
++ if (g_client == NULL)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return g_client->DeleteTimer(timer,bForceDelete);
++}
++
++PVR_ERROR UpdateTimer(const PVR_TIMER &timer)
++{
++ if (g_client == NULL)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return g_client->UpdateTimer(timer);
++}
++
++
++/*******************************************/
++/** PVR Live Stream Functions **/
++
++bool OpenLiveStream(const PVR_CHANNEL &channel)
++{
++ if (g_client == NULL)
++ return false;
++
++ return g_client->OpenLiveStream(channel);
++}
++
++void CloseLiveStream(void)
++{
++ if (g_client == NULL)
++ return;
++
++ g_client->CloseLiveStream();
++}
++
++int ReadLiveStream(unsigned char *pBuffer, unsigned int iBufferSize)
++{
++ if (g_client == NULL)
++ return -1;
++ int dataread=g_client->ReadLiveStream(pBuffer,iBufferSize);
++ return dataread;
++}
++
++int GetCurrentClientChannel()
++{
++ if (g_client == NULL)
++ return -1;
++ return g_client->GetCurrentClientChannel();
++}
++
++bool SwitchChannel(const PVR_CHANNEL &channelinfo)
++{
++ if (g_client == NULL)
++ return false;
++ if(g_client->SwitchChannel(channelinfo))
++ return true;
++ else
++ XBMC->QueueNotification(QUEUE_WARNING,"Failed to change channel. No free tuners?");
++ return false;
++}
++
++PVR_ERROR SignalStatus(PVR_SIGNAL_STATUS &signalStatus)
++{
++ if (g_client == NULL)
++ return PVR_ERROR_SERVER_ERROR;
++ return g_client->SignalStatus(signalStatus);
++}
++
++long long SeekLiveStream(long long iPosition, int iWhence)
++{
++ if (g_client == NULL)
++ return -1;
++ return g_client->SeekLiveStream(iPosition,iWhence);
++}
++
++long long PositionLiveStream(void)
++{
++ if (g_client == NULL)
++ return -1;
++ return g_client->SeekLiveStream(0,SEEK_CUR);
++
++}
++
++long long LengthLiveStream(void)
++{
++ if (g_client == NULL)
++ return -1;
++ return g_client->LengthLiveStream();
++}
++
++/*******************************************/
++/** PVR Recording Stream Functions **/
++
++bool OpenRecordedStream(const PVR_RECORDING &recinfo)
++{
++ if (g_client == NULL)
++ return false;
++
++ return g_client->OpenRecordedStream(recinfo);
++}
++
++void CloseRecordedStream(void)
++{
++ if (g_client == NULL)
++ return;
++
++ g_client->CloseRecordedStream();
++}
++
++int ReadRecordedStream(unsigned char *pBuffer, unsigned int iBufferSize)
++{
++ if (g_client == NULL)
++ return -1;
++
++ return g_client->ReadRecordedStream(pBuffer,iBufferSize);
++}
++
++long long SeekRecordedStream(long long iPosition, int iWhence)
++{
++ if (g_client == NULL)
++ return -1;
++ return g_client->SeekRecordedStream(iPosition,iWhence);
++}
++
++long long PositionRecordedStream(void)
++{
++ if (g_client == NULL)
++ return -1;
++ return g_client->SeekRecordedStream(0,SEEK_CUR);
++}
++
++long long LengthRecordedStream(void)
++{
++ if (g_client == NULL)
++ return -1;
++ return g_client->LengthRecordedStream();
++}
++
++
++/** UNUSED API FUNCTIONS */
++DemuxPacket* DemuxRead() { return NULL; }
++void DemuxAbort() {}
++void DemuxReset() {}
++void DemuxFlush() {}
++
++const char * GetLiveStreamURL(const PVR_CHANNEL &channelinfo) { return ""; }
++
++//@}
++/** @name PVR channel group methods */
++//@{
++
++/*!
++* @return The total amount of channel groups on the server or -1 on error.
++*/
++int GetChannelGroupsAmount(void)
++{
++ if (g_client == NULL)
++ return -1;
++ return g_client->GetChannelGroupsAmount();
++}
++
++/*!
++* @brief Request the list of all channel groups from the backend.
++* @param bRadio True to get the radio channel groups, false to get the TV channel groups.
++* @return PVR_ERROR_NO_ERROR if the list has been fetched successfully.
++*/
++PVR_ERROR GetChannelGroups(PVR_HANDLE handle, bool bRadio)
++{
++ if (g_client == NULL)
++ return PVR_ERROR_SERVER_ERROR;
++ return g_client->GetChannelGroups(handle,bRadio);
++
++}
++
++/*!
++* @brief Request the list of all group members of a group.
++* @param handle Callback.
++* @param group The group to get the members for.
++* @return PVR_ERROR_NO_ERROR if the list has been fetched successfully.
++*/
++PVR_ERROR GetChannelGroupMembers(PVR_HANDLE handle, const PVR_CHANNEL_GROUP &group){
++ if (g_client == NULL)
++ return PVR_ERROR_SERVER_ERROR;
++ return g_client->GetChannelGroupMembers(handle,group);
++}
++} //end extern "C"
+diff --git a/xbmc/pvrclients/mythtv-cmyth/client.h b/xbmc/pvrclients/mythtv-cmyth/client.h
+new file mode 100644
+index 0000000..2c199a1
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/client.h
+@@ -0,0 +1,67 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2009 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#ifndef CLIENT_H
++#define CLIENT_H
++
++#include "utils/StdString.h"
++#include "../../../addons/library.xbmc.addon/libXBMC_addon.h"
++#include "../../../addons/library.xbmc.pvr/libXBMC_pvr.h"
++#include "../../../addons/library.xbmc.gui/libXBMC_gui.h"
++#include "libcmyth.h"
++
++#define DEFAULT_HOST "127.0.0.1"
++#define DEFAULT_EXTRA_DEBUG false
++#define DEFAULT_LIVETV_PRIORITY false
++#define DEFAULT_PORT 6543
++#define DEFAULT_DB_USER "mythtv"
++#define DEFAULT_DB_PASSWORD "mythtv"
++#define DEFAULT_DB_NAME "mythconverg"
++#define DEFAULT_MIN_MOVIE_LENGTH 65
++#define DEFAULT_SERIES_REGEX "^(?<folder>.+?)::(?<title>.+)"
++#define DEFAULT_SERIES_IDENTIFIER ""
++
++extern bool g_bCreated; ///< Shows that the Create function was successfully called
++extern int g_iClientID; ///< The PVR client ID used by XBMC for this driver
++extern CStdString g_szUserPath; ///< The Path to the user directory inside user profile
++extern CStdString g_szClientPath; ///< The Path where this driver is located
++
++/* Client Settings */
++extern CStdString g_szHostname; ///< The Host name or IP of the mythtv server
++extern int g_iMythPort; ///< The mythtv Port (default is 6543)
++extern CStdString g_szMythDBuser; ///< The mythtv sql username (default is mythtv)
++extern CStdString g_szMythDBpassword; ///< The mythtv sql password (default is mythtv)
++extern CStdString g_szMythDBname; ///< The mythtv sql database name (default is mythconverg)
++extern bool g_bExtraDebug;
++extern bool g_bLiveTVPriority; ///< MythTV Backend setting to allow live TV to move scheduled shows
++
++extern int g_iMinMovieLength; ///< Minimum length (in minutes) of a recording to be considered to be a movie
++extern CStdString g_szSeriesRegEx; ///< The Regular expression to use to extract the series name (and maybe also episode number)
++extern CStdString g_szSeriesIdentifier; ///< The optional regular expression to use to detect series
++
++
++extern ADDON::CHelper_libXBMC_addon *XBMC;
++extern CHelper_libXBMC_pvr *PVR;
++extern CHelper_libcmyth *CMYTH;
++extern CHelper_libXBMC_gui *GUI;
++
++#endif /* CLIENT_H */
+diff --git a/xbmc/pvrclients/mythtv-cmyth/cppmyth.h b/xbmc/pvrclients/mythtv-cmyth/cppmyth.h
+new file mode 100644
+index 0000000..ea40975
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/cppmyth.h
+@@ -0,0 +1,12 @@
++#pragma once
++
++#include "cppmyth/MythChannel.h"
++#include "cppmyth/MythConnection.h"
++#include "cppmyth/MythDatabase.h"
++#include "cppmyth/MythEventHandler.h"
++#include "cppmyth/MythFile.h"
++#include "cppmyth/MythProgramInfo.h"
++#include "cppmyth/MythRecorder.h"
++#include "cppmyth/MythSignal.h"
++#include "cppmyth/MythTimer.h"
++#include "cppmyth/MythTimestamp.h"
+diff --git a/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythChannel.cpp b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythChannel.cpp
+new file mode 100644
+index 0000000..d732f8f
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythChannel.cpp
+@@ -0,0 +1,79 @@
++#include "MythChannel.h"
++#include "client.h"
++
++using namespace ADDON;
++
++/*
++* MythChannel
++*/
++MythChannel::MythChannel(cmyth_channel_t cmyth_channel,bool isRadio)
++ : m_channel_t(new MythPointer<cmyth_channel_t>()),m_radio(isRadio)
++{
++ *m_channel_t=(cmyth_channel);
++}
++
++
++MythChannel::MythChannel()
++ : m_channel_t(),m_radio(false)
++{
++}
++
++int MythChannel::ID()
++{
++ return CMYTH->ChannelChanid(*m_channel_t);
++}
++
++int MythChannel::NumberInt()
++{
++ return CMYTH->ChannelChannum(*m_channel_t);
++}
++
++CStdString MythChannel::Number()
++{
++ CStdString retval( CMYTH->ChannelChannumstr(*m_channel_t));
++ return retval;
++}
++
++ CStdString MythChannel::Callsign()
++{
++ CStdString retval( CMYTH->ChannelCallsign(*m_channel_t));
++ return retval;
++}
++
++int MythChannel::SourceID()
++{
++ return CMYTH->ChannelSourceid(*m_channel_t);
++}
++
++CStdString MythChannel::Name()
++{
++ char* cChan=CMYTH->ChannelName(*m_channel_t);
++ CStdString retval(cChan);
++ CMYTH->RefRelease(cChan);
++ return retval;
++}
++
++CStdString MythChannel::Icon()
++{
++ char* cIcon=CMYTH->ChannelIcon(*m_channel_t);
++ CStdString retval(cIcon);
++ CMYTH->RefRelease(cIcon);
++ return retval;
++}
++
++bool MythChannel::Visible()
++{
++ return CMYTH->ChannelVisible(*m_channel_t)>0;
++}
++
++bool MythChannel::IsRadio()
++{
++ return m_radio;
++}
++
++bool MythChannel::IsNull()
++{
++ if(m_channel_t==NULL)
++ return true;
++ return *m_channel_t==NULL;
++}
+\ No newline at end of file
+diff --git a/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythChannel.h b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythChannel.h
+new file mode 100644
+index 0000000..f06164e
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythChannel.h
+@@ -0,0 +1,26 @@
++#pragma once
++
++#include "MythPointer.h"
++#include "libcmyth.h"
++#include <boost/shared_ptr.hpp>
++#include "utils/StdString.h"
++
++class MythChannel
++{
++public:
++ MythChannel();
++ MythChannel(cmyth_channel_t cmyth_channel,bool isRadio);
++ int ID();
++ int NumberInt();
++ CStdString Number();
++ CStdString Callsign();
++ int SourceID();
++ CStdString Name();
++ CStdString Icon();
++ bool Visible();
++ bool IsRadio();
++ bool IsNull();
++private:
++ boost::shared_ptr< MythPointer< cmyth_channel_t > > m_channel_t;
++ bool m_radio;
++};
+\ No newline at end of file
+diff --git a/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythConnection.cpp b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythConnection.cpp
+new file mode 100644
+index 0000000..ae4c073
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythConnection.cpp
+@@ -0,0 +1,317 @@
++#include "MythConnection.h"
++#include "MythRecorder.h"
++#include "MythFile.h"
++#include "MythSGFile.h"
++#include "MythProgramInfo.h"
++#include "MythEventHandler.h"
++#include "MythTimer.h"
++#include "client.h"
++
++using namespace ADDON;
++
++
++/*
++* MythConnection
++*/
++
++MythConnection::MythConnection():
++m_conn_t(new MythPointerThreadSafe<cmyth_conn_t>()),m_server(""),m_port(0)
++{
++}
++
++
++MythConnection::MythConnection(CStdString server,unsigned short port):
++m_conn_t(new MythPointerThreadSafe<cmyth_conn_t>),m_server(server),m_port(port)
++{
++ char *cserver=strdup(server.c_str());
++ cmyth_conn_t connection=CMYTH->ConnConnectCtrl(cserver,port,64*1024, 16*1024);
++ free(cserver);
++ *m_conn_t=(connection);
++
++}
++
++std::vector< CStdString > MythConnection::GetStorageGroupFileList_(CStdString sgGetList)
++ {
++ std::vector< CStdString > retval;
++ Lock();
++ char **sg;
++ CStdString bckHostNme = GetBackendHostname();
++ int len = CMYTH->StoragegroupFilelist(*m_conn_t,&sg,sgGetList.Buffer(),bckHostNme.Buffer());
++ if(!sg)
++ return retval;
++ for(int i=0;i<len;i++)
++ {
++ char *tmp=sg[i];
++ CStdString tmpSG(tmp);
++ XBMC->Log(LOG_DEBUG,"%s - ############################# - %s",__FUNCTION__,tmpSG.c_str());
++ retval.push_back(tmpSG/*.c_str()*/);
++ }
++
++ CMYTH->RefRelease(sg);
++ Unlock();
++ return retval;
++ }
++
++std::vector< MythSGFile > MythConnection::GetStorageGroupFileList(CStdString storagegroup)
++{
++ Lock();
++ CStdString hostname = GetBackendHostname();
++ cmyth_storagegroup_filelist_t filelist=CMYTH->StoragegroupGetFilelist(*m_conn_t,storagegroup.Buffer(),hostname.Buffer());
++ int len=CMYTH->StoragegroupFilelistCount(filelist);
++ std::vector< MythSGFile > retval(len);
++ for(int i=0;i<len;i++)
++ {
++ cmyth_storagegroup_file_t file=CMYTH->StoragegroupFilelistGetItem(filelist,i);
++ retval.push_back(MythSGFile(file));
++ }
++ CMYTH->RefRelease(filelist);
++ Unlock();
++ return retval;
++}
++
++MythFile MythConnection::ConnectPath(CStdString filename, CStdString storageGroup)
++{
++ Lock();
++ MythFile retval = MythFile(CMYTH->ConnConnectPath(filename.Buffer(),*m_conn_t,64*1024, 16*1024,storageGroup.Buffer()),*this);
++ Unlock();
++ return retval;
++}
++
++ bool MythConnection::IsConnected()
++{
++ return *m_conn_t!=0;
++}
++
++MythRecorder MythConnection::GetFreeRecorder()
++{
++ Lock();
++ MythRecorder retval = MythRecorder(CMYTH->ConnGetFreeRecorder(*m_conn_t),*this);
++ Unlock();
++ return retval;
++}
++
++MythRecorder MythConnection::GetRecorder(int n)
++{
++ Lock();
++ MythRecorder retval = MythRecorder(CMYTH->ConnGetRecorderFromNum(*m_conn_t,n),*this);
++ Unlock();
++ return retval;
++}
++
++ boost::unordered_map<CStdString, MythProgramInfo> MythConnection::GetRecordedPrograms()
++ {
++ Lock();
++ boost::unordered_map<CStdString, MythProgramInfo> retval;
++ cmyth_proglist_t proglist=CMYTH->ProglistGetAllRecorded(*m_conn_t);
++ int len=CMYTH->ProglistGetCount(proglist);
++ for(int i=0;i<len;i++)
++ {
++ cmyth_proginfo_t cmprog=CMYTH->ProglistGetItem(proglist,i);
++ MythProgramInfo prog=CMYTH->ProginfoGetDetail(*m_conn_t,cmprog);
++ CStdString path=prog.Path();
++ retval.insert(std::pair<CStdString,MythProgramInfo>(path.c_str(),prog));
++ }
++ CMYTH->RefRelease(proglist);
++ Unlock();
++ return retval;
++ }
++
++ boost::unordered_map<CStdString, MythProgramInfo> MythConnection::GetPendingPrograms()
++ {
++ Lock();
++ boost::unordered_map<CStdString, MythProgramInfo> retval;
++ cmyth_proglist_t proglist=CMYTH->ProglistGetAllPending(*m_conn_t);
++ int len=CMYTH->ProglistGetCount(proglist);
++ for(int i=0;i<len;i++)
++ {
++ MythProgramInfo prog=CMYTH->ProglistGetItem(proglist,i);
++ CStdString filename;
++ filename.Format("%i_%i",prog.ChannelID(),prog.StartTime());
++ retval.insert(std::pair<CStdString,MythProgramInfo>(filename.c_str(),prog));
++ }
++ CMYTH->RefRelease(proglist);
++ Unlock();
++ return retval;
++ }
++
++ boost::unordered_map<CStdString, MythProgramInfo> MythConnection::GetScheduledPrograms()
++ {
++ Lock();
++ boost::unordered_map<CStdString, MythProgramInfo> retval;
++ cmyth_proglist_t proglist=CMYTH->ProglistGetAllScheduled(*m_conn_t);
++ int len=CMYTH->ProglistGetCount(proglist);
++ for(int i=0;i<len;i++)
++ {
++ cmyth_proginfo_t cmprog=CMYTH->ProglistGetItem(proglist,i);
++ MythProgramInfo prog=CMYTH->ProginfoGetDetail(*m_conn_t,cmprog);//Release cmprog????
++ CStdString path=prog.Path();
++ retval.insert(std::pair<CStdString,MythProgramInfo>(path.c_str(),prog));
++ }
++ CMYTH->RefRelease(proglist);
++ Unlock();
++ return retval;
++ }
++
++ bool MythConnection::DeleteRecording(MythProgramInfo &recording)
++ {
++ Lock();
++ bool retval = CMYTH->ProginfoDeleteRecording(*m_conn_t,*recording.m_proginfo_t)==0;
++ Unlock();
++ return retval;
++ }
++
++
++MythEventHandler MythConnection::CreateEventHandler()
++{
++ return MythEventHandler(m_server,m_port);
++}
++
++CStdString MythConnection::GetServer()
++{
++ return m_server;
++}
++
++int MythConnection::GetProtocolVersion()
++{
++ Lock();
++ int retval = CMYTH->ConnGetProtocolVersion(*m_conn_t);
++ Unlock();
++ return retval;
++}
++
++bool MythConnection::GetDriveSpace(long long &total,long long &used)
++{
++ Lock();
++ bool retval = CMYTH->ConnGetFreespace(*m_conn_t,&total,&used)==0;
++ Unlock();
++ return retval;
++}
++
++bool MythConnection::UpdateSchedules(int id)
++{
++ Lock();
++ CStdString cmd;
++ cmd.Format("RESCHEDULE_RECORDINGS %i",id);
++ bool retval = CMYTH->ScheduleRecording(*m_conn_t,cmd.Buffer())>=0;
++ Unlock();
++ return retval;
++}
++
++MythFile MythConnection::ConnectFile(MythProgramInfo &recording)
++{
++ Lock();
++ MythFile retval = MythFile(CMYTH->ConnConnectFile(*recording.m_proginfo_t,*m_conn_t,64*1024, 16*1024),*this);
++ Unlock();
++ return retval;
++}
++
++bool MythConnection::IsNull()
++{
++ if(m_conn_t==NULL)
++ return true;
++ return *m_conn_t==NULL;
++}
++
++void MythConnection::Lock()
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"Lock %i",m_conn_t.get());
++ m_conn_t->Lock();
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"Lock acquired %i",m_conn_t.get());
++}
++
++void MythConnection::Unlock()
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"Unlock %i",m_conn_t.get());
++ m_conn_t->Unlock();
++
++}
++
++ CStdString MythConnection::GetSetting(CStdString hostname,CStdString setting)
++ {
++ CStdString retval;
++ Lock();
++ char * value = CMYTH->ConnGetSetting(*m_conn_t,hostname.Buffer(),setting.Buffer());
++ retval = value;
++ CMYTH->RefRelease(value);
++ value = NULL;
++ Unlock();
++ return retval;
++ }
++
++ bool MythConnection::SetSetting(CStdString hostname,CStdString setting,CStdString value)
++ {
++ bool retval = false;
++ Lock();
++ retval = CMYTH->ConnSetSetting(*m_conn_t,hostname.Buffer(),setting.Buffer(),value.Buffer()) >= 0;
++ Unlock();
++ return retval;
++ }
++
++ CStdString MythConnection::GetHostname()
++ {
++ CStdString retval;
++ Lock();
++ char * hostname = CMYTH->ConnGetClientHostname(*m_conn_t);
++ retval = hostname;
++ CMYTH->RefRelease(hostname);
++ hostname = NULL;
++ Unlock();
++ return retval;
++ }
++
++ CStdString MythConnection::GetBackendHostname()
++ {
++ CStdString retval;
++ Lock();
++ char * hostname = CMYTH->ConnGetBackendHostname(*m_conn_t);
++ retval = hostname;
++ CMYTH->RefRelease(hostname);
++ hostname = NULL;
++ Unlock();
++ return retval;
++ }
++
++
++
++/*
++Profile = Default
++recpriority = 0
++maxepisodes = 0
++maxnewest = 0
++recgroup = Default
++dupmethod = 6
++dupin = 15
++playgroup = Default
++storagegroup = Default
++
++Defaults for MythTimer
++AutoTranscode / DefaultTranscoder
++AutoRunUserJob1
++AutoRunUserJob2
++AutoRunUserJob3
++AutoRunUserJob4
++autocommflag => AutoCommercialFlag
++AutoExpireDefault
++transcoder => DefaultTranscoder??
++
++start/endoffset => DefaultStartOffset/DefaultEndOffset
++
++*/
++
++ void MythConnection::DefaultTimer(MythTimer &timer)
++ {
++ timer.AutoTranscode(atoi(GetSetting("NULL","AutoTranscode").c_str())>0);
++ timer.Userjob(1,atoi(GetSetting("NULL","AutoRunUserJob1").c_str())>0);
++ timer.Userjob(2,atoi(GetSetting("NULL","AutoRunUserJob2").c_str())>0);
++ timer.Userjob(3,atoi(GetSetting("NULL","AutoRunUserJob3").c_str())>0);
++ timer.Userjob(4,atoi(GetSetting("NULL","AutoRunUserJob4").c_str())>0);
++ timer.AutoCommFlag(atoi(GetSetting("NULL","AutoCommercialFlag").c_str())>0);
++ timer.AutoExpire(atoi(GetSetting("NULL","AutoExpireDefault").c_str())>0);
++ timer.Transcoder(atoi(GetSetting("NULL","DefaultTranscoder").c_str()));
++ timer.StartOffset(atoi(GetSetting("NULL","DefaultStartOffset").c_str()));
++ timer.StartOffset(atoi(GetSetting("NULL","DefaultEndOffset").c_str()));
++
++ }
+diff --git a/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythConnection.h b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythConnection.h
+new file mode 100644
+index 0000000..ce8b55a
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythConnection.h
+@@ -0,0 +1,51 @@
++#pragma once
++
++#include "libcmyth.h"
++#include "utils/StdString.h"
++#include <boost/shared_ptr.hpp>
++#include <boost/unordered_map.hpp>
++#include "MythPointer.h"
++
++class MythRecorder;
++class MythFile;
++class MythProgramInfo;
++class MythEventHandler;
++class MythTimer;
++class MythSGFile;
++
++
++class MythConnection
++{
++public:
++ MythConnection();
++ MythConnection(CStdString server,unsigned short port);
++ MythRecorder GetFreeRecorder();
++ MythRecorder GetRecorder(int n);
++ MythEventHandler CreateEventHandler();
++ boost::unordered_map< CStdString, MythProgramInfo > GetRecordedPrograms();
++ boost::unordered_map< CStdString, MythProgramInfo > GetPendingPrograms();
++ boost::unordered_map< CStdString, MythProgramInfo > GetScheduledPrograms();
++ bool DeleteRecording(MythProgramInfo &recording);
++ bool IsConnected();
++ CStdString GetServer();
++ int GetProtocolVersion();
++ bool GetDriveSpace(long long &total,long long &used);
++ bool UpdateSchedules(int id);
++ MythFile ConnectFile(MythProgramInfo &recording);
++ bool IsNull();
++ void Lock();
++ void Unlock();
++ CStdString GetSetting(CStdString hostname,CStdString setting);
++ bool SetSetting(CStdString hostname,CStdString setting,CStdString value);
++ CStdString GetHostname();
++ CStdString GetBackendHostname();
++ void DefaultTimer(MythTimer &timer);
++ MythFile ConnectPath(CStdString filename, CStdString storageGroup);
++ std::vector< CStdString > GetStorageGroupFileList_(CStdString sgGetList);
++ std::vector< MythSGFile > GetStorageGroupFileList(CStdString storagegroup);
++
++private:
++ boost::shared_ptr< MythPointerThreadSafe< cmyth_conn_t > > m_conn_t;
++ CStdString m_server;
++ unsigned short m_port;
++};
+diff --git a/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythDatabase.cpp b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythDatabase.cpp
+new file mode 100644
+index 0000000..fe02145
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythDatabase.cpp
+@@ -0,0 +1,224 @@
++#include "MythDatabase.h"
++#include "MythChannel.h"
++#include "MythTimer.h"
++#include "client.h"
++
++using namespace ADDON;
++
++/*
++* MythDatabase
++*/
++
++MythDatabase::MythDatabase()
++ :m_database_t()
++{
++}
++
++
++MythDatabase::MythDatabase(CStdString server,CStdString database,CStdString user,CStdString password):
++m_database_t(new MythPointerThreadSafe<cmyth_database_t>())
++{
++ char *cserver=strdup(server.c_str());
++ char *cdatabase=strdup(database.c_str());
++ char *cuser=strdup(user.c_str());
++ char *cpassword=strdup(password.c_str());
++
++ *m_database_t=(CMYTH->DatabaseInit(cserver,cdatabase,cuser,cpassword));
++
++ free(cserver);
++ free(cdatabase);
++ free(cuser);
++ free(cpassword);
++}
++
++bool MythDatabase::TestConnection(CStdString &msg)
++{
++ char* cmyth_msg;
++ m_database_t->Lock();
++ bool retval=CMYTH->MysqlTestdbConnection(*m_database_t,&cmyth_msg)==1;
++ msg=cmyth_msg;
++ free(cmyth_msg);
++ m_database_t->Unlock();
++ return retval;
++}
++
++
++std::map<int,MythChannel> MythDatabase::ChannelList()
++{
++ std::map<int,MythChannel> retval;
++ m_database_t->Lock();
++ cmyth_chanlist_t cChannels=CMYTH->MysqlGetChanlist(*m_database_t);
++ m_database_t->Unlock();
++ int nChannels=CMYTH->ChanlistGetCount(cChannels);
++ for(int i=0;i<nChannels;i++)
++ {
++ cmyth_channel_t chan=CMYTH->ChanlistGetItem(cChannels,i);
++ int chanid=CMYTH->ChannelChanid(chan);
++ retval.insert(std::pair<int,MythChannel>(chanid,MythChannel(chan,1==CMYTH->MysqlIsRadio(*m_database_t,chanid))));
++ }
++ CMYTH->RefRelease(cChannels);
++ return retval;
++}
++
++std::vector<MythProgram> MythDatabase::GetGuide(time_t starttime, time_t endtime)
++{
++ MythProgram *programs=0;
++ m_database_t->Lock();
++ int len=CMYTH->MysqlGetGuide(*m_database_t,&programs,starttime,endtime);
++ m_database_t->Unlock();
++ if(len<1)
++ return std::vector<MythProgram>();
++ std::vector<MythProgram> retval(programs,programs+len);
++ CMYTH->RefRelease(programs);
++ return retval;
++}
++
++std::map<int, MythTimer> MythDatabase::GetTimers()
++{
++ std::map<int, MythTimer> retval;
++ m_database_t->Lock();
++ cmyth_timerlist_t timers=CMYTH->MysqlGetTimers(*m_database_t);
++ m_database_t->Unlock();
++ int nTimers=CMYTH->TimerlistGetCount(timers);
++ for(int i=0;i<nTimers;i++)
++ {
++ cmyth_timer_t timer=CMYTH->TimerlistGetItem(timers,i);
++ MythTimer t(timer);
++ retval.insert(std::pair<int,MythTimer>(t.RecordID(),t));
++ }
++ CMYTH->RefRelease(timers);
++ return retval;
++}
++
++std::vector<MythRecordingProfile > MythDatabase::GetRecordingProfiles()
++{
++ std::vector<MythRecordingProfile > retval;
++ cmyth_recprofile* cmythProfiles;
++ m_database_t->Lock();
++ int len = CMYTH->MysqlGetRecprofiles(*m_database_t,&cmythProfiles);
++ for(int i=0;i<len;i++)
++ {
++ std::vector<MythRecordingProfile >::iterator it = std::find(retval.begin(),retval.end(),CStdString(cmythProfiles[i].cardtype));
++ if(it == retval.end())
++ {
++ retval.push_back(MythRecordingProfile());
++ it = --retval.end();
++ it->Format("%s",cmythProfiles[i].cardtype);
++ }
++ it->profile.insert(std::pair<int, CStdString >(cmythProfiles[i].id,cmythProfiles[i].name));
++ }
++ CMYTH->RefRelease(cmythProfiles);
++ m_database_t->Unlock();
++ return retval;
++}
++
++bool MythDatabase::GetWatchedStatus(int recordid)
++{
++ m_database_t->Lock();
++ bool watched = CMYTH->GetWatchedStatusMysql(*m_database_t,recordid)==1;
++ m_database_t->Unlock();
++ return watched;
++}
++
++int MythDatabase::SetWatchedStatus(int recordid,bool watched)
++{
++ m_database_t->Lock();
++ int retval = CMYTH->SetWatchedStatusMysql(*m_database_t,recordid, watched?1:0);
++ m_database_t->Unlock();
++ return retval;
++}
++
++int MythDatabase::AddTimer(MythTimer &timer)
++{
++
++ m_database_t->Lock();
++
++ int retval=CMYTH->MysqlAddTimer(*m_database_t,timer.ChanID(),timer.m_callsign.Buffer(),timer.m_description.Buffer(),timer.StartTime(), timer.EndTime(),timer.m_title.Buffer(),timer.m_category.Buffer(),
++ timer.Type(),timer.m_subtitle.Buffer(),timer.Priority(),timer.StartOffset(),timer.EndOffset(),timer.SearchType(),timer.Inactive()?1:0,timer.DupMethod(),timer.CheckDupIn(),timer.RecGroup().Buffer(),
++ timer.StoreGroup().Buffer(),timer.PlayGroup().Buffer(),timer.AutoTranscode(),timer.Userjobs(),timer.AutoCommFlag(),timer.AutoExpire(),timer.MaxEpisodes(),timer.NewExpireOldRecord(),timer.Transcoder());
++ timer.m_recordid=retval;
++ m_database_t->Unlock();
++ return retval;
++}
++
++ bool MythDatabase::DeleteTimer(int recordid)
++ {
++ m_database_t->Lock();
++ bool retval= CMYTH->MysqlDeleteTimer(*m_database_t,recordid)==0;
++ m_database_t->Unlock();
++ return retval;
++ }
++
++ bool MythDatabase::UpdateTimer(MythTimer &timer)
++ {
++ m_database_t->Lock();
++ bool retval = CMYTH->MysqlUpdateTimer(*m_database_t,timer.RecordID(),timer.ChanID(),timer.m_callsign.Buffer(),timer.m_description.Buffer(),timer.StartTime(), timer.EndTime(),timer.m_title.Buffer(),
++ timer.m_category.Buffer(),timer.Type(),timer.m_subtitle.Buffer(),timer.Priority(),timer.StartOffset(),timer.EndOffset(),timer.SearchType(),timer.Inactive()?1:0,timer.DupMethod(),timer.CheckDupIn(),timer.RecGroup().Buffer(),
++ timer.StoreGroup().Buffer(),timer.PlayGroup().Buffer(),timer.AutoTranscode(),timer.Userjobs(),timer.AutoCommFlag(),timer.AutoExpire(),timer.MaxEpisodes(),timer.NewExpireOldRecord(),timer.Transcoder())==0;
++ m_database_t->Unlock();
++ return retval;
++ }
++
++ bool MythDatabase::FindProgram(const time_t starttime,const int channelid,CStdString &title,MythProgram* pprogram)
++ {
++ m_database_t->Lock();
++ bool retval=CMYTH->MysqlGetProgFinderTimeTitleChan(*m_database_t,pprogram,title.Buffer(),starttime,channelid)>0;
++ m_database_t->Unlock();
++ return retval;
++ }
++
++ boost::unordered_map< CStdString, std::vector< int > > MythDatabase::GetChannelGroups()
++ {
++ boost::unordered_map< CStdString, std::vector< int > > retval;
++ m_database_t->Lock();
++ cmyth_channelgroups_t *cg =0;
++ int len = CMYTH->MysqlGetChannelgroups(*m_database_t,&cg);
++ if(!cg)
++ return retval;
++ for(int i=0;i<len;i++)
++ {
++ MythChannelGroup changroup;
++ changroup.first=cg[i].channelgroup;
++ int* chanid=0;
++ int numchan=CMYTH->MysqlGetChannelidsInGroup(*m_database_t,cg[i].ID,&chanid);
++ if(numchan)
++ {
++ changroup.second=std::vector<int>(chanid,chanid+numchan);
++ CMYTH->RefRelease(chanid);
++ }
++ else
++ changroup.second=std::vector<int>();
++
++ retval.insert(changroup);
++ }
++ CMYTH->RefRelease(cg);
++ m_database_t->Unlock();
++ return retval;
++ }
++
++ std::map< int, std::vector< int > > MythDatabase::SourceList()
++ {
++ std::map< int, std::vector< int > > retval;
++ cmyth_rec_t *r=0;
++ m_database_t->Lock();
++ int len=CMYTH->MysqlGetRecorderList(*m_database_t,&r);
++ for(int i=0;i<len;i++)
++ {
++ std::map< int, std::vector< int > >::iterator it=retval.find(r[i].sourceid);
++ if(it!=retval.end())
++ it->second.push_back(r[i].recid);
++ else
++ retval[r[i].sourceid]=std::vector<int>(1,r[i].recid);
++ }
++ CMYTH->RefRelease(r);
++ r=0;
++ m_database_t->Unlock();
++ return retval;
++ }
++
++ bool MythDatabase::IsNull()
++ {
++ if(m_database_t==NULL)
++ return true;
++ return *m_database_t==NULL;
++ }
+diff --git a/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythDatabase.h b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythDatabase.h
+new file mode 100644
+index 0000000..f045bdc
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythDatabase.h
+@@ -0,0 +1,45 @@
++#pragma once
++
++#include "libcmyth.h"
++#include <vector>
++#include <map>
++#include <boost/unordered_map.hpp>
++#include <boost/shared_ptr.hpp>
++#include "utils/StdString.h"
++#include "MythPointer.h"
++
++class MythChannel;
++class MythTimer;
++
++typedef cmyth_program_t MythProgram;
++typedef std::pair< CStdString, std::vector< int > > MythChannelGroup;
++//typedef std::vector< MythChannel > MythChannelList;
++
++class MythRecordingProfile : public CStdString{
++public:
++ std::map< int, CStdString > profile;
++};
++
++
++class MythDatabase
++{
++public:
++ MythDatabase();
++ MythDatabase(CStdString server,CStdString database,CStdString user,CStdString password);
++ bool TestConnection(CStdString &msg);
++ std::map< int , MythChannel > ChannelList();
++ std::vector< MythProgram > GetGuide(time_t starttime, time_t endtime);
++ std::map<int, MythTimer > GetTimers();
++ bool FindProgram(const time_t starttime,const int channelid,CStdString &title,MythProgram* pprogram);
++ int AddTimer(MythTimer &timer);
++ bool DeleteTimer(int recordid);
++ bool UpdateTimer(MythTimer &timer);
++ boost::unordered_map< CStdString, std::vector< int > > GetChannelGroups();
++ std::map< int, std::vector< int > > SourceList();
++ bool IsNull();
++ std::vector<MythRecordingProfile > GetRecordingProfiles();
++ int SetWatchedStatus(int recordid,bool watched);
++ bool GetWatchedStatus(int recordid);
++private:
++ boost::shared_ptr< MythPointerThreadSafe< cmyth_database_t > > m_database_t;
++};
+\ No newline at end of file
+diff --git a/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythEventHandler.cpp b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythEventHandler.cpp
+new file mode 100644
+index 0000000..51b0f54
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythEventHandler.cpp
+@@ -0,0 +1,258 @@
++#include "MythEventHandler.h"
++#include "MythRecorder.h"
++#include "MythSignal.h"
++#include "client.h"
++
++using namespace ADDON;
++/*
++ * Tokenizer
++ */
++
++template < class ContainerT >
++void tokenize(const std::string& str, ContainerT& tokens,
++ const std::string& delimiters = " ", const bool trimEmpty = false)
++{
++ std::string::size_type pos, lastPos = 0;
++ while(true)
++ {
++ pos = str.find_first_of(delimiters, lastPos);
++ if(pos == std::string::npos)
++ {
++ pos = str.length();
++
++ if(pos != lastPos || !trimEmpty)
++ tokens.push_back(typename ContainerT::value_type(str.data()+lastPos,(pos)-lastPos ));
++ //static_cast<ContainerT::value_type::size_type>*/(pos)-lastPos ));
++
++ break;
++ }
++ else
++ {
++ if(pos != lastPos || !trimEmpty)
++ tokens.push_back(typename ContainerT::value_type(str.data()+lastPos,(pos)-lastPos ));
++ //static_cast<ContainerT::value_type::size_type>(pos)-lastPos ));
++ }
++
++ lastPos = pos + 1;
++ }
++};
++
++/*
++* MythEventHandler
++*/
++
++class MythEventHandler::ImpMythEventHandler : public CThread, public CMutex
++{
++ friend class MythEventHandler;
++public:
++ ImpMythEventHandler(CStdString server,unsigned short port);
++ MythRecorder m_rec;
++ MythSignal m_signal;
++ MythFile m_file;
++ CStdString curRecordingId;
++ void UpdateFilesize(CStdString signal);
++ void SetRecEventListener(MythFile &file, CStdString recId);
++ cmyth_conn_t m_conn_t;
++ virtual void* Process(void);
++ virtual ~ImpMythEventHandler();
++ void UpdateSignal(CStdString &signal);
++ };
++
++MythEventHandler::ImpMythEventHandler::ImpMythEventHandler(CStdString server,unsigned short port)
++:m_rec(MythRecorder()),m_conn_t(0),CThread(),m_signal(),CMutex()
++ {
++ char *cserver=strdup(server.c_str());
++ cmyth_conn_t connection=CMYTH->ConnConnectEvent(cserver,port,64*1024, 16*1024);
++ free(cserver);
++ m_conn_t=connection;
++ }
++
++
++ MythEventHandler::ImpMythEventHandler::~ImpMythEventHandler()
++ {
++ StopThread(30);
++ CMYTH->RefRelease(m_conn_t);
++ m_conn_t=0;
++ }
++
++MythEventHandler::MythEventHandler(CStdString server,unsigned short port)
++ :m_imp(new ImpMythEventHandler(server,port))
++{
++ m_imp->CreateThread();
++}
++
++MythEventHandler::MythEventHandler()
++ :m_imp()
++{
++}
++
++void MythEventHandler::Stop()
++{
++ m_imp.reset();
++}
++
++void MythEventHandler::PreventLiveChainUpdate()
++{
++ m_imp->Lock();
++}
++
++void MythEventHandler::AllowLiveChainUpdate()
++{
++ m_imp->Unlock();
++}
++
++void MythEventHandler::SetRecorder(MythRecorder &rec)
++{
++ m_imp->Lock();
++ m_imp->m_rec=rec;
++ m_imp->Unlock();
++}
++
++MythSignal MythEventHandler::GetSignal()
++{
++ return m_imp->m_signal;
++}
++
++void MythEventHandler::SetRecordingListener ( MythFile &file, CStdString recId )
++{
++ m_imp->Lock();
++ m_imp->SetRecEventListener(file,recId);
++ m_imp->Unlock();
++}
++
++void MythEventHandler::ImpMythEventHandler::SetRecEventListener ( MythFile &file, CStdString recId )
++{
++ m_file = file;
++ char b [20];
++ sscanf(recId.c_str(),"/%4s_%14s",b,b+4);
++ CStdString uniqId=b;
++ curRecordingId = uniqId;
++}
++
++void MythEventHandler::ImpMythEventHandler::UpdateFilesize(CStdString signal)
++{
++ long long length;
++ char b [20];
++ sscanf(signal.c_str(),"%4s %4s-%2s-%2sT%2s:%2s:%2s %lli",b,b+4,b+8,b+10,b+12,b+14,b+16,&length);
++
++ CStdString uniqId=b;
++
++ if (curRecordingId.compare(uniqId) == 0) {
++ XBMC->Log(LOG_DEBUG,"EVENT: %s, --UPDATING CURRENT RECORDING LENGTH-- EVENT msg: %s %ll",
++ __FUNCTION__,uniqId.c_str(),length);
++ m_file.UpdateDuration(length);
++ }
++}
++
++void MythEventHandler::ImpMythEventHandler::UpdateSignal(CStdString &signal)
++{
++ std::vector< std::string > tok;
++ tokenize<std::vector< std::string > >( signal, tok, ";");
++
++ for(std::vector<std::string>::iterator it=tok.begin();it!=tok.end();it++)
++ {
++ std::vector< std::string > tok2;
++ tokenize< std::vector< std::string > >(*it,tok2," ");
++ if(tok2.size()>=2)
++ {
++ if(tok2[0]=="slock")
++ {
++ m_signal.m_AdapterStatus=tok2[1]=="1"?"Locked":"No lock";
++ }
++ else if(tok2[0]=="signal")
++ {
++ m_signal.m_Signal=atoi(tok2[1].c_str());
++ }
++ else if(tok2[0]=="snr")
++ {
++ m_signal.m_SNR=std::atoi(tok2[1].c_str());
++ }
++ else if(tok2[0]=="ber")
++ {
++ m_signal.m_BER=std::atoi(tok2[1].c_str());
++ }
++ else if(tok2[0]=="ucb")
++ {
++ m_signal.m_UNC=std::atoi(tok2[1].c_str());
++ }
++ }
++ }
++}
++
++void* MythEventHandler::ImpMythEventHandler::Process(void)
++{
++ const char* events[]={ "CMYTH_EVENT_UNKNOWN",\
++ "CMYTH_EVENT_CLOSE",\
++ "CMYTH_EVENT_RECORDING_LIST_CHANGE",\
++ "CMYTH_EVENT_RECORDING_LIST_CHANGE_ADD",\
++ "CMYTH_EVENT_RECORDING_LIST_CHANGE_UPDATE",\
++ "CMYTH_EVENT_RECORDING_LIST_CHANGE_DELETE",\
++ "CMYTH_EVENT_SCHEDULE_CHANGE",\
++ "CMYTH_EVENT_DONE_RECORDING",\
++ "CMYTH_EVENT_QUIT_LIVETV",\
++ "CMYTH_EVENT_WATCH_LIVETV",\
++ "CMYTH_EVENT_LIVETV_CHAIN_UPDATE",\
++ "CMYTH_EVENT_SIGNAL",\
++ "CMYTH_EVENT_ASK_RECORDING",\
++ "CMYTH_EVENT_SYSTEM_EVENT",\
++ "CMYTH_EVENT_UPDATE_FILE_SIZE",\
++ "CMYTH_EVENT_GENERATED_PIXMAP",\
++ "CMYTH_EVENT_CLEAR_SETTINGS_CACHE"};
++ cmyth_event_t myth_event;
++ char databuf[2049];
++ databuf[0]=0;
++ timeval timeout;
++ timeout.tv_sec=0;
++ timeout.tv_usec=100000;
++
++ while(!IsStopped())
++ {
++
++ if(CMYTH->EventSelect(m_conn_t,&timeout)>0)
++ {
++ myth_event=CMYTH->EventGet(m_conn_t,databuf,2048);
++ XBMC->Log(LOG_DEBUG,"EVENT ID: %s, EVENT databuf: %s",events[myth_event],databuf);
++ if(myth_event==CMYTH_EVENT_UPDATE_FILE_SIZE)
++ {
++ CStdString signal=databuf;
++ UpdateFilesize(signal);
++ //1044 2012-03-20T20:00:00 54229688
++ XBMC->Log(LOG_NOTICE,"%s: FILE_SIZE_UPDATE: %i",__FUNCTION__,databuf);
++ }
++ if(myth_event==CMYTH_EVENT_LIVETV_CHAIN_UPDATE)
++ {
++ Lock();
++ if(!m_rec.IsNull())
++ {
++ bool retval=m_rec.LiveTVChainUpdate(CStdString(databuf));
++ XBMC->Log(LOG_NOTICE,"%s: CHAIN_UPDATE: %i",__FUNCTION__,retval);
++ }
++ else
++ XBMC->Log(LOG_NOTICE,"%s: CHAIN_UPDATE - No recorder",__FUNCTION__);
++ Unlock();
++ }
++ if(myth_event==CMYTH_EVENT_SIGNAL)
++ {
++ CStdString signal=databuf;
++ UpdateSignal(signal);
++ }
++ if(myth_event==CMYTH_EVENT_SCHEDULE_CHANGE)
++ {
++ XBMC->Log(LOG_NOTICE,"Schedule change",__FUNCTION__);
++ PVR->TriggerTimerUpdate();
++ PVR->TriggerRecordingUpdate();
++ }
++ if(myth_event==CMYTH_EVENT_RECORDING_LIST_CHANGE_ADD||myth_event==CMYTH_EVENT_RECORDING_LIST_CHANGE_DELETE||myth_event==CMYTH_EVENT_RECORDING_LIST_CHANGE_UPDATE||myth_event==CMYTH_EVENT_RECORDING_LIST_CHANGE)
++ {
++ XBMC->Log(LOG_NOTICE,"Recording list change",__FUNCTION__);
++ PVR->TriggerRecordingUpdate();
++ }
++ databuf[0]=0;
++
++ }
++ //Restore timeout
++ timeout.tv_sec=0;
++ timeout.tv_usec=100000;
++ }
++ return NULL;
++}
+diff --git a/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythEventHandler.h b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythEventHandler.h
+new file mode 100644
+index 0000000..f341767
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythEventHandler.h
+@@ -0,0 +1,26 @@
++#pragma once
++
++#include "libcmyth.h"
++#include "utils/StdString.h"
++#include <boost/shared_ptr.hpp>
++#include "MythPointer.h"
++#include "MythFile.h"
++
++class MythRecorder;
++class MythSignal;
++
++class MythEventHandler
++{
++public:
++ MythEventHandler();
++ MythEventHandler(CStdString, unsigned short port);
++ void SetRecorder(MythRecorder &rec);
++ MythSignal GetSignal();
++ void Stop();
++ void PreventLiveChainUpdate();
++ void AllowLiveChainUpdate();
++ void SetRecordingListener(MythFile &file, CStdString recId);
++private:
++ class ImpMythEventHandler;
++ boost::shared_ptr< ImpMythEventHandler > m_imp;
++};
+\ No newline at end of file
+diff --git a/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythFile.cpp b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythFile.cpp
+new file mode 100644
+index 0000000..3e4fcec
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythFile.cpp
+@@ -0,0 +1,68 @@
++
++#include "MythFile.h"
++#include "client.h"
++
++
++using namespace ADDON;
++
++/*
++ * MythFile
++ */
++
++
++ MythFile::MythFile()
++ :m_file_t(new MythPointer<cmyth_file_t>()),m_conn(MythConnection())
++ {
++ }
++
++ MythFile::MythFile(cmyth_file_t myth_file,MythConnection conn)
++ : m_file_t(new MythPointer<cmyth_file_t>()),m_conn(conn)
++ {
++ *m_file_t=myth_file;
++ }
++
++void MythFile::UpdateDuration (unsigned long long length )
++ {
++ m_conn.Lock();
++ CMYTH->UpdateFileLength(*m_file_t,length);
++ m_conn.Unlock();
++ }
++
++ bool MythFile::IsNull()
++ {
++ if(m_file_t==NULL)
++ return true;
++ return *m_file_t==NULL;
++ }
++
++ int MythFile::Read(void* buffer,long long length)
++ {
++ m_conn.Lock();
++ int bytesRead=CMYTH->FileRead(*m_file_t,static_cast<char*>(buffer),length);
++ m_conn.Unlock();
++ return bytesRead;
++ }
++
++ long long MythFile::Seek(long long offset, int whence)
++ {
++ m_conn.Lock();
++ long long retval = CMYTH->FileSeek(*m_file_t,offset,whence);
++ m_conn.Unlock();
++ return retval;
++ }
++
++ unsigned long long MythFile::CurrentPosition()
++ {
++ m_conn.Lock();
++ unsigned long long retval = CMYTH->FilePosition(*m_file_t);
++ m_conn.Unlock();
++ return retval;
++ }
++
++ unsigned long long MythFile::Duration()
++ {
++ m_conn.Lock();
++ unsigned long long retval = CMYTH->FileLength(*m_file_t);
++ m_conn.Unlock();
++ return retval;
++ }
+\ No newline at end of file
+diff --git a/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythFile.h b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythFile.h
+new file mode 100644
+index 0000000..2d112a4
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythFile.h
+@@ -0,0 +1,24 @@
++#pragma once
++
++#include "MythPointer.h"
++#include "MythConnection.h"
++#include "libcmyth.h"
++#include <boost/shared_ptr.hpp>
++
++template < class T > class MythPointer;
++
++class MythFile
++{
++public:
++ MythFile();
++ MythFile(cmyth_file_t myth_file,MythConnection conn);
++ bool IsNull();
++ int Read(void* buffer,long long length);
++ long long Seek(long long offset, int whence);
++ unsigned long long Duration();
++ unsigned long long CurrentPosition();
++ void UpdateDuration(unsigned long long length);
++ private:
++ boost::shared_ptr< MythPointer< cmyth_file_t > > m_file_t;
++ MythConnection m_conn;
++};
+diff --git a/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythPointer.h b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythPointer.h
+new file mode 100644
+index 0000000..e1bbc35
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythPointer.h
+@@ -0,0 +1,48 @@
++#pragma once
++
++#include "libcmyth.h"
++#include "../../../../lib/platform/threads/threads.h"
++
++extern CHelper_libcmyth *CMYTH;
++
++using namespace PLATFORM;
++
++template <class T> class MythPointer
++{
++public:
++ ~MythPointer()
++ {
++ CMYTH->RefRelease(m_mythpointer);
++ m_mythpointer=0;
++ }
++ MythPointer()
++ {
++ m_mythpointer=0;
++ }
++ operator T()
++ {
++ return m_mythpointer;
++ }
++ MythPointer & operator=(const T mythpointer)
++ {
++ m_mythpointer=mythpointer;
++ return *this;
++ }
++protected:
++ T m_mythpointer;
++};
++
++template <class T> class MythPointerThreadSafe : public MythPointer<T>, public CMutex
++{
++public:
++ operator T()
++ {
++ return this->m_mythpointer;
++ }
++
++ MythPointerThreadSafe & operator=(const T mythpointer)
++ {
++ this->m_mythpointer=mythpointer;
++ return *this;
++ }
++};
+diff --git a/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythProgramInfo.cpp b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythProgramInfo.cpp
+new file mode 100644
+index 0000000..4fb3a0d
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythProgramInfo.cpp
+@@ -0,0 +1,169 @@
++#include "MythProgramInfo.h"
++#include "MythTimestamp.h"
++#include "client.h"
++
++using namespace ADDON;
++
++ /*
++ * MythProgramInfo
++ */
++
++MythProgramInfo::MythProgramInfo()
++ :m_proginfo_t()
++{
++}
++
++MythProgramInfo::MythProgramInfo(cmyth_proginfo_t cmyth_proginfo)
++ :m_proginfo_t(new MythPointer<cmyth_proginfo_t>())
++{
++ *m_proginfo_t=cmyth_proginfo;
++
++}
++
++ CStdString MythProgramInfo::ProgramID()
++ {
++ CStdString retval;
++ char* progId=CMYTH->ProginfoProgramid(*m_proginfo_t);
++ retval=progId;
++ CMYTH->RefRelease(progId);
++ return retval;
++ }
++
++ CStdString MythProgramInfo::Title(bool subtitleEncoded)
++ {
++ CStdString retval;
++ char* title=CMYTH->ProginfoTitle(*m_proginfo_t);
++ retval=title;
++ CMYTH->RefRelease(title);
++ if(subtitleEncoded)
++ {
++ CStdString subtitle = this->Subtitle();
++ if(!subtitle.empty())
++ retval.Format("%s::%s",retval,subtitle);
++ }
++ return retval;
++ }
++
++ CStdString MythProgramInfo::Subtitle()
++ {
++ CStdString retval;
++ char* subtitle=CMYTH->ProginfoSubtitle(*m_proginfo_t);
++ retval=subtitle;
++ CMYTH->RefRelease(subtitle);
++ return retval;
++ }
++
++ CStdString MythProgramInfo::Path()
++ {
++ CStdString retval;
++
++ char* path=CMYTH->ProginfoPathname(*m_proginfo_t);
++ // XBMC->Log(LOG_DEBUG,"ProgInfo path: %s, status %i",path,CMYTH->ProginfoRecStatus(*m_proginfo_t));
++ retval=path;
++ CMYTH->RefRelease(path);
++ return retval;
++ }
++
++ CStdString MythProgramInfo::Description()
++ {
++ CStdString retval;
++ char* desc=CMYTH->ProginfoDescription(*m_proginfo_t);
++ retval=desc;
++ CMYTH->RefRelease(desc);
++ return retval;
++ }
++
++ CStdString MythProgramInfo::ChannelName()
++ {
++ CStdString retval;
++ char* chan=CMYTH->ProginfoChanname(*m_proginfo_t);
++ retval=chan;
++ CMYTH->RefRelease(chan);
++ return retval;
++ }
++
++ int MythProgramInfo::ChannelID()
++ {
++ return CMYTH->ProginfoChanId(*m_proginfo_t);
++ }
++
++ unsigned long MythProgramInfo::RecordID()
++ {
++ return CMYTH->ProginfoRecordid(*m_proginfo_t);
++ }
++
++ time_t MythProgramInfo::RecStart()
++ {
++ time_t retval;
++ MythTimestamp time=CMYTH->ProginfoRecStart(*m_proginfo_t);
++ retval=time.UnixTime();
++ return retval;
++ }
++
++ time_t MythProgramInfo::StartTime()
++ {
++ time_t retval;
++ MythTimestamp time=CMYTH->ProginfoStart(*m_proginfo_t);
++ retval=time.UnixTime();
++ return retval;
++ }
++
++ time_t MythProgramInfo::EndTime()
++ {
++ time_t retval;
++ MythTimestamp time=CMYTH->ProginfoEnd(*m_proginfo_t);
++ retval=time.UnixTime();
++ return retval;
++ }
++
++ int MythProgramInfo::Priority()
++ {
++ return CMYTH->ProginfoPriority(*m_proginfo_t);//Might want to use recpriority2 instead.
++ }
++
++ MythProgramInfo::record_status MythProgramInfo::Status()
++ {
++ return CMYTH->ProginfoRecStatus(*m_proginfo_t);
++ }
++
++ int MythProgramInfo::Duration()
++ {
++ MythTimestamp end=CMYTH->ProginfoRecEnd(*m_proginfo_t);
++ MythTimestamp start=CMYTH->ProginfoRecStart(*m_proginfo_t);
++ return end.UnixTime()-start.UnixTime();
++ return CMYTH->ProginfoLengthSec(*m_proginfo_t);
++ }
++
++ CStdString MythProgramInfo::Category()
++ {
++ CStdString retval;
++ char* cat=CMYTH->ProginfoCategory(*m_proginfo_t);
++ retval=cat;
++ CMYTH->RefRelease(cat);
++ return retval;
++ }
++
++ CStdString MythProgramInfo::RecordingGroup()
++ {
++ CStdString retval;
++ char* recgroup=CMYTH->ProginfoRecgroup(*m_proginfo_t);
++ retval=recgroup;
++ CMYTH->RefRelease(recgroup);
++ return retval;
++ }
++
++ long long MythProgramInfo::uid()
++ {
++ long long retval=RecStart();
++ retval<<=32;
++ retval+=ChannelID();
++ if(retval>0)
++ retval=-retval;
++ return retval;
++ }
++ bool MythProgramInfo::IsNull()
++ {
++ if(m_proginfo_t==NULL)
++ return true;
++ return *m_proginfo_t==NULL;
++ }
+diff --git a/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythProgramInfo.h b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythProgramInfo.h
+new file mode 100644
+index 0000000..cfb4925
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythProgramInfo.h
+@@ -0,0 +1,37 @@
++#pragma once
++
++#include "libcmyth.h"
++#include "utils/StdString.h"
++#include <boost/shared_ptr.hpp>
++#include "MythPointer.h"
++
++
++class MythProgramInfo
++{
++ friend class MythConnection;
++public:
++ typedef cmyth_proginfo_rec_status_t record_status;
++
++ MythProgramInfo();
++ MythProgramInfo(cmyth_proginfo_t cmyth_proginfo);
++ CStdString ProgramID();
++ CStdString Title(bool subtitleEncoded);
++ CStdString Subtitle();
++ CStdString Path();
++ CStdString Description();
++ CStdString ChannelName();
++ int ChannelID();
++ unsigned long RecordID();
++ time_t RecStart();
++ time_t StartTime();
++ time_t EndTime();
++ int Priority();
++ record_status Status();
++ int Duration();
++ CStdString Category();
++ CStdString RecordingGroup();
++ long long uid();
++ bool IsNull();
++private:
++ boost::shared_ptr< MythPointer< cmyth_proginfo_t > > m_proginfo_t;
++};
+diff --git a/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythRecorder.cpp b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythRecorder.cpp
+new file mode 100644
+index 0000000..a0c284e
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythRecorder.cpp
+@@ -0,0 +1,223 @@
++
++#include "MythRecorder.h"
++#include "MythProgramInfo.h"
++#include "MythChannel.h"
++#include "client.h"
++
++using namespace ADDON;
++
++/*
++* Myth Recorder
++*/
++
++
++MythRecorder::MythRecorder():
++m_recorder_t(new MythPointerThreadSafe<cmyth_recorder_t>()),livechainupdated(new int(0)),m_conn()
++{
++}
++
++MythRecorder::MythRecorder(cmyth_recorder_t cmyth_recorder,MythConnection conn):
++m_recorder_t(new MythPointerThreadSafe<cmyth_recorder_t>()),livechainupdated(new int(0)),m_conn(conn)
++{
++ *m_recorder_t=cmyth_recorder;
++}
++
++bool MythRecorder::SpawnLiveTV(MythChannel &channel)
++{
++ char* pErr=NULL;
++ CStdString channelNum = channel.Number();
++ m_conn.Lock();
++ //m_recorder_t->Lock();
++ //check channel
++ *livechainupdated=0;
++ *m_recorder_t=(CMYTH->SpawnLiveTv(*m_recorder_t,64*1024, 16*1024,MythRecorder::prog_update_callback,&pErr,channelNum.GetBuffer()));
++ int i=20;
++ while(*livechainupdated==0&&i--!=0)
++ {
++ //m_recorder_t->Unlock();
++ m_conn.Unlock();
++ usleep(100000);
++ m_conn.Lock();
++ //m_recorder_t->Lock();
++ }
++ //m_recorder_t->Unlock();
++ m_conn.Unlock();
++ ASSERT(*m_recorder_t);
++
++ if(pErr)
++ XBMC->Log(LOG_ERROR,"%s - %s",__FUNCTION__,pErr);
++ return pErr==NULL;
++}
++
++bool MythRecorder::LiveTVChainUpdate(CStdString chainID)
++{
++ m_conn.Lock();
++ char* buffer=strdup(chainID.c_str());
++ //m_recorder_t->Lock();
++ bool retval=CMYTH->LivetvChainUpdate(*m_recorder_t,buffer,16*1024)==0;
++ if(!retval)
++ XBMC->Log(LOG_ERROR,"LiveTVChainUpdate failed on chainID: %s",buffer);
++ *livechainupdated=1;
++ //m_recorder_t->Unlock();
++ free(buffer);
++ m_conn.Unlock();
++ return retval;
++}
++
++void MythRecorder::prog_update_callback(cmyth_proginfo_t prog)
++{
++ XBMC->Log(LOG_DEBUG,"prog_update_callback");
++
++}
++
++
++bool MythRecorder::IsNull()
++{
++ if(m_recorder_t==NULL)
++ return true;
++ return *m_recorder_t==NULL;
++}
++
++
++
++bool MythRecorder::IsRecording()
++{
++ m_conn.Lock();
++ //m_recorder_t->Lock();
++ bool retval=CMYTH->RecorderIsRecording(*m_recorder_t)==1;
++ //m_recorder_t->Unlock();
++ m_conn.Unlock();
++ return retval;
++}
++
++bool MythRecorder::CheckChannel(MythChannel &channel)
++{
++ m_conn.Lock();
++ //m_recorder_t->Lock();
++ CStdString channelNum=channel.Number();
++ bool retval=CMYTH->RecorderCheckChannel(*m_recorder_t,channelNum.GetBuffer())==0;
++ //m_recorder_t->Unlock();
++ m_conn.Unlock();
++ return retval;
++}
++
++bool MythRecorder::SetChannel(MythChannel &channel)
++{
++
++ //m_recorder_t->Lock();
++ m_conn.Lock();
++ if(!IsRecording())
++ {
++ XBMC->Log(LOG_ERROR,"%s: Recorder %i is not recording",__FUNCTION__,ID(),channel.Name().c_str());
++ //m_recorder_t->Unlock();
++ m_conn.Unlock();
++ return false;
++ }
++ CStdString channelNum=channel.Number();
++ if(CMYTH->RecorderPause(*m_recorder_t)!=0)
++ {
++ XBMC->Log(LOG_ERROR,"%s: Failed to pause recorder %i",__FUNCTION__,ID());
++ //m_recorder_t->Unlock();
++ m_conn.Unlock();
++ return false;
++ }
++ if(!CheckChannel(channel))
++ {
++ XBMC->Log(LOG_ERROR,"%s: Recorder %i doesn't provide channel %s",__FUNCTION__,ID(),channel.Name().c_str());
++ //m_recorder_t->Unlock();
++ m_conn.Unlock();
++ return false;
++ }
++ if(CMYTH->RecorderSetChannel(*m_recorder_t,channelNum.GetBuffer())!=0)
++ {
++ XBMC->Log(LOG_ERROR,"%s: Failed to change recorder %i to channel %s",__FUNCTION__,ID(),channel.Name().c_str());
++ //m_recorder_t->Unlock();
++ m_conn.Unlock();
++ return false;
++ }
++ if(CMYTH->LivetvChainSwitchLast(*m_recorder_t)!=1)
++ {
++ XBMC->Log(LOG_ERROR,"%s: Failed to switch chain for recorder %i",__FUNCTION__,ID(),channel.Name().c_str());
++ //m_recorder_t->Unlock();
++ m_conn.Unlock();
++ return false;
++ }
++ *livechainupdated=0;
++ int i=20;
++ while(*livechainupdated==0&&i--!=0)
++ {
++ //m_recorder_t->Unlock();
++ m_conn.Unlock();
++ usleep(100000);
++ //m_recorder_t->Lock();
++ m_conn.Lock();
++ }
++
++ //m_recorder_t->Unlock();
++ m_conn.Unlock();
++ for(int i=0;i<20;i++)
++ {
++ if(!IsRecording())
++ usleep(1000);
++ else
++ break;
++ }
++
++ return true;
++}
++
++int MythRecorder::ReadLiveTV(void* buffer,long long length)
++{
++ m_conn.Lock();
++ //m_recorder_t->Lock();
++ int bytesRead=CMYTH->LivetvRead(*m_recorder_t,static_cast<char*>(buffer),length);
++ //m_recorder_t->Unlock();
++ m_conn.Unlock();
++ return bytesRead;
++}
++
++MythProgramInfo MythRecorder::GetCurrentProgram()
++{
++ m_conn.Lock();
++ //m_recorder_t->Lock();
++ MythProgramInfo retval=CMYTH->RecorderGetCurProginfo(*m_recorder_t);
++ //m_recorder_t->Unlock();
++ m_conn.Unlock();
++ return retval;
++}
++
++long long MythRecorder::LiveTVSeek(long long offset, int whence)
++{
++ m_conn.Lock();
++ //m_recorder_t->Lock();
++ long long retval = CMYTH->LivetvSeek(*m_recorder_t,offset,whence);
++ //m_recorder_t->Unlock();
++ m_conn.Unlock();
++ return retval;
++}
++
++long long MythRecorder::LiveTVDuration()
++{
++ m_conn.Lock();
++ //m_recorder_t->Lock();
++ long long retval = CMYTH->LivetvChainDuration(*m_recorder_t);
++ //m_recorder_t->Unlock();
++ m_conn.Unlock();
++ return retval;
++}
++
++int MythRecorder::ID()
++{
++ m_conn.Lock();
++ int retval = CMYTH->RecorderGetRecorderId(*m_recorder_t);
++ m_conn.Unlock();
++ return retval;
++}
++
++ bool MythRecorder::Stop()
++ {
++ m_conn.Lock();
++ bool retval = CMYTH->RecorderStopLivetv(*m_recorder_t)==0;
++ m_conn.Unlock();
++ return retval;
++ }
+diff --git a/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythRecorder.h b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythRecorder.h
+new file mode 100644
+index 0000000..908e2db
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythRecorder.h
+@@ -0,0 +1,35 @@
++#pragma once
++
++#include "libcmyth.h"
++#include "utils/StdString.h"
++#include <boost/shared_ptr.hpp>
++#include "MythPointer.h"
++#include "MythConnection.h"
++#include "../../../../lib/platform/threads/threads.h"
++
++class MythProgramInfo;
++class MythChannel;
++
++class MythRecorder
++{
++public:
++ MythRecorder();
++ MythRecorder(cmyth_recorder_t cmyth_recorder,MythConnection conn);
++ bool SpawnLiveTV(MythChannel &channel);
++ bool LiveTVChainUpdate(CStdString chainID);
++ bool IsNull();
++ bool IsRecording();
++ int ID();
++ bool CheckChannel(MythChannel &channel);
++ bool SetChannel(MythChannel &channel);
++ int ReadLiveTV(void* buffer,long long length);
++ MythProgramInfo GetCurrentProgram();
++ long long LiveTVSeek(long long offset, int whence);
++ long long LiveTVDuration();
++ bool Stop();
++private:
++ boost::shared_ptr< MythPointerThreadSafe< cmyth_recorder_t > > m_recorder_t;
++ static void prog_update_callback(cmyth_proginfo_t prog);
++ boost::shared_ptr< int > livechainupdated;
++ MythConnection m_conn;
++};
+\ No newline at end of file
+diff --git a/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythSGFile.cpp b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythSGFile.cpp
+new file mode 100644
+index 0000000..95a3c00
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythSGFile.cpp
+@@ -0,0 +1,43 @@
++#include "MythSGFile.h"
++#include "client.h"
++
++using namespace ADDON;
++
++ MythSGFile::MythSGFile()
++ :m_storagegroup_file_t(new MythPointer<cmyth_storagegroup_file_t>())
++ {
++ }
++
++ MythSGFile::MythSGFile(cmyth_storagegroup_file_t myth_storagegroup_file)
++ : m_storagegroup_file_t(new MythPointer<cmyth_storagegroup_file_t>())
++ {
++ *m_storagegroup_file_t=myth_storagegroup_file;
++ }
++
++ CStdString MythSGFile::Filename()
++ {
++ char* name = CMYTH->StoragegroupFileGetFilename(*m_storagegroup_file_t);
++ CStdString retval(name);
++ CMYTH->RefRelease(name);
++ name = 0;
++ return retval;
++ }
++
++ unsigned long long MythSGFile::Size()
++ {
++ unsigned long long retval = CMYTH->StoragegroupFileGetSize(*m_storagegroup_file_t);
++ return retval;
++ }
++
++ unsigned long MythSGFile::LastModified()
++ {
++ unsigned long retval = CMYTH->StoragegroupFileGetLastmodified(*m_storagegroup_file_t);
++ return retval;
++ }
++
++ bool MythSGFile::IsNull()
++ {
++ if(m_storagegroup_file_t==NULL)
++ return true;
++ return *m_storagegroup_file_t==NULL;
++ }
+\ No newline at end of file
+diff --git a/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythSGFile.h b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythSGFile.h
+new file mode 100644
+index 0000000..11d431c
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythSGFile.h
+@@ -0,0 +1,21 @@
++#pragma once
++
++#include "MythPointer.h"
++#include "MythConnection.h"
++#include "libcmyth.h"
++#include <boost/shared_ptr.hpp>
++
++template < class T > class MythPointer;
++
++class MythSGFile
++{
++public:
++ MythSGFile(cmyth_storagegroup_file_t myth_storagegroup_file);
++ MythSGFile();
++ CStdString Filename();
++ unsigned long long Size();
++ unsigned long LastModified();
++ bool IsNull();
++private:
++ boost::shared_ptr< MythPointer< cmyth_storagegroup_file_t > > m_storagegroup_file_t;
++};
+diff --git a/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythSignal.cpp b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythSignal.cpp
+new file mode 100644
+index 0000000..216e8b1
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythSignal.cpp
+@@ -0,0 +1,14 @@
++#include "MythSignal.h"
++
++/*
++ * MythSignal
++ */
++
++ MythSignal::MythSignal() :m_AdapterStatus(),m_SNR(0),m_Signal(0),m_BER(0),m_UNC(0) {}
++
++ CStdString MythSignal::AdapterStatus(){return m_AdapterStatus;} /*!< @brief (optional) status of the adapter that's being used */
++ int MythSignal::SNR(){return m_SNR;} /*!< @brief (optional) signal/noise ratio */
++ int MythSignal::Signal(){return m_Signal;} /*!< @brief (optional) signal strength */
++ long MythSignal::BER(){return m_BER;} /*!< @brief (optional) bit error rate */
++ long MythSignal::UNC(){return m_UNC;} /*!< @brief (optional) uncorrected blocks */
++
+diff --git a/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythSignal.h b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythSignal.h
+new file mode 100644
+index 0000000..6ce0ef0
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythSignal.h
+@@ -0,0 +1,21 @@
++#pragma once
++
++#include "utils/StdString.h"
++
++class MythSignal
++{
++ friend class MythEventHandler;
++public:
++ MythSignal();
++ CStdString AdapterStatus(); /*!< @brief (optional) status of the adapter that's being used */
++ int SNR(); /*!< @brief (optional) signal/noise ratio */
++ int Signal(); /*!< @brief (optional) signal strength */
++ long BER(); /*!< @brief (optional) bit error rate */
++ long UNC(); /*!< @brief (optional) uncorrected blocks */
++private:
++ CStdString m_AdapterStatus; /*!< @brief (optional) status of the adapter that's being used */
++ int m_SNR; /*!< @brief (optional) signal/noise ratio */
++ int m_Signal; /*!< @brief (optional) signal strength */
++ long m_BER; /*!< @brief (optional) bit error rate */
++ long m_UNC; /*!< @brief (optional) uncorrected blocks */
++};
+\ No newline at end of file
+diff --git a/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythTimer.cpp b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythTimer.cpp
+new file mode 100644
+index 0000000..908b3a3
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythTimer.cpp
+@@ -0,0 +1,378 @@
++#include "MythTimer.h"
++
++#include "client.h"
++
++using namespace ADDON;
++
++/*
++ * MythTimer
++ */
++
++
++MythTimer::MythTimer()
++ : m_recordid(-1),m_chanid(-1),m_callsign(""),m_starttime(0),m_endtime(0),m_title(""),m_description(""),m_type(NotRecording),m_category(""),m_subtitle(""),m_priority(0),m_startoffset(0),m_endoffset(0),m_searchtype(NoSearch),m_inactive(true),
++ m_dupmethod(CheckSubDesc), m_dupin(InAll), m_recgroup("Default"), m_storegroup("Default"), m_playgroup("Default"), m_autotranscode(false), m_userjobs(0), m_autocommflag(false), m_autoexpire(false), m_maxepisodes(0), m_maxnewest(0), m_transcoder(0)
++{}
++
++
++ MythTimer::MythTimer(cmyth_timer_t cmyth_timer,bool release)
++ : m_recordid(CMYTH->TimerRecordid(cmyth_timer)),
++ m_chanid(CMYTH->TimerChanid(cmyth_timer)),
++ m_callsign(""),
++ m_starttime(CMYTH->TimerStarttime(cmyth_timer)),
++ m_endtime(CMYTH->TimerEndtime(cmyth_timer)),
++ m_title(""),
++ m_description(""),
++ m_type(static_cast<TimerType>(CMYTH->TimerType(cmyth_timer))),
++ m_category(""),
++ m_subtitle(""),
++ m_priority(CMYTH->TimerPriority(cmyth_timer)),
++ m_startoffset(CMYTH->TimerStartoffset(cmyth_timer)),
++ m_endoffset(CMYTH->TimerEndoffset(cmyth_timer)),
++ m_searchtype(static_cast<TimerSearchType>(CMYTH->TimerSearchtype(cmyth_timer))),
++ m_inactive(CMYTH->TimerInactive(cmyth_timer)!=0),
++ m_dupmethod(static_cast< DuplicateControlMethods >(CMYTH->TimerDupMethod(cmyth_timer))),
++ m_dupin(static_cast< CheckDuplicatesInTypes >(CMYTH->TimerDupIn(cmyth_timer))),
++ m_recgroup(""),
++ m_storegroup(""),
++ m_playgroup(""),
++ m_autotranscode(CMYTH->TimerAutotranscode(cmyth_timer) == 1),
++ m_userjobs(CMYTH->TimerUserjobs(cmyth_timer)),
++ m_autocommflag(CMYTH->TimerAutocommflag(cmyth_timer) == 1),
++ m_autoexpire(CMYTH->TimerAutoexpire(cmyth_timer) == 1),
++ m_maxepisodes(CMYTH->TimerMaxepisodes(cmyth_timer)),
++ m_maxnewest(CMYTH->TimerMaxnewest(cmyth_timer) == 1),
++ m_transcoder(CMYTH->TimerTranscoder(cmyth_timer))
++ {
++ char *title = CMYTH->TimerTitle(cmyth_timer);
++ char *description = CMYTH->TimerDescription(cmyth_timer);
++ char *category = CMYTH->TimerCategory(cmyth_timer);
++ char *subtitle = CMYTH->TimerSubtitle(cmyth_timer);
++ char *callsign = CMYTH->TimerCallsign(cmyth_timer);
++ char *recgroup = CMYTH->TimerRecGroup(cmyth_timer);
++ char *storegroup = CMYTH->TimerStoreGroup(cmyth_timer);
++ char *playgroup = CMYTH->TimerPlayGroup(cmyth_timer);
++
++ m_title = title;
++ m_description = description;
++ m_category = category;
++ m_subtitle = subtitle;
++ m_callsign = callsign;
++ m_recgroup = recgroup;
++ m_storegroup = storegroup;
++ m_playgroup = playgroup;
++
++ CMYTH->RefRelease(title);
++ CMYTH->RefRelease(description);
++ CMYTH->RefRelease(category);
++ CMYTH->RefRelease(subtitle);
++ CMYTH->RefRelease(callsign);
++ CMYTH->RefRelease(recgroup);
++ CMYTH->RefRelease(storegroup);
++ CMYTH->RefRelease(playgroup);
++ if(release)
++ CMYTH->RefRelease(cmyth_timer);
++ }
++
++ int MythTimer::RecordID() const
++ {
++ return m_recordid;
++ }
++
++ void MythTimer::RecordID(int recordid)
++ {
++ m_recordid=recordid;
++ }
++
++ int MythTimer::ChanID() const
++ {
++ return m_chanid;
++ }
++
++ void MythTimer::ChanID(int chanid)
++ {
++ m_chanid = chanid;
++ }
++
++ CStdString MythTimer::Callsign() const
++ {
++ return m_callsign;
++ }
++
++ void MythTimer::Callsign(const CStdString& channame)
++ {
++ m_callsign = channame;
++ }
++
++ time_t MythTimer::StartTime() const
++ {
++ return m_starttime;
++ }
++
++ void MythTimer::StartTime(time_t starttime)
++ {
++ m_starttime=starttime;
++ }
++
++ time_t MythTimer::EndTime() const
++ {
++ return m_endtime;
++ }
++
++ void MythTimer::EndTime(time_t endtime)
++ {
++ m_endtime=endtime;
++ }
++
++ CStdString MythTimer::Title(bool subtitleEncoded) const
++ {
++ if(subtitleEncoded && !m_subtitle.empty())
++ {
++ CStdString retval;
++ retval.Format("%s::%s",m_title,m_subtitle);
++ return retval;
++ }
++ return m_title;
++ }
++
++ void MythTimer::Title(const CStdString& title,bool subtitleEncoded)
++ {
++ if(subtitleEncoded)
++ {
++ size_t seppos = title.find("::");
++ if(seppos != CStdString::npos)
++ {
++ m_title = title.substr(0,seppos);
++ m_subtitle = title.substr(seppos+2);
++ return;
++ }
++ }
++ m_title=title;
++ }
++
++ CStdString MythTimer::Subtitle() const
++ {
++ return m_subtitle;
++ }
++
++ void MythTimer::Subtitle(const CStdString& subtitle)
++ {
++ m_subtitle=subtitle;
++ }
++
++ CStdString MythTimer::Description() const
++ {
++ return m_description;
++ }
++
++ void MythTimer::Description(const CStdString& description)
++ {
++ m_description=description;
++ }
++
++ MythTimer::TimerType MythTimer::Type() const
++ {
++ return m_type;
++ }
++
++ void MythTimer::Type(MythTimer::TimerType type)
++ {
++ m_type=type;
++ }
++
++ CStdString MythTimer::Category() const
++ {
++ return m_category;
++ }
++
++ void MythTimer::Category(const CStdString& category)
++ {
++ m_category=category;
++ }
++
++ int MythTimer::StartOffset() const
++ {
++ return m_startoffset;
++ }
++
++ void MythTimer::StartOffset(int startoffset)
++ {
++ m_startoffset=startoffset;
++ }
++
++ int MythTimer::EndOffset() const
++ {
++ return m_endoffset;
++ }
++
++ void MythTimer::EndOffset(int endoffset)
++ {
++ m_endoffset=endoffset;
++ }
++
++ int MythTimer::Priority() const
++ {
++ return m_priority;
++ }
++
++ void MythTimer::Priority(int priority)
++ {
++ m_priority=priority;
++ }
++
++ bool MythTimer::Inactive() const
++ {
++ return m_inactive;
++ }
++
++ void MythTimer::Inactive(bool inactive)
++ {
++ m_inactive=inactive;
++ }
++
++ MythTimer::TimerSearchType MythTimer::SearchType() const
++ {
++ return m_searchtype;
++ }
++
++ void MythTimer::SearchType(MythTimer::TimerSearchType searchtype)
++ {
++ m_searchtype=searchtype;
++ }
++
++ MythTimer::DuplicateControlMethod MythTimer::DupMethod()
++ {
++ return m_dupmethod;
++ }
++
++ void MythTimer::DupMethod( MythTimer::DuplicateControlMethod method)
++ {
++ m_dupmethod = method;
++ }
++
++ MythTimer::CheckDuplicatesInType MythTimer::CheckDupIn()
++ {
++ return m_dupin;
++ }
++
++ void MythTimer::CheckDupIn( MythTimer::CheckDuplicatesInType in)
++ {
++ m_dupin = in;
++ }
++
++ CStdString MythTimer::RecGroup()
++ {
++ return m_recgroup;
++ }
++
++ void MythTimer::RecGroup(CStdString group)
++ {
++ m_recgroup = group;
++ }
++
++ CStdString MythTimer::StoreGroup()
++ {
++ return m_storegroup;
++ }
++
++ void MythTimer::StoreGroup(CStdString group)
++ {
++ m_storegroup = group;
++ }
++
++ CStdString MythTimer::PlayGroup()
++ {
++ return m_playgroup;
++ }
++
++ void MythTimer::PlayGroup(CStdString group)
++ {
++ m_playgroup = group;
++ }
++
++ bool MythTimer::AutoTranscode()
++ {
++ return m_autotranscode;
++ }
++
++ void MythTimer::AutoTranscode(bool enable)
++ {
++ m_autotranscode = enable;
++ }
++
++ bool MythTimer::Userjob(int jobnumber)
++ {
++ if(jobnumber<1 || jobnumber > 4)
++ return false;
++ return (m_userjobs&(1<<(jobnumber-1))) == 1 ;
++ }
++
++ void MythTimer::Userjob(int jobnumber, bool enable)
++ {
++ if(jobnumber<1 || jobnumber > 4)
++ return;
++ if(enable)
++ m_userjobs |= 1 << (jobnumber - 1);
++ else
++ m_userjobs &= ~(1 << (jobnumber - 1) );
++ }
++
++ int MythTimer::Userjobs()
++ {
++ return m_userjobs;
++ }
++
++ void MythTimer::Userjobs(int jobs)
++ {
++ m_userjobs = jobs;
++ }
++
++ bool MythTimer::AutoCommFlag()
++ {
++ return m_autocommflag;
++ }
++
++ void MythTimer::AutoCommFlag(bool enable)
++ {
++ m_autocommflag = enable;
++ }
++
++ bool MythTimer::AutoExpire()
++ {
++ return m_autoexpire;
++ }
++
++ void MythTimer::AutoExpire(bool enable)
++ {
++ m_autoexpire = enable;
++ }
++
++ int MythTimer::MaxEpisodes()
++ {
++ return m_maxepisodes;
++ }
++
++ void MythTimer::MaxEpisodes(int max)
++ {
++ m_maxepisodes = max;
++ }
++
++ bool MythTimer::NewExpireOldRecord()
++ {
++ return m_maxnewest;
++ }
++
++ void MythTimer::NewExpireOldRecord(bool enable)
++ {
++ m_maxnewest = enable;
++ }
++
++ int MythTimer::Transcoder()
++ {
++ return m_transcoder;
++ }
++
++ void MythTimer::Transcoder(int transcoder)
++ {
++ m_transcoder = transcoder;
++ }
+\ No newline at end of file
+diff --git a/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythTimer.h b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythTimer.h
+new file mode 100644
+index 0000000..715452c
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythTimer.h
+@@ -0,0 +1,138 @@
++#pragma once
++
++#include "libcmyth.h"
++#include "utils/StdString.h"
++
++class MythTimer
++{
++ friend class MythDatabase;
++public:
++ MythTimer();
++ MythTimer(cmyth_timer_t cmyth_timer,bool release=true);
++ int RecordID() const;
++ void RecordID(int recordid);
++ int ChanID() const;
++ void ChanID(int chanid);
++ CStdString Callsign() const;
++ void Callsign(const CStdString& chancallsign);
++ time_t StartTime() const;
++ void StartTime(time_t starttime);
++ time_t EndTime() const;
++ void EndTime(time_t endtime);
++ CStdString Title(bool subtitleEncoded) const;
++ void Title(const CStdString& title,bool subtitleEncoded);
++ CStdString Subtitle() const;
++ void Subtitle(const CStdString& subtitle);
++ CStdString Description() const;
++ void Description(const CStdString& description);
++ typedef enum TimerTypes
++{
++ NotRecording = 0,
++ SingleRecord = 1,
++ TimeslotRecord,
++ ChannelRecord,
++ AllRecord,
++ WeekslotRecord,
++ FindOneRecord,
++ OverrideRecord,
++ DontRecord,
++ FindDailyRecord,
++ FindWeeklyRecord
++} TimerType;
++ TimerType Type() const;
++ void Type(TimerType type);
++ CStdString Category() const;
++ void Category(const CStdString& category);
++ int StartOffset() const;
++ void StartOffset(int startoffset);
++ int EndOffset() const;
++ void EndOffset(int endoffset);
++ int Priority() const;
++ void Priority(int priority);
++ bool Inactive() const;
++ void Inactive(bool inactive);
++
++typedef enum TimerSearchTypes
++{
++ NoSearch = 0,
++ PowerSearch,
++ TitleSearch,
++ KeywordSearch,
++ PeopleSearch,
++ ManualSearch
++} TimerSearchType;
++ TimerSearchType SearchType() const;
++ void SearchType(TimerSearchType searchtype);
++ typedef enum DuplicateControlMethods
++ {
++ CheckNone = 0x01,
++ CheckSub = 0x02,
++ CheckDesc = 0x04,
++ CheckSubDesc = 0x06,
++ CheckSubThenDesc = 0x08
++ } DuplicateControlMethod;
++ typedef enum CheckDuplicatesInTypes
++ {
++ InRecorded = 0x01,
++ InOldRecorded = 0x02,
++ InAll = 0x0F,
++ NewEpi = 0x10
++ } CheckDuplicatesInType;
++
++ DuplicateControlMethod DupMethod();
++ void DupMethod(DuplicateControlMethod method);
++ CheckDuplicatesInType CheckDupIn();
++ void CheckDupIn(CheckDuplicatesInType in);
++ CStdString RecGroup();
++ void RecGroup(CStdString group);
++ CStdString StoreGroup();
++ void StoreGroup(CStdString group);
++ CStdString PlayGroup();
++ void PlayGroup(CStdString group);
++ bool AutoTranscode();
++ void AutoTranscode(bool enable);
++ bool Userjob(int jobnumber);
++ void Userjob(int jobnumber, bool enable);
++ int Userjobs();
++ void Userjobs(int jobs);
++ bool AutoCommFlag();
++ void AutoCommFlag(bool enable);
++ bool AutoExpire();
++ void AutoExpire(bool enable);
++ int MaxEpisodes();
++ void MaxEpisodes(int max);
++ bool NewExpireOldRecord();
++ void NewExpireOldRecord(bool enable);
++ int Transcoder();
++ void Transcoder(int transcoder);
++
++private:
++ int m_recordid;
++ int m_chanid;
++ CStdString m_callsign;
++ time_t m_starttime;
++ time_t m_endtime;
++ CStdString m_title;
++ CStdString m_description;
++ TimerType m_type;
++ CStdString m_category;
++ CStdString m_subtitle;
++ int m_priority;
++ int m_startoffset;
++ int m_endoffset;
++ TimerSearchType m_searchtype;
++ bool m_inactive;
++
++ DuplicateControlMethod m_dupmethod;
++ CheckDuplicatesInType m_dupin;
++ CStdString m_recgroup;
++ CStdString m_storegroup;
++ CStdString m_playgroup;
++ bool m_autotranscode;
++ int m_userjobs;
++ bool m_autocommflag;
++ bool m_autoexpire;
++ int m_maxepisodes;
++ bool m_maxnewest;
++ int m_transcoder;
++};
+\ No newline at end of file
+diff --git a/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythTimestamp.cpp b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythTimestamp.cpp
+new file mode 100644
+index 0000000..8c2f56e
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythTimestamp.cpp
+@@ -0,0 +1,88 @@
++#include "MythTimestamp.h"
++#include "client.h"
++
++using namespace ADDON;
++
++
++/*
++ * MythTimestamp
++ */
++
++
++MythTimestamp::MythTimestamp()
++ :m_timestamp_t()
++{
++}
++
++MythTimestamp::MythTimestamp(cmyth_timestamp_t cmyth_timestamp)
++ :m_timestamp_t(new MythPointer<cmyth_timestamp_t>())
++{
++ *m_timestamp_t=(cmyth_timestamp);
++}
++
++ MythTimestamp::MythTimestamp(CStdString time,bool datetime)
++ :m_timestamp_t(new MythPointer<cmyth_timestamp_t>())
++{
++ *m_timestamp_t=(/*datetime?CMYTH->DatetimeFromString(time.Buffer()):*/CMYTH->TimestampFromString(time.Buffer()));
++}
++
++ MythTimestamp::MythTimestamp(time_t time)
++ :m_timestamp_t(new MythPointer<cmyth_timestamp_t>())
++{
++ *m_timestamp_t=(CMYTH->TimestampFromUnixtime(time));
++}
++
++ bool MythTimestamp::operator==(const MythTimestamp &other)
++ {
++ return CMYTH->TimestampCompare(*m_timestamp_t,*other.m_timestamp_t)==0;
++ }
++
++ bool MythTimestamp::operator>(const MythTimestamp &other)
++ {
++ return CMYTH->TimestampCompare(*m_timestamp_t,*other.m_timestamp_t)==1;
++ }
++
++ bool MythTimestamp::operator<(const MythTimestamp &other)
++ {
++ return CMYTH->TimestampCompare(*m_timestamp_t,*other.m_timestamp_t)==-1;
++ }
++
++ time_t MythTimestamp::UnixTime()
++ {
++ return CMYTH->TimestampToUnixtime(*m_timestamp_t);
++ }
++
++ CStdString MythTimestamp::String()
++ {
++ CStdString retval;
++ char time[25];
++ bool succeded=CMYTH->TimestampToString(time,*m_timestamp_t)==0;
++ retval=succeded?time:"";
++ return retval;
++ }
++
++ CStdString MythTimestamp::Isostring()
++ {
++ CStdString retval;
++ char time[25];
++ bool succeded=CMYTH->TimestampToIsostring(time,*m_timestamp_t)==0;
++ retval=succeded?time:"";
++ return retval;
++ }
++
++ CStdString MythTimestamp::Displaystring(bool use12hClock)
++ {
++ CStdString retval;
++ char time[25];
++ bool succeded=CMYTH->TimestampToDisplayString(time,*m_timestamp_t,use12hClock)==0;
++ retval=succeded?time:"";
++ return retval;
++ }
++
++ bool MythTimestamp::IsNull()
++ {
++ if(m_timestamp_t==NULL)
++ return true;
++ return *m_timestamp_t==NULL;
++ }
++
+diff --git a/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythTimestamp.h b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythTimestamp.h
+new file mode 100644
+index 0000000..02e2c26
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/cppmyth/MythTimestamp.h
+@@ -0,0 +1,28 @@
++#pragma once
++
++#include "libcmyth.h"
++#include "utils/StdString.h"
++#include <boost/shared_ptr.hpp>
++#include "MythPointer.h"
++
++class MythTimestamp
++{
++public:
++ MythTimestamp();
++ MythTimestamp(cmyth_timestamp_t cmyth_timestamp);
++ MythTimestamp(CStdString time,bool datetime);
++ MythTimestamp(time_t time);
++ bool operator==(const MythTimestamp &other);
++ bool operator!=(const MythTimestamp &other){return !(*this == other);}
++ bool operator>(const MythTimestamp &other);
++ bool operator>=(const MythTimestamp &other){return (*this == other||*this > other);}
++ bool operator<(const MythTimestamp &other);
++ bool operator<=(const MythTimestamp &other){return (*this == other||*this < other);}
++ time_t UnixTime();
++ CStdString String();
++ CStdString Isostring();
++ CStdString Displaystring(bool use12hClock);
++ bool IsNull();
++private:
++ boost::shared_ptr< MythPointer< cmyth_timestamp_t > > m_timestamp_t;
++};
+diff --git a/xbmc/pvrclients/mythtv-cmyth/fileOps.cpp b/xbmc/pvrclients/mythtv-cmyth/fileOps.cpp
+new file mode 100644
+index 0000000..3ceabb6
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/fileOps.cpp
+@@ -0,0 +1,313 @@
++#include <stdio.h>
++#include <map>
++#include <ctime>
++#include <algorithm>
++
++#include <boost/filesystem/fstream.hpp>
++#include <boost/regex.hpp>
++#include "cppmyth/MythFile.h"
++#include "client.h"
++#include "fileOps.h"
++#include "stdio.h"
++
++extern ADDON::CHelper_libXBMC_addon *XBMC;
++using namespace ADDON;
++
++fileOps2::fileOps2(MythConnection &mythConnection)
++ :m_con(mythConnection),m_localBasePath(g_szUserPath.c_str()),m_sg_strings(),CThread(),CMutex(),m_queue_content(), m_jobqueue()
++{
++ m_localBasePath /= "cache";
++ if(!createDirectory(m_localBasePath))
++ XBMC->Log(LOG_ERROR,"%s - Failed to create cache directory %s",__FUNCTION__,m_localBasePath.c_str());
++ m_sg_strings[FILE_OPS_GET_COVERART] = "coverart";
++ m_sg_strings[FILE_OPS_GET_FANART] = "fanart";
++ m_sg_strings[FILE_OPS_GET_BANNER] = "banner";
++ m_sg_strings[FILE_OPS_GET_SCREENSHOT] = "screenshot";
++ m_sg_strings[FILE_OPS_GET_POSTER] = "poster";
++ m_sg_strings[FILE_OPS_GET_BACKCOVER] = "backcover";
++ m_sg_strings[FILE_OPS_GET_INSIDECOVER] = "insidecover";
++ m_sg_strings[FILE_OPS_GET_CD_IMAGE] = "cdimage";
++ m_sg_strings[FILE_OPS_GET_CHAN_ICONS] = "ChannelIcons";
++ CreateThread();
++}
++
++CStdString fileOps2::getChannelIconPath(CStdString remotePath)
++{
++ //Check local directory
++ if(remotePath == "")
++ return "";
++ XBMC->Log(LOG_DEBUG,"%s: channelicon: %s",__FUNCTION__,remotePath.c_str());
++ if(m_icons.count(remotePath)>0)
++ return m_icons.at(remotePath);
++ CStdString remoteFilename = boost::filesystem::path(remotePath.c_str()).filename().string();
++ boost::filesystem::path localFilePath = m_localBasePath / "channels" / remoteFilename.c_str();
++ if(!boost::filesystem::exists(localFilePath))
++ {
++ Lock();
++ fileOps2::jobItem job(localFilePath, "/channels/"+remoteFilename,"");
++ m_jobqueue.push(job);
++ m_queue_content.Signal();
++ Unlock();
++ }
++ m_icons[remotePath] = localFilePath.string();
++ return localFilePath.string();
++
++}
++
++CStdString fileOps2::getPreviewIconPath(CStdString remotePath)
++{
++ //Check local directory
++ XBMC->Log(LOG_DEBUG,"%s: preview icon: %s",__FUNCTION__,remotePath.c_str());
++ if(m_preview.count(remotePath)>0)
++ return m_preview.at(remotePath);
++ CStdString remoteFilename = boost::filesystem::path(remotePath.c_str()).filename().string();
++ boost::filesystem::path localFilePath = m_localBasePath / "preview" / remoteFilename.c_str();
++ if(!boost::filesystem::exists(localFilePath))
++ {
++ Lock();
++ fileOps2::jobItem job(localFilePath, remoteFilename,"Default");
++ m_jobqueue.push(job);
++ m_queue_content.Signal();
++ Unlock();
++ }
++ m_preview[remotePath] = localFilePath.string();
++ return localFilePath.string();
++}
++
++CStdString fileOps2::getArtworkPath(CStdString title,FILE_OPTIONS Get_What)
++{
++ CStdString retval;
++ //update remote filelist
++ time_t curTime;
++ time(&curTime);
++ if (m_SGFilelist.count(Get_What)==0||((int)curTime - m_lastSGupdate.at(Get_What)) > 30) { // Limit storage group updates to once every 30 seconds
++ m_SGFilelist[Get_What] = m_con.GetStorageGroupFileList(m_sg_strings.at(Get_What));//=GetStorageGroupFiles(Get_What)
++ //for(auto it=m_SGFilelist.at(Get_What).begin();it!=m_SGFilelist.at(Get_What).end();it++)XBMC->Log(LOG_DEBUG,"%s: Storagegroup %s, filename: %s",__FUNCTION__,m_sg_strings.at(Get_What).c_str(),it->Filename().c_str());
++ m_lastSGupdate[Get_What] = curTime;
++ }
++ //check title against remote regex_match(title .*_storagegroup)
++ CStdString re_string;
++ CStdString esctitle = boost::regex_replace(title,boost::regex("[\\.\\[\\{\\}\\(\\)\\\\\\*\\+\\?\\|\\^\\$]"),"\\\\$&");
++ re_string.Format("%s.*_%s\\.(?:jpg|png|bmp)",esctitle,m_sg_strings.at(Get_What));
++ if(Get_What==FILE_OPS_GET_CHAN_ICONS)
++ {
++ boost::filesystem::path chanicon(title.c_str());
++ re_string = chanicon.filename().string();
++ }
++ boost::regex re(re_string.c_str());
++ std::vector< MythSGFile >::iterator it = m_SGFilelist.at(Get_What).begin();
++ for(;it!=m_SGFilelist.at(Get_What).end()&&!boost::regex_match(it->Filename(),re);it++);
++ if(it==m_SGFilelist.at(Get_What).end())
++ {
++ //if no match return ""
++ return "";
++ }
++ //check local file cache for up to date match
++ CStdString localFilename;
++ CStdString filename=it->Filename();
++ localFilename.Format("%u_%s",it->LastModified(),filename);
++ boost::filesystem::path localFilePath = m_localBasePath / m_sg_strings.at(Get_What).c_str() / localFilename.c_str();
++ if(boost::filesystem::exists(localFilePath))
++ return localFilePath.string();
++ //else add to "files to fetch" and return expected path
++ Lock();
++ fileOps2::jobItem job(localFilePath, it->Filename(), m_sg_strings.at(Get_What));
++ m_jobqueue.push(job);
++ m_queue_content.Signal();
++ Unlock();
++ return localFilePath.string();
++}
++
++fileOps2::~fileOps2()
++{
++ cleanCache();
++ StopThread(-1); //Set stopping. don't wait as we need to signal the thread first.
++ m_queue_content.Signal();
++ StopThread(); //wait for thread to stop;
++}
++
++void fileOps2::cleanCache()
++{
++ //Need to be redone at some time. Too much repeated code. But at least it works now.
++ try{
++ boost::regex re("^([[:digit:]]{1,20})_(.*)");
++ boost::smatch match;
++ for(std::map< FILE_OPTIONS, std::vector< MythSGFile > >::iterator it = m_SGFilelist.begin();it != m_SGFilelist.end(); it++)
++ {
++ boost::filesystem::path dirPath = m_localBasePath;
++ dirPath /= m_sg_strings.at(it->first).c_str();
++ for(boost::filesystem::recursive_directory_iterator dit(dirPath);dit != boost::filesystem::recursive_directory_iterator();dit++)
++ {
++ bool deletefile = true;
++ CStdString filename = dit->path().filename().string();
++ if(boost::regex_search(filename,match,re)&&match[0].matched)
++ {
++ std::string lastmodified = std::string(match[1].first,match[1].second);
++ std::string title = std::string(match[2].first,match[2].second);
++ for( std::vector< MythSGFile >::iterator mit = it->second.begin();mit != it->second.end(); mit++ )
++ {
++ CStdString mfilename = mit->Filename();
++ unsigned int mlm = mit->LastModified();
++ if(!mit->Filename().CompareNoCase(title.c_str())&&mit->LastModified()==atoi(lastmodified.c_str()))
++ {
++ if(boost::filesystem::file_size(dit->path())>0)
++ deletefile = false;
++ break;
++ }
++ }
++ if(deletefile)
++ boost::filesystem::remove(dit->path());
++ }
++ }
++ }
++ if(boost::filesystem::exists( m_localBasePath / "channels" ))
++ for(boost::filesystem::recursive_directory_iterator dit( m_localBasePath / "channels");dit != boost::filesystem::recursive_directory_iterator();dit++)
++ {
++ bool deletefile = true;
++ for(std::map< CStdString, CStdString >::iterator it = m_icons.begin(); it != m_icons.end(); it++ )
++ if( !it->second.CompareNoCase(dit->path().string().c_str()))
++ {
++ if(boost::filesystem::file_size(dit->path())>0)
++ deletefile = false;
++ break;
++ }
++ if(deletefile)
++ boost::filesystem::remove(dit->path());
++ }
++ if(boost::filesystem::exists( m_localBasePath / "preview" ))
++ for(boost::filesystem::recursive_directory_iterator dit( m_localBasePath / "preview");dit != boost::filesystem::recursive_directory_iterator();dit++)
++ {
++ bool deletefile = true;
++ for(std::map< CStdString, CStdString >::iterator it = m_preview.begin(); it != m_preview.end(); it++ )
++ if( !it->second.CompareNoCase(dit->path().string().c_str()))
++ {
++ if(boost::filesystem::file_size(dit->path())>0)
++ deletefile = false;
++ break;
++ }
++ if(deletefile)
++ boost::filesystem::remove(dit->path());
++ }
++ }
++ catch(...)
++ {
++ XBMC->Log(LOG_ERROR,"%s, caught exception during cache cleaning",__FUNCTION__);
++ }
++}
++
++void* fileOps2::Process()
++{
++ time_t curTime;
++ time_t lastCacheClean=0;
++
++ while(!IsStopped())
++ {
++ m_queue_content.Wait(60*1000);
++ while(!m_jobqueue.empty()&&!IsStopped())
++ {
++ Lock();
++ fileOps2::jobItem job = m_jobqueue.front();
++ m_jobqueue.pop();
++ Unlock();
++ try
++ {
++ XBMC->Log(LOG_DEBUG,"%s Job fetched: local: %s, remote: %s, storagegroup: %s",__FUNCTION__,job.localFilename.string().c_str(),job.remoteFilename.c_str(),job.storageGroup.c_str());
++ m_con.Lock();
++ MythFile file = m_con.ConnectPath(job.remoteFilename,job.storageGroup);
++ if(!writeFile(job.localFilename,file))
++ if(boost::filesystem::exists(job.localFilename))
++ boost::filesystem::remove(job.localFilename);
++ m_con.Unlock();
++ }
++ catch(...)
++ {
++ XBMC->Log(LOG_ERROR,"%s Error executing job: local: %s, remote: %s, storagegroup: %s",__FUNCTION__,job.localFilename.string().c_str(),job.remoteFilename.c_str(),job.storageGroup.c_str());
++ m_con.Unlock();
++ }
++ }
++ time(&curTime);
++ if(curTime>lastCacheClean+60*60*24)
++ cleanCache();
++ }
++ return NULL;
++}
++
++bool fileOps2::writeFile(boost::filesystem::path destination, MythFile &source)
++{
++ if(!createDirectory(destination,true))
++ {
++ XBMC->Log(LOG_ERROR,"%s - Failed to create destination directory: %s",
++ __FUNCTION__,destination.parent_path().c_str());
++ return false;
++ }
++ if(source.IsNull())
++ {
++ XBMC->Log(LOG_ERROR,"%s - NULL file provided.",
++ __FUNCTION__,destination.parent_path().c_str());
++ return false;
++ }
++ unsigned long long length = source.Duration();
++
++ FILE* f = fopen(destination.string().c_str(),"w");
++ fwrite("HEJ",1,3,f);
++ fclose(f);
++
++ FILE* writeFile = fopen(destination.string().c_str(),"w");
++ //std::ofstream writeFile(destination.string(),std::ios_base::binary /*| std::ios_base::trunc*/);
++
++ if (writeFile)
++ {
++ //char* theFileBuff = new char[theFilesLength];
++ unsigned long long totalRead = 0;
++ unsigned int buffersize = 4096;
++ char* buffer = new char[buffersize];
++ long long readsize = 1024;
++ while (totalRead < length)
++ {
++
++ int readData = source.Read(buffer,readsize);
++ if (readData <= 0)
++ {
++ break;
++ }
++ //writeFile.write(buffer,readData);
++ fwrite(buffer,1,readData,writeFile);
++ if(readsize == readData)
++ {
++ readsize <<=1;
++ if(readsize > buffersize)
++ {
++ buffersize <<=1;
++ delete buffer;
++ buffer = new char[buffersize];
++ }
++ }
++ totalRead += readData;
++ }
++ //writeFile.close();
++ fclose(writeFile);
++ writeFile = 0;
++ delete buffer;
++ if (totalRead < length)
++ {
++ XBMC->Log(LOG_DEBUG,"%s - Did not Read all data - %s - %d - %d",
++ __FUNCTION__,destination.c_str(),totalRead,length);
++ }
++ return true;
++ }
++ else
++ {
++ XBMC->Log(LOG_ERROR,"%s - Failed to create destination file: %s",
++ __FUNCTION__,destination.filename().c_str());
++ return false;
++ }
++}
++
++bool fileOps2::createDirectory(boost::filesystem::path dirPath, bool hasFilename/* = false */)
++{
++ if(hasFilename)
++ return boost::filesystem::is_directory(dirPath.parent_path())?true:boost::filesystem::create_directory(dirPath.parent_path());
++ else
++ return boost::filesystem::is_directory(dirPath)?true:boost::filesystem::create_directory(dirPath);
++}
++
+diff --git a/xbmc/pvrclients/mythtv-cmyth/fileOps.h b/xbmc/pvrclients/mythtv-cmyth/fileOps.h
+new file mode 100644
+index 0000000..9a6022d
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/fileOps.h
+@@ -0,0 +1,66 @@
++#ifndef __FILEOPS_H
++#define __FILEOPS_H
++//TODO merge into MythConnection ??
++
++#include "utils/StdString.h"
++#include <vector>
++#include <map>
++#include <queue>
++#include "cppmyth/MythSGFile.h"
++#include "../../../lib/platform/threads/threads.h"
++#define BOOST_FILESYSTEM_NO_DEPRECATED
++#define BOOST_FILESYSTEM_VERSION 3
++#include <boost/filesystem.hpp>
++
++
++
++class MythConnection;
++
++typedef enum
++ {
++ FILE_OPS_GET_COVERART,
++ FILE_OPS_GET_FANART,
++ FILE_OPS_GET_CHAN_ICONS, //TODO: channel icons will be available as a storage group in v.0.25
++ FILE_OPS_GET_BANNER,
++ FILE_OPS_GET_SCREENSHOT,
++ FILE_OPS_GET_POSTER,
++ FILE_OPS_GET_BACKCOVER,
++ FILE_OPS_GET_INSIDECOVER,
++ FILE_OPS_GET_CD_IMAGE,
++ FILE_OPS_GET_THUMBNAIL
++ } FILE_OPTIONS;
++
++
++class fileOps2 : public CThread, public CMutex
++{
++public:
++ fileOps2(MythConnection &mythConnection);
++ virtual ~fileOps2();
++ CStdString getArtworkPath(CStdString title,FILE_OPTIONS Get_What);
++ CStdString getChannelIconPath(CStdString remotePath);
++ CStdString getPreviewIconPath(CStdString remotePath);
++protected:
++ bool createDirectory(boost::filesystem::path dirPath, bool hasFilename = false);
++ bool localFileExists(MythSGFile &remotefile, boost::filesystem::path dirPath);
++ void* Process();
++ bool writeFile(boost::filesystem::path destination, MythFile &source);
++ void cleanCache();
++ std::map< FILE_OPTIONS, std::vector< MythSGFile > > m_SGFilelist;
++ std::map< FILE_OPTIONS, int > m_lastSGupdate;
++ std::map< CStdString, CStdString > m_icons;
++ std::map< CStdString, CStdString > m_preview;
++ boost::filesystem::path m_localBasePath;
++ MythConnection m_con;
++ std::map< FILE_OPTIONS, CStdString > m_sg_strings;
++ CEvent m_queue_content;
++ struct jobItem {
++ boost::filesystem::path localFilename;
++ CStdString remoteFilename;
++ CStdString storageGroup;
++ jobItem(boost::filesystem::path localFilename,CStdString remoteFilename,CStdString storageGroup):localFilename(localFilename),remoteFilename(remoteFilename),storageGroup(storageGroup){}
++ };
++ std::queue< fileOps2::jobItem > m_jobqueue;
++
++};
++
++#endif
+\ No newline at end of file
+diff --git a/xbmc/pvrclients/mythtv-cmyth/libcmyth.h b/xbmc/pvrclients/mythtv-cmyth/libcmyth.h
+new file mode 100644
+index 0000000..d6a5933
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/libcmyth.h
+@@ -0,0 +1,1642 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++//$(SolutionDir)\..\..\lib\cmyth\Win32\$(Configuration)\vs2010\libcmyth.lib
++
++#include <string>
++#include <vector>
++#include <string.h>
++#include <stdlib.h>
++#include <stdio.h>
++
++#ifndef _LINUX
++#include "../../../addons/library.xbmc.addon/dlfcn-win32.h"
++#define LIBCMYTH_DLL "\\..\\system\\libcmyth.dll"
++#else
++#include <dlfcn.h>
++#if defined(__APPLE__)
++#if defined(__POWERPC__)
++#define LIBCMYTH_DLL "/../system/libcmyth-powerpc-osx.so"
++#elif defined(__arm__)
++#define LIBCMYTH_DLL "/../system/libcmyth-arm-osx.so"
++#else
++#define LIBCMYTH_DLL "/../system/libcmyth-x86-osx.so"
++#endif
++#elif defined(__x86_64__)
++#define LIBCMYTH_DLL "/../system/libcmyth-x86_64-linux.so"
++#elif defined(_POWERPC)
++#define LIBCMYTH_DLL "/../system/libcmyth-powerpc-linux.so"
++#elif defined(_POWERPC64)
++#define LIBCMYTH_DLL "/../system/libcmyth-powerpc64-linux.so"
++#elif defined(_ARMEL)
++#define LIBCMYTH_DLL "/../system/libcmyth-arm.so"
++#else /* !__x86_64__ && !__powerpc__ */
++#define LIBCMYTH_DLL "/../system/libcmyth-i486-linux.so"
++#endif /* __x86_64__ */
++#endif /* _LINUX */
++
++#ifdef __APPLE__
++#include <sys/time.h>
++#else
++#include <time.h>
++#endif
++
++#if defined(DEBUG)
++#define ref_alloc(l) (__ref_alloc((l), __FILE__, __FUNCTION__, __LINE__))
++#else
++#define ref_alloc(l) (__ref_alloc((l), (char *)0, (char *)0, 0))
++#endif
++
++/*
++ * -----------------------------------------------------------------
++ * Types
++ * -----------------------------------------------------------------
++ */
++
++typedef void (*ref_destroy_t)(void *p);
++
++struct cmyth_conn;
++typedef struct cmyth_conn *cmyth_conn_t;
++
++/* Sergio: Added to support the new livetv protocol */
++struct cmyth_livetv_chain;
++typedef struct cmyth_livetv_chain *cmyth_livetv_chain_t;
++
++struct cmyth_recorder;
++typedef struct cmyth_recorder *cmyth_recorder_t;
++
++struct cmyth_ringbuf;
++typedef struct cmyth_ringbuf *cmyth_ringbuf_t;
++
++struct cmyth_rec_num;
++typedef struct cmyth_rec_num *cmyth_rec_num_t;
++
++struct cmyth_posmap;
++typedef struct cmyth_posmap *cmyth_posmap_t;
++
++struct cmyth_proginfo;
++typedef struct cmyth_proginfo *cmyth_proginfo_t;
++
++struct cmyth_database;
++typedef struct cmyth_database *cmyth_database_t;
++
++
++typedef enum {
++ CHANNEL_DIRECTION_UP = 0,
++ CHANNEL_DIRECTION_DOWN = 1,
++ CHANNEL_DIRECTION_FAVORITE = 2,
++ CHANNEL_DIRECTION_SAME = 4
++} cmyth_channeldir_t;
++
++typedef enum {
++ ADJ_DIRECTION_UP = 1,
++ ADJ_DIRECTION_DOWN = 0
++} cmyth_adjdir_t;
++
++typedef enum {
++ BROWSE_DIRECTION_SAME = 0,
++ BROWSE_DIRECTION_UP = 1,
++ BROWSE_DIRECTION_DOWN = 2,
++ BROWSE_DIRECTION_LEFT = 3,
++ BROWSE_DIRECTION_RIGHT = 4,
++ BROWSE_DIRECTION_FAVORITE = 5
++} cmyth_browsedir_t;
++
++typedef enum {
++ WHENCE_SET = 0,
++ WHENCE_CUR = 1,
++ WHENCE_END = 2
++} cmyth_whence_t;
++
++typedef enum {
++ CMYTH_EVENT_UNKNOWN = 0,
++ CMYTH_EVENT_CLOSE = 1,
++ CMYTH_EVENT_RECORDING_LIST_CHANGE,
++ CMYTH_EVENT_RECORDING_LIST_CHANGE_ADD,
++ CMYTH_EVENT_RECORDING_LIST_CHANGE_UPDATE,
++ CMYTH_EVENT_RECORDING_LIST_CHANGE_DELETE,
++ CMYTH_EVENT_SCHEDULE_CHANGE,
++ CMYTH_EVENT_DONE_RECORDING,
++ CMYTH_EVENT_QUIT_LIVETV,
++ CMYTH_EVENT_WATCH_LIVETV,
++ CMYTH_EVENT_LIVETV_CHAIN_UPDATE,
++ CMYTH_EVENT_SIGNAL,
++ CMYTH_EVENT_ASK_RECORDING,
++ CMYTH_EVENT_SYSTEM_EVENT,
++ CMYTH_EVENT_UPDATE_FILE_SIZE,
++ CMYTH_EVENT_GENERATED_PIXMAP,
++ CMYTH_EVENT_CLEAR_SETTINGS_CACHE
++} cmyth_event_t;
++
++#define CMYTH_NUM_SORTS 2
++typedef enum {
++ MYTHTV_SORT_DATE_RECORDED = 0,
++ MYTHTV_SORT_ORIGINAL_AIRDATE
++} cmyth_proglist_sort_t;
++
++struct cmyth_timestamp;
++typedef struct cmyth_timestamp *cmyth_timestamp_t;
++
++struct cmyth_keyframe;
++typedef struct cmyth_keyframe *cmyth_keyframe_t;
++
++struct cmyth_freespace;
++typedef struct cmyth_freespace *cmyth_freespace_t;
++
++struct cmyth_proglist;
++typedef struct cmyth_proglist *cmyth_proglist_t;
++
++struct cmyth_file;
++typedef struct cmyth_file *cmyth_file_t;
++
++struct cmyth_commbreak {
++ long long start_mark;
++ long long start_offset;
++ long long end_mark;
++ long long end_offset;
++};
++typedef struct cmyth_commbreak *cmyth_commbreak_t;
++
++struct cmyth_commbreaklist {
++ cmyth_commbreak_t *commbreak_list;
++ long commbreak_count;
++};
++typedef struct cmyth_commbreaklist *cmyth_commbreaklist_t;
++
++/* Sergio: Added to support the tvguide functionality */
++
++struct cmyth_channel;
++typedef struct cmyth_channel *cmyth_channel_t;
++
++struct cmyth_chanlist;
++typedef struct cmyth_chanlist *cmyth_chanlist_t;
++
++struct cmyth_tvguide_progs;
++typedef struct cmyth_tvguide_progs *cmyth_tvguide_progs_t;
++
++typedef struct cmyth_program {
++ int chanid;
++ char callsign[30];
++ char name[84];
++ int sourceid;
++ char title[150];
++ char subtitle[150];
++ char description[280];
++ time_t starttime;
++ time_t endtime;
++ char programid[30];
++ char seriesid[24];
++ char category[84];
++ int recording;
++ int rec_status;
++ int channum;
++ int event_flags;
++ int startoffset;
++ int endoffset;
++ cmyth_program(){memset(this,0,sizeof(cmyth_program));}
++}cmyth_program_t;
++
++typedef struct cmyth_recgrougs {
++ char recgroups[33];
++}cmyth_recgroups_t;
++
++/*typedef enum {
++ RS_DELETED = -5,
++ RS_STOPPED = -4,
++ RS_RECORDED = -3,
++ RS_RECORDING = -2,
++ RS_WILL_RECORD = -1,
++ RS_DONT_RECORD = 1,
++ RS_PREVIOUS_RECORDING = 2,
++ RS_CURRENT_RECORDING = 3,
++ RS_EARLIER_RECORDING = 4,
++ RS_TOO_MANY_RECORDINGS = 5,
++ RS_CANCELLED = 6,
++ RS_CONFLICT = 7,
++ RS_LATER_SHOWING = 8,
++ RS_REPEAT = 9,
++ RS_LOW_DISKSPACE = 11,
++ RS_TUNER_BUSY = 12
++} cmyth_proginfo_rec_status_t;*/
++typedef enum {
++ RS_TUNING = -10,
++ RS_FAILED = -9,
++ RS_TUNER_BUSY = -8,
++ RS_LOW_DISKSPACE = -7,
++ RS_CANCELLED = -6,
++ RS_MISSED = -5,
++ RS_ABORTED = -4,
++ RS_RECORDED = -3,
++ RS_RECORDING = -2,
++ RS_WILL_RECORD = -1,
++ RS_UNKNOWN = 0,
++ RS__DONT_RECORD = 1,
++ RS_PREVIOUS_RECORDING = 2,
++ RS_CURRENT_RECORDING = 3,
++ RS_EARLIER_SHOWING = 4,
++ RS_TOO_MANY_RECORDINGS = 5,
++ RS_NOT_LISTED = 6,
++ RS_CONFLICT = 7,
++ RS_LATER_SHOWING = 8,
++ RS_REPEAT = 9,
++ RS_INACTIVE = 10,
++ RS_NEVER_RECORD = 11,
++ RS_OFFLINE = 12,
++ RS_OTHER_SHOWING = 13
++} cmyth_proginfo_rec_status_t;
++
++/*From libmyth 0.24 Note difference from above:
++typedef enum RecStatusTypes {
++ rsTuning = -10,
++ rsFailed = -9,
++ rsTunerBusy = -8,
++ rsLowDiskSpace = -7,
++ rsCancelled = -6,
++ rsMissed = -5,
++ rsAborted = -4,
++ rsRecorded = -3,
++ rsRecording = -2,
++ rsWillRecord = -1,
++ rsUnknown = 0,
++ rsDontRecord = 1,
++ rsPreviousRecording = 2,
++ rsCurrentRecording = 3,
++ rsEarlierShowing = 4,
++ rsTooManyRecordings = 5,
++ rsNotListed = 6,
++ rsConflict = 7,
++ rsLaterShowing = 8,
++ rsRepeat = 9,
++ rsInactive = 10,
++ rsNeverRecord = 11,
++ rsOffLine = 12,
++ rsOtherShowing = 13
++} RecStatusType;
++*/
++struct cmyth_timer;
++typedef struct cmyth_timer* cmyth_timer_t;
++
++struct cmyth_timerlist;
++typedef struct cmyth_timerlist* cmyth_timerlist_t;
++
++typedef struct cmyth_channelgroups {
++ char channelgroup[65];
++ unsigned int ID;
++} cmyth_channelgroups_t;
++
++typedef struct cmyth_rec {
++ int recid;
++ int sourceid;
++} cmyth_rec_t;
++
++typedef struct cmyth_recprofile{
++int id;
++char name[128];
++char cardtype[32];
++} cmyth_recprofile_t;
++
++struct cmyth_storagegroup_filelist;
++typedef struct cmyth_storagegroup_filelist* cmyth_storagegroup_filelist_t;
++
++struct cmyth_storagegroup_file;
++typedef struct cmyth_storagegroup_file* cmyth_storagegroup_file_t;
++
++#define CMYTH_DBG_NONE -1
++#define CMYTH_DBG_ERROR 0
++#define CMYTH_DBG_WARN 1
++#define CMYTH_DBG_INFO 2
++#define CMYTH_DBG_DETAIL 3
++#define CMYTH_DBG_DEBUG 4
++#define CMYTH_DBG_PROTO 5
++#define CMYTH_DBG_ALL 6
++
++#define PROGRAM_ADJUST 3600
++
++class CHelper_libcmyth
++{
++public:
++ CHelper_libcmyth()
++ {
++ m_libcmyth = NULL;
++ m_Handle = NULL;
++ }
++
++ ~CHelper_libcmyth()
++ {
++ if (m_libcmyth)
++ {
++ //XBMC_unregister_me();
++ dlclose(m_libcmyth);
++ }
++ }
++
++ bool RegisterMe(void *Handle)
++ {
++ m_Handle = Handle;
++
++ std::string libBasePath;
++ libBasePath = ((cb_array*)m_Handle)->libPath;
++ libBasePath += LIBCMYTH_DLL;
++
++ m_libcmyth = dlopen(libBasePath.c_str(), RTLD_LAZY);
++ if (m_libcmyth == NULL)
++ {
++ fprintf(stderr, "Unable to load %s\n", dlerror());
++ return false;
++ }
++
++ DbgLevel = (void (*)(int l))
++dlsym(m_libcmyth, "cmyth_dbg_level");
++if (DbgLevel == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ DbgAll = (void (*)(void))
++dlsym(m_libcmyth, "cmyth_dbg_all");
++if (DbgAll == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ DbgNone = (void (*)(void))
++dlsym(m_libcmyth, "cmyth_dbg_none");
++if (DbgNone == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ Dbg = (void (*)(int level, char* fmt, ...))
++dlsym(m_libcmyth, "cmyth_dbg");
++if (Dbg == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ SetDbgMsgcallback = (void (*)(void (* msgcb)(int level,char* )))
++dlsym(m_libcmyth, "cmyth_set_dbg_msgcallback");
++if (SetDbgMsgcallback == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ConnConnectCtrl = (cmyth_conn_t (*)(char* server, unsigned short port, unsigned buflen, int tcp_rcvbuf))
++dlsym(m_libcmyth, "cmyth_conn_connect_ctrl");
++if (ConnConnectCtrl == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ConnConnectEvent = (cmyth_conn_t (*)(char* server, unsigned short port, unsigned buflen, int tcp_rcvbuf))
++dlsym(m_libcmyth, "cmyth_conn_connect_event");
++if (ConnConnectEvent == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ConnConnectFile = (cmyth_file_t (*)(cmyth_proginfo_t prog, cmyth_conn_t control, unsigned buflen, int tcp_rcvbuf))
++dlsym(m_libcmyth, "cmyth_conn_connect_file");
++if (ConnConnectFile == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ConnConnectPath = (cmyth_file_t (*)(char* path, cmyth_conn_t control, unsigned buflen, int tcp_rcvbuf,char* sgToGetFrom))
++dlsym(m_libcmyth, "cmyth_conn_connect_path");
++if (ConnConnectPath == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ConnConnectRing = (int (*)(cmyth_recorder_t rec, unsigned buflen,int tcp_rcvbuf))
++dlsym(m_libcmyth, "cmyth_conn_connect_ring");
++if (ConnConnectRing == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ConnConnectRecorder = (int (*)(cmyth_recorder_t rec, unsigned buflen, int tcp_rcvbuf))
++dlsym(m_libcmyth, "cmyth_conn_connect_recorder");
++if (ConnConnectRecorder == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ConnCheckBlock = (int (*)(cmyth_conn_t conn, unsigned long size))
++dlsym(m_libcmyth, "cmyth_conn_check_block");
++if (ConnCheckBlock == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ConnGetRecorderFromNum = (cmyth_recorder_t (*)(cmyth_conn_t conn, int num))
++dlsym(m_libcmyth, "cmyth_conn_get_recorder_from_num");
++if (ConnGetRecorderFromNum == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ConnGetFreeRecorder = (cmyth_recorder_t (*)(cmyth_conn_t conn))
++dlsym(m_libcmyth, "cmyth_conn_get_free_recorder");
++if (ConnGetFreeRecorder == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ConnGetFreespace = (int (*)(cmyth_conn_t control, long long* total, long long* used))
++dlsym(m_libcmyth, "cmyth_conn_get_freespace");
++if (ConnGetFreespace == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ConnHung = (int (*)(cmyth_conn_t control))
++dlsym(m_libcmyth, "cmyth_conn_hung");
++if (ConnHung == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ConnGetFreeRecorderCount = (int (*)(cmyth_conn_t conn))
++dlsym(m_libcmyth, "cmyth_conn_get_free_recorder_count");
++if (ConnGetFreeRecorderCount == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ConnGetProtocolVersion = (int (*)(cmyth_conn_t conn))
++dlsym(m_libcmyth, "cmyth_conn_get_protocol_version");
++if (ConnGetProtocolVersion == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ConnGetSetting = (char* (*)(cmyth_conn_t conn,const char* hostname, const char* setting))
++dlsym(m_libcmyth, "cmyth_conn_get_setting");
++if (ConnGetSetting == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ConnSetSetting = (int (*)(cmyth_conn_t conn,const char* hostname, const char* setting, const char* value))
++dlsym(m_libcmyth, "cmyth_conn_set_setting");
++if (ConnSetSetting == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ConnGetBackendHostname = (char* (*)(cmyth_conn_t conn))
++dlsym(m_libcmyth, "cmyth_conn_get_backend_hostname");
++if (ConnGetBackendHostname == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ConnGetClientHostname = (char* (*)(cmyth_conn_t conn))
++dlsym(m_libcmyth, "cmyth_conn_get_client_hostname");
++if (ConnGetClientHostname == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ EventGet = (cmyth_event_t (*)(cmyth_conn_t conn, char* data, int len))
++dlsym(m_libcmyth, "cmyth_event_get");
++if (EventGet == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ EventSelect = (int (*)(cmyth_conn_t conn, struct timeval* timeout))
++dlsym(m_libcmyth, "cmyth_event_select");
++if (EventSelect == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderCreate = (cmyth_recorder_t (*)(void))
++dlsym(m_libcmyth, "cmyth_recorder_create");
++if (RecorderCreate == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderDup = (cmyth_recorder_t (*)(cmyth_recorder_t p))
++dlsym(m_libcmyth, "cmyth_recorder_dup");
++if (RecorderDup == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderIsRecording = (int (*)(cmyth_recorder_t rec))
++dlsym(m_libcmyth, "cmyth_recorder_is_recording");
++if (RecorderIsRecording == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderGetFramerate = (int (*)(cmyth_recorder_t rec,double* rate))
++dlsym(m_libcmyth, "cmyth_recorder_get_framerate");
++if (RecorderGetFramerate == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderGetFramesWritten = (long long (*)(cmyth_recorder_t rec))
++dlsym(m_libcmyth, "cmyth_recorder_get_frames_written");
++if (RecorderGetFramesWritten == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderGetFreeSpace = (long long (*)(cmyth_recorder_t rec))
++dlsym(m_libcmyth, "cmyth_recorder_get_free_space");
++if (RecorderGetFreeSpace == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderGetKeyframePos = (long long (*)(cmyth_recorder_t rec, unsigned long keynum))
++dlsym(m_libcmyth, "cmyth_recorder_get_keyframe_pos");
++if (RecorderGetKeyframePos == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderGetPositionMap = (cmyth_posmap_t (*)(cmyth_recorder_t rec,unsigned long start,unsigned long end))
++dlsym(m_libcmyth, "cmyth_recorder_get_position_map");
++if (RecorderGetPositionMap == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderGetRecording = (cmyth_proginfo_t (*)(cmyth_recorder_t rec))
++dlsym(m_libcmyth, "cmyth_recorder_get_recording");
++if (RecorderGetRecording == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderStopPlaying = (int (*)(cmyth_recorder_t rec))
++dlsym(m_libcmyth, "cmyth_recorder_stop_playing");
++if (RecorderStopPlaying == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderFrontendReady = (int (*)(cmyth_recorder_t rec))
++dlsym(m_libcmyth, "cmyth_recorder_frontend_ready");
++if (RecorderFrontendReady == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderCancelNextRecording = (int (*)(cmyth_recorder_t rec))
++dlsym(m_libcmyth, "cmyth_recorder_cancel_next_recording");
++if (RecorderCancelNextRecording == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderPause = (int (*)(cmyth_recorder_t rec))
++dlsym(m_libcmyth, "cmyth_recorder_pause");
++if (RecorderPause == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderFinishRecording = (int (*)(cmyth_recorder_t rec))
++dlsym(m_libcmyth, "cmyth_recorder_finish_recording");
++if (RecorderFinishRecording == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderToggleChannelFavorite = (int (*)(cmyth_recorder_t rec))
++dlsym(m_libcmyth, "cmyth_recorder_toggle_channel_favorite");
++if (RecorderToggleChannelFavorite == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderChangeChannel = (int (*)(cmyth_recorder_t rec, cmyth_channeldir_t direction))
++dlsym(m_libcmyth, "cmyth_recorder_change_channel");
++if (RecorderChangeChannel == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderSetChannel = (int (*)(cmyth_recorder_t rec,char* channame))
++dlsym(m_libcmyth, "cmyth_recorder_set_channel");
++if (RecorderSetChannel == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderChangeColor = (int (*)(cmyth_recorder_t rec, cmyth_adjdir_t direction))
++dlsym(m_libcmyth, "cmyth_recorder_change_color");
++if (RecorderChangeColor == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderChangeBrightness = (int (*)(cmyth_recorder_t rec, cmyth_adjdir_t direction))
++dlsym(m_libcmyth, "cmyth_recorder_change_brightness");
++if (RecorderChangeBrightness == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderChangeContrast = (int (*)(cmyth_recorder_t rec, cmyth_adjdir_t direction))
++dlsym(m_libcmyth, "cmyth_recorder_change_contrast");
++if (RecorderChangeContrast == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderChangeHue = (int (*)(cmyth_recorder_t rec, cmyth_adjdir_t direction))
++dlsym(m_libcmyth, "cmyth_recorder_change_hue");
++if (RecorderChangeHue == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderCheckChannel = (int (*)(cmyth_recorder_t rec,char* channame))
++dlsym(m_libcmyth, "cmyth_recorder_check_channel");
++if (RecorderCheckChannel == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderCheckChannelPrefix = (int (*)(cmyth_recorder_t rec, char* channame))
++dlsym(m_libcmyth, "cmyth_recorder_check_channel_prefix");
++if (RecorderCheckChannelPrefix == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderGetCurProginfo = (cmyth_proginfo_t (*)(cmyth_recorder_t rec))
++dlsym(m_libcmyth, "cmyth_recorder_get_cur_proginfo");
++if (RecorderGetCurProginfo == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderGetNextProginfo = (cmyth_proginfo_t (*)(cmyth_recorder_t rec,cmyth_proginfo_t current,cmyth_browsedir_t direction))
++dlsym(m_libcmyth, "cmyth_recorder_get_next_proginfo");
++if (RecorderGetNextProginfo == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderGetInputName = (int (*)(cmyth_recorder_t rec, char* name, unsigned len))
++dlsym(m_libcmyth, "cmyth_recorder_get_input_name");
++if (RecorderGetInputName == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderSeek = (long long (*)(cmyth_recorder_t rec, long long pos, cmyth_whence_t whence, long long curpos))
++dlsym(m_libcmyth, "cmyth_recorder_seek");
++if (RecorderSeek == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderSpawnChainLivetv = (int (*)(cmyth_recorder_t rec, char* channame))
++dlsym(m_libcmyth, "cmyth_recorder_spawn_chain_livetv");
++if (RecorderSpawnChainLivetv == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderSpawnLivetv = (int (*)(cmyth_recorder_t rec))
++dlsym(m_libcmyth, "cmyth_recorder_spawn_livetv");
++if (RecorderSpawnLivetv == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderStartStream = (int (*)(cmyth_recorder_t rec))
++dlsym(m_libcmyth, "cmyth_recorder_start_stream");
++if (RecorderStartStream == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderEndStream = (int (*)(cmyth_recorder_t rec))
++dlsym(m_libcmyth, "cmyth_recorder_end_stream");
++if (RecorderEndStream == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderGetFilename = (char* (*)(cmyth_recorder_t rec))
++dlsym(m_libcmyth, "cmyth_recorder_get_filename");
++if (RecorderGetFilename == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderStopLivetv = (int (*)(cmyth_recorder_t rec))
++dlsym(m_libcmyth, "cmyth_recorder_stop_livetv");
++if (RecorderStopLivetv == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderDoneRingbuf = (int (*)(cmyth_recorder_t rec))
++dlsym(m_libcmyth, "cmyth_recorder_done_ringbuf");
++if (RecorderDoneRingbuf == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecorderGetRecorderId = (int (*)(cmyth_recorder_t rec))
++dlsym(m_libcmyth, "cmyth_recorder_get_recorder_id");
++if (RecorderGetRecorderId == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ LivetvChainCreate = (cmyth_livetv_chain_t (*)(char* chainid))
++dlsym(m_libcmyth, "cmyth_livetv_chain_create");
++if (LivetvChainCreate == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ LivetvChainDuration = (long long (*)(cmyth_recorder_t rec))
++dlsym(m_libcmyth, "cmyth_livetv_chain_duration");
++if (LivetvChainDuration == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ LivetvChainSwitch = (int (*)(cmyth_recorder_t rec, int dir))
++dlsym(m_libcmyth, "cmyth_livetv_chain_switch");
++if (LivetvChainSwitch == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ LivetvChainSwitchLast = (int (*)(cmyth_recorder_t rec))
++dlsym(m_libcmyth, "cmyth_livetv_chain_switch_last");
++if (LivetvChainSwitchLast == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ LivetvChainUpdate = (int (*)(cmyth_recorder_t rec, char* chainid,int tcp_rcvbuf))
++dlsym(m_libcmyth, "cmyth_livetv_chain_update");
++if (LivetvChainUpdate == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ SpawnLiveTv = (cmyth_recorder_t (*)(cmyth_recorder_t rec,unsigned buflen,int tcp_rcvbuf, void (* prog_update_callback)(cmyth_proginfo_t),char** err, char* channame))
++dlsym(m_libcmyth, "cmyth_spawn_live_tv");
++if (SpawnLiveTv == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ LivetvChainSetup = (cmyth_recorder_t (*)(cmyth_recorder_t old_rec, int tcp_rcvbuf, void (* prog_update_callback)(cmyth_proginfo_t)))
++dlsym(m_libcmyth, "cmyth_livetv_chain_setup");
++if (LivetvChainSetup == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ LivetvGetBlock = (int (*)(cmyth_recorder_t rec, char* buf, unsigned long len))
++dlsym(m_libcmyth, "cmyth_livetv_get_block");
++if (LivetvGetBlock == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ LivetvSelect = (int (*)(cmyth_recorder_t rec, struct timeval* timeout))
++dlsym(m_libcmyth, "cmyth_livetv_select");
++if (LivetvSelect == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ LivetvRequestBlock = (int (*)(cmyth_recorder_t rec, unsigned long len))
++dlsym(m_libcmyth, "cmyth_livetv_request_block");
++if (LivetvRequestBlock == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ LivetvSeek = (long long (*)(cmyth_recorder_t rec,long long offset, int whence))
++dlsym(m_libcmyth, "cmyth_livetv_seek");
++if (LivetvSeek == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ LivetvRead = (int (*)(cmyth_recorder_t rec, char* buf, unsigned long len))
++dlsym(m_libcmyth, "cmyth_livetv_read");
++if (LivetvRead == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ LivetvKeepRecording = (int (*)(cmyth_recorder_t rec, cmyth_database_t db, int keep))
++dlsym(m_libcmyth, "cmyth_livetv_keep_recording");
++if (LivetvKeepRecording == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ DatabaseInit = (cmyth_database_t (*)(char* host, char* db_name, char* user, char* pass))
++dlsym(m_libcmyth, "cmyth_database_init");
++if (DatabaseInit == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ DatabaseSetHost = (int (*)(cmyth_database_t db, char* host))
++dlsym(m_libcmyth, "cmyth_database_set_host");
++if (DatabaseSetHost == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ DatabaseSetUser = (int (*)(cmyth_database_t db, char* user))
++dlsym(m_libcmyth, "cmyth_database_set_user");
++if (DatabaseSetUser == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ DatabaseSetPass = (int (*)(cmyth_database_t db, char* pass))
++dlsym(m_libcmyth, "cmyth_database_set_pass");
++if (DatabaseSetPass == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ DatabaseSetName = (int (*)(cmyth_database_t db, char* name))
++dlsym(m_libcmyth, "cmyth_database_set_name");
++if (DatabaseSetName == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ GetWatchedStatusMysql = (int (*)(cmyth_database_t db, int recordid))
++dlsym(m_libcmyth, "cmyth_get_watched_status_mysql");
++if (GetWatchedStatusMysql == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ SetWatchedStatusMysql = (int (*)(cmyth_database_t db, int recordid, int watchedStat))
++dlsym(m_libcmyth, "cmyth_set_watched_status_mysql");
++if (SetWatchedStatusMysql == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RingbufPathname = (char* (*)(cmyth_recorder_t rec))
++dlsym(m_libcmyth, "cmyth_ringbuf_pathname");
++if (RingbufPathname == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RingbufCreate = (cmyth_ringbuf_t (*)(void))
++dlsym(m_libcmyth, "cmyth_ringbuf_create");
++if (RingbufCreate == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RingbufSetup = (cmyth_recorder_t (*)(cmyth_recorder_t old_rec))
++dlsym(m_libcmyth, "cmyth_ringbuf_setup");
++if (RingbufSetup == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RingbufRequestBlock = (int (*)(cmyth_recorder_t rec, unsigned long len))
++dlsym(m_libcmyth, "cmyth_ringbuf_request_block");
++if (RingbufRequestBlock == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RingbufSelect = (int (*)(cmyth_recorder_t rec, struct timeval* timeout))
++dlsym(m_libcmyth, "cmyth_ringbuf_select");
++if (RingbufSelect == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RingbufGetBlock = (int (*)(cmyth_recorder_t rec,char* buf,unsigned long len))
++dlsym(m_libcmyth, "cmyth_ringbuf_get_block");
++if (RingbufGetBlock == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RingbufSeek = (long long (*)(cmyth_recorder_t rec, long long offset, int whence))
++dlsym(m_libcmyth, "cmyth_ringbuf_seek");
++if (RingbufSeek == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RingbufRead = (int (*)(cmyth_recorder_t rec,char* buf,unsigned long len))
++dlsym(m_libcmyth, "cmyth_ringbuf_read");
++if (RingbufRead == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecNumCreate = (cmyth_rec_num_t (*)(void))
++dlsym(m_libcmyth, "cmyth_rec_num_create");
++if (RecNumCreate == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecNumGet = (cmyth_rec_num_t (*)(char* host, unsigned short port, unsigned id))
++dlsym(m_libcmyth, "cmyth_rec_num_get");
++if (RecNumGet == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RecNumString = (char* (*)(cmyth_rec_num_t rn))
++dlsym(m_libcmyth, "cmyth_rec_num_string");
++if (RecNumString == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimestampCreate = (cmyth_timestamp_t (*)(void))
++dlsym(m_libcmyth, "cmyth_timestamp_create");
++if (TimestampCreate == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimestampFromString = (cmyth_timestamp_t (*)(char* str))
++dlsym(m_libcmyth, "cmyth_timestamp_from_string");
++if (TimestampFromString == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimestampFromUnixtime = (cmyth_timestamp_t (*)(time_t l))
++dlsym(m_libcmyth, "cmyth_timestamp_from_unixtime");
++if (TimestampFromUnixtime == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimestampToUnixtime = (time_t (*)(cmyth_timestamp_t ts))
++dlsym(m_libcmyth, "cmyth_timestamp_to_unixtime");
++if (TimestampToUnixtime == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimestampToString = (int (*)(char* str, cmyth_timestamp_t ts))
++dlsym(m_libcmyth, "cmyth_timestamp_to_string");
++if (TimestampToString == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimestampToIsostring = (int (*)(char* str, cmyth_timestamp_t ts))
++dlsym(m_libcmyth, "cmyth_timestamp_to_isostring");
++if (TimestampToIsostring == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimestampToDisplayString = (int (*)(char* str, cmyth_timestamp_t ts, int time_format_12))
++dlsym(m_libcmyth, "cmyth_timestamp_to_display_string");
++if (TimestampToDisplayString == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ DatetimeToString = (int (*)(char* str, cmyth_timestamp_t ts))
++dlsym(m_libcmyth, "cmyth_datetime_to_string");
++if (DatetimeToString == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimestampCompare = (int (*)(cmyth_timestamp_t ts1,cmyth_timestamp_t ts2))
++dlsym(m_libcmyth, "cmyth_timestamp_compare");
++if (TimestampCompare == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ KeyframeCreate = (cmyth_keyframe_t (*)(void))
++dlsym(m_libcmyth, "cmyth_keyframe_create");
++if (KeyframeCreate == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ KeyframeString = (char* (*)(cmyth_keyframe_t kf))
++dlsym(m_libcmyth, "cmyth_keyframe_string");
++if (KeyframeString == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ PosmapCreate = (cmyth_posmap_t (*)(void))
++dlsym(m_libcmyth, "cmyth_posmap_create");
++if (PosmapCreate == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoCreate = (cmyth_proginfo_t (*)(void))
++dlsym(m_libcmyth, "cmyth_proginfo_create");
++if (ProginfoCreate == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoStopRecording = (int (*)(cmyth_conn_t control, cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_stop_recording");
++if (ProginfoStopRecording == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoCheckRecording = (int (*)(cmyth_conn_t control, cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_check_recording");
++if (ProginfoCheckRecording == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoDeleteRecording = (int (*)(cmyth_conn_t control,cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_delete_recording");
++if (ProginfoDeleteRecording == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoForgetRecording = (int (*)(cmyth_conn_t control,cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_forget_recording");
++if (ProginfoForgetRecording == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoGetRecorderNum = (int (*)(cmyth_conn_t control,cmyth_rec_num_t rnum,cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_get_recorder_num");
++if (ProginfoGetRecorderNum == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoGetFromBasename = (cmyth_proginfo_t (*)(cmyth_conn_t control, const char* basename))
++dlsym(m_libcmyth, "cmyth_proginfo_get_from_basename");
++if (ProginfoGetFromBasename == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoTitle = (char* (*)(cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_title");
++if (ProginfoTitle == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoSubtitle = (char* (*)(cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_subtitle");
++if (ProginfoSubtitle == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoDescription = (char* (*)(cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_description");
++if (ProginfoDescription == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoCategory = (char* (*)(cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_category");
++if (ProginfoCategory == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoChanstr = (char* (*)(cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_chanstr");
++if (ProginfoChanstr == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoChansign = (char* (*)(cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_chansign");
++if (ProginfoChansign == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoChanname = (char* (*)(cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_channame");
++if (ProginfoChanname == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoChanId = (long (*)(cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_chan_id");
++if (ProginfoChanId == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoPathname = (char* (*)(cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_pathname");
++if (ProginfoPathname == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoSeriesid = (char* (*)(cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_seriesid");
++if (ProginfoSeriesid == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoProgramid = (char* (*)(cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_programid");
++if (ProginfoProgramid == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoRecordid = (unsigned long (*)(cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_recordid");
++if (ProginfoRecordid == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoPriority = (long (*)(cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_priority");
++if (ProginfoPriority == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoStars = (char* (*)(cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_stars");
++if (ProginfoStars == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoRecStart = (cmyth_timestamp_t (*)(cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_rec_start");
++if (ProginfoRecStart == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoRecEnd = (cmyth_timestamp_t (*)(cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_rec_end");
++if (ProginfoRecEnd == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoOriginalairdate = (cmyth_timestamp_t (*)(cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_originalairdate");
++if (ProginfoOriginalairdate == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoRecStatus = (cmyth_proginfo_rec_status_t (*)(cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_rec_status");
++if (ProginfoRecStatus == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoFlags = (unsigned long (*)( cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_flags");
++if (ProginfoFlags == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoLength = (long long (*)(cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_length");
++if (ProginfoLength == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoHost = (char* (*)(cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_host");
++if (ProginfoHost == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoCompare = (int (*)(cmyth_proginfo_t a, cmyth_proginfo_t b))
++dlsym(m_libcmyth, "cmyth_proginfo_compare");
++if (ProginfoCompare == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoLengthSec = (int (*)(cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_length_sec");
++if (ProginfoLengthSec == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoGetDetail = (cmyth_proginfo_t (*)(cmyth_conn_t control, cmyth_proginfo_t p))
++dlsym(m_libcmyth, "cmyth_proginfo_get_detail");
++if (ProginfoGetDetail == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoStart = (cmyth_timestamp_t (*)(cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_start");
++if (ProginfoStart == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoEnd = (cmyth_timestamp_t (*)(cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_end");
++if (ProginfoEnd == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoCardId = (long (*)(cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_card_id");
++if (ProginfoCardId == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoRecgroup = (char* (*)(cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_recgroup");
++if (ProginfoRecgroup == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoChanicon = (char* (*)(cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_chanicon");
++if (ProginfoChanicon == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProginfoProdyear = (char* (*)(cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proginfo_prodyear");
++if (ProginfoProdyear == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProglistCreate = (cmyth_proglist_t (*)(void))
++dlsym(m_libcmyth, "cmyth_proglist_create");
++if (ProglistCreate == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProglistGetAllRecorded = (cmyth_proglist_t (*)(cmyth_conn_t control))
++dlsym(m_libcmyth, "cmyth_proglist_get_all_recorded");
++if (ProglistGetAllRecorded == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProglistGetAllPending = (cmyth_proglist_t (*)(cmyth_conn_t control))
++dlsym(m_libcmyth, "cmyth_proglist_get_all_pending");
++if (ProglistGetAllPending == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProglistGetAllScheduled = (cmyth_proglist_t (*)(cmyth_conn_t control))
++dlsym(m_libcmyth, "cmyth_proglist_get_all_scheduled");
++if (ProglistGetAllScheduled == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProglistGetConflicting = (cmyth_proglist_t (*)(cmyth_conn_t control))
++dlsym(m_libcmyth, "cmyth_proglist_get_conflicting");
++if (ProglistGetConflicting == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProglistGetItem = (cmyth_proginfo_t (*)(cmyth_proglist_t pl,int index))
++dlsym(m_libcmyth, "cmyth_proglist_get_item");
++if (ProglistGetItem == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProglistDeleteItem = (int (*)(cmyth_proglist_t pl,cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_proglist_delete_item");
++if (ProglistDeleteItem == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProglistGetCount = (int (*)(cmyth_proglist_t pl))
++dlsym(m_libcmyth, "cmyth_proglist_get_count");
++if (ProglistGetCount == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ProglistSort = (int (*)(cmyth_proglist_t pl, int count, cmyth_proglist_sort_t sort))
++dlsym(m_libcmyth, "cmyth_proglist_sort");
++if (ProglistSort == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ FileData = (cmyth_conn_t (*)(cmyth_file_t file))
++dlsym(m_libcmyth, "cmyth_file_data");
++if (FileData == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ FileStart = (unsigned long long (*)(cmyth_file_t file))
++dlsym(m_libcmyth, "cmyth_file_start");
++if (FileStart == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ FileLength = (unsigned long long (*)(cmyth_file_t file))
++dlsym(m_libcmyth, "cmyth_file_length");
++if (FileLength == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ FilePosition = (unsigned long long (*)(cmyth_file_t file))
++dlsym(m_libcmyth, "cmyth_file_position");
++if (FilePosition == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ UpdateFileLength = (int (*)(cmyth_file_t file, unsigned long long newLength))
++dlsym(m_libcmyth, "cmyth_update_file_length");
++if (UpdateFileLength == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ FileGetBlock = (int (*)(cmyth_file_t file, char* buf,unsigned long len))
++dlsym(m_libcmyth, "cmyth_file_get_block");
++if (FileGetBlock == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ FileRequestBlock = (int (*)(cmyth_file_t file, unsigned long len))
++dlsym(m_libcmyth, "cmyth_file_request_block");
++if (FileRequestBlock == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ FileSeek = (long long (*)(cmyth_file_t file, long long offset, int whence))
++dlsym(m_libcmyth, "cmyth_file_seek");
++if (FileSeek == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ FileSelect = (int (*)(cmyth_file_t file, struct timeval* timeout))
++dlsym(m_libcmyth, "cmyth_file_select");
++if (FileSelect == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ FileSetClosedCallback = (void (*)(cmyth_file_t file,void (* callback)(cmyth_file_t)))
++dlsym(m_libcmyth, "cmyth_file_set_closed_callback");
++if (FileSetClosedCallback == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ FileRead = (int (*)(cmyth_file_t file,char* buf,unsigned long len))
++dlsym(m_libcmyth, "cmyth_file_read");
++if (FileRead == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ChannelChanid = (long (*)(cmyth_channel_t channel))
++dlsym(m_libcmyth, "cmyth_channel_chanid");
++if (ChannelChanid == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ChannelChannum = (long (*)(cmyth_channel_t channel))
++dlsym(m_libcmyth, "cmyth_channel_channum");
++if (ChannelChannum == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ChannelChannumstr = (char* (*)(cmyth_channel_t channel))
++dlsym(m_libcmyth, "cmyth_channel_channumstr");
++if (ChannelChannumstr == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ChannelCallsign = (char* (*)(cmyth_channel_t channel))
++dlsym(m_libcmyth, "cmyth_channel_callsign");
++if (ChannelCallsign == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ChannelName = (char* (*)(cmyth_channel_t channel))
++dlsym(m_libcmyth, "cmyth_channel_name");
++if (ChannelName == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ChannelIcon = (char* (*)(cmyth_channel_t channel))
++dlsym(m_libcmyth, "cmyth_channel_icon");
++if (ChannelIcon == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ChannelVisible = (int (*)(cmyth_channel_t channel))
++dlsym(m_libcmyth, "cmyth_channel_visible");
++if (ChannelVisible == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ChanlistGetItem = (cmyth_channel_t (*)(cmyth_chanlist_t pl, int index))
++dlsym(m_libcmyth, "cmyth_chanlist_get_item");
++if (ChanlistGetItem == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ChanlistGetCount = (int (*)(cmyth_chanlist_t pl))
++dlsym(m_libcmyth, "cmyth_chanlist_get_count");
++if (ChanlistGetCount == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ FreespaceCreate = (cmyth_freespace_t (*)(void))
++dlsym(m_libcmyth, "cmyth_freespace_create");
++if (FreespaceCreate == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ GetBookmark = (long long (*)(cmyth_conn_t conn, cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_get_bookmark");
++if (GetBookmark == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ GetBookmarkOffset = (int (*)(cmyth_database_t db, long chanid, long long mark))
++dlsym(m_libcmyth, "cmyth_get_bookmark_offset");
++if (GetBookmarkOffset == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ GetBookmarkMark = (int (*)(cmyth_database_t, cmyth_proginfo_t, long long))
++dlsym(m_libcmyth, "cmyth_get_bookmark_mark");
++if (GetBookmarkMark == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ SetBookmark = (int (*)(cmyth_conn_t conn, cmyth_proginfo_t prog,long long bookmark))
++dlsym(m_libcmyth, "cmyth_set_bookmark");
++if (SetBookmark == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ CommbreaklistCreate = (cmyth_commbreaklist_t (*)(void))
++dlsym(m_libcmyth, "cmyth_commbreaklist_create");
++if (CommbreaklistCreate == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ CommbreakCreate = (cmyth_commbreak_t (*)(void))
++dlsym(m_libcmyth, "cmyth_commbreak_create");
++if (CommbreakCreate == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ GetCommbreaklist = (cmyth_commbreaklist_t (*)(cmyth_conn_t conn, cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_get_commbreaklist");
++if (GetCommbreaklist == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ GetCutlist = (cmyth_commbreaklist_t (*)(cmyth_conn_t conn, cmyth_proginfo_t prog))
++dlsym(m_libcmyth, "cmyth_get_cutlist");
++if (GetCutlist == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RcvCommbreaklist = (int (*)(cmyth_conn_t conn, int* err, cmyth_commbreaklist_t breaklist, int count))
++dlsym(m_libcmyth, "cmyth_rcv_commbreaklist");
++if (RcvCommbreaklist == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ MysqlGetRecgroups = (int (*)(cmyth_database_t, cmyth_recgroups_t**))
++dlsym(m_libcmyth, "cmyth_mysql_get_recgroups");
++if (MysqlGetRecgroups == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ MysqlDeleteScheduledRecording = (int (*)(cmyth_database_t db, char* query))
++dlsym(m_libcmyth, "cmyth_mysql_delete_scheduled_recording");
++if (MysqlDeleteScheduledRecording == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ MysqlInsertIntoRecord = (int (*)(cmyth_database_t db, char* query, char* query1, char* query2, char* title, char* subtitle, char* description, char* callsign))
++dlsym(m_libcmyth, "cmyth_mysql_insert_into_record");
++if (MysqlInsertIntoRecord == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ GetRecordidMysql = (char* (*)(cmyth_database_t, int, char* , char* , char* , char* , char* ))
++dlsym(m_libcmyth, "cmyth_get_recordid_mysql");
++if (GetRecordidMysql == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ GetOffsetMysql = (int (*)(cmyth_database_t, int, char* , int, char* , char* , char* , char* , char* ))
++dlsym(m_libcmyth, "cmyth_get_offset_mysql");
++if (GetOffsetMysql == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ MysqlGetProgFinderCharTitle = (int (*)(cmyth_database_t db, cmyth_program_t**prog, time_t starttime, char* program_name))
++dlsym(m_libcmyth, "cmyth_mysql_get_prog_finder_char_title");
++if (MysqlGetProgFinderCharTitle == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ MysqlGetProgFinderTime = (int (*)(cmyth_database_t db, cmyth_program_t**prog, time_t starttime, char* program_name))
++dlsym(m_libcmyth, "cmyth_mysql_get_prog_finder_time");
++if (MysqlGetProgFinderTime == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ MysqlGetGuide = (int (*)(cmyth_database_t db, cmyth_program_t**prog, time_t starttime, time_t endtime))
++dlsym(m_libcmyth, "cmyth_mysql_get_guide");
++if (MysqlGetGuide == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ MysqlTestdbConnection = (int (*)(cmyth_database_t db,char**message))
++dlsym(m_libcmyth, "cmyth_mysql_testdb_connection");
++if (MysqlTestdbConnection == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ScheduleRecording = (int (*)(cmyth_conn_t conn, char* msg))
++dlsym(m_libcmyth, "cmyth_schedule_recording");
++if (ScheduleRecording == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ MysqlEscapeChars = (char* (*)(cmyth_database_t db, char* string))
++dlsym(m_libcmyth, "cmyth_mysql_escape_chars");
++if (MysqlEscapeChars == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ MysqlGetCommbreakList = (int (*)(cmyth_database_t db, int chanid, char* start_ts_dt, cmyth_commbreaklist_t breaklist, int conn_version))
++dlsym(m_libcmyth, "cmyth_mysql_get_commbreak_list");
++if (MysqlGetCommbreakList == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ MysqlGetPrevRecorded = (int (*)(cmyth_database_t db, cmyth_program_t**prog))
++dlsym(m_libcmyth, "cmyth_mysql_get_prev_recorded");
++if (MysqlGetPrevRecorded == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ GetDeleteList = (int (*)(cmyth_conn_t, char* , cmyth_proglist_t))
++dlsym(m_libcmyth, "cmyth_get_delete_list");
++if (GetDeleteList == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ MythtvRemovePreviosRecorded = (int (*)(cmyth_database_t db,char* query))
++dlsym(m_libcmyth, "cmyth_mythtv_remove_previos_recorded");
++if (MythtvRemovePreviosRecorded == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ MysqlGetChanlist = (cmyth_chanlist_t (*)(cmyth_database_t db))
++dlsym(m_libcmyth, "cmyth_mysql_get_chanlist");
++if (MysqlGetChanlist == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ MysqlIsRadio = (int (*)(cmyth_database_t db, int chanid))
++dlsym(m_libcmyth, "cmyth_mysql_is_radio");
++if (MysqlIsRadio == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimerRecordid = (int (*)(cmyth_timer_t timer))
++dlsym(m_libcmyth, "cmyth_timer_recordid");
++if (TimerRecordid == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimerChanid = (int (*)(cmyth_timer_t timer))
++dlsym(m_libcmyth, "cmyth_timer_chanid");
++if (TimerChanid == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimerStarttime = (time_t (*)(cmyth_timer_t timer))
++dlsym(m_libcmyth, "cmyth_timer_starttime");
++if (TimerStarttime == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimerEndtime = (time_t (*)(cmyth_timer_t timer))
++dlsym(m_libcmyth, "cmyth_timer_endtime");
++if (TimerEndtime == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimerTitle = (char* (*)(cmyth_timer_t timer))
++dlsym(m_libcmyth, "cmyth_timer_title");
++if (TimerTitle == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimerDescription = (char* (*)(cmyth_timer_t timer))
++dlsym(m_libcmyth, "cmyth_timer_description");
++if (TimerDescription == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimerType = (int (*)(cmyth_timer_t timer))
++dlsym(m_libcmyth, "cmyth_timer_type");
++if (TimerType == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimerCategory = (char* (*)(cmyth_timer_t timer))
++dlsym(m_libcmyth, "cmyth_timer_category");
++if (TimerCategory == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimerSubtitle = (char* (*)(cmyth_timer_t timer))
++dlsym(m_libcmyth, "cmyth_timer_subtitle");
++if (TimerSubtitle == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimerPriority = (int (*)(cmyth_timer_t timer))
++dlsym(m_libcmyth, "cmyth_timer_priority");
++if (TimerPriority == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimerStartoffset = (int (*)(cmyth_timer_t timer))
++dlsym(m_libcmyth, "cmyth_timer_startoffset");
++if (TimerStartoffset == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimerEndoffset = (int (*)(cmyth_timer_t timer))
++dlsym(m_libcmyth, "cmyth_timer_endoffset");
++if (TimerEndoffset == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimerSearchtype = (int (*)(cmyth_timer_t timer))
++dlsym(m_libcmyth, "cmyth_timer_searchtype");
++if (TimerSearchtype == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimerInactive = (int (*)(cmyth_timer_t timer))
++dlsym(m_libcmyth, "cmyth_timer_inactive");
++if (TimerInactive == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimerCallsign = (char* (*)(cmyth_timer_t timer))
++dlsym(m_libcmyth, "cmyth_timer_callsign");
++if (TimerCallsign == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimerDupMethod = (int (*)(cmyth_timer_t timer))
++dlsym(m_libcmyth, "cmyth_timer_dup_method");
++if (TimerDupMethod == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimerDupIn = (int (*)(cmyth_timer_t timer))
++dlsym(m_libcmyth, "cmyth_timer_dup_in");
++if (TimerDupIn == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimerRecGroup = (char* (*)(cmyth_timer_t timer))
++dlsym(m_libcmyth, "cmyth_timer_rec_group");
++if (TimerRecGroup == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimerStoreGroup = (char* (*)(cmyth_timer_t timer))
++dlsym(m_libcmyth, "cmyth_timer_store_group");
++if (TimerStoreGroup == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimerPlayGroup = (char* (*)(cmyth_timer_t timer))
++dlsym(m_libcmyth, "cmyth_timer_play_group");
++if (TimerPlayGroup == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimerAutotranscode = (int (*)(cmyth_timer_t timer))
++dlsym(m_libcmyth, "cmyth_timer_autotranscode");
++if (TimerAutotranscode == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimerUserjobs = (int (*)(cmyth_timer_t timer))
++dlsym(m_libcmyth, "cmyth_timer_userjobs");
++if (TimerUserjobs == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimerAutocommflag = (int (*)(cmyth_timer_t timer))
++dlsym(m_libcmyth, "cmyth_timer_autocommflag");
++if (TimerAutocommflag == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimerAutoexpire = (int (*)(cmyth_timer_t timer))
++dlsym(m_libcmyth, "cmyth_timer_autoexpire");
++if (TimerAutoexpire == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimerMaxepisodes = (int (*)(cmyth_timer_t timer))
++dlsym(m_libcmyth, "cmyth_timer_maxepisodes");
++if (TimerMaxepisodes == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimerMaxnewest = (int (*)(cmyth_timer_t timer))
++dlsym(m_libcmyth, "cmyth_timer_maxnewest");
++if (TimerMaxnewest == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimerTranscoder = (int (*)(cmyth_timer_t timer))
++dlsym(m_libcmyth, "cmyth_timer_transcoder");
++if (TimerTranscoder == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimerlistGetItem = (cmyth_timer_t (*)(cmyth_timerlist_t pl, int index))
++dlsym(m_libcmyth, "cmyth_timerlist_get_item");
++if (TimerlistGetItem == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ TimerlistGetCount = (int (*)(cmyth_timerlist_t pl))
++dlsym(m_libcmyth, "cmyth_timerlist_get_count");
++if (TimerlistGetCount == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ MysqlGetTimers = (cmyth_timerlist_t (*)(cmyth_database_t db))
++dlsym(m_libcmyth, "cmyth_mysql_get_timers");
++if (MysqlGetTimers == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ MysqlAddTimer = (int (*)(cmyth_database_t db, int chanid,char* callsign,char* description, time_t starttime, time_t endtime,char* title,char* category,int type,char* subtitle,int priority,int startoffset,int endoffset,int searchtype,int inactive, int dup_method, int dup_in, char* rec_group, char* store_group, char* play_group, int autotranscode, int userjobs, int autocommflag, int autoexpire, int maxepisodes, int maxnewest, int transcoder))
++dlsym(m_libcmyth, "cmyth_mysql_add_timer");
++if (MysqlAddTimer == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ MysqlDeleteTimer = (int (*)(cmyth_database_t db, int recordid))
++dlsym(m_libcmyth, "cmyth_mysql_delete_timer");
++if (MysqlDeleteTimer == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ MysqlUpdateTimer = (int (*)(cmyth_database_t db, int recordid, int chanid,char* callsign,char* description, time_t starttime, time_t endtime,char* title,char* category, int type,char* subtitle,int priority,int startoffset,int endoffset,int searchtype,int inactive, int dup_method, int dup_in, char* rec_group, char* store_group, char* play_group, int autotranscode, int userjobs, int autocommflag, int autoexpire, int maxepisodes, int maxnewest, int transcoder))
++dlsym(m_libcmyth, "cmyth_mysql_update_timer");
++if (MysqlUpdateTimer == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ MysqlGetChannelgroups = (int (*)(cmyth_database_t db,cmyth_channelgroups_t** changroups))
++dlsym(m_libcmyth, "cmyth_mysql_get_channelgroups");
++if (MysqlGetChannelgroups == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ MysqlGetChannelidsInGroup = (int (*)(cmyth_database_t db,unsigned int groupid,int** chanids))
++dlsym(m_libcmyth, "cmyth_mysql_get_channelids_in_group");
++if (MysqlGetChannelidsInGroup == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ChannelSourceid = (int (*)(cmyth_channel_t channel))
++dlsym(m_libcmyth, "cmyth_channel_sourceid");
++if (ChannelSourceid == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ ChannelMultiplex = (int (*)(cmyth_channel_t channel))
++dlsym(m_libcmyth, "cmyth_channel_multiplex");
++if (ChannelMultiplex == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ MysqlGetRecorderList = (int (*)(cmyth_database_t db,cmyth_rec_t** reclist))
++dlsym(m_libcmyth, "cmyth_mysql_get_recorder_list");
++if (MysqlGetRecorderList == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ MysqlGetProgFinderTimeTitleChan = (int (*)(cmyth_database_t db,cmyth_program_t* prog, char* title,time_t starttime,int chanid))
++dlsym(m_libcmyth, "cmyth_mysql_get_prog_finder_time_title_chan");
++if (MysqlGetProgFinderTimeTitleChan == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ MysqlGetStoragegroups = (int (*)(cmyth_database_t db, char*** profiles))
++dlsym(m_libcmyth, "cmyth_mysql_get_storagegroups");
++if (MysqlGetStoragegroups == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ MysqlGetPlaygroups = (int (*)(cmyth_database_t db, char*** profiles))
++dlsym(m_libcmyth, "cmyth_mysql_get_playgroups");
++if (MysqlGetPlaygroups == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ MysqlGetRecprofiles = (int (*)(cmyth_database_t db, cmyth_recprofile_t** profiles))
++dlsym(m_libcmyth, "cmyth_mysql_get_recprofiles");
++if (MysqlGetRecprofiles == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ MysqlGetCardtype = (char* (*)(cmyth_database_t db, int chanid))
++dlsym(m_libcmyth, "cmyth_mysql_get_cardtype");
++if (MysqlGetCardtype == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ StoragegroupFilelist = (int (*)(cmyth_conn_t control, char*** sgFilelist, char* sg2List, char* mythostname))
++dlsym(m_libcmyth, "cmyth_storagegroup_filelist");
++if (StoragegroupFilelist == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ StoragegroupFilelistGetItem = (cmyth_storagegroup_file_t (*)(cmyth_storagegroup_filelist_t fl, int index))
++dlsym(m_libcmyth, "cmyth_storagegroup_filelist_get_item");
++if (StoragegroupFilelistGetItem == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ StoragegroupFilelistCount = (int (*)(cmyth_storagegroup_filelist_t fl))
++dlsym(m_libcmyth, "cmyth_storagegroup_filelist_count");
++if (StoragegroupFilelistCount == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ StoragegroupGetFilelist = (cmyth_storagegroup_filelist_t (*)(cmyth_conn_t control,char* storagegroup, char* hostname))
++dlsym(m_libcmyth, "cmyth_storagegroup_get_filelist");
++if (StoragegroupGetFilelist == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ StoragegroupFileGetFilename = (char* (*)(cmyth_storagegroup_file_t file))
++dlsym(m_libcmyth, "cmyth_storagegroup_file_get_filename");
++if (StoragegroupFileGetFilename == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ StoragegroupFileGetLastmodified = (unsigned long (*)(cmyth_storagegroup_file_t file))
++dlsym(m_libcmyth, "cmyth_storagegroup_file_get_lastmodified");
++if (StoragegroupFileGetLastmodified == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ StoragegroupFileGetSize = (unsigned long long (*)(cmyth_storagegroup_file_t file))
++dlsym(m_libcmyth, "cmyth_storagegroup_file_get_size");
++if (StoragegroupFileGetSize == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RefRelease = (void (*)(void* p))
++dlsym(m_libcmyth, "ref_release");
++if (RefRelease == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RefHold = (void* (*)(void* p))
++dlsym(m_libcmyth, "ref_hold");
++if (RefHold == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RefStrdup = (char* (*)(char* str))
++dlsym(m_libcmyth, "ref_strdup");
++if (RefStrdup == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RefRealloc = (void* (*)(void* p, size_t len))
++dlsym(m_libcmyth, "ref_realloc");
++if (RefRealloc == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RefSetDestroy = (void (*)(void* block, ref_destroy_t func))
++dlsym(m_libcmyth, "ref_set_destroy");
++if (RefSetDestroy == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ RefAllocShow = (void (*)(void))
++dlsym(m_libcmyth, "ref_alloc_show");
++if (RefAllocShow == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
++
++ return true;
++ }
++
++//dll functions
++
++void (*DbgLevel)(int l);
++void (*DbgAll)(void);
++void (*DbgNone)(void);
++void (*Dbg)(int level, char* fmt, ...);
++void (*SetDbgMsgcallback)(void (* msgcb)(int level,char* ));
++cmyth_conn_t (*ConnConnectCtrl)(char* server, unsigned short port, unsigned buflen, int tcp_rcvbuf);
++cmyth_conn_t (*ConnConnectEvent)(char* server, unsigned short port, unsigned buflen, int tcp_rcvbuf);
++cmyth_file_t (*ConnConnectFile)(cmyth_proginfo_t prog, cmyth_conn_t control, unsigned buflen, int tcp_rcvbuf);
++cmyth_file_t (*ConnConnectPath)(char* path, cmyth_conn_t control, unsigned buflen, int tcp_rcvbuf,char* sgToGetFrom);
++int (*ConnConnectRing)(cmyth_recorder_t rec, unsigned buflen,int tcp_rcvbuf);
++int (*ConnConnectRecorder)(cmyth_recorder_t rec, unsigned buflen, int tcp_rcvbuf);
++int (*ConnCheckBlock)(cmyth_conn_t conn, unsigned long size);
++cmyth_recorder_t (*ConnGetRecorderFromNum)(cmyth_conn_t conn, int num);
++cmyth_recorder_t (*ConnGetFreeRecorder)(cmyth_conn_t conn);
++int (*ConnGetFreespace)(cmyth_conn_t control, long long* total, long long* used);
++int (*ConnHung)(cmyth_conn_t control);
++int (*ConnGetFreeRecorderCount)(cmyth_conn_t conn);
++int (*ConnGetProtocolVersion)(cmyth_conn_t conn);
++char* (*ConnGetSetting)(cmyth_conn_t conn,const char* hostname, const char* setting);
++int (*ConnSetSetting)(cmyth_conn_t conn,const char* hostname, const char* setting, const char* value);
++char* (*ConnGetBackendHostname)(cmyth_conn_t conn);
++char* (*ConnGetClientHostname)(cmyth_conn_t conn);
++cmyth_event_t (*EventGet)(cmyth_conn_t conn, char* data, int len);
++int (*EventSelect)(cmyth_conn_t conn, struct timeval* timeout);
++cmyth_recorder_t (*RecorderCreate)(void);
++cmyth_recorder_t (*RecorderDup)(cmyth_recorder_t p);
++int (*RecorderIsRecording)(cmyth_recorder_t rec);
++int (*RecorderGetFramerate)(cmyth_recorder_t rec,double* rate);
++long long (*RecorderGetFramesWritten)(cmyth_recorder_t rec);
++long long (*RecorderGetFreeSpace)(cmyth_recorder_t rec);
++long long (*RecorderGetKeyframePos)(cmyth_recorder_t rec, unsigned long keynum);
++cmyth_posmap_t (*RecorderGetPositionMap)(cmyth_recorder_t rec,unsigned long start,unsigned long end);
++cmyth_proginfo_t (*RecorderGetRecording)(cmyth_recorder_t rec);
++int (*RecorderStopPlaying)(cmyth_recorder_t rec);
++int (*RecorderFrontendReady)(cmyth_recorder_t rec);
++int (*RecorderCancelNextRecording)(cmyth_recorder_t rec);
++int (*RecorderPause)(cmyth_recorder_t rec);
++int (*RecorderFinishRecording)(cmyth_recorder_t rec);
++int (*RecorderToggleChannelFavorite)(cmyth_recorder_t rec);
++int (*RecorderChangeChannel)(cmyth_recorder_t rec, cmyth_channeldir_t direction);
++int (*RecorderSetChannel)(cmyth_recorder_t rec,char* channame);
++int (*RecorderChangeColor)(cmyth_recorder_t rec, cmyth_adjdir_t direction);
++int (*RecorderChangeBrightness)(cmyth_recorder_t rec, cmyth_adjdir_t direction);
++int (*RecorderChangeContrast)(cmyth_recorder_t rec, cmyth_adjdir_t direction);
++int (*RecorderChangeHue)(cmyth_recorder_t rec, cmyth_adjdir_t direction);
++int (*RecorderCheckChannel)(cmyth_recorder_t rec,char* channame);
++int (*RecorderCheckChannelPrefix)(cmyth_recorder_t rec, char* channame);
++cmyth_proginfo_t (*RecorderGetCurProginfo)(cmyth_recorder_t rec);
++cmyth_proginfo_t (*RecorderGetNextProginfo)(cmyth_recorder_t rec,cmyth_proginfo_t current,cmyth_browsedir_t direction);
++int (*RecorderGetInputName)(cmyth_recorder_t rec, char* name, unsigned len);
++long long (*RecorderSeek)(cmyth_recorder_t rec, long long pos, cmyth_whence_t whence, long long curpos);
++int (*RecorderSpawnChainLivetv)(cmyth_recorder_t rec, char* channame);
++int (*RecorderSpawnLivetv)(cmyth_recorder_t rec);
++int (*RecorderStartStream)(cmyth_recorder_t rec);
++int (*RecorderEndStream)(cmyth_recorder_t rec);
++char* (*RecorderGetFilename)(cmyth_recorder_t rec);
++int (*RecorderStopLivetv)(cmyth_recorder_t rec);
++int (*RecorderDoneRingbuf)(cmyth_recorder_t rec);
++int (*RecorderGetRecorderId)(cmyth_recorder_t rec);
++cmyth_livetv_chain_t (*LivetvChainCreate)(char* chainid);
++long long (*LivetvChainDuration)(cmyth_recorder_t rec);
++int (*LivetvChainSwitch)(cmyth_recorder_t rec, int dir);
++int (*LivetvChainSwitchLast)(cmyth_recorder_t rec);
++int (*LivetvChainUpdate)(cmyth_recorder_t rec, char* chainid,int tcp_rcvbuf);
++cmyth_recorder_t (*SpawnLiveTv)(cmyth_recorder_t rec,unsigned buflen,int tcp_rcvbuf, void (* prog_update_callback)(cmyth_proginfo_t),char** err, char* channame);
++cmyth_recorder_t (*LivetvChainSetup)(cmyth_recorder_t old_rec, int tcp_rcvbuf, void (* prog_update_callback)(cmyth_proginfo_t));
++int (*LivetvGetBlock)(cmyth_recorder_t rec, char* buf, unsigned long len);
++int (*LivetvSelect)(cmyth_recorder_t rec, struct timeval* timeout);
++int (*LivetvRequestBlock)(cmyth_recorder_t rec, unsigned long len);
++long long (*LivetvSeek)(cmyth_recorder_t rec,long long offset, int whence);
++int (*LivetvRead)(cmyth_recorder_t rec, char* buf, unsigned long len);
++int (*LivetvKeepRecording)(cmyth_recorder_t rec, cmyth_database_t db, int keep);
++cmyth_database_t (*DatabaseInit)(char* host, char* db_name, char* user, char* pass);
++int (*DatabaseSetHost)(cmyth_database_t db, char* host);
++int (*DatabaseSetUser)(cmyth_database_t db, char* user);
++int (*DatabaseSetPass)(cmyth_database_t db, char* pass);
++int (*DatabaseSetName)(cmyth_database_t db, char* name);
++int (*GetWatchedStatusMysql)(cmyth_database_t db, int recordid);
++int (*SetWatchedStatusMysql)(cmyth_database_t db, int recordid, int watchedStat);
++char* (*RingbufPathname)(cmyth_recorder_t rec);
++cmyth_ringbuf_t (*RingbufCreate)(void);
++cmyth_recorder_t (*RingbufSetup)(cmyth_recorder_t old_rec);
++int (*RingbufRequestBlock)(cmyth_recorder_t rec, unsigned long len);
++int (*RingbufSelect)(cmyth_recorder_t rec, struct timeval* timeout);
++int (*RingbufGetBlock)(cmyth_recorder_t rec,char* buf,unsigned long len);
++long long (*RingbufSeek)(cmyth_recorder_t rec, long long offset, int whence);
++int (*RingbufRead)(cmyth_recorder_t rec,char* buf,unsigned long len);
++cmyth_rec_num_t (*RecNumCreate)(void);
++cmyth_rec_num_t (*RecNumGet)(char* host, unsigned short port, unsigned id);
++char* (*RecNumString)(cmyth_rec_num_t rn);
++cmyth_timestamp_t (*TimestampCreate)(void);
++cmyth_timestamp_t (*TimestampFromString)(char* str);
++cmyth_timestamp_t (*TimestampFromUnixtime)(time_t l);
++time_t (*TimestampToUnixtime)(cmyth_timestamp_t ts);
++int (*TimestampToString)(char* str, cmyth_timestamp_t ts);
++int (*TimestampToIsostring)(char* str, cmyth_timestamp_t ts);
++int (*TimestampToDisplayString)(char* str, cmyth_timestamp_t ts, int time_format_12);
++int (*DatetimeToString)(char* str, cmyth_timestamp_t ts);
++int (*TimestampCompare)(cmyth_timestamp_t ts1,cmyth_timestamp_t ts2);
++cmyth_keyframe_t (*KeyframeCreate)(void);
++char* (*KeyframeString)(cmyth_keyframe_t kf);
++cmyth_posmap_t (*PosmapCreate)(void);
++cmyth_proginfo_t (*ProginfoCreate)(void);
++int (*ProginfoStopRecording)(cmyth_conn_t control, cmyth_proginfo_t prog);
++int (*ProginfoCheckRecording)(cmyth_conn_t control, cmyth_proginfo_t prog);
++int (*ProginfoDeleteRecording)(cmyth_conn_t control,cmyth_proginfo_t prog);
++int (*ProginfoForgetRecording)(cmyth_conn_t control,cmyth_proginfo_t prog);
++int (*ProginfoGetRecorderNum)(cmyth_conn_t control,cmyth_rec_num_t rnum,cmyth_proginfo_t prog);
++cmyth_proginfo_t (*ProginfoGetFromBasename)(cmyth_conn_t control, const char* basename);
++char* (*ProginfoTitle)(cmyth_proginfo_t prog);
++char* (*ProginfoSubtitle)(cmyth_proginfo_t prog);
++char* (*ProginfoDescription)(cmyth_proginfo_t prog);
++char* (*ProginfoCategory)(cmyth_proginfo_t prog);
++char* (*ProginfoChanstr)(cmyth_proginfo_t prog);
++char* (*ProginfoChansign)(cmyth_proginfo_t prog);
++char* (*ProginfoChanname)(cmyth_proginfo_t prog);
++long (*ProginfoChanId)(cmyth_proginfo_t prog);
++char* (*ProginfoPathname)(cmyth_proginfo_t prog);
++char* (*ProginfoSeriesid)(cmyth_proginfo_t prog);
++char* (*ProginfoProgramid)(cmyth_proginfo_t prog);
++unsigned long (*ProginfoRecordid)(cmyth_proginfo_t prog);
++long (*ProginfoPriority)(cmyth_proginfo_t prog);
++char* (*ProginfoStars)(cmyth_proginfo_t prog);
++cmyth_timestamp_t (*ProginfoRecStart)(cmyth_proginfo_t prog);
++cmyth_timestamp_t (*ProginfoRecEnd)(cmyth_proginfo_t prog);
++cmyth_timestamp_t (*ProginfoOriginalairdate)(cmyth_proginfo_t prog);
++cmyth_proginfo_rec_status_t (*ProginfoRecStatus)(cmyth_proginfo_t prog);
++unsigned long (*ProginfoFlags)( cmyth_proginfo_t prog);
++long long (*ProginfoLength)(cmyth_proginfo_t prog);
++char* (*ProginfoHost)(cmyth_proginfo_t prog);
++int (*ProginfoCompare)(cmyth_proginfo_t a, cmyth_proginfo_t b);
++int (*ProginfoLengthSec)(cmyth_proginfo_t prog);
++cmyth_proginfo_t (*ProginfoGetDetail)(cmyth_conn_t control, cmyth_proginfo_t p);
++cmyth_timestamp_t (*ProginfoStart)(cmyth_proginfo_t prog);
++cmyth_timestamp_t (*ProginfoEnd)(cmyth_proginfo_t prog);
++long (*ProginfoCardId)(cmyth_proginfo_t prog);
++char* (*ProginfoRecgroup)(cmyth_proginfo_t prog);
++char* (*ProginfoChanicon)(cmyth_proginfo_t prog);
++char* (*ProginfoProdyear)(cmyth_proginfo_t prog);
++cmyth_proglist_t (*ProglistCreate)(void);
++cmyth_proglist_t (*ProglistGetAllRecorded)(cmyth_conn_t control);
++cmyth_proglist_t (*ProglistGetAllPending)(cmyth_conn_t control);
++cmyth_proglist_t (*ProglistGetAllScheduled)(cmyth_conn_t control);
++cmyth_proglist_t (*ProglistGetConflicting)(cmyth_conn_t control);
++cmyth_proginfo_t (*ProglistGetItem)(cmyth_proglist_t pl,int index);
++int (*ProglistDeleteItem)(cmyth_proglist_t pl,cmyth_proginfo_t prog);
++int (*ProglistGetCount)(cmyth_proglist_t pl);
++int (*ProglistSort)(cmyth_proglist_t pl, int count, cmyth_proglist_sort_t sort);
++cmyth_conn_t (*FileData)(cmyth_file_t file);
++unsigned long long (*FileStart)(cmyth_file_t file);
++unsigned long long (*FileLength)(cmyth_file_t file);
++unsigned long long (*FilePosition)(cmyth_file_t file);
++int (*UpdateFileLength)(cmyth_file_t file, unsigned long long newLength);
++int (*FileGetBlock)(cmyth_file_t file, char* buf,unsigned long len);
++int (*FileRequestBlock)(cmyth_file_t file, unsigned long len);
++long long (*FileSeek)(cmyth_file_t file, long long offset, int whence);
++int (*FileSelect)(cmyth_file_t file, struct timeval* timeout);
++void (*FileSetClosedCallback)(cmyth_file_t file,void (* callback)(cmyth_file_t));
++int (*FileRead)(cmyth_file_t file,char* buf,unsigned long len);
++long (*ChannelChanid)(cmyth_channel_t channel);
++long (*ChannelChannum)(cmyth_channel_t channel);
++char* (*ChannelChannumstr)(cmyth_channel_t channel);
++char* (*ChannelCallsign)(cmyth_channel_t channel);
++char* (*ChannelName)(cmyth_channel_t channel);
++char* (*ChannelIcon)(cmyth_channel_t channel);
++int (*ChannelVisible)(cmyth_channel_t channel);
++cmyth_channel_t (*ChanlistGetItem)(cmyth_chanlist_t pl, int index);
++int (*ChanlistGetCount)(cmyth_chanlist_t pl);
++cmyth_freespace_t (*FreespaceCreate)(void);
++long long (*GetBookmark)(cmyth_conn_t conn, cmyth_proginfo_t prog);
++int (*GetBookmarkOffset)(cmyth_database_t db, long chanid, long long mark);
++int (*GetBookmarkMark)(cmyth_database_t, cmyth_proginfo_t, long long);
++int (*SetBookmark)(cmyth_conn_t conn, cmyth_proginfo_t prog,long long bookmark);
++cmyth_commbreaklist_t (*CommbreaklistCreate)(void);
++cmyth_commbreak_t (*CommbreakCreate)(void);
++cmyth_commbreaklist_t (*GetCommbreaklist)(cmyth_conn_t conn, cmyth_proginfo_t prog);
++cmyth_commbreaklist_t (*GetCutlist)(cmyth_conn_t conn, cmyth_proginfo_t prog);
++int (*RcvCommbreaklist)(cmyth_conn_t conn, int* err, cmyth_commbreaklist_t breaklist, int count);
++int (*MysqlGetRecgroups)(cmyth_database_t, cmyth_recgroups_t**);
++int (*MysqlDeleteScheduledRecording)(cmyth_database_t db, char* query);
++int (*MysqlInsertIntoRecord)(cmyth_database_t db, char* query, char* query1, char* query2, char* title, char* subtitle, char* description, char* callsign);
++char* (*GetRecordidMysql)(cmyth_database_t, int, char* , char* , char* , char* , char* );
++int (*GetOffsetMysql)(cmyth_database_t, int, char* , int, char* , char* , char* , char* , char* );
++int (*MysqlGetProgFinderCharTitle)(cmyth_database_t db, cmyth_program_t**prog, time_t starttime, char* program_name);
++int (*MysqlGetProgFinderTime)(cmyth_database_t db, cmyth_program_t**prog, time_t starttime, char* program_name);
++int (*MysqlGetGuide)(cmyth_database_t db, cmyth_program_t**prog, time_t starttime, time_t endtime);
++int (*MysqlTestdbConnection)(cmyth_database_t db,char**message);
++int (*ScheduleRecording)(cmyth_conn_t conn, char* msg);
++char* (*MysqlEscapeChars)(cmyth_database_t db, char* string);
++int (*MysqlGetCommbreakList)(cmyth_database_t db, int chanid, char* start_ts_dt, cmyth_commbreaklist_t breaklist, int conn_version);
++int (*MysqlGetPrevRecorded)(cmyth_database_t db, cmyth_program_t**prog);
++int (*GetDeleteList)(cmyth_conn_t, char* , cmyth_proglist_t);
++int (*MythtvRemovePreviosRecorded)(cmyth_database_t db,char* query);
++cmyth_chanlist_t (*MysqlGetChanlist)(cmyth_database_t db);
++int (*MysqlIsRadio)(cmyth_database_t db, int chanid);
++int (*TimerRecordid)(cmyth_timer_t timer);
++int (*TimerChanid)(cmyth_timer_t timer);
++time_t (*TimerStarttime)(cmyth_timer_t timer);
++time_t (*TimerEndtime)(cmyth_timer_t timer);
++char* (*TimerTitle)(cmyth_timer_t timer);
++char* (*TimerDescription)(cmyth_timer_t timer);
++int (*TimerType)(cmyth_timer_t timer);
++char* (*TimerCategory)(cmyth_timer_t timer);
++char* (*TimerSubtitle)(cmyth_timer_t timer);
++int (*TimerPriority)(cmyth_timer_t timer);
++int (*TimerStartoffset)(cmyth_timer_t timer);
++int (*TimerEndoffset)(cmyth_timer_t timer);
++int (*TimerSearchtype)(cmyth_timer_t timer);
++int (*TimerInactive)(cmyth_timer_t timer);
++char* (*TimerCallsign)(cmyth_timer_t timer);
++int (*TimerDupMethod)(cmyth_timer_t timer);
++int (*TimerDupIn)(cmyth_timer_t timer);
++char* (*TimerRecGroup)(cmyth_timer_t timer);
++char* (*TimerStoreGroup)(cmyth_timer_t timer);
++char* (*TimerPlayGroup)(cmyth_timer_t timer);
++int (*TimerAutotranscode)(cmyth_timer_t timer);
++int (*TimerUserjobs)(cmyth_timer_t timer);
++int (*TimerAutocommflag)(cmyth_timer_t timer);
++int (*TimerAutoexpire)(cmyth_timer_t timer);
++int (*TimerMaxepisodes)(cmyth_timer_t timer);
++int (*TimerMaxnewest)(cmyth_timer_t timer);
++int (*TimerTranscoder)(cmyth_timer_t timer);
++cmyth_timer_t (*TimerlistGetItem)(cmyth_timerlist_t pl, int index);
++int (*TimerlistGetCount)(cmyth_timerlist_t pl);
++cmyth_timerlist_t (*MysqlGetTimers)(cmyth_database_t db);
++int (*MysqlAddTimer)(cmyth_database_t db, int chanid,char* callsign,char* description, time_t starttime, time_t endtime,char* title,char* category,int type,char* subtitle,int priority,int startoffset,int endoffset,int searchtype,int inactive, int dup_method, int dup_in, char* rec_group, char* store_group, char* play_group, int autotranscode, int userjobs, int autocommflag, int autoexpire, int maxepisodes, int maxnewest, int transcoder);
++int (*MysqlDeleteTimer)(cmyth_database_t db, int recordid);
++int (*MysqlUpdateTimer)(cmyth_database_t db, int recordid, int chanid,char* callsign,char* description, time_t starttime, time_t endtime,char* title,char* category, int type,char* subtitle,int priority,int startoffset,int endoffset,int searchtype,int inactive, int dup_method, int dup_in, char* rec_group, char* store_group, char* play_group, int autotranscode, int userjobs, int autocommflag, int autoexpire, int maxepisodes, int maxnewest, int transcoder);
++int (*MysqlGetChannelgroups)(cmyth_database_t db,cmyth_channelgroups_t** changroups);
++int (*MysqlGetChannelidsInGroup)(cmyth_database_t db,unsigned int groupid,int** chanids);
++int (*ChannelSourceid)(cmyth_channel_t channel);
++int (*ChannelMultiplex)(cmyth_channel_t channel);
++int (*MysqlGetRecorderList)(cmyth_database_t db,cmyth_rec_t** reclist);
++int (*MysqlGetProgFinderTimeTitleChan)(cmyth_database_t db,cmyth_program_t* prog, char* title,time_t starttime,int chanid);
++int (*MysqlGetStoragegroups)(cmyth_database_t db, char*** profiles);
++int (*MysqlGetPlaygroups)(cmyth_database_t db, char*** profiles);
++int (*MysqlGetRecprofiles)(cmyth_database_t db, cmyth_recprofile_t** profiles);
++char* (*MysqlGetCardtype)(cmyth_database_t db, int chanid);
++int (*StoragegroupFilelist)(cmyth_conn_t control, char*** sgFilelist, char* sg2List, char* mythostname);
++cmyth_storagegroup_file_t (*StoragegroupFilelistGetItem)(cmyth_storagegroup_filelist_t fl, int index);
++int (*StoragegroupFilelistCount)(cmyth_storagegroup_filelist_t fl);
++cmyth_storagegroup_filelist_t (*StoragegroupGetFilelist)(cmyth_conn_t control,char* storagegroup, char* hostname);
++char* (*StoragegroupFileGetFilename)(cmyth_storagegroup_file_t file);
++unsigned long (*StoragegroupFileGetLastmodified)(cmyth_storagegroup_file_t file);
++unsigned long long (*StoragegroupFileGetSize)(cmyth_storagegroup_file_t file);
++void (*RefRelease)(void* p);
++void* (*RefHold)(void* p);
++char* (*RefStrdup)(char* str);
++void* (*RefRealloc)(void* p, size_t len);
++void (*RefSetDestroy)(void* block, ref_destroy_t func);
++void (*RefAllocShow)(void);
++
++
++
++
++
++protected:
++// int (*XBMC_register_me)(void *HANDLE);
++// void (*XBMC_unregister_me)();
++
++private:
++ void *m_libcmyth;
++ void *m_Handle;
++ struct cb_array
++ {
++ const char* libPath;
++ };
++};
+diff --git a/xbmc/pvrclients/mythtv-cmyth/project/VS2010Express/XBMC_mythtc_cmyth.vcxproj b/xbmc/pvrclients/mythtv-cmyth/project/VS2010Express/XBMC_mythtc_cmyth.vcxproj
+new file mode 100644
+index 0000000..da1b12d
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/project/VS2010Express/XBMC_mythtc_cmyth.vcxproj
+@@ -0,0 +1,163 @@
++<?xml version="1.0" encoding="utf-8"?>
++<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
++ <ItemGroup Label="ProjectConfigurations">
++ <ProjectConfiguration Include="Debug|Win32">
++ <Configuration>Debug</Configuration>
++ <Platform>Win32</Platform>
++ </ProjectConfiguration>
++ <ProjectConfiguration Include="Release|Win32">
++ <Configuration>Release</Configuration>
++ <Platform>Win32</Platform>
++ </ProjectConfiguration>
++ </ItemGroup>
++ <ItemGroup>
++ <ClCompile Include="..\..\..\..\..\addons\library.xbmc.addon\dlfcn-win32.cpp" />
++ <ClCompile Include="..\..\..\..\..\lib\platform\windows\os-threads.cpp" />
++ <ClCompile Include="..\..\client.cpp" />
++ <ClCompile Include="..\..\cppmyth\MythChannel.cpp" />
++ <ClCompile Include="..\..\cppmyth\MythConnection.cpp" />
++ <ClCompile Include="..\..\cppmyth\MythDatabase.cpp" />
++ <ClCompile Include="..\..\cppmyth\MythEventHandler.cpp" />
++ <ClCompile Include="..\..\cppmyth\MythFile.cpp" />
++ <ClCompile Include="..\..\cppmyth\MythProgramInfo.cpp" />
++ <ClCompile Include="..\..\cppmyth\MythRecorder.cpp" />
++ <ClCompile Include="..\..\cppmyth\MythSGFile.cpp" />
++ <ClCompile Include="..\..\cppmyth\MythSignal.cpp" />
++ <ClCompile Include="..\..\cppmyth\MythTimer.cpp" />
++ <ClCompile Include="..\..\cppmyth\MythTimestamp.cpp" />
++ <ClCompile Include="..\..\fileOps.cpp" />
++ <ClCompile Include="..\..\pvrclient-mythtv.cpp" />
++ <ClCompile Include="..\..\recordingrules.cpp" />
++ </ItemGroup>
++ <ItemGroup>
++ <ClInclude Include="..\..\..\..\..\addons\library.xbmc.addon\dlfcn-win32.h" />
++ <ClInclude Include="..\..\client.h" />
++ <ClInclude Include="..\..\cppmyth.h" />
++ <ClInclude Include="..\..\cppmyth\MythChannel.h" />
++ <ClInclude Include="..\..\cppmyth\MythConnection.h" />
++ <ClInclude Include="..\..\cppmyth\MythDatabase.h" />
++ <ClInclude Include="..\..\cppmyth\MythEventHandler.h" />
++ <ClInclude Include="..\..\cppmyth\MythFile.h" />
++ <ClInclude Include="..\..\cppmyth\MythPointer.h" />
++ <ClInclude Include="..\..\cppmyth\MythProgramInfo.h" />
++ <ClInclude Include="..\..\cppmyth\MythRecorder.h" />
++ <ClInclude Include="..\..\cppmyth\MythSGFile.h" />
++ <ClInclude Include="..\..\cppmyth\MythSignal.h" />
++ <ClInclude Include="..\..\cppmyth\MythTimer.h" />
++ <ClInclude Include="..\..\cppmyth\MythTimestamp.h" />
++ <ClInclude Include="..\..\fileOps.h" />
++ <ClInclude Include="..\..\libcmyth.h" />
++ <ClInclude Include="..\..\pvrclient-mythtv.h" />
++ <ClInclude Include="..\..\recordingrules.h" />
++ <ClInclude Include="..\..\tools.h" />
++ </ItemGroup>
++ <ItemGroup>
++ <None Include="..\..\..\..\..\lib\cmyth\cmythimport.txt" />
++ </ItemGroup>
++ <PropertyGroup Label="Globals">
++ <ProjectGuid>{A55ACC6B-837D-4784-8E41-66947B1D9B8C}</ProjectGuid>
++ <Keyword>Win32Proj</Keyword>
++ <RootNamespace>XBMC_mythtv_cmyth</RootNamespace>
++ <ProjectName>pvrclient_mythtv_cmyth</ProjectName>
++ </PropertyGroup>
++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
++ <ConfigurationType>DynamicLibrary</ConfigurationType>
++ <UseDebugLibraries>true</UseDebugLibraries>
++ <CharacterSet>MultiByte</CharacterSet>
++ </PropertyGroup>
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
++ <ConfigurationType>DynamicLibrary</ConfigurationType>
++ <UseDebugLibraries>false</UseDebugLibraries>
++ <WholeProgramOptimization>true</WholeProgramOptimization>
++ <CharacterSet>MultiByte</CharacterSet>
++ </PropertyGroup>
++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
++ <ImportGroup Label="ExtensionSettings">
++ </ImportGroup>
++ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
++ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
++ </ImportGroup>
++ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
++ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
++ </ImportGroup>
++ <PropertyGroup Label="UserMacros" />
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
++ <OutDir>$(ProjectDir)..\..\..\..\..\addons\pvr.mythtv.cmyth\</OutDir>
++ </PropertyGroup>
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
++ <TargetName>XBMC_MythTV_cmyth_win32</TargetName>
++ </PropertyGroup>
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
++ <TargetExt>.pvr</TargetExt>
++ </PropertyGroup>
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
++ <ExtensionsToDeleteOnClean>*.cdf;*.cache;*.obj;*.ilk;*.resources;*.tlb;*.tli;*.tlh;*.tmp;*.rsp;*.pgc;*.pgd;*.meta;*.tlog;*.manifest;*.res;*.pch;*.exp;*.idb;*.rep;*.xdc;*.pdb;*_manifest.rc;*.bsc;*.sbr;*.xml;*.pvr</ExtensionsToDeleteOnClean>
++ <IncludePath>$(SolutionDir)\..\..\xbmc;$(IncludePath)</IncludePath>
++ </PropertyGroup>
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
++ <OutDir>$(ProjectDir)..\..\..\..\..\addons\pvr.mythtv.cmyth\</OutDir>
++ <TargetName>XBMC_MythTV_cmyth_win32</TargetName>
++ <TargetExt>.pvr</TargetExt>
++ <ExtensionsToDeleteOnClean>*.cdf;*.cache;*.obj;*.ilk;*.resources;*.tlb;*.tli;*.tlh;*.tmp;*.rsp;*.pgc;*.pgd;*.meta;*.tlog;*.manifest;*.res;*.pch;*.exp;*.idb;*.rep;*.xdc;*.pdb;*_manifest.rc;*.bsc;*.sbr;*.xml;*.pvr</ExtensionsToDeleteOnClean>
++ <IncludePath>$(SolutionDir)\..\..\xbmc;$(IncludePath)</IncludePath>
++ </PropertyGroup>
++ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
++ <ClCompile>
++ <WarningLevel>Level3</WarningLevel>
++ <Optimization>Disabled</Optimization>
++ <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_USE_32BIT_TIME_T;_WINSOCKAPI_;USE_DEMUX;__STDC_CONSTANT_MACROS;__WINDOWS__;TARGET_WINDOWS;_WINDOWS;_MSVC;WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++ <AdditionalIncludeDirectories>..\..\..\..\..\project\BuildDependencies\include;..\..\..\..\..\lib\cmyth\include;..\..\..\..\win32;..\..\..\..\..\lib\ffmpeg;..\..\..\..\..\lib\ffmpeg\include;..\..\..\..\..\lib\ffmpeg\include-xbmc-win32;..\..\;..\..\pthread_win32;..\..\windows;..\..\..\..\addons\include;..\..\..\..\cores\dvdplayer\DVDDemuxers;..\..\..\..\..\lib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
++ <DisableSpecificWarnings>4996</DisableSpecificWarnings>
++ </ClCompile>
++ <Link>
++ <GenerateDebugInformation>true</GenerateDebugInformation>
++ <OutputFile>..\..\..\..\..\addons\pvr.mythtv.cmyth\XBMC_mythtv_cmyth_win32.pvr</OutputFile>
++ <AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies);libboost_filesystem-vc100-mt-gd.lib;libboost_system-vc100-mt-gd.lib;libboost_regex-vc100-mt-gd.lib</AdditionalDependencies>
++ <IgnoreSpecificDefaultLibraries>libcmtd;libboost_filesystem-vc100-mt-gd-1_46_1;libboost_system-vc100-mt-gd-1_46_1;libboost_regex-vc100-mt-gd-1_46_1</IgnoreSpecificDefaultLibraries>
++ <AdditionalLibraryDirectories>../../lib</AdditionalLibraryDirectories>
++ </Link>
++ <PostBuildEvent>
++ <Command>
++ </Command>
++ </PostBuildEvent>
++ <PreBuildEvent>
++ <Command>$(ProjectDir)\..\..\boost_win32_dependency.bat</Command>
++ </PreBuildEvent>
++ <PreBuildEvent>
++ <Message>Check boost install</Message>
++ </PreBuildEvent>
++ </ItemDefinitionGroup>
++ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
++ <ClCompile>
++ <WarningLevel>Level3</WarningLevel>
++ <Optimization>MaxSpeed</Optimization>
++ <FunctionLevelLinking>true</FunctionLevelLinking>
++ <IntrinsicFunctions>true</IntrinsicFunctions>
++ <AdditionalIncludeDirectories>..\..\..\..\..\project\BuildDependencies\include;..\..\..\..\..\lib\cmyth\include;..\..\..\..\..\lib\ffmpeg;..\..\..\..\..\lib\ffmpeg\include;..\..\..\..\..\lib\ffmpeg\include-xbmc-win32;..\..\;..\..\pthread_win32;..\..\windows;..\..\..\..\addons\include;..\..\..\..\cores\dvdplayer\DVDDemuxers;..\..\..\..\..\lib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
++ <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_USE_32BIT_TIME_T;_WINSOCKAPI_;USE_DEMUX;__STDC_CONSTANT_MACROS;__WINDOWS__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++ <DisableSpecificWarnings>4996</DisableSpecificWarnings>
++ </ClCompile>
++ <Link>
++ <GenerateDebugInformation>true</GenerateDebugInformation>
++ <EnableCOMDATFolding>true</EnableCOMDATFolding>
++ <OptimizeReferences>true</OptimizeReferences>
++ <OutputFile>..\..\..\..\..\addons\pvr.mythtv.cmyth\XBMC_mythtv_cmyth_win32.pvr</OutputFile>
++ <AdditionalDependencies>ws2_32.lib;libboost_filesystem-vc100-mt.lib;libboost_system-vc100-mt.lib;libboost_regex-vc100-mt.lib;%(AdditionalDependencies)</AdditionalDependencies>
++ <IgnoreSpecificDefaultLibraries>libcmt;libboost_filesystem-vc100-mt-1_46_1;libboost_system-vc100-mt-1_46_1;libboost_regex-vc100-mt-1_46_1</IgnoreSpecificDefaultLibraries>
++ <AdditionalLibraryDirectories>../../lib</AdditionalLibraryDirectories>
++ </Link>
++ <PostBuildEvent>
++ <Command>copy ..\..\* "E:\atom\mythtv cmyth\"</Command>
++ </PostBuildEvent>
++ <PreBuildEvent>
++ <Command>$(ProjectDir)\..\..\boost_win32_dependency.bat</Command>
++ </PreBuildEvent>
++ <PreBuildEvent>
++ <Message>Check boost install</Message>
++ </PreBuildEvent>
++ </ItemDefinitionGroup>
++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
++ <ImportGroup Label="ExtensionTargets">
++ </ImportGroup>
++</Project>
+\ No newline at end of file
+diff --git a/xbmc/pvrclients/mythtv-cmyth/project/VS2010Express/XBMC_mythtc_cmyth.vcxproj.filters b/xbmc/pvrclients/mythtv-cmyth/project/VS2010Express/XBMC_mythtc_cmyth.vcxproj.filters
+new file mode 100644
+index 0000000..ab01f36
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/project/VS2010Express/XBMC_mythtc_cmyth.vcxproj.filters
+@@ -0,0 +1,141 @@
++<?xml version="1.0" encoding="utf-8"?>
++<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
++ <ItemGroup>
++ <Filter Include="Source Files">
++ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
++ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
++ </Filter>
++ <Filter Include="Header Files">
++ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
++ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
++ </Filter>
++ <Filter Include="Resource Files">
++ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
++ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
++ </Filter>
++ <Filter Include="Source Files\cppmyth">
++ <UniqueIdentifier>{bf8da2ba-8002-494c-b026-b8ef3bc7b4d2}</UniqueIdentifier>
++ </Filter>
++ <Filter Include="Header Files\cppmyth">
++ <UniqueIdentifier>{a401a7c8-645a-41c6-8a38-aafda02d84de}</UniqueIdentifier>
++ </Filter>
++ </ItemGroup>
++ <ItemGroup>
++ <ClCompile Include="..\..\client.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\pvrclient-mythtv.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\..\..\..\addons\library.xbmc.addon\dlfcn-win32.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\cppmyth\MythChannel.cpp">
++ <Filter>Source Files\cppmyth</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\cppmyth\MythConnection.cpp">
++ <Filter>Source Files\cppmyth</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\cppmyth\MythDatabase.cpp">
++ <Filter>Source Files\cppmyth</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\cppmyth\MythEventHandler.cpp">
++ <Filter>Source Files\cppmyth</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\cppmyth\MythFile.cpp">
++ <Filter>Source Files\cppmyth</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\cppmyth\MythProgramInfo.cpp">
++ <Filter>Source Files\cppmyth</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\cppmyth\MythRecorder.cpp">
++ <Filter>Source Files\cppmyth</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\cppmyth\MythSignal.cpp">
++ <Filter>Source Files\cppmyth</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\cppmyth\MythTimer.cpp">
++ <Filter>Source Files\cppmyth</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\cppmyth\MythTimestamp.cpp">
++ <Filter>Source Files\cppmyth</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\recordingrules.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\..\..\..\lib\platform\windows\os-threads.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\fileOps.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\cppmyth\MythSGFile.cpp">
++ <Filter>Source Files\cppmyth</Filter>
++ </ClCompile>
++ </ItemGroup>
++ <ItemGroup>
++ <ClInclude Include="..\..\client.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\cppmyth.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\tools.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\pvrclient-mythtv.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\libcmyth.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\..\..\..\addons\library.xbmc.addon\dlfcn-win32.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\cppmyth\MythChannel.h">
++ <Filter>Header Files\cppmyth</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\cppmyth\MythConnection.h">
++ <Filter>Header Files\cppmyth</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\cppmyth\MythDatabase.h">
++ <Filter>Header Files\cppmyth</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\cppmyth\MythEventHandler.h">
++ <Filter>Header Files\cppmyth</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\cppmyth\MythFile.h">
++ <Filter>Header Files\cppmyth</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\cppmyth\MythPointer.h">
++ <Filter>Header Files\cppmyth</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\cppmyth\MythProgramInfo.h">
++ <Filter>Header Files\cppmyth</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\cppmyth\MythRecorder.h">
++ <Filter>Header Files\cppmyth</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\cppmyth\MythSignal.h">
++ <Filter>Header Files\cppmyth</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\cppmyth\MythTimer.h">
++ <Filter>Header Files\cppmyth</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\cppmyth\MythTimestamp.h">
++ <Filter>Header Files\cppmyth</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\recordingrules.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\fileOps.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\cppmyth\MythSGFile.h">
++ <Filter>Header Files\cppmyth</Filter>
++ </ClInclude>
++ </ItemGroup>
++ <ItemGroup>
++ <None Include="..\..\..\..\..\lib\cmyth\cmythimport.txt" />
++ </ItemGroup>
++</Project>
+\ No newline at end of file
+diff --git a/xbmc/pvrclients/mythtv-cmyth/pvrclient-mythtv.cpp b/xbmc/pvrclients/mythtv-cmyth/pvrclient-mythtv.cpp
+new file mode 100644
+index 0000000..a469050
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/pvrclient-mythtv.cpp
+@@ -0,0 +1,1291 @@
++#include "pvrclient-mythtv.h"
++#include "client.h"
++#include <time.h>
++#include "recordingRules.h"
++#include "tools.h"
++#include <boost/regex.hpp>
++using namespace ADDON;
++
++
++RecordingRule::RecordingRule(const MythTimer& timer)
++ :MythTimer(timer),m_parent(0)
++{}
++
++RecordingRule& RecordingRule::operator=(const MythTimer& t)
++{
++ MythTimer::operator=(t);
++ clear();
++ return *this;
++}
++
++bool RecordingRule::operator==(const int &id)
++{
++ return id==RecordID();
++}
++
++RecordingRule* RecordingRule::GetParent()
++{
++ return m_parent;
++}
++
++void RecordingRule::SetParent(RecordingRule& parent)
++{
++ m_parent = &parent;
++}
++
++void RecordingRule::AddModifier(RecordingRule& modifier)
++{
++ m_modifiers.push_back(&modifier);
++}
++
++std::vector< RecordingRule* > RecordingRule::GetModifiers()
++{
++ return m_modifiers;
++}
++
++bool RecordingRule::HasModifiers()
++{
++ return !m_modifiers.empty();
++}
++
++bool RecordingRule::SameTimeslot(RecordingRule& rule)
++{
++ time_t starttime = StartTime();
++ time_t rStarttime = rule.StartTime();
++ switch( rule.Type() )
++ {
++ case MythTimer::NotRecording:
++ case MythTimer::SingleRecord:
++ case MythTimer::OverrideRecord:
++ case MythTimer::DontRecord:
++ return rStarttime == starttime && rule.EndTime() == EndTime() && rule.ChanID() == ChanID();
++ case MythTimer::FindDailyRecord:
++ case MythTimer::FindWeeklyRecord:
++ case MythTimer::FindOneRecord:
++ return rule.Title(false) == Title(false);
++ case MythTimer::TimeslotRecord:
++ return rule.Title(false) == Title(false) && daytime( &starttime) == daytime( &rStarttime ) && rule.ChanID() == ChanID();
++ case MythTimer::ChannelRecord:
++ return rule.Title(false) == Title(false) && rule.ChanID() == ChanID(); //TODO: dup
++ case MythTimer::AllRecord:
++ return rule.Title(false) == Title(false);//TODO: dup
++ case MythTimer::WeekslotRecord:
++ return rule.Title(false) == Title(false) && daytime( &starttime) == daytime( &rStarttime ) && weekday( &starttime) == weekday( &rStarttime) && rule.ChanID() == ChanID();
++ }
++ return false;
++}
++
++void RecordingRule::push_back(std::pair< PVR_TIMER, MythProgramInfo > &_val)
++{
++ SaveTimerString(_val.first);
++ std::vector< std::pair< PVR_TIMER, MythProgramInfo > >::push_back(_val);
++}
++
++void RecordingRule::SaveTimerString(PVR_TIMER& timer)
++{
++ m_stringStore.push_back(boost::shared_ptr<CStdString>(new CStdString(timer.strTitle)));
++ timer.strTitle=m_stringStore.back()->c_str();
++ m_stringStore.push_back(boost::shared_ptr<CStdString>(new CStdString(timer.strDirectory)));
++ timer.strDirectory=m_stringStore.back()->c_str();
++ m_stringStore.push_back(boost::shared_ptr<CStdString>(new CStdString(timer.strSummary)));
++ timer.strSummary=m_stringStore.back()->c_str();
++}
++
++
++PVRClientMythTV::PVRClientMythTV()
++ :m_con(),m_eventHandler(),m_db(),m_protocolVersion(""),m_connectionString(""),m_EPGstart(0),m_EPGend(0),m_channelGroups(),m_categoryMap(),m_fOps2_client(0)
++{
++ m_categoryMap.insert(catbimap::value_type("Movie",0x10));
++ m_categoryMap.insert(catbimap::value_type("Movie", 0x10));
++ m_categoryMap.insert(catbimap::value_type("Movie - Detective/Thriller", 0x11));
++ m_categoryMap.insert(catbimap::value_type("Movie - Adventure/Western/War", 0x12));
++ m_categoryMap.insert(catbimap::value_type("Movie - Science Fiction/Fantasy/Horror", 0x13));
++ m_categoryMap.insert(catbimap::value_type("Movie - Comedy", 0x14));
++ m_categoryMap.insert(catbimap::value_type("Movie - Soap/melodrama/folkloric", 0x15));
++ m_categoryMap.insert(catbimap::value_type("Movie - Romance", 0x16));
++ m_categoryMap.insert(catbimap::value_type("Movie - Serious/Classical/Religious/Historical Movie/Drama", 0x17));
++ m_categoryMap.insert(catbimap::value_type("Movie - Adult ", 0x18));
++ m_categoryMap.insert(catbimap::value_type("Drama", 0x1F));//MythTV use 0xF0 but xbmc doesn't implement this.
++ m_categoryMap.insert(catbimap::value_type("News", 0x20));
++ m_categoryMap.insert(catbimap::value_type("News/weather report", 0x21));
++ m_categoryMap.insert(catbimap::value_type("News magazine", 0x22));
++ m_categoryMap.insert(catbimap::value_type("Documentary", 0x23));
++ m_categoryMap.insert(catbimap::value_type("Intelligent Programmes", 0x24));
++ m_categoryMap.insert(catbimap::value_type("Entertainment", 0x30));
++ m_categoryMap.insert(catbimap::value_type("Game Show", 0x31));
++ m_categoryMap.insert(catbimap::value_type("Variety Show", 0x32));
++ m_categoryMap.insert(catbimap::value_type("Talk Show", 0x33));
++ m_categoryMap.insert(catbimap::value_type("Sports", 0x40));
++ m_categoryMap.insert(catbimap::value_type("Special Events (World Cup, World Series, etc)", 0x41));
++ m_categoryMap.insert(catbimap::value_type("Sports Magazines", 0x42));
++ m_categoryMap.insert(catbimap::value_type("Football (Soccer)", 0x43));
++ m_categoryMap.insert(catbimap::value_type("Tennis/Squash", 0x44));
++ m_categoryMap.insert(catbimap::value_type("Misc. Team Sports", 0x45));
++ m_categoryMap.insert(catbimap::value_type("Athletics", 0x46));
++ m_categoryMap.insert(catbimap::value_type("Motor Sport", 0x47));
++ m_categoryMap.insert(catbimap::value_type("Water Sport", 0x48));
++ m_categoryMap.insert(catbimap::value_type("Winter Sports", 0x49));
++ m_categoryMap.insert(catbimap::value_type("Equestrian", 0x4A));
++ m_categoryMap.insert(catbimap::value_type("Martial Sports", 0x4B));
++ m_categoryMap.insert(catbimap::value_type("Kids", 0x50));
++ m_categoryMap.insert(catbimap::value_type("Pre-School Children's Programmes", 0x51));
++ m_categoryMap.insert(catbimap::value_type("Entertainment Programmes for 6 to 14", 0x52));
++ m_categoryMap.insert(catbimap::value_type("Entertainment Programmes for 10 to 16", 0x53));
++ m_categoryMap.insert(catbimap::value_type("Informational/Educational", 0x54));
++ m_categoryMap.insert(catbimap::value_type("Cartoons/Puppets", 0x55));
++ m_categoryMap.insert(catbimap::value_type("Music/Ballet/Dance", 0x60));
++ m_categoryMap.insert(catbimap::value_type("Rock/Pop", 0x61));
++ m_categoryMap.insert(catbimap::value_type("Classical Music", 0x62));
++ m_categoryMap.insert(catbimap::value_type("Folk Music", 0x63));
++ m_categoryMap.insert(catbimap::value_type("Jazz", 0x64));
++ m_categoryMap.insert(catbimap::value_type("Musical/Opera", 0x65));
++ m_categoryMap.insert(catbimap::value_type("Ballet", 0x66));
++ m_categoryMap.insert(catbimap::value_type("Arts/Culture", 0x70));
++ m_categoryMap.insert(catbimap::value_type("Performing Arts", 0x71));
++ m_categoryMap.insert(catbimap::value_type("Fine Arts", 0x72));
++ m_categoryMap.insert(catbimap::value_type("Religion", 0x73));
++ m_categoryMap.insert(catbimap::value_type("Popular Culture/Traditional Arts", 0x74));
++ m_categoryMap.insert(catbimap::value_type("Literature", 0x75));
++ m_categoryMap.insert(catbimap::value_type("Film/Cinema", 0x76));
++ m_categoryMap.insert(catbimap::value_type("Experimental Film/Video", 0x77));
++ m_categoryMap.insert(catbimap::value_type("Broadcasting/Press", 0x78));
++ m_categoryMap.insert(catbimap::value_type("New Media", 0x79));
++ m_categoryMap.insert(catbimap::value_type("Arts/Culture Magazines", 0x7A));
++ m_categoryMap.insert(catbimap::value_type("Fashion", 0x7B));
++ m_categoryMap.insert(catbimap::value_type("Social/Policical/Economics", 0x80));
++ m_categoryMap.insert(catbimap::value_type("Magazines/Reports/Documentary", 0x81));
++ m_categoryMap.insert(catbimap::value_type("Economics/Social Advisory", 0x82));
++ m_categoryMap.insert(catbimap::value_type("Remarkable People", 0x83));
++ m_categoryMap.insert(catbimap::value_type("Education/Science/Factual", 0x90));
++ m_categoryMap.insert(catbimap::value_type("Nature/animals/Environment", 0x91));
++ m_categoryMap.insert(catbimap::value_type("Technology/Natural Sciences", 0x92));
++ m_categoryMap.insert(catbimap::value_type("Medicine/Physiology/Psychology", 0x93));
++ m_categoryMap.insert(catbimap::value_type("Foreign Countries/Expeditions", 0x94));
++ m_categoryMap.insert(catbimap::value_type("Social/Spiritual Sciences", 0x95));
++ m_categoryMap.insert(catbimap::value_type("Further Education", 0x96));
++ m_categoryMap.insert(catbimap::value_type("Languages", 0x97));
++ m_categoryMap.insert(catbimap::value_type("Leisure/Hobbies", 0xA0));
++ m_categoryMap.insert(catbimap::value_type("Tourism/Travel", 0xA1));
++ m_categoryMap.insert(catbimap::value_type("Handicraft", 0xA2));
++ m_categoryMap.insert(catbimap::value_type("Motoring", 0xA3));
++ m_categoryMap.insert(catbimap::value_type("Fitness & Health", 0xA4));
++ m_categoryMap.insert(catbimap::value_type("Cooking", 0xA5));
++ m_categoryMap.insert(catbimap::value_type("Advertizement/Shopping", 0xA6));
++ m_categoryMap.insert(catbimap::value_type("Gardening", 0xA7));
++ m_categoryMap.insert(catbimap::value_type("Original Language", 0xB0));
++ m_categoryMap.insert(catbimap::value_type("Black & White", 0xB1));
++ m_categoryMap.insert(catbimap::value_type("\"Unpublished\" Programmes", 0xB2));
++ m_categoryMap.insert(catbimap::value_type("Live Broadcast", 0xB3));
++
++ m_categoryMap.insert(catbimap::value_type("Community", 0));
++ m_categoryMap.insert(catbimap::value_type("Fundraiser", 0));
++ m_categoryMap.insert(catbimap::value_type("Bus./financial", 0));
++ m_categoryMap.insert(catbimap::value_type("Variety", 0));
++ m_categoryMap.insert(catbimap::value_type("Romance-comedy", 0xC6));
++ m_categoryMap.insert(catbimap::value_type("Sports event", 0x40));
++ m_categoryMap.insert(catbimap::value_type("Sports talk", 0x40));
++ m_categoryMap.insert(catbimap::value_type("Computers", 0x92));
++ m_categoryMap.insert(catbimap::value_type("How-to", 0xA2));
++ m_categoryMap.insert(catbimap::value_type("Religious", 0x73));
++ m_categoryMap.insert(catbimap::value_type("Parenting", 0));
++ m_categoryMap.insert(catbimap::value_type("Art", 0x70));
++ m_categoryMap.insert(catbimap::value_type("Musical comedy", 0x64));
++ m_categoryMap.insert(catbimap::value_type("Environment", 0x91));
++ m_categoryMap.insert(catbimap::value_type("Politics", 0x80));
++ m_categoryMap.insert(catbimap::value_type("Animated", 0x55));
++ m_categoryMap.insert(catbimap::value_type("Gaming", 0));
++ m_categoryMap.insert(catbimap::value_type("Interview", 0x24));
++ m_categoryMap.insert(catbimap::value_type("Historical drama", 0xC7));
++ m_categoryMap.insert(catbimap::value_type("Biography", 0));
++ m_categoryMap.insert(catbimap::value_type("Home improvement", 0));
++ m_categoryMap.insert(catbimap::value_type("Hunting", 0xA0));
++ m_categoryMap.insert(catbimap::value_type("Outdoors", 0xA0));
++ m_categoryMap.insert(catbimap::value_type("Auto", 0x47));
++ m_categoryMap.insert(catbimap::value_type("Auto racing", 0x47));
++ m_categoryMap.insert(catbimap::value_type("Horror", 0xC4));
++ m_categoryMap.insert(catbimap::value_type("Medical", 0x93));
++ m_categoryMap.insert(catbimap::value_type("Romance", 0xC6));
++ m_categoryMap.insert(catbimap::value_type("Spanish", 0x97));
++ m_categoryMap.insert(catbimap::value_type("Adults only", 0xC8));
++ m_categoryMap.insert(catbimap::value_type("Musical", 0x64));
++ m_categoryMap.insert(catbimap::value_type("Self improvement", 0xA0));
++ m_categoryMap.insert(catbimap::value_type("Pro wrestling", 0x40));
++ m_categoryMap.insert(catbimap::value_type("Wrestling", 0x40));
++ m_categoryMap.insert(catbimap::value_type("Fishing", 0));
++ m_categoryMap.insert(catbimap::value_type("Agriculture", 0));
++ m_categoryMap.insert(catbimap::value_type("Arts/crafts", 0x70));
++ m_categoryMap.insert(catbimap::value_type("Technology", 0x92));
++ m_categoryMap.insert(catbimap::value_type("Docudrama", 0xC0));
++ m_categoryMap.insert(catbimap::value_type("Science fiction", 0xC3));
++ m_categoryMap.insert(catbimap::value_type("Paranormal", 0));
++ m_categoryMap.insert(catbimap::value_type("Comedy", 0xC4));
++ m_categoryMap.insert(catbimap::value_type("Science", 0));
++ m_categoryMap.insert(catbimap::value_type("Travel", 0));
++ m_categoryMap.insert(catbimap::value_type("Adventure", 0));
++ m_categoryMap.insert(catbimap::value_type("Suspense", 0xC1));
++ m_categoryMap.insert(catbimap::value_type("History", 0));
++ m_categoryMap.insert(catbimap::value_type("Collectibles", 0));
++ m_categoryMap.insert(catbimap::value_type("Crime", 0));
++ m_categoryMap.insert(catbimap::value_type("French", 0));
++ m_categoryMap.insert(catbimap::value_type("House/garden", 0));
++ m_categoryMap.insert(catbimap::value_type("Action", 0));
++ m_categoryMap.insert(catbimap::value_type("Fantasy", 0));
++ m_categoryMap.insert(catbimap::value_type("Mystery", 0));
++ m_categoryMap.insert(catbimap::value_type("Health", 0));
++ m_categoryMap.insert(catbimap::value_type("Comedy-drama", 0));
++ m_categoryMap.insert(catbimap::value_type("Special", 0));
++ m_categoryMap.insert(catbimap::value_type("Holiday", 0));
++ m_categoryMap.insert(catbimap::value_type("Weather", 0));
++ m_categoryMap.insert(catbimap::value_type("Western", 0));
++ m_categoryMap.insert(catbimap::value_type("Children", 0));
++ m_categoryMap.insert(catbimap::value_type("Nature", 0));
++ m_categoryMap.insert(catbimap::value_type("Animals", 0));
++ m_categoryMap.insert(catbimap::value_type("Public affairs", 0));
++ m_categoryMap.insert(catbimap::value_type("Educational", 0));
++ m_categoryMap.insert(catbimap::value_type("Shopping", 0xA6));
++ m_categoryMap.insert(catbimap::value_type("Consumer", 0));
++ m_categoryMap.insert(catbimap::value_type("Soap", 0));
++ m_categoryMap.insert(catbimap::value_type("Newsmagazine", 0));
++ m_categoryMap.insert(catbimap::value_type("Exercise", 0));
++ m_categoryMap.insert(catbimap::value_type("Music", 0x60));
++ m_categoryMap.insert(catbimap::value_type("Game show", 0));
++ m_categoryMap.insert(catbimap::value_type("Sitcom", 0));
++ m_categoryMap.insert(catbimap::value_type("Talk", 0));
++ m_categoryMap.insert(catbimap::value_type("Crime drama", 0));
++ m_categoryMap.insert(catbimap::value_type("Sports non-event", 0x40));
++ m_categoryMap.insert(catbimap::value_type("Reality", 0));
++
++
++}
++
++PVRClientMythTV::~PVRClientMythTV()
++{
++ if(m_fOps2_client)
++ {
++ delete m_fOps2_client;
++ m_fOps2_client = 0;
++ }
++ m_eventHandler.Stop();
++}
++
++CStdString PVRClientMythTV::GetArtWork(FILE_OPTIONS storageGroup, CStdString shwTitle) {
++ if ((storageGroup == FILE_OPS_GET_COVERART) ||
++ (storageGroup == FILE_OPS_GET_FANART))
++ {
++ return m_fOps2_client->getArtworkPath(shwTitle,storageGroup);
++ }
++ else if(storageGroup == FILE_OPS_GET_CHAN_ICONS)
++ {
++ return m_fOps2_client->getChannelIconPath(shwTitle);
++ }
++ else
++ {
++ XBMC->Log(LOG_DEBUG,"%s - ## Not a valid storageGroup ##",__FUNCTION__);
++ return "";
++ }
++
++}
++
++int PVRClientMythTV::Genre(CStdString g)
++{
++ int retval=0;
++ //XBMC->Log(LOG_DEBUG,"- %s - ## Genre ## - %s -",__FUNCTION__,g.c_str());
++ try{
++ if(m_categoryMap.by< mythcat >().count(g))
++ retval=m_categoryMap.by< mythcat >().at(g);
++
++ }
++ catch(std::out_of_range){}
++ //XBMC->Log(LOG_DEBUG,"- %s - ## Genre ## - ret: %d -",__FUNCTION__,retval);
++ return retval;
++}
++CStdString PVRClientMythTV::Genre(int g)
++{
++ CStdString retval="";
++ try{
++ if(m_categoryMap.by< pvrcat >().count(g))
++ retval=m_categoryMap.by< pvrcat >().at(g);
++ }
++ catch(std::out_of_range){}
++ return retval;
++}
++
++
++void Log(int l,char* msg)
++{
++
++ if(msg&&l!=CMYTH_DBG_NONE)
++ {
++ bool doLog=g_bExtraDebug;
++ addon_log_t loglevel=LOG_DEBUG;
++ switch( l)
++ {
++ case CMYTH_DBG_ERROR:
++ loglevel=LOG_ERROR;
++ doLog=true;
++ break;
++ case CMYTH_DBG_WARN:
++ case CMYTH_DBG_INFO:
++ loglevel=LOG_INFO;
++ break;
++ case CMYTH_DBG_DETAIL:
++ case CMYTH_DBG_DEBUG:
++ case CMYTH_DBG_PROTO:
++ case CMYTH_DBG_ALL:
++ loglevel=LOG_DEBUG;
++ break;
++ }
++ if(XBMC&&doLog)
++ XBMC->Log(loglevel,"LibCMyth: %s", msg);
++ }
++}
++
++bool PVRClientMythTV::Connect()
++{
++ if(g_bExtraDebug)
++ CMYTH->DbgAll();
++ else
++ CMYTH->DbgLevel(CMYTH_DBG_ERROR);
++ CMYTH->SetDbgMsgcallback(Log);
++ m_con=MythConnection(g_szHostname,g_iMythPort);
++ if(!m_con.IsConnected())
++ {
++ XBMC->QueueNotification(QUEUE_ERROR,"%s: Failed to connect to MythTV backend %s:%i",__FUNCTION__,g_szHostname.c_str(),g_iMythPort);
++ return false;
++ }
++ m_eventHandler=m_con.CreateEventHandler();
++ m_protocolVersion.Format("%i",m_con.GetProtocolVersion());
++ m_connectionString.Format("%s:%i",g_szHostname,g_iMythPort);
++ m_fOps2_client = new fileOps2(m_con);
++ m_db=MythDatabase(g_szHostname,g_szMythDBname,g_szMythDBuser,g_szMythDBpassword);
++ if(m_db.IsNull())
++ {
++ XBMC->QueueNotification(QUEUE_ERROR,"Failed to connect to MythTV MySQL database %s@%s %s/%s",g_szMythDBname.c_str(),g_szHostname.c_str(),g_szMythDBuser.c_str(),g_szMythDBpassword.c_str());
++ m_eventHandler.Stop();
++ return false;
++ }
++ CStdString db_test;
++ if(!m_db.TestConnection(db_test))
++ {
++ XBMC->QueueNotification(QUEUE_ERROR,"Failed to connect to MythTV MySQL database %s@%s %s/%s \n %s",g_szMythDBname.c_str(),g_szHostname.c_str(),g_szMythDBuser.c_str(),g_szMythDBpassword.c_str(),db_test.c_str());
++ m_eventHandler.Stop();
++ return false;
++ }
++ m_channels=m_db.ChannelList();
++ if(m_channels.size()==0)
++ XBMC->Log(LOG_INFO,"%s: Empty channellist",__FUNCTION__);
++ m_sources=m_db.SourceList();
++ if(m_sources.size()==0)
++ XBMC->Log(LOG_INFO,"%s: Empty source list",__FUNCTION__);
++
++ m_channelGroups=m_db.GetChannelGroups();
++ if(m_channelGroups.size()==0)
++ XBMC->Log(LOG_INFO,"%s: No channelgroups",__FUNCTION__);
++
++ std::vector<MythRecordingProfile > rp = m_db.GetRecordingProfiles();
++
++ return true;
++}
++
++const char* PVRClientMythTV::GetBackendName()
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s",__FUNCTION__);
++ return m_con.GetServer();
++}
++
++
++const char * PVRClientMythTV::GetBackendVersion()
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s",__FUNCTION__);
++ return m_protocolVersion;
++}
++
++const char * PVRClientMythTV::GetConnectionString()
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s",__FUNCTION__);
++ return m_connectionString;
++}
++
++bool PVRClientMythTV::GetDriveSpace(long long *iTotal, long long *iUsed)
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s",__FUNCTION__);
++ return m_con.GetDriveSpace(*iTotal,*iUsed);
++}
++
++PVR_ERROR PVRClientMythTV::GetEPGForChannel(PVR_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd)
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - start: %i, end: %i, ChanID: %i",__FUNCTION__,iStart,iEnd,channel.iUniqueId);
++ if(iStart!=m_EPGstart&&iEnd!=m_EPGend)
++ {
++ m_EPG=m_db.GetGuide(iStart,iEnd);
++ XBMC->Log(LOG_DEBUG,"%s: Fetching EPG - size: %i",__FUNCTION__,m_EPG.size());
++ m_EPGstart=iStart;
++ m_EPGend=iEnd;
++ }
++ for(std::vector< MythProgram >::iterator it=m_EPG.begin();it!=m_EPG.end();it++)
++ {
++ if(it->chanid==channel.iUniqueId)
++ {
++ EPG_TAG tag;
++ tag.endTime=it->endtime;
++ tag.iChannelNumber=it->channum;
++ tag.startTime=it->starttime;
++ CStdString title=it->title;
++ CStdString subtitle=it->subtitle;
++ if (!subtitle.IsEmpty())
++ title+=": " + subtitle;
++ tag.strTitle=title;
++ tag.strPlot= it->description;
++ /*unsigned int seriesid=atoi(it->seriesid);
++ if(seriesid!=0)
++ tag.iUniqueBroadcastId=atoi(it->seriesid);
++ else*/
++ tag.iUniqueBroadcastId=(tag.startTime<<16)+(tag.iChannelNumber&0xffff);
++ int genre=Genre(it->category);
++ tag.iGenreSubType=genre&0x0F;
++ tag.iGenreType=genre&0xF0;
++
++ //unimplemented
++ tag.strEpisodeName="";
++ tag.strGenreDescription="";
++ tag.strIconPath="";
++ tag.strPlotOutline="";
++ tag.bNotify=false;
++ tag.firstAired=0;
++ tag.iEpisodeNumber=0;
++ tag.iEpisodePartNumber=0;
++ tag.iParentalRating=0;
++ tag.iSeriesNumber=0;
++ tag.iStarRating=0;
++
++
++ PVR->TransferEpgEntry(handle,&tag);
++ }
++ }
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - Done",__FUNCTION__);
++ return PVR_ERROR_NO_ERROR;
++}
++
++
++int PVRClientMythTV::GetNumChannels()
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s",__FUNCTION__);
++ return m_channels.size();
++}
++
++PVR_ERROR PVRClientMythTV::GetChannels(PVR_HANDLE handle, bool bRadio)
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - radio: %i",__FUNCTION__,bRadio);
++ for (std::map< int, MythChannel >::iterator it = m_channels.begin(); it != m_channels.end(); it++)
++ {
++ if (it->second.IsRadio()==bRadio&&!it->second.IsNull())
++ {
++ PVR_CHANNEL tag;
++ tag.bIsHidden=!it->second.Visible();
++ tag.bIsRadio=it->second.IsRadio();
++ tag.iUniqueId=it->first;
++ tag.iChannelNumber=it->second.NumberInt(); //Use ID instead as mythtv channel number is a string?
++ CStdString chanName= it->second.Name();
++ tag.strChannelName = chanName;
++ CStdString icon = it->second.Icon();
++
++ icon = GetArtWork(FILE_OPS_GET_CHAN_ICONS,icon);
++
++ tag.strIconPath = icon;
++ //Unimplemented
++ tag.strStreamURL="";
++ tag.strInputFormat="";
++ tag.iEncryptionSystem=0;
++
++
++ PVR->TransferChannelEntry(handle,&tag);
++ }
++ }
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - Done",__FUNCTION__);
++ return PVR_ERROR_NO_ERROR;
++}
++
++int PVRClientMythTV::GetRecordingsAmount(void)
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s",__FUNCTION__);
++ m_recordings=m_con.GetRecordedPrograms();
++ return m_recordings.size();
++}
++
++
++PVR_ERROR PVRClientMythTV::GetRecordings(PVR_HANDLE handle)
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s",__FUNCTION__);
++ m_recordings=m_con.GetRecordedPrograms();
++ for (boost::unordered_map< CStdString, MythProgramInfo >::iterator it = m_recordings.begin(); it != m_recordings.end(); it++)
++ {
++ if(!it->second.IsNull())
++ {
++ PVR_RECORDING tag;
++ tag.iDuration=it->second.Duration();
++ tag.recordingTime=it->second.RecStart();
++ CStdString chanName=it->second.ChannelName();
++ CStdString plot=it->second.Description();
++ CStdString path=it->second.Path();
++ CStdString title=it->second.Title(true);
++
++ tag.strChannelName=chanName;
++ tag.strPlot=plot;
++ CStdString id=it->second.Path();
++ tag.strRecordingId=id;
++ CStdString group=it->second.RecordingGroup();
++ tag.strDirectory=group=="Default"?"":group;
++ tag.strTitle=title;
++ int genre=Genre(it->second.Category());
++ tag.iGenreSubType=genre&0x0F;
++ tag.iGenreType=genre&0xF0;
++
++ CStdString foldername = it->second.Title(true);
++ CStdString foldertitle = foldername;
++ CStdString newtitle = title;
++ bool regex_series_match = false;
++ try {
++ boost::regex re(g_szSeriesIdentifier);
++ regex_series_match = boost::regex_match(title,re);
++ }
++ catch(...)
++ {
++ XBMC->Log(LOG_ERROR,"%s: Malformed regulare expression : \"%s\" ",__FUNCTION__,g_szSeriesIdentifier.c_str());
++ }
++
++ if((tag.iGenreType==0x10||genre==0x00)&&tag.iDuration>(g_iMinMovieLength*60)&&!regex_series_match&&(it->second.ProgramID().substr(0,2)=="MV"||it->second.ProgramID()==""))
++ {
++ group.Format("%s/Movies",tag.strDirectory);
++ tag.strDirectory=group;
++ }
++ else
++ {
++ try {
++ boost::regex re(g_szSeriesRegEx);
++ boost::smatch result;
++ if(boost::regex_search(foldertitle,result,re))
++ {
++ if(result.length("folder"))
++ foldername = result.str("folder");
++ if(result.length("title"))
++ newtitle=result.str("title");
++ tag.strTitle = newtitle;
++ }
++ }
++ catch(...)
++ {
++ XBMC->Log(LOG_ERROR,"%s: Malformed regulare expression : \"%s\" ",__FUNCTION__,g_szSeriesRegEx.c_str());
++ }
++ group.Format("%s/%s",tag.strDirectory,foldername);
++ tag.strDirectory=group;
++ }
++ time_t startTime = it->second.StartTime();
++
++ CStdString defIcon = GetArtWork(FILE_OPS_GET_COVERART,title);
++ if(defIcon == "")
++ defIcon = m_fOps2_client->getPreviewIconPath(id+".png");
++ CStdString fanIcon = GetArtWork(FILE_OPS_GET_FANART,title);
++ tag.strIconPath=defIcon.c_str();
++ tag.strDefFanart=fanIcon.c_str();
++
++ //Unimplemented
++ tag.iLifetime=0;
++ tag.iPriority=0;
++ tag.strPlotOutline="";
++ tag.strStreamURL="";
++
++ PVR->TransferRecordingEntry(handle,&tag);
++
++ size_t dirsep = title.find_last_of("/");
++ if(dirsep != CStdString::npos)
++ {
++ tag.strDirectory = "/All Recordings/";
++ newtitle = title.substr(dirsep+1,title.size());
++ tag.strTitle = newtitle.c_str();
++ id.append("@");
++ tag.strRecordingId = id.c_str();
++ PVR->TransferRecordingEntry(handle,&tag);
++ }
++ else
++ {
++ tag.strDirectory = "/All Recordings/";
++ tag.strTitle = title.c_str();
++ id.append("@");
++ tag.strRecordingId = id.c_str();
++ PVR->TransferRecordingEntry(handle,&tag);
++ }
++ }
++ }
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - Done",__FUNCTION__);
++ return PVR_ERROR_NO_ERROR;
++}
++
++PVR_ERROR PVRClientMythTV::DeleteRecording(const PVR_RECORDING &recording)
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s",__FUNCTION__);
++ CStdString id=recording.strRecordingId;
++ if(*id.rbegin() == '@')
++ id = id.substr(0,id.size()-1);
++ bool ret = m_con.DeleteRecording(m_recordings.at(id));
++ if(ret && m_recordings.erase(recording.strRecordingId))
++ {
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - Deleted",__FUNCTION__);
++ return PVR_ERROR_NO_ERROR;
++
++ }
++ else
++ {
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - Not Deleted",__FUNCTION__);
++ return PVR_ERROR_NOT_DELETED;
++ }
++}
++
++int PVRClientMythTV::GetTimersAmount(void)
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s",__FUNCTION__);
++ std::map< int, MythTimer > m_timers=m_db.GetTimers();
++ return m_timers.size();
++}
++
++PVR_ERROR PVRClientMythTV::GetTimers(PVR_HANDLE handle)
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s",__FUNCTION__);
++ std::map< int, MythTimer > timers=m_db.GetTimers();
++ m_recordingRules.clear();
++
++ for(std::map< int, MythTimer >::iterator it = timers.begin();it != timers.end();it++)
++ m_recordingRules.push_back(it->second);
++ //Search for modifiers and add links to them
++ for(std::vector <RecordingRule >::iterator it = m_recordingRules.begin();it != m_recordingRules.end(); it++)
++ if(it->Type() == MythTimer::DontRecord || it->Type() == MythTimer::OverrideRecord)
++ for(std::vector <RecordingRule >::iterator it2 = m_recordingRules.begin();it2 != m_recordingRules.end(); it2++)
++ if(it2->Type() != MythTimer::DontRecord && it2->Type() != MythTimer::OverrideRecord)
++ if(it->SameTimeslot(*it2)&&!it->GetParent())
++ {
++ it2->AddModifier(*it);
++ it->SetParent(*it2);
++ }
++
++
++ boost::unordered_map< CStdString, MythProgramInfo > upcomingRecordings = m_con.GetPendingPrograms();
++ for (boost::unordered_map< CStdString, MythProgramInfo >::iterator it = upcomingRecordings.begin(); it != upcomingRecordings.end(); it++)
++ {
++ PVR_TIMER tag;
++ MythProgramInfo& proginfo = it->second;
++ tag.endTime=proginfo.EndTime();
++ //EndTime();
++ tag.iClientChannelUid=proginfo.ChannelID();
++ tag.iClientIndex=proginfo.RecordID();
++ tag.startTime=proginfo.StartTime();
++ CStdString title=proginfo.Title(true);
++ if(title.empty())
++ {
++ MythProgram epgProgram;
++ title="%";
++ m_db.FindProgram(tag.startTime,tag.iClientChannelUid,title,&epgProgram);
++ title=epgProgram.title;
++ }
++ tag.strTitle=title;
++ CStdString summary=proginfo.Description();
++ tag.strSummary=summary;
++ XBMC->Log(LOG_DEBUG,"%s ## - State: %d - ##",__FUNCTION__,proginfo.Status());
++ switch(proginfo.Status())
++ {
++ case RS_RECORDING:
++ case RS_TUNING:
++ tag.state=PVR_TIMER_STATE_RECORDING;
++ break;
++ case RS_ABORTED:
++ tag.state=PVR_TIMER_STATE_ABORTED;
++ break;
++ case RS_RECORDED:
++ tag.state=PVR_TIMER_STATE_COMPLETED;
++ break;
++ case RS_WILL_RECORD:
++ tag.state=PVR_TIMER_STATE_SCHEDULED;
++ break;
++ case RS_UNKNOWN:
++ tag.state=PVR_TIMER_STATE_INVALID;
++ break;
++ case RS__DONT_RECORD:
++ case RS_PREVIOUS_RECORDING:
++ case RS_CURRENT_RECORDING:
++ case RS_EARLIER_SHOWING:
++ case RS_TOO_MANY_RECORDINGS:
++ case RS_NOT_LISTED:
++ case RS_CONFLICT:
++ case RS_LATER_SHOWING:
++ case RS_REPEAT:
++ case RS_INACTIVE:
++ case RS_NEVER_RECORD:
++ case RS_OFFLINE:
++ case RS_OTHER_SHOWING:
++ case RS_FAILED:
++ case RS_TUNER_BUSY:
++ case RS_LOW_DISKSPACE:
++ case RS_CANCELLED:
++ case RS_MISSED:
++ default:
++ tag.state=PVR_TIMER_STATE_CANCELLED;
++ break;
++ }
++ int genre=Genre(proginfo.Category());
++ tag.iGenreSubType=genre&0x0F;
++ tag.iGenreType=genre&0xF0;
++ tag.iMarginEnd=timers.at(proginfo.RecordID()).EndOffset();
++ tag.iMarginStart=timers.at(proginfo.RecordID()).StartOffset();
++ tag.iPriority=proginfo.Priority();
++
++ tag.bIsRepeating = false;
++ tag.firstDay=0;
++ tag.iWeekdays=0;
++
++ //Unimplemented
++ tag.iEpgUid=0;
++ tag.iLifetime=0;
++ tag.strDirectory="";
++
++ std::vector< RecordingRule >::iterator recRule = std::find(m_recordingRules.begin(), m_recordingRules.end() , it->second.RecordID());
++
++ tag.iClientIndex = ((recRule - m_recordingRules.begin())<<16) + recRule->size();
++
++ //recRule->SaveTimerString(tag);
++ std::pair< PVR_TIMER, MythProgramInfo > rrtmp(tag, proginfo);
++ recRule->push_back(rrtmp);
++
++ PVR->TransferTimerEntry(handle,&tag);
++ }
++ /*
++ std::vector< MythTimer > m_timers=m_db.GetTimers();
++ for (std::vector< MythTimer >::iterator it = m_timers.begin(); it != m_timers.end(); it++)
++ {
++
++ PVR_TIMER tag;
++ tag.endTime=it->EndTime();
++ tag.iClientChannelUid=it->ChanID();
++ tag.iClientIndex=it->RecordID();
++ tag.startTime=it->StartTime();
++ CStdString title=it->Title();
++ tag.strTitle=title;
++ CStdString summary=it->Description();
++ tag.strSummary=summary;
++ tag.state=it->Type()==MythTimer::NotRecording?PVR_TIMER_STATE_CANCELLED:PVR_TIMER_STATE_SCHEDULED;
++ int genre=Genre(it->Category());
++ tag.iGenreSubType=genre&0x0F;
++ tag.iGenreType=genre&0xF0;
++ tag.iMarginEnd=it->EndOffset();
++ tag.iMarginStart=it->StartOffset();
++ tag.iPriority=it->Priority()+50;
++
++ if(it->Type()==MythTimer::WeekslotRecord)
++ {
++ tag.bIsRepeating = true;
++ tm *lc = localtime(&tag.startTime);
++ int shift = lc->tm_wday? 6 : lc->tm_wday-1;//Monday is the first day
++ tag.iWeekdays = 1 << shift;
++ tag.firstDay = tag.startTime;
++ }
++ else if(it->Type()==MythTimer::TimeslotRecord)
++ {
++ tag.bIsRepeating = true;
++ tag.iWeekdays = 127; //daily
++ tag.firstDay = tag.startTime;
++ }
++ else if(it->Type() == MythTimer::FindWeeklyRecord||it->Type() == MythTimer::FindDailyRecord||
++ it->Type() == MythTimer::ChannelRecord || it->Type() == MythTimer::AllRecord)
++ {
++ tag.bIsRepeating = true;
++ tag.iWeekdays = 0; //Special
++ tag.firstDay = tag.startTime;
++ }
++ else
++ {
++ tag.bIsRepeating = false;
++ tag.firstDay=0;
++ tag.iWeekdays=0;
++ }
++ //Unimplemented
++ tag.iEpgUid=0;
++ tag.iLifetime=0;
++ tag.strDirectory="";
++
++ PVR->TransferTimerEntry(handle,&tag);
++
++ }*/
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - Done",__FUNCTION__);
++ return PVR_ERROR_NO_ERROR;
++}
++
++//kManualSearch = http://www.gossamer-threads.com/lists/mythtv/dev/155150?search_string=kManualSearch;#155150
++
++PVR_ERROR PVRClientMythTV::AddTimer(const PVR_TIMER &timer)
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - title: %s, start: %i, end: %i, chanID: %i",__FUNCTION__,timer.strTitle,timer.startTime,timer.iClientChannelUid);
++ MythTimer mt;
++ m_con.DefaultTimer(mt);
++ CStdString category=Genre(timer.iGenreType);
++ mt.Category(category);
++ mt.ChanID(timer.iClientChannelUid);
++ mt.Callsign(m_channels.at(timer.iClientChannelUid).Callsign());
++ mt.Description(timer.strSummary);
++ mt.EndOffset(timer.iMarginEnd);
++ mt.EndTime(timer.endTime);
++ mt.Inactive(timer.state == PVR_TIMER_STATE_ABORTED ||timer.state == PVR_TIMER_STATE_CANCELLED);
++ mt.Priority(timer.iPriority);
++ mt.StartOffset(timer.iMarginStart);
++ mt.StartTime(timer.startTime);
++ mt.Title(timer.strTitle,true);
++ CStdString title=mt.Title(false);
++ mt.SearchType(m_db.FindProgram(timer.startTime,timer.iClientChannelUid,title,NULL)?MythTimer::NoSearch:MythTimer::ManualSearch);
++ mt.Type(timer.bIsRepeating? (timer.iWeekdays==127? MythTimer::TimeslotRecord : MythTimer::WeekslotRecord) : MythTimer::SingleRecord);
++ int id=m_db.AddTimer(mt);
++ if(id<0)
++ return PVR_ERROR_NOT_POSSIBLE;
++ if(!m_con.UpdateSchedules(id))
++ return PVR_ERROR_NOT_POSSIBLE;
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - Done - %i",__FUNCTION__,id);
++ //PVR->TriggerTimerUpdate();
++ return PVR_ERROR_NO_ERROR;
++}
++
++PVR_ERROR PVRClientMythTV::DeleteTimer(const PVR_TIMER &timer, bool bForceDelete)
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - title: %s, start: %i, end: %i, chanID: %i, ID: %i",__FUNCTION__,timer.strTitle,timer.startTime,timer.iClientChannelUid,timer.iClientIndex);
++ std::map< int, MythTimer > timers=m_db.GetTimers();
++ RecordingRule r = m_recordingRules[(timer.iClientIndex)>>16];
++ if(r.GetParent())
++ r = *r.GetParent();
++ if(r.Type()!=MythTimer::FindOneRecord && r.Type()!=MythTimer::SingleRecord)
++ {
++ CStdString line0;
++ line0.Format(XBMC->GetLocalizedString(30008)/*"This will delete the recording rule and \nan additional %i timer(s)."*/,r.size()-1);
++ if(!(GUI->Dialog_showYesNo(XBMC->GetLocalizedString(19060)/*"Delete Timer"*/,line0,"",XBMC->GetLocalizedString(30007)/*"Do you still want to delete?"*/,NULL,NULL,NULL)))
++ return PVR_ERROR_NO_ERROR;
++ }
++ //delete related Override and Don't Record timers
++ std::vector<RecordingRule* > modifiers = r.GetModifiers();
++ for(std::vector <RecordingRule* >::iterator it = modifiers.begin(); it != modifiers.end(); it++)
++ m_db.DeleteTimer((*it)->RecordID());
++ if(!m_db.DeleteTimer(r.RecordID()))
++ return PVR_ERROR_NOT_POSSIBLE;
++ m_con.UpdateSchedules(-1);
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - Done",__FUNCTION__);
++ return PVR_ERROR_NO_ERROR;
++}
++
++void PVRClientMythTV::PVRtoMythTimer(const PVR_TIMER timer, MythTimer& mt)
++{
++ CStdString category=Genre(timer.iGenreType);
++ mt.Category(category);
++ mt.ChanID(timer.iClientChannelUid);
++ mt.Callsign(m_channels.at(timer.iClientChannelUid).Callsign());
++ mt.Description(timer.strSummary);
++ mt.EndOffset(timer.iMarginEnd);
++ mt.EndTime(timer.endTime);
++ mt.Inactive(timer.state == PVR_TIMER_STATE_ABORTED ||timer.state == PVR_TIMER_STATE_CANCELLED);
++ mt.Priority(timer.iPriority);
++ mt.StartOffset(timer.iMarginStart);
++ mt.StartTime(timer.startTime);
++ mt.Title(timer.strTitle,true);
++ CStdString title=mt.Title(false);
++ mt.SearchType(m_db.FindProgram(timer.startTime,timer.iClientChannelUid,title,NULL)?MythTimer::NoSearch:MythTimer::ManualSearch);
++ mt.Type(timer.bIsRepeating? (timer.iWeekdays==127? MythTimer::TimeslotRecord : MythTimer::WeekslotRecord) : MythTimer::SingleRecord);
++ mt.RecordID(timer.iClientIndex);
++}
++
++PVR_ERROR PVRClientMythTV::UpdateTimer(const PVR_TIMER &timer)
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - title: %s, start: %i, end: %i, chanID: %i, ID: %i",__FUNCTION__,timer.strTitle,timer.startTime,timer.iClientChannelUid,timer.iClientIndex);
++ RecordingRule r = m_recordingRules[(timer.iClientIndex)>>16];
++ PVR_TIMER oldPvrTimer = r[(timer.iClientIndex)&0xffff].first;
++ {
++ bool createNewRule = false;
++ bool createNewOverrideRule = false;
++ MythTimer mt;
++ PVRtoMythTimer(timer,mt);
++ mt.Description(oldPvrTimer.strSummary);//Fix broken description
++ mt.Inactive(false);
++ mt.RecordID(r.RecordID());
++
++ //These should trigger a manual search or a new rule
++ if(oldPvrTimer.iClientChannelUid != timer.iClientChannelUid ||
++ oldPvrTimer.endTime != timer.endTime ||
++ oldPvrTimer.startTime != timer.startTime ||
++ oldPvrTimer.startTime != timer.startTime ||
++ strcmp(oldPvrTimer.strTitle, timer.strTitle) ||
++ //strcmp(oldPvrTimer.strSummary, timer.strSummary) ||
++ timer.bIsRepeating
++ )
++ createNewRule = true;
++
++ //Change type
++ if(oldPvrTimer.state != timer.state)
++ {
++ if( r.Type() != MythTimer::SingleRecord && !createNewRule)
++ {
++ if(timer.state == PVR_TIMER_STATE_ABORTED ||timer.state == PVR_TIMER_STATE_CANCELLED)
++ mt.Type( MythTimer::DontRecord );
++ else
++ mt.Type( MythTimer::OverrideRecord );
++ createNewOverrideRule = true;
++ }
++ else
++ mt.Inactive( timer.state == PVR_TIMER_STATE_ABORTED ||timer.state == PVR_TIMER_STATE_CANCELLED);
++ }
++
++ //These can be updated without triggering a new rule
++ if(oldPvrTimer.iMarginEnd != timer.iMarginEnd ||
++ oldPvrTimer.iPriority != timer.iPriority ||
++ oldPvrTimer.iMarginStart != timer.iMarginStart )
++ createNewOverrideRule = true;
++
++ CStdString title = timer.strTitle;
++ if(createNewRule)
++ mt.SearchType(m_db.FindProgram(timer.startTime,timer.iClientChannelUid,title,NULL)?MythTimer::NoSearch:MythTimer::ManualSearch);
++ if(createNewOverrideRule && r.SearchType() == MythTimer::ManualSearch)
++ mt.SearchType( MythTimer::ManualSearch);
++
++ if(r.Type() == MythTimer::DontRecord || r.Type() == MythTimer::OverrideRecord)
++ createNewOverrideRule = false;
++
++ if(createNewRule && r.Type() != MythTimer::SingleRecord )
++ {
++ if(!m_db.AddTimer(mt))
++ return PVR_ERROR_NOT_POSSIBLE;
++
++ MythTimer mtold;
++ PVRtoMythTimer(oldPvrTimer,mtold);
++ mtold.Type(MythTimer::DontRecord);
++ mtold.Inactive(false);
++ mtold.RecordID(r.RecordID());
++ int id=r.RecordID();
++ if(r.Type() == MythTimer::DontRecord || r.Type() == MythTimer::OverrideRecord)
++ m_db.UpdateTimer(mtold);
++ else
++ id=m_db.AddTimer(mtold);//Blocks old record rule
++ m_con.UpdateSchedules(id);
++ }
++ else if(createNewOverrideRule && r.Type() != MythTimer::SingleRecord )
++ {
++ if(mt.Type() != MythTimer::DontRecord && mt.Type() != MythTimer::OverrideRecord)
++ mt.Type(MythTimer::OverrideRecord);
++ if(!m_db.AddTimer(mt))
++ return PVR_ERROR_NOT_POSSIBLE;
++ }
++ else
++ {
++ if(!m_db.UpdateTimer(mt))
++ return PVR_ERROR_NOT_POSSIBLE;
++ }
++ m_con.UpdateSchedules(mt.RecordID());
++ }
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - Done",__FUNCTION__);
++ return PVR_ERROR_NO_ERROR;
++}
++
++
++bool PVRClientMythTV::OpenLiveStream(const PVR_CHANNEL &channel)
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - chanID: %i, channumber: %i",__FUNCTION__,channel.iUniqueId,channel.iChannelNumber);
++ if(m_rec.IsNull())
++ {
++ MythChannel chan=m_channels.at(channel.iUniqueId);
++ for(std::vector<int>::iterator it=m_sources.at(chan.SourceID()).begin();it!=m_sources.at(chan.SourceID()).end();it++)
++ {
++ m_rec=m_con.GetRecorder(*it);
++ if(!m_rec.IsRecording())
++ {
++ XBMC->Log(LOG_DEBUG,"%s: Opening new recorder %i",__FUNCTION__,m_rec.ID());
++ m_eventHandler.SetRecorder(m_rec);
++ if(m_rec.SpawnLiveTV(chan))
++ return true;
++ }
++ m_rec=MythRecorder();
++ m_eventHandler.SetRecorder(m_rec);//Redundant
++ }
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - Done",__FUNCTION__);
++ return false;
++ }
++ else
++ {
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - Done",__FUNCTION__);
++ return true;
++ }
++}
++
++
++void PVRClientMythTV::CloseLiveStream()
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s",__FUNCTION__);
++ m_eventHandler.PreventLiveChainUpdate();
++ m_rec.Stop();
++ m_rec=MythRecorder();
++ m_eventHandler.SetRecorder(m_rec);
++ m_eventHandler.AllowLiveChainUpdate();
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - Done",__FUNCTION__);
++ return;
++}
++
++int PVRClientMythTV::ReadLiveStream(unsigned char *pBuffer, unsigned int iBufferSize)
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - size: %i",__FUNCTION__,iBufferSize);
++ if(m_rec.IsNull())
++ return -1;
++ int dataread=m_rec.ReadLiveTV(pBuffer,iBufferSize);
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s: Read %i Bytes",__FUNCTION__,dataread);
++ else if(dataread==0)
++ XBMC->Log(LOG_INFO,"%s: Read 0 Bytes!",__FUNCTION__);
++ return dataread;
++}
++
++int PVRClientMythTV::GetCurrentClientChannel()
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s",__FUNCTION__);
++ if(m_rec.IsNull())
++ return -1;
++ MythProgramInfo currentProgram=m_rec.GetCurrentProgram();
++ return currentProgram.ChannelID();
++}
++
++bool PVRClientMythTV::SwitchChannel(const PVR_CHANNEL &channelinfo)
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - chanID: %i",__FUNCTION__,channelinfo.iUniqueId);
++ MythChannel chan=m_channels.at(channelinfo.iUniqueId);
++ bool retval=false;
++ //retval=m_rec.SetChannel(chan); //Disabled for now. Seeking will hang if using setchannel.
++ if(!retval)
++ {
++ //XBMC->Log(LOG_INFO,"%s - Failed to change to channel: %s(%i) - Reopening Livestream.",__FUNCTION__,channelinfo.strChannelName,channelinfo.iUniqueId);
++ CloseLiveStream();
++ retval=OpenLiveStream(channelinfo);
++ if(!retval)
++ XBMC->Log(LOG_ERROR,"%s - Failed to reopening Livestream!",__FUNCTION__);
++ }
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - Done",__FUNCTION__);
++ return retval;
++}
++
++
++long long PVRClientMythTV::SeekLiveStream(long long iPosition, int iWhence) {
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - pos: %i, whence: %i",__FUNCTION__,iPosition,iWhence);
++ int whence=iWhence==SEEK_SET?WHENCE_SET:iWhence==SEEK_CUR?WHENCE_CUR:WHENCE_END;
++ long long retval= m_rec.LiveTVSeek(iPosition,whence);
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - Done - pos: %i",__FUNCTION__,retval);
++ return retval;
++}
++
++long long PVRClientMythTV::LengthLiveStream()
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s",__FUNCTION__);
++ long long retval=m_rec.LiveTVDuration();
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - Done - duration: %i",__FUNCTION__,retval);
++ return retval;
++}
++
++PVR_ERROR PVRClientMythTV::SignalStatus(PVR_SIGNAL_STATUS &signalStatus)
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s",__FUNCTION__);
++ MythSignal signal=m_eventHandler.GetSignal();
++ signalStatus.dAudioBitrate=0;
++ signalStatus.dDolbyBitrate=0;
++ signalStatus.dVideoBitrate=0;
++ signalStatus.iBER=signal.BER();
++ signalStatus.iSignal=signal.Signal();
++ signalStatus.iSNR=signal.SNR();
++ signalStatus.iUNC=signal.UNC();
++ CStdString ID;
++ CStdString adaptorStatus=signal.AdapterStatus();
++ ID.Format("Myth Recorder %i",m_rec.ID());
++ strcpy(signalStatus.strAdapterName,ID.Buffer());
++ strcpy(signalStatus.strAdapterStatus,adaptorStatus.Buffer());
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - Done",__FUNCTION__);
++ return PVR_ERROR_NO_ERROR;
++}
++
++bool PVRClientMythTV::OpenRecordedStream(const PVR_RECORDING &recinfo)
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - title: %s, ID: %s, duration: %i",__FUNCTION__,recinfo.strTitle,recinfo.strRecordingId,recinfo.iDuration);
++ CStdString id=recinfo.strRecordingId;
++ if(*id.rbegin() == '@')
++ id = id.substr(0,id.size()-1);
++ m_file=m_con.ConnectFile(m_recordings.at(id));
++ m_eventHandler.SetRecordingListener(m_file,id);
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - Done - %i",__FUNCTION__,!m_file.IsNull());
++ return !m_file.IsNull();
++}
++
++void PVRClientMythTV::CloseRecordedStream()
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s",__FUNCTION__);
++ m_file=MythFile();
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - Done",__FUNCTION__);
++}
++
++int PVRClientMythTV::ReadRecordedStream(unsigned char *pBuffer, unsigned int iBufferSize)
++{
++
++ XBMC->Log(LOG_DEBUG,"%s - curPos: %i TotalLength: %i",__FUNCTION__,(int)m_file.CurrentPosition(),(int)m_file.Duration());
++
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - size: %i",__FUNCTION__,iBufferSize);
++ int dataread=m_file.Read(pBuffer,iBufferSize);
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s: Read %i Bytes",__FUNCTION__,dataread);
++ else if(dataread==0)
++ XBMC->Log(LOG_INFO,"%s: Read 0 Bytes!",__FUNCTION__);
++ return dataread;
++}
++
++long long PVRClientMythTV::SeekRecordedStream(long long iPosition, int iWhence)
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - pos: %i, whence: %i",__FUNCTION__,iPosition,iWhence);
++ int whence=iWhence==SEEK_SET?WHENCE_SET:iWhence==SEEK_CUR?WHENCE_CUR:WHENCE_END;
++ long long retval= m_file.Seek(iPosition,whence);
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - Done - pos: %i",__FUNCTION__,retval);
++ return retval;
++}
++
++
++long long PVRClientMythTV::LengthRecordedStream()
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s",__FUNCTION__);
++ long long retval = m_file.Duration();
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - Done - duration: %i",__FUNCTION__,retval);
++ return retval;
++}
++
++
++int PVRClientMythTV::GetChannelGroupsAmount()
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s",__FUNCTION__);
++ return m_channelGroups.size();
++}
++
++PVR_ERROR PVRClientMythTV::GetChannelGroups(PVR_HANDLE handle, bool bRadio)
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - radio: %i",__FUNCTION__,bRadio);
++ PVR_CHANNEL_GROUP tag;
++ for(boost::unordered_map< CStdString, std::vector< int > >::iterator it=m_channelGroups.begin();it!=m_channelGroups.end();it++)
++ {
++ tag.strGroupName=it->first;
++ tag.bIsRadio=bRadio;
++ for(std::vector< int >::iterator it2=it->second.begin();it2!=it->second.end();it2++)
++ if(m_channels.find(*it2)!=m_channels.end()&&m_channels.at(*it2).IsRadio()==bRadio)
++ {
++ PVR->TransferChannelGroup(handle,&tag);
++ break;
++ }
++ }
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - Done",__FUNCTION__);
++ return PVR_ERROR_NO_ERROR;
++
++}
++
++PVR_ERROR PVRClientMythTV::GetChannelGroupMembers(PVR_HANDLE handle, const PVR_CHANNEL_GROUP &group)
++{
++ if(g_bExtraDebug)
++ XBMC->Log(LOG_DEBUG,"%s - group: %s",__FUNCTION__,group.strGroupName);
++ PVR_CHANNEL_GROUP_MEMBER tag;
++ int i=0;
++ for(std::vector< int >::iterator it=m_channelGroups.at(group.strGroupName).begin();it!=m_channelGroups.at(group.strGroupName).end();it++)
++ {
++ if(m_channels.find(*it)!=m_channels.end())
++ {
++ MythChannel chan=m_channels.at(*it);
++ if(group.bIsRadio==chan.IsRadio())
++ {
++ tag.iChannelNumber=i++;
++ tag.iChannelUniqueId=chan.ID();
++ tag.strGroupName=group.strGroupName;
++ PVR->TransferChannelGroupMember(handle,&tag);
++ }
++ }
++ }
++ return PVR_ERROR_NO_ERROR;
++}
++
++PVR_ERROR PVRClientMythTV::CallMenuHook(const PVR_MENUHOOK &menuhook)
++{
++ if(menuhook.iHookId == RECORDING_RULES)
++ {
++ std::map< int, MythTimer > timers=m_db.GetTimers();
++ RecordingRulesWindow wnd(timers);
++ wnd.Open();
++ }
++ return PVR_ERROR_NO_ERROR;
++}
++
++bool PVRClientMythTV::GetLiveTVPriority()
++{
++ if(!m_con.IsNull())
++ {
++ CStdString value;
++ value = m_con.GetSetting(m_con.GetHostname(),"LiveTVPriority");
++
++ if( value.compare("1")==0)
++ return true;
++ else
++ return false;
++ }
++ return false;
++}
++
++void PVRClientMythTV::SetLiveTVPriority(bool enabled)
++{
++ if(!m_con.IsNull())
++ {
++ CStdString value = enabled? "1" : "0";
++ m_con.SetSetting(m_con.GetHostname(),"LiveTVPriority",value);
++ }
++}
+\ No newline at end of file
+diff --git a/xbmc/pvrclients/mythtv-cmyth/pvrclient-mythtv.h b/xbmc/pvrclients/mythtv-cmyth/pvrclient-mythtv.h
+new file mode 100644
+index 0000000..f8e058f
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/pvrclient-mythtv.h
+@@ -0,0 +1,109 @@
++#include "cppmyth.h"
++#include "xbmc_pvr_types.h"
++#include <map>
++#include <boost/bimap.hpp>
++#include <boost/bimap/unordered_set_of.hpp>
++#include "fileOps.h"
++
++const int RECORDING_RULES = 30006;
++
++
++class RecordingRule : public MythTimer, public std::vector< std::pair< PVR_TIMER, MythProgramInfo > >
++{
++public:
++ RecordingRule(const MythTimer& timer);
++ RecordingRule& operator=(const MythTimer& t);
++ bool operator==(const int &id);
++ RecordingRule* GetParent();
++ void SetParent(RecordingRule& parent);
++ void AddModifier(RecordingRule& modifier);
++ std::vector< RecordingRule* > GetModifiers();
++ bool HasModifiers();
++ bool SameTimeslot(RecordingRule& rule);
++ void push_back(std::pair< PVR_TIMER, MythProgramInfo > &_val);
++
++private:
++ void SaveTimerString(PVR_TIMER& timer);
++ RecordingRule* m_parent;
++ std::vector< RecordingRule* > m_modifiers;
++ std::vector< boost::shared_ptr<CStdString> > m_stringStore;
++};
++
++
++class PVRClientMythTV
++{
++public:
++ PVRClientMythTV();
++ ~PVRClientMythTV();
++
++ /* Server handling */
++ bool Connect();
++ CStdString GetArtWork(FILE_OPTIONS storageGroup, CStdString shwTitle);
++ const char * GetBackendName();
++ const char * GetBackendVersion();
++ const char * GetConnectionString();
++ bool GetDriveSpace(long long *iTotal, long long *iUsed);
++ PVR_ERROR GetEPGForChannel(PVR_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd);
++ int GetNumChannels();
++ PVR_ERROR GetChannels(PVR_HANDLE handle, bool bRadio);
++ int GetRecordingsAmount(void);
++ PVR_ERROR GetRecordings(PVR_HANDLE handle);
++ PVR_ERROR DeleteRecording(const PVR_RECORDING &recording);
++ int GetTimersAmount();
++ PVR_ERROR GetTimers(PVR_HANDLE handle);
++ PVR_ERROR AddTimer(const PVR_TIMER &timer);
++ PVR_ERROR DeleteTimer(const PVR_TIMER &timer, bool bForceDelete);
++ PVR_ERROR UpdateTimer(const PVR_TIMER &timer);
++ bool OpenLiveStream(const PVR_CHANNEL &channel);
++ void CloseLiveStream();
++ int ReadLiveStream(unsigned char *pBuffer, unsigned int iBufferSize);
++ int GetCurrentClientChannel();
++ bool SwitchChannel(const PVR_CHANNEL &channelinfo);
++ //SET_SIGNAL_MONITORING_RATE ->signal
++ long long SeekLiveStream(long long iPosition, int iWhence);
++ long long LengthLiveStream();
++ PVR_ERROR SignalStatus(PVR_SIGNAL_STATUS &signalStatus);
++
++ bool OpenRecordedStream(const PVR_RECORDING &recinfo);
++ void CloseRecordedStream();
++ int ReadRecordedStream(unsigned char *pBuffer, unsigned int iBufferSize);
++ long long SeekRecordedStream(long long iPosition, int iWhence);
++ long long LengthRecordedStream();
++
++ int GetChannelGroupsAmount();
++ PVR_ERROR GetChannelGroups(PVR_HANDLE handle, bool bRadio);
++ PVR_ERROR GetChannelGroupMembers(PVR_HANDLE handle, const PVR_CHANNEL_GROUP &group);
++
++ PVR_ERROR CallMenuHook(const PVR_MENUHOOK &menuhook);
++
++ bool GetLiveTVPriority();
++ void SetLiveTVPriority(bool enabled);
++private:
++ struct mythcat{};
++ struct pvrcat{};
++ typedef boost::bimap<
++ boost::bimaps::unordered_set_of< boost::bimaps::tagged< CStdString , mythcat >,boost::hash< CStdString > >,
++ boost::bimaps::tagged< int , pvrcat >
++ > catbimap;
++
++ fileOps2 *m_fOps2_client;
++ int Genre(CStdString g);
++ CStdString Genre(int g);
++ MythConnection m_con;
++ MythEventHandler m_eventHandler;
++ MythDatabase m_db;
++ MythRecorder m_rec;
++ MythFile m_file;
++ CStdString m_protocolVersion;
++ CStdString m_connectionString;
++ time_t m_EPGstart;
++ time_t m_EPGend;
++ std::vector< MythProgram > m_EPG;
++ std::vector< RecordingRule > m_recordingRules;
++ std::map< int , MythChannel > m_channels;
++ std::map< int, std::vector< int > > m_sources;
++ boost::unordered_map< CStdString, MythProgramInfo > m_recordings;
++ boost::unordered_map< CStdString, std::vector< int > > m_channelGroups;
++ catbimap m_categoryMap;
++ void PVRtoMythTimer(const PVR_TIMER timer, MythTimer& mt);
++};
+\ No newline at end of file
+diff --git a/xbmc/pvrclients/mythtv-cmyth/recordingRules.cpp b/xbmc/pvrclients/mythtv-cmyth/recordingRules.cpp
+new file mode 100644
+index 0000000..ecc0e03
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/recordingRules.cpp
+@@ -0,0 +1,312 @@
++#include "client.h"
++#include "recordingRules.h"
++
++using namespace ADDON;
++
++#define BUTTON_START 5
++#define BUTTON_BACK 32
++#define BUTTON_CANCEL 7
++#define HEADER_LABEL 8
++
++#define RECRULE_NEWRULE 0
++#define RECRULE_EDIT 1
++#define RECRULE_ENABLE 2
++#define RECRULE_DELETE 3
++
++RecordingRulesWindow::RecordingRulesWindow(std::map< int, MythTimer > &recordingRules)
++ :m_window(NULL),m_list(NULL),m_recRules(recordingRules)
++{
++
++}
++
++RecordingRulesWindow::~RecordingRulesWindow()
++{
++
++}
++
++bool RecordingRulesWindow::Open()
++{
++ m_window = GUI->Window_create("RecordingRules.xml", "Confluence", false, true);
++ //m_window = GUI->Window_create("test.xml", "Confluence", false, true);
++ m_window->m_cbhdl = this;
++ m_window->CBOnInit = OnInitCB;
++ m_window->CBOnFocus = OnFocusCB;
++ m_window->CBOnClick = OnClickCB;
++ m_window->CBOnAction= OnActionCB;
++ m_window->CBOnContextMenu = OnContextMenuCB;
++ m_window->DoModal();
++
++ GUI->Window_destroy(m_window);
++
++ return true;
++}
++
++
++bool RecordingRulesWindow::OnAction(int actionId)
++{
++ if (actionId == ADDON_ACTION_CLOSE_DIALOG || actionId == ADDON_ACTION_PREVIOUS_MENU)
++ OnClick(BUTTON_BACK);
++
++ return true;
++}
++
++bool RecordingRulesWindow::OnInitCB(GUIHANDLE cbhdl)
++{
++ RecordingRulesWindow* window = static_cast<RecordingRulesWindow*>(cbhdl);
++ return window->OnInit();
++}
++
++bool RecordingRulesWindow::OnClickCB(GUIHANDLE cbhdl, int controlId)
++{
++ RecordingRulesWindow* window = static_cast<RecordingRulesWindow*>(cbhdl);
++ return window->OnClick(controlId);
++}
++
++bool RecordingRulesWindow::OnFocusCB(GUIHANDLE cbhdl, int controlId)
++{
++ RecordingRulesWindow* window = static_cast<RecordingRulesWindow*>(cbhdl);
++ return window->OnFocus(controlId);
++}
++
++bool RecordingRulesWindow::OnActionCB(GUIHANDLE cbhdl, int actionId)
++{
++ RecordingRulesWindow* window = static_cast<RecordingRulesWindow*>(cbhdl);
++ return window->OnAction(actionId);
++}
++
++bool RecordingRulesWindow::OnContextMenuCB(GUIHANDLE cbhdl,int controlId,int itemNumber, unsigned int contextButtonId)
++{
++ RecordingRulesWindow* window = static_cast<RecordingRulesWindow*>(cbhdl);
++ return window->OnContextMenu(controlId, itemNumber, contextButtonId);
++}
++bool RecordingRulesWindow::OnClick(int controlId)
++{
++ if (controlId == BUTTON_BACK)
++ {
++ m_window->Close();
++
++ }
++ if (controlId == 14 )
++ {
++ int l=m_list->GetSelected();
++ XBMC->Log(LOG_DEBUG,"CLP: %i",l);
++ }
++ return true;
++}
++
++bool RecordingRulesWindow::OnFocus(int controlId)
++{
++ return true;
++}
++
++bool RecordingRulesWindow::OnInit()
++{
++
++ m_list = GUI->Control_getListContainer(m_window,14);
++
++ AddonListItemPtr listItem(GUI->ListItem_create("Add rule...","","","",""));
++ listItem->SetProperty("time","Add rule...");
++ listItem->SetProperty("ContextMenuOverride","1401");
++ m_window->AddContextMenuButton(1401,RECRULE_NEWRULE,"New Rule");
++ m_list->AddItem(listItem.get());
++ for(std::map< int, MythTimer >::iterator it=m_recRules.begin();it!=m_recRules.end();it++)
++ m_list->AddItem(AddRecordingRule(it->second).get());
++ if(m_recRules.size())
++ {
++ //m_window->AddContextMenuButton(1401,RECRULE_SORTNAME,"Sort by: Name");
++ //m_window->AddContextMenuButton(1401,RECRULE_SORTDATE,"Sort by: Name");
++ }
++ m_window->AddContextMenuButton(14,RECRULE_NEWRULE,"New Rule");
++ m_window->AddContextMenuButton(14,RECRULE_EDIT,"Edit Rule");
++ m_window->AddContextMenuButton(14,RECRULE_ENABLE,"Rule enabled");
++ m_window->AddContextMenuButton(14,RECRULE_DELETE,"Delete");
++ return true;
++}
++
++bool RecordingRulesWindow::OnContextMenu(int controlId,int itemNumber, unsigned int contextButtonId)
++{
++ if(controlId == 14 || controlId == 1401)
++ {
++ CAddonListItem* listItem = m_list->GetItem(itemNumber);
++ CStdString t = m_recRules.at(atoi(listItem->GetLabel())).Title(false);
++ switch(contextButtonId)
++ {
++ case RECRULE_NEWRULE:
++ XBMC->Log(LOG_DEBUG,"Item number: %i selected. Item title: %s. Button new rule pressed",itemNumber,t.c_str());
++ break;
++ case RECRULE_EDIT:
++ XBMC->Log(LOG_DEBUG,"Item number: %i selected. Button edit rule pressed",itemNumber);
++ break;
++ case RECRULE_ENABLE:
++ XBMC->Log(LOG_DEBUG,"Item number: %i selected. Button enable rule pressed",itemNumber);
++ break;
++ case RECRULE_DELETE:
++ XBMC->Log(LOG_DEBUG,"Item number: %i selected. Button delete rule pressed",itemNumber);
++ break;
++ }
++ delete listItem;
++ }
++
++ return true;
++}
++
++CStdString DayToString(int day)
++{
++ switch(day)
++ {
++ case 0:
++ return XBMC->GetLocalizedString(19149);
++ case 1:
++ return XBMC->GetLocalizedString(19150);
++ case 2:
++ return XBMC->GetLocalizedString(19151);
++ case 3:
++ return XBMC->GetLocalizedString(19152);
++ case 4:
++ return XBMC->GetLocalizedString(19153);
++ case 5:
++ return XBMC->GetLocalizedString(19154);
++ case 6:
++ return XBMC->GetLocalizedString(19155);
++ }
++ return CStdString("");
++}
++
++
++AddonListItemPtr RecordingRulesWindow::AddRecordingRule(MythTimer &rule)
++{
++ CStdString path;
++ CStdString id;
++ CStdString channel;
++ CStdString time;
++ CStdString search;
++ CStdString status("Enabled");
++ id.Format("%i",rule.RecordID());
++ path.Format("pvr://recordingrules/%i",rule.RecordID());
++ AddonListItemPtr listItem(GUI->ListItem_create(id.c_str(),"","","",path.c_str()));
++ if(rule.Type() == MythTimer::AllRecord || rule.ChanID()==0)
++ channel = "Any";
++ else
++ channel = rule.Callsign(); //TODO: Chan name instead??
++
++ time_t starttime = rule.StartTime();
++ tm *lc = localtime(&starttime);
++ int shift = lc->tm_wday? lc->tm_wday-1 : 6;//Monday is the first day
++
++ switch( rule.Type())
++ {
++
++ case MythTimer::DontRecord:
++ case MythTimer::OverrideRecord:
++ case MythTimer::SingleRecord:
++ time.Format("%s %s %s %s",
++ DayToString(shift),
++ XBMC->GetLocalizedDate(rule.StartTime(),false,true),
++ XBMC->GetLocalizedString(19159),
++ XBMC->GetLocalizedTime(rule.StartTime(), false)
++ );
++
++ if(rule.SearchType() == MythTimer::ManualSearch )
++ {
++ time.Format("%s %s %s",time,
++ XBMC->GetLocalizedString(19160),
++ XBMC->GetLocalizedTime(rule.EndTime(), false));
++ }
++ break;
++ case MythTimer::FindOneRecord:
++ time="Once";
++ break;
++ case MythTimer::WeekslotRecord:
++ time.Format("%s %s %s %s %s",
++ "Every ",
++ DayToString(shift),
++ XBMC->GetLocalizedDate(rule.StartTime(),false,true),
++ XBMC->GetLocalizedString(19159),
++ XBMC->GetLocalizedTime(rule.StartTime(), false)
++ );
++ if(rule.SearchType() == MythTimer::ManualSearch )
++ {
++ time.Format("%s %s %s",time,
++ XBMC->GetLocalizedString(19160),
++ XBMC->GetLocalizedTime(rule.EndTime(), false));
++ }
++ break;
++ case MythTimer::FindWeeklyRecord:
++ time="Once every week";
++ break;
++ case MythTimer::TimeslotRecord:
++ time.Format("%s %s %s",
++ "Daily ",
++ XBMC->GetLocalizedString(19159),
++ XBMC->GetLocalizedTime(rule.StartTime(), false)
++ );
++ if(rule.SearchType() == MythTimer::ManualSearch )
++ {
++ time.Format("%s %s %s",time,
++ XBMC->GetLocalizedString(19160),
++ XBMC->GetLocalizedTime(rule.EndTime(), false));
++ }
++ break;
++ break;
++ case MythTimer::FindDailyRecord:
++ time="Once daily";
++ break;
++ case MythTimer::ChannelRecord:
++ case MythTimer::AllRecord:
++ time="Each episode";
++ break;
++
++
++ }
++ switch( rule.SearchType() )
++ {
++ case MythTimer::NoSearch:
++ search.Format("%s",
++ rule.Title(false));
++ break;
++ case MythTimer::TitleSearch:
++ search.Format("%s %s %s",
++ XBMC->GetLocalizedString(369),//Title
++ ": ",
++ rule.Description()
++ );
++ break;
++ case MythTimer::KeywordSearch:
++ search.Format("%s %s %s",
++ XBMC->GetLocalizedString(21861), //keywords
++ ": ",
++ rule.Description()
++ );
++ break;
++ case MythTimer::PeopleSearch:
++ search.Format("%s %s %s",
++ XBMC->GetLocalizedString(21861),//Actors: 21861
++ ": ",
++ rule.Description()
++ );
++ break;
++ case MythTimer::PowerSearch:
++ search.Format("%s %s %s %s",
++ "Powersearch: SELECT FROM ",
++ rule.Subtitle(),
++ " WHERE ",
++ rule.Description());
++
++ break;
++ }
++ if(rule.Inactive())
++ status = "Disabled";
++ else if(rule.Type() == MythTimer::DontRecord)
++ status = "Don't record";
++ else if(rule.Type() == MythTimer::OverrideRecord)
++ status = "Override";
++ //remove newlines
++ search.Replace("\n"," ");
++ listItem->SetProperty("channel",channel.c_str());
++ listItem->SetProperty("time",time.c_str());
++ listItem->SetProperty("search",search.c_str());
++ listItem->SetProperty("status",status.c_str());
++
++ return listItem;
++}
++
+diff --git a/xbmc/pvrclients/mythtv-cmyth/recordingRules.h b/xbmc/pvrclients/mythtv-cmyth/recordingRules.h
+new file mode 100644
+index 0000000..c3e9ffd
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/recordingRules.h
+@@ -0,0 +1,35 @@
++#include "client.h"
++#include "cppmyth/MythTimer.h"
++#include <boost/shared_ptr.hpp>
++#include <string>
++#include <map>
++
++typedef boost::shared_ptr< CAddonListItem > AddonListItemPtr;
++
++class RecordingRulesWindow
++{
++public:
++
++ RecordingRulesWindow(std::map< int, MythTimer > &recordingRules);
++ ~RecordingRulesWindow();
++
++ bool Open();
++
++ bool OnClick(int controlId);
++ bool OnFocus(int controlId);
++ bool OnInit();
++ bool OnAction(int actionId);
++ bool OnContextMenu(int controlId,int itemNumber, unsigned int contextButtonId);
++
++ static bool OnClickCB(GUIHANDLE cbhdl, int controlId);
++ static bool OnFocusCB(GUIHANDLE cbhdl, int controlId);
++ static bool OnInitCB(GUIHANDLE cbhdl);
++ static bool OnActionCB(GUIHANDLE cbhdl, int actionId);
++ static bool OnContextMenuCB(GUIHANDLE cbhdl,int controlId,int itemNumber, unsigned int contextButtonId);
++
++private:
++ CAddonGUIWindow *m_window;
++ CAddonGUIListContainer *m_list;
++ std::map< int, MythTimer > m_recRules;
++ AddonListItemPtr AddRecordingRule(MythTimer &rule);
++};
+\ No newline at end of file
+diff --git a/xbmc/pvrclients/mythtv-cmyth/tools.h b/xbmc/pvrclients/mythtv-cmyth/tools.h
+new file mode 100644
+index 0000000..f973d4a
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv-cmyth/tools.h
+@@ -0,0 +1,99 @@
++/*
++ * Copyright (C) 2005-2009 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#ifndef __TOOLS_H
++#define __TOOLS_H
++/*
++#if defined(_WIN32) || defined(_WIN64)
++#ifndef __WINDOWS__
++#define __WINDOWS__
++#endif
++#endif
++#include "libcmyth.h"
++extern CHelper_libcmyth *CMYTH;
++#include "utils/StdString.h"
++#include <errno.h>
++#include <fcntl.h>
++#include <stddef.h>
++#include <stdint.h>
++#include <stdio.h>
++#include <stdlib.h>*/
++#ifndef __WINDOWS__
++#include <time.h>
++#endif
++/*
++#define ERRNUL(e) {errno=e;return 0;}
++#define ERRSYS(e) {errno=e;return -1;}
++
++#define SECSINDAY 86400
++
++#define KILOBYTE(n) ((n) * 1024)
++#define MEGABYTE(n) ((n) * 1024LL * 1024LL)
++
++#define MALLOC(type, size) (type *)malloc(sizeof(type) * (size))
++
++#define DELETENULL(p) (delete (p), p = NULL)
++//
++//#define CHECK(s) { if ((s) < 0) LOG_ERROR; } // used for 'ioctl()' calls
++#define FATALERRNO (errno && errno != EAGAIN && errno != EINTR)
++
++class cTimeMs
++{
++private:
++ uint64_t begin;
++public:
++ cTimeMs(int Ms = 0);
++ ///< Creates a timer with ms resolution and an initial timeout of Ms.
++ static uint64_t Now(void);
++ void Set(int Ms = 0);
++ bool TimedOut(void);
++ uint64_t Elapsed(void);
++};
++
++void inline cSleep(const int ms)
++{
++#if defined(__WINDOWS__)
++ Sleep(ms);
++#else
++ struct timespec timeOut,remains;
++ timeOut.tv_sec = ms/1000;
++ timeOut.tv_nsec = (ms-timeOut.tv_sec*1000)*1000000;
++ while(EINTR==nanosleep(&timeOut, &remains))
++ {
++ timeOut=remains;
++ }
++#endif
++}*/
++
++int inline daytime(time_t *time)
++{
++ struct tm* ptm = localtime( time );
++ int retval = ptm->tm_sec*60+ptm->tm_min*60+ptm->tm_hour;
++ return retval;
++}
++
++int inline weekday(time_t *time)
++{
++ struct tm* ptm = localtime( time );
++ int retval = ptm->tm_wday;
++ return retval;
++}
++#endif //__TOOLS_H
+diff --git a/xbmc/pvrclients/mythtv/Makefile.in b/xbmc/pvrclients/mythtv/Makefile.in
+new file mode 100644
+index 0000000..5d02ff2
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv/Makefile.in
+@@ -0,0 +1,25 @@
++#
++# Makefile for the XBMC MythTV PVR AddOn
++#
++# See the README for copyright information and
++# how to reach the author.
++#
++
++LIBS = libmythxml/libmythxml.a
++LIBDIR = @abs_top_srcdir@/addons/pvr.mythtv
++LIB = @abs_top_srcdir@/addons/pvr.mythtv/XBMC_Mythtv.pvr
++
++SRCS=client.cpp \
++ MythXml.cpp
++
++include ../Makefile.include
++
++clean:
++ -rm -f $(OBJS) $(LIB) *.P *~
++ $(MAKE) -C libmythxml clean
++
++INCLUDES += -I../../linux
++
++$(LIB): $(OBJS)
++ $(MAKE) -C libmythxml
++ $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared -g $(OBJS) $(LIBS) $(LIBDIRS) $(SILIB) -o $(LIB)
+diff --git a/xbmc/pvrclients/mythtv/MythXml.cpp b/xbmc/pvrclients/mythtv/MythXml.cpp
+new file mode 100644
+index 0000000..444be1e
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv/MythXml.cpp
+@@ -0,0 +1,144 @@
++/*
++ * MythXml.cpp
++ *
++ * Created on: Oct 7, 2010
++ * Author: tafypz
++ */
++
++#include "MythXml.h"
++
++#include "filesystem/FileCurl.h"
++#include "utils/log.h"
++
++#include "libmythxml/GetNumChannelsParameters.h"
++#include "libmythxml/GetNumChannelsResult.h"
++#include "libmythxml/GetNumChannelsCommand.h"
++#include "libmythxml/GetChannelListCommand.h"
++#include "libmythxml/GetChannelListParameters.h"
++#include "libmythxml/GetChannelListResult.h"
++#include "libmythxml/GetProgramGuideParameters.h"
++#include "libmythxml/GetProgramGuideResult.h"
++#include "libmythxml/GetProgramGuideCommand.h"
++
++
++using namespace XFILE;
++
++MythXml::MythXml() {
++ hostname_ = "";
++ port_ = -1;
++ pin_ = -1;
++ timeout_ = -1;
++}
++
++MythXml::~MythXml() {
++}
++
++void MythXml::init(){
++}
++
++void MythXml::cleanup(){
++}
++
++bool MythXml::open(CStdString hostname, int port, CStdString user, CStdString pass, int pin, long timeout){
++ hostname_ = hostname;
++ port_ = port;
++ timeout_ = timeout;
++ pin_ = pin;
++ CStdString strUrl;
++ strUrl.Format("http://%s:%i/Myth/GetConnectionInfo?Pin=%i", hostname.c_str(), port, pin);
++ CStdString strXML;
++
++ CFileCurl http;
++
++ http.SetTimeout(timeout);
++ if(!http.Get(strUrl, strXML)){
++ CLog::Log(LOGDEBUG, "MythXml - Could not open connection to mythtv backend.");
++ http.Cancel();
++ return false;
++ }
++ http.Cancel();
++ return true;
++}
++
++int MythXml::getNumChannels(){
++ if(!checkConnection())
++ return 0;
++ GetNumChannelsCommand cmd;
++ GetNumChannelsParameters params;
++ GetNumChannelsResult result;
++ cmd.execute(hostname_, port_, params, result, timeout_);
++ return result.getNumberOfChannels();
++}
++
++PVR_ERROR MythXml::requestChannelList(PVR_HANDLE handle, bool bRadio){
++ if(!checkConnection())
++ return PVR_ERROR_SERVER_ERROR;
++ GetChannelListCommand cmd;
++ GetChannelListParameters params;
++ GetChannelListResult result;
++ cmd.execute(hostname_, port_, params, result, timeout_);
++
++ if(!result.isSuccess())
++ return PVR_ERROR_UNKNOWN;
++
++ const vector<SChannel>& channellist = result.getChannels();
++ vector<SChannel>::const_iterator it;
++ PVR_CHANNEL tag;
++ for( it = channellist.begin(); it != channellist.end(); ++it){
++ const SChannel& channel = *it;
++ memset(&tag, 0 , sizeof(tag));
++ tag.iUniqueId = channel.id;
++ tag.iChannelNumber = channel.id;
++ tag.strChannelName = channel.name.c_str();
++// tag.callsign = channel.callsign.c_str();;
++ tag.bIsRadio = false;
++ tag.strInputFormat = "";
++ tag.strStreamURL = "";
++
++ PVR->TransferChannelEntry(handle, &tag);
++ }
++ return PVR_ERROR_NO_ERROR;
++}
++
++PVR_ERROR MythXml::requestEPGForChannel(PVR_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd){
++ if(!checkConnection())
++ return PVR_ERROR_SERVER_ERROR;
++ GetProgramGuideCommand cmd;
++ GetProgramGuideParameters params(channel.iUniqueId, CDateTime(iStart), CDateTime(iEnd), true);
++ GetProgramGuideResult result;
++
++ cmd.execute(hostname_, port_, params, result, timeout_);
++
++ if(!result.isSuccess())
++ return PVR_ERROR_UNKNOWN;
++
++ EPG_TAG guideItem;
++ const vector<SEpg>& epgInfo = result.getEpg();
++ vector<SEpg>::const_iterator it;
++ for( it = epgInfo.begin(); it != epgInfo.end(); ++it)
++ {
++ const SEpg& epg = *it;
++ time_t itemStart;
++ time_t itemEnd;
++ epg.start_time.GetAsTime(itemStart);
++ epg.end_time.GetAsTime(itemEnd);
++
++ guideItem.iChannelNumber = epg.chan_num;
++ guideItem.iUniqueBroadcastId = epg.id;
++ guideItem.strTitle = epg.title;
++ guideItem.strPlotOutline = epg.subtitle;
++ guideItem.strPlot = epg.description;
++ guideItem.iGenreType = epg.genre_type;
++ guideItem.iGenreSubType = epg.genre_subtype;
++ guideItem.strGenreDescription = "";
++ guideItem.iParentalRating = epg.parental_rating;
++ guideItem.startTime = itemStart;
++ guideItem.endTime = itemEnd;
++ PVR->TransferEpgEntry(handle, &guideItem);
++ }
++ return PVR_ERROR_NO_ERROR;
++}
++
++bool MythXml::checkConnection(){
++ return true;
++}
+diff --git a/xbmc/pvrclients/mythtv/MythXml.h b/xbmc/pvrclients/mythtv/MythXml.h
+new file mode 100644
+index 0000000..f47b744
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv/MythXml.h
+@@ -0,0 +1,34 @@
++/*
++ * MythXml.h
++ *
++ * Created on: Oct 7, 2010
++ * Author: tafypz
++ */
++
++#ifndef XBMC_PVRCLIENTS_MYTHTV_MYTHXML_H_
++#define XBMC_PVRCLIENTS_MYTHTV_MYTHXML_H_
++
++#include "client.h"
++
++/*! \class MythXml
++ \brief Acts as the glue between the PVR Addon world and the mythXML world.
++ */
++class MythXml {
++public:
++ MythXml();
++ virtual ~MythXml();
++ void init();
++ void cleanup();
++ bool open(CStdString hostname, int port, CStdString user, CStdString pass, int pin, long timeout);
++ int getNumChannels();
++ PVR_ERROR requestChannelList(PVR_HANDLE handle, bool bRadio);
++ PVR_ERROR requestEPGForChannel(PVR_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd);
++private:
++ bool checkConnection();
++ CStdString hostname_;
++ int port_;
++ int timeout_;
++ int pin_;
++};
++
++#endif /* XBMC_PVRCLIENTS_MYTHTV_MYTHXML_H_ */
+diff --git a/xbmc/pvrclients/mythtv/client.cpp b/xbmc/pvrclients/mythtv/client.cpp
+new file mode 100644
+index 0000000..95f7c32
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv/client.cpp
+@@ -0,0 +1,482 @@
++/*
++ * Copyright (C) 2005-2009 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "client.h"
++#include "xbmc_pvr_dll.h"
++#include "MythXml.h"
++
++using namespace std;
++using namespace ADDON;
++
++#define SEEK_POSSIBLE 0x10 // flag used to check if protocol allows seeks
++
++/* User adjustable settings are saved here.
++ * Default values are defined inside client.h
++ * and exported to the other source files.
++ */
++CStdString g_szHostname = DEFAULT_HOST; ///< The Host name or IP of MythTV
++int g_iMythXmlPort = DEFAULT_MYTHXML_PORT; ///< The MyhtXML Port of MythTV (default is 6544)
++int g_iPin = DEFAULT_PIN; ///< The Mythtv server PIN (default is 0000)
++int g_iMythXmlConnectTimeout = DEFAULT_MYTHXML_CONNECTION_TIMEOUT; ///< The MYTHXML Connection Timeout value (default is 30 seconds)
++///* Client member variables */
++
++bool m_recordingFirstRead;
++char m_noSignalStreamData[ 6 + 0xffff ];
++long m_noSignalStreamSize = 0;
++long m_noSignalStreamReadPos = 0;
++bool m_bPlayingNoSignal = false;
++int m_iCurrentChannel = 1;
++ADDON_STATUS m_CurStatus = ADDON_STATUS_UNKNOWN;
++bool g_bCreated = false;
++int g_iClientID = -1;
++CStdString g_szUserPath = "";
++CStdString g_szClientPath = "";
++MythXml *MythXmlApi = NULL;
++CHelper_libXBMC_addon *XBMC = NULL;
++CHelper_libXBMC_pvr *PVR = NULL;
++
++
++extern "C" {
++
++/***********************************************************
++ * Standard AddOn related public library functions
++ ***********************************************************/
++
++ADDON_STATUS ADDON_Create(void* hdl, void* props)
++{
++ if (!props)
++ return ADDON_STATUS_UNKNOWN;
++
++ PVR_PROPERTIES* pvrprops = (PVR_PROPERTIES*)props;
++
++ XBMC = new CHelper_libXBMC_addon;
++ if (!XBMC->RegisterMe(hdl))
++ return ADDON_STATUS_UNKNOWN;
++
++ PVR = new CHelper_libXBMC_pvr;
++ if (!PVR->RegisterMe(hdl))
++ return ADDON_STATUS_UNKNOWN;
++
++ XBMC->Log(LOG_DEBUG, "Creating MythTV PVR-Client");
++
++ m_CurStatus = ADDON_STATUS_UNKNOWN;
++ g_iClientID = pvrprops->iClientId;
++ g_szUserPath = pvrprops->strUserPath;
++ g_szClientPath = pvrprops->strClientPath;
++
++ /* Read setting "host" from settings.xml */
++ char * buffer;
++ buffer = (char*) malloc (1024);
++ buffer[0] = 0; /* Set the end of string */
++
++ if (XBMC->GetSetting("host", buffer))
++ g_szHostname = buffer;
++ else
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'host' setting, falling back to '%s' as default", DEFAULT_HOST);
++ g_szHostname = DEFAULT_HOST;
++ }
++ free (buffer);
++
++ /* Read setting "port" from settings.xml */
++ if (!XBMC->GetSetting("mythXMLPort", &g_iMythXmlPort))
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'mythXMLPort' setting, falling back to '%i' as default", DEFAULT_MYTHXML_PORT);
++ g_iMythXmlPort = DEFAULT_MYTHXML_PORT;
++ }
++
++ /* Read setting "pin" from settings.xml */
++ if (!XBMC->GetSetting("mythXMLTimeout", &g_iMythXmlConnectTimeout))
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'mythXMLTimeout' setting, falling back to '%i' as default", DEFAULT_MYTHXML_CONNECTION_TIMEOUT);
++ g_iMythXmlConnectTimeout = DEFAULT_MYTHXML_CONNECTION_TIMEOUT;
++ } else {
++ // we need to multiply by 1000 the value as the settings file is in seconds
++ g_iMythXmlConnectTimeout *= 1000;
++ }
++
++ /* Read setting "pin" from settings.xml */
++ if (!XBMC->GetSetting("pin", &g_iPin))
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'pin' setting, falling back to '%i' as default", DEFAULT_PIN);
++ g_iPin = DEFAULT_PIN;
++ }
++
++ MythXmlApi = new MythXml();
++ if (!MythXmlApi->open(g_szHostname, g_iMythXmlPort, "", "", g_iPin, g_iMythXmlConnectTimeout))
++ {
++ m_CurStatus = ADDON_STATUS_LOST_CONNECTION;
++ return m_CurStatus;
++ }
++
++ m_CurStatus = ADDON_STATUS_OK;
++
++ g_bCreated = true;
++ return m_CurStatus;
++}
++
++void ADDON_Destroy()
++{
++ if (g_bCreated)
++ {
++ delete MythXmlApi;
++ MythXmlApi = NULL;
++ g_bCreated = false;
++ }
++ m_CurStatus = ADDON_STATUS_UNKNOWN;
++}
++
++ADDON_STATUS ADDON_GetStatus()
++{
++ return m_CurStatus;
++}
++
++bool ADDON_HasSettings()
++{
++ return true;
++}
++
++unsigned int ADDON_GetSettings(ADDON_StructSetting ***sSet)
++{
++ return 0;
++}
++
++ADDON_STATUS ADDON_SetSetting(const char *settingName, const void *settingValue)
++{
++ string str = settingName;
++ if (str == "host")
++ {
++ string tmp_sHostname;
++ XBMC->Log(LOG_INFO, "Changed Setting 'host' from %s to %s", g_szHostname.c_str(), (const char*) settingValue);
++ tmp_sHostname = g_szHostname;
++ g_szHostname = (const char*) settingValue;
++ if (tmp_sHostname != g_szHostname)
++ return ADDON_STATUS_NEED_RESTART;
++ }
++ else if (str == "mythXMLPort")
++ {
++ XBMC->Log(LOG_INFO, "Changed Setting 'port' from %u to %u", g_iMythXmlPort, *(int*) settingValue);
++ if (g_iMythXmlPort != *(int*) settingValue)
++ {
++ g_iMythXmlPort = *(int*) settingValue;
++ return ADDON_STATUS_NEED_RESTART;
++ }
++ }
++ else if (str == "mythXMLTimeout")
++ {
++ XBMC->Log(LOG_INFO, "Changed Setting 'mythXMLTimeout' from %u to %u", g_iMythXmlConnectTimeout, *(int*) settingValue);
++ if (g_iMythXmlConnectTimeout / 1000 != *(int*) settingValue)
++ {
++ g_iMythXmlConnectTimeout = *(int*) settingValue;
++ g_iMythXmlConnectTimeout *= 1000;
++ return ADDON_STATUS_NEED_RESTART;
++ }
++ }
++ else if (str == "pin")
++ {
++ XBMC->Log(LOG_INFO, "Changed Setting 'pin' from %u to %u", g_iPin, *(int*) settingValue);
++ if (g_iPin != *(int*) settingValue)
++ {
++ g_iPin = *(int*) settingValue;
++ return ADDON_STATUS_NEED_RESTART;
++ }
++ }
++ return ADDON_STATUS_OK;
++}
++
++void ADDON_Stop()
++{
++ return;
++}
++
++void ADDON_FreeSettings()
++{
++ return;
++}
++
++
++/***********************************************************
++ * PVR Client AddOn specific public library functions
++ ***********************************************************/
++
++PVR_ERROR GetAddonCapabilities(PVR_ADDON_CAPABILITIES *pCapabilities)
++{
++ pCapabilities->bSupportsTimeshift = false;
++ pCapabilities->bSupportsEPG = true;
++ pCapabilities->bSupportsRecordings = false;
++ pCapabilities->bSupportsTimers = false;
++ pCapabilities->bSupportsTV = false;
++ pCapabilities->bSupportsRadio = false;
++ pCapabilities->bSupportsChannelSettings = false;
++ pCapabilities->bSupportsChannelGroups = false;
++ pCapabilities->bHandlesInputStream = false;
++ pCapabilities->bHandlesDemuxing = false;
++ pCapabilities->bSupportsChannelScan = false;
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++PVR_ERROR GetStreamProperties(PVR_STREAM_PROPERTIES* props)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++const char * GetBackendName()
++{
++ return "";
++}
++
++const char * GetBackendVersion()
++{
++ return "";
++}
++
++const char * GetConnectionString()
++{
++ return "";
++}
++
++PVR_ERROR GetDriveSpace(long long *iTotal, long long *iUsed)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++PVR_ERROR GetBackendTime(time_t *localTime, int *gmtOffset)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++PVR_ERROR DialogChannelScan()
++{
++ return PVR_ERROR_NOT_POSSIBLE;
++}
++
++PVR_ERROR CallMenuHook(const PVR_MENUHOOK &menuhook)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++/*******************************************/
++/** PVR EPG Functions **/
++
++PVR_ERROR GetEPGForChannel(PVR_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd)
++{
++ if (MythXmlApi == NULL)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return MythXmlApi->requestEPGForChannel(handle, channel, iStart, iEnd);
++}
++
++/*******************************************/
++/** PVR Channel Functions **/
++
++int GetChannelsAmount(void)
++{
++ if (MythXmlApi == NULL)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return MythXmlApi->getNumChannels();
++}
++
++PVR_ERROR GetChannels(PVR_HANDLE handle, bool bRadio)
++{
++ if (MythXmlApi == NULL)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return MythXmlApi->requestChannelList(handle, bRadio);
++}
++
++int GetChannelGroupsAmount(void)
++{
++ return 0;
++}
++
++PVR_ERROR GetChannelGroups(PVR_HANDLE handle, bool bRadio)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++PVR_ERROR GetChannelGroupMembers(PVR_HANDLE handle, const PVR_CHANNEL_GROUP &group)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++PVR_ERROR DeleteChannel(const PVR_CHANNEL &channel)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++PVR_ERROR RenameChannel(const PVR_CHANNEL &channel)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++PVR_ERROR MoveChannel(const PVR_CHANNEL &channel)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++PVR_ERROR DialogChannelSettings(const PVR_CHANNEL &channel)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++PVR_ERROR DialogAddChannel(const PVR_CHANNEL &channel)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++
++/*******************************************/
++/** PVR Recording Functions **/
++
++int GetRecordingsAmount(void)
++{
++ return 0;
++}
++
++PVR_ERROR GetRecordings(PVR_HANDLE handle)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++PVR_ERROR DeleteRecording(const PVR_RECORDING &recording)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++PVR_ERROR RenameRecording(const PVR_RECORDING &recording)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++/*******************************************/
++/** PVR Timer Functions **/
++
++int GetTimersAmount(void)
++{
++ return 0;
++}
++
++PVR_ERROR GetTimers(PVR_HANDLE handle)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++PVR_ERROR AddTimer(const PVR_TIMER &timer)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++PVR_ERROR DeleteTimer(const PVR_TIMER &timer, bool bForceDelete)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++PVR_ERROR UpdateTimer(const PVR_TIMER &timer)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++
++/*******************************************/
++/** PVR Live Stream Functions **/
++
++bool OpenLiveStream(const PVR_CHANNEL &channel)
++{
++ return false;
++}
++
++void CloseLiveStream(void)
++{
++ return;
++}
++
++int ReadLiveStream(unsigned char *pBuffer, unsigned int iBufferSize)
++{
++ return -1;
++}
++
++int GetCurrentClientChannel()
++{
++ return m_iCurrentChannel;
++}
++
++bool SwitchChannel(const PVR_CHANNEL &channelinfo)
++{
++ return false;
++}
++
++PVR_ERROR SignalStatus(PVR_SIGNAL_STATUS &signalStatus)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++
++/*******************************************/
++/** PVR Recording Stream Functions **/
++
++bool OpenRecordedStream(const PVR_RECORDING &recinfo)
++{
++ return false;
++}
++
++void CloseRecordedStream(void)
++{
++ return;
++}
++
++int ReadRecordedStream(unsigned char *pBuffer, unsigned int iBufferSize)
++{
++ return 0;
++}
++
++long long SeekRecordedStream(long long iPosition, int iWhence)
++{
++ return -1;
++}
++
++long long PositionRecordedStream(void)
++{
++ return -1;
++}
++
++long long LengthRecordedStream(void)
++{
++ return 0;
++}
++
++
++/** UNUSED API FUNCTIONS */
++DemuxPacket* DemuxRead() { return NULL; }
++void DemuxAbort() {}
++void DemuxReset() {}
++void DemuxFlush() {}
++long long SeekLiveStream(long long iPosition, int iWhence) { return -1; }
++long long PositionLiveStream(void) { return -1; }
++long long LengthLiveStream(void) { return -1; }
++const char * GetLiveStreamURL(const PVR_CHANNEL &channelinfo) { return ""; }
++
++} //end extern "C"
+diff --git a/xbmc/pvrclients/mythtv/client.h b/xbmc/pvrclients/mythtv/client.h
+new file mode 100644
+index 0000000..6ee071c
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv/client.h
+@@ -0,0 +1,48 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2009 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#ifndef CLIENT_H
++#define CLIENT_H
++
++#include "utils/StdString.h"
++#include "../../../addons/library.xbmc.addon/libXBMC_addon.h"
++#include "../../../addons/library.xbmc.pvr/libXBMC_pvr.h"
++
++#define DEFAULT_HOST "127.0.0.1"
++#define DEFAULT_MYTHXML_PORT 6544
++#define DEFAULT_PIN 0000
++#define DEFAULT_MYTHXML_CONNECTION_TIMEOUT 30000
++
++extern bool g_bCreated; ///< Shows that the Create function was successfully called
++extern int g_iClientID; ///< The PVR client ID used by XBMC for this driver
++extern CStdString g_szUserPath; ///< The Path to the user directory inside user profile
++extern CStdString g_szClientPath; ///< The Path where this driver is located
++
++/* Client Settings */
++extern CStdString g_szHostname; ///< The Host name or IP of the mythtv server
++extern int g_iMythXmlPort; ///< The MYTHXML Port (default is 6544)
++extern int g_iPin; ///< The Mythtv server PIN (default is 0000)
++
++extern ADDON::CHelper_libXBMC_addon *XBMC;
++extern CHelper_libXBMC_pvr *PVR;
++
++#endif /* CLIENT_H */
+diff --git a/xbmc/pvrclients/mythtv/libmythxml/GetChannelListCommand.h b/xbmc/pvrclients/mythtv/libmythxml/GetChannelListCommand.h
+new file mode 100644
+index 0000000..541b26a
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv/libmythxml/GetChannelListCommand.h
+@@ -0,0 +1,25 @@
++/*
++ * GetChannelListCommand.h
++ *
++ * Created on: Oct 7, 2010
++ * Author: tafypz
++ */
++
++#ifndef XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETCHANNELLISTCOMMAND_H_
++#define XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETCHANNELLISTCOMMAND_H_
++
++#include "MythXmlCommand.h"
++
++class GetChannelListCommand: public MythXmlCommand {
++
++public:
++ GetChannelListCommand() {};
++ virtual ~GetChannelListCommand() {};
++protected:
++ virtual const CStdString& getCommand() const {
++ static CStdString result = "GetProgramGuide";
++ return result;
++ };
++};
++
++#endif /* XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETCHANNELLISTCOMMAND_H_ */
+diff --git a/xbmc/pvrclients/mythtv/libmythxml/GetChannelListParameters.h b/xbmc/pvrclients/mythtv/libmythxml/GetChannelListParameters.h
+new file mode 100644
+index 0000000..6627ed0
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv/libmythxml/GetChannelListParameters.h
+@@ -0,0 +1,19 @@
++/*
++ * GetChannelListParameters.h
++ *
++ * Created on: Oct 8, 2010
++ * Author: tafypz
++ */
++
++#ifndef XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETCHANNELLISTPARAMETERS_H_
++#define XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETCHANNELLISTPARAMETERS_H_
++
++#include "GetProgramGuideParameters.h"
++
++class GetChannelListParameters: public GetProgramGuideParameters {
++public:
++ GetChannelListParameters() : GetProgramGuideParameters(false) {};
++ virtual ~GetChannelListParameters(){};
++};
++
++#endif /* XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETCHANNELLISTPARAMETERS_H_ */
+diff --git a/xbmc/pvrclients/mythtv/libmythxml/GetChannelListResult.cpp b/xbmc/pvrclients/mythtv/libmythxml/GetChannelListResult.cpp
+new file mode 100644
+index 0000000..9504c00
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv/libmythxml/GetChannelListResult.cpp
+@@ -0,0 +1,53 @@
++#include "GetChannelListResult.h"
++#include <stdlib.h>
++
++#include "tinyXML/tinyxml.h"
++#include "utils/log.h"
++
++GetChannelListResult::GetChannelListResult() {
++}
++
++GetChannelListResult::~GetChannelListResult() {
++}
++
++void GetChannelListResult::parseData(const CStdString& xmlData) {
++ TiXmlDocument xml;
++ xml.Parse(xmlData.c_str(), 0, TIXML_ENCODING_LEGACY);
++
++ TiXmlElement* rootXmlNode = xml.RootElement();
++
++ if (!rootXmlNode) {
++ errors_.push_back(" No root node parsed");
++ CLog::Log(LOGDEBUG, "MythXML GetChannelListResult - No root node parsed");
++ return;
++ }
++
++ TiXmlElement* programGuideResponseNode = NULL;
++ CStdString strValue = rootXmlNode->Value();
++ if (strValue.Find("GetProgramGuideResponse") >= 0 ) {
++ programGuideResponseNode = rootXmlNode;
++ }
++ else if (strValue.Find("detail") >= 0 ) {
++ // process the error.
++ TiXmlElement* errorCodeXmlNode = rootXmlNode->FirstChildElement("errorCode");
++ TiXmlElement* errorDescXmlNode = rootXmlNode->FirstChildElement("errorDescription");
++ CStdString error;
++ error.Format("ErrorCode [%i] - %s", errorCodeXmlNode->GetText(), errorDescXmlNode->GetText());
++ errors_.push_back(error);
++ return;
++ }
++ else
++ return;
++
++ TiXmlElement* programGuideNode = programGuideResponseNode->FirstChildElement("ProgramGuide");
++ TiXmlElement* channelsNode = programGuideNode->FirstChildElement("Channels");
++ TiXmlElement* child = NULL;
++ for( child = channelsNode->FirstChildElement("Channel"); child; child = child->NextSiblingElement("Channel")){
++ SChannel channel;
++ channel.id = atoi(child->Attribute("chanId"));
++ channel.name = child->Attribute("channelName");
++ channel.callsign = child->Attribute("callSign");
++ channel.number = child->Attribute("chanNum");
++ channels_.push_back(channel);
++ }
++}
+diff --git a/xbmc/pvrclients/mythtv/libmythxml/GetChannelListResult.h b/xbmc/pvrclients/mythtv/libmythxml/GetChannelListResult.h
+new file mode 100644
+index 0000000..daf2988
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv/libmythxml/GetChannelListResult.h
+@@ -0,0 +1,17 @@
++#ifndef XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETCHANNELLISTRESULT_H
++#define XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETCHANNELLISTRESULT_H
++
++#include "MythXmlCommandResult.h"
++#include "SChannel.h"
++
++class GetChannelListResult: public MythXmlCommandResult {
++public:
++ GetChannelListResult();
++ virtual ~GetChannelListResult();
++ virtual void parseData(const CStdString& xmlData);
++ inline const vector<SChannel>& getChannels() {return channels_;};
++private:
++ vector<SChannel> channels_;
++};
++
++#endif // XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETCHANNELLISTRESULT_H
+diff --git a/xbmc/pvrclients/mythtv/libmythxml/GetNumChannelsCommand.h b/xbmc/pvrclients/mythtv/libmythxml/GetNumChannelsCommand.h
+new file mode 100644
+index 0000000..cede160
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv/libmythxml/GetNumChannelsCommand.h
+@@ -0,0 +1,24 @@
++/*
++ * GetNumChannelsCommand.h
++ *
++ * Created on: Oct 7, 2010
++ * Author: tafypz
++ */
++
++#ifndef XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETNUMCHANNELSCOMMAND_H_
++#define XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETNUMCHANNELSCOMMAND_H_
++
++#include "MythXmlCommand.h"
++
++class GetNumChannelsCommand: public MythXmlCommand {
++public:
++ GetNumChannelsCommand(){};
++ virtual ~GetNumChannelsCommand(){};
++protected:
++ virtual const CStdString& getCommand() const{
++ static CStdString result = "GetProgramGuide";
++ return result;
++ }
++};
++
++#endif /* XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETNUMCHANNELSCOMMAND_H_ */
+diff --git a/xbmc/pvrclients/mythtv/libmythxml/GetNumChannelsParameters.h b/xbmc/pvrclients/mythtv/libmythxml/GetNumChannelsParameters.h
+new file mode 100644
+index 0000000..fad18da
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv/libmythxml/GetNumChannelsParameters.h
+@@ -0,0 +1,19 @@
++/*
++ * GetNumChannelsParameters.h
++ *
++ * Created on: Oct 8, 2010
++ * Author: tafypz
++ */
++
++#ifndef XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETNUMCHANNELSPARAMETERS_H_
++#define XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETNUMCHANNELSPARAMETERS_H_
++
++#include "GetProgramGuideParameters.h"
++
++class GetNumChannelsParameters: public GetProgramGuideParameters {
++public:
++ GetNumChannelsParameters(): GetProgramGuideParameters(false) {};
++ virtual ~GetNumChannelsParameters() {};
++};
++
++#endif /* XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETNUMCHANNELSPARAMETERS_H_ */
+diff --git a/xbmc/pvrclients/mythtv/libmythxml/GetNumChannelsResult.cpp b/xbmc/pvrclients/mythtv/libmythxml/GetNumChannelsResult.cpp
+new file mode 100644
+index 0000000..79efc8b
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv/libmythxml/GetNumChannelsResult.cpp
+@@ -0,0 +1,54 @@
++/*
++ * GetNumChannelsResult.cpp
++ *
++ * Created on: Oct 7, 2010
++ * Author: tafypz
++ */
++
++#include "GetNumChannelsResult.h"
++#include <stdlib.h>
++
++#include "tinyXML/tinyxml.h"
++#include "utils/log.h"
++
++GetNumChannelsResult::GetNumChannelsResult() {
++ numberOfChannels_ = 0;
++}
++
++GetNumChannelsResult::~GetNumChannelsResult() {
++}
++
++void GetNumChannelsResult::parseData(const CStdString& xmlData) {
++ TiXmlDocument xml;
++ xml.Parse(xmlData.c_str(), 0, TIXML_ENCODING_LEGACY);
++
++ TiXmlElement* rootXmlNode = xml.RootElement();
++
++ if (!rootXmlNode) {
++ errors_.push_back(" No root node parsed");
++ CLog::Log(LOGDEBUG, "MythXML GetNumChannelsResult - No root node parsed");
++ return;
++ }
++
++ TiXmlElement* programGuideResponseNode = NULL;
++ CStdString strValue = rootXmlNode->Value();
++ if (strValue.Find("GetProgramGuideResponse") >= 0 ) {
++ programGuideResponseNode = rootXmlNode;
++ }
++ else if (strValue.Find("detail") >= 0 ) {
++ // process the error.
++ TiXmlElement* errorCodeXmlNode = rootXmlNode->FirstChildElement("errorCode");
++ TiXmlElement* errorDescXmlNode = rootXmlNode->FirstChildElement("errorDescription");
++ CStdString error;
++ error.Format("ErrorCode [%i] - %s", errorCodeXmlNode->GetText(), errorDescXmlNode->GetText());
++ errors_.push_back(error);
++ return;
++ }
++ else
++ return;
++
++ TiXmlElement* numOfChannelsXmlNode = programGuideResponseNode->FirstChildElement("NumOfChannels");
++ CStdString val = numOfChannelsXmlNode->GetText();
++ int numberOfChannels = atoi(val.c_str());
++ numberOfChannels_ = numberOfChannels;
++}
+diff --git a/xbmc/pvrclients/mythtv/libmythxml/GetNumChannelsResult.h b/xbmc/pvrclients/mythtv/libmythxml/GetNumChannelsResult.h
+new file mode 100644
+index 0000000..b39919b
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv/libmythxml/GetNumChannelsResult.h
+@@ -0,0 +1,23 @@
++/*
++ * GetNumChannelsResult.h
++ *
++ * Created on: Oct 7, 2010
++ * Author: tafypz
++ */
++
++#ifndef XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETNUMCHANNELSRESULT_H_
++#define XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETNUMCHANNELSRESULT_H_
++
++#include "MythXmlCommandResult.h"
++
++class GetNumChannelsResult: public MythXmlCommandResult {
++public:
++ GetNumChannelsResult();
++ virtual ~GetNumChannelsResult();
++ virtual void parseData(const CStdString& xmlData);
++ inline int getNumberOfChannels() const {return numberOfChannels_;};
++private:
++ int numberOfChannels_;
++};
++
++#endif /* XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETNUMCHANNELSRESULT_H_ */
+diff --git a/xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideCommand.h b/xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideCommand.h
+new file mode 100644
+index 0000000..4daf3bd
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideCommand.h
+@@ -0,0 +1,18 @@
++#ifndef XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETPROGRAMGUIDECOMMAND_H
++#define XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETPROGRAMGUIDECOMMAND_H
++//
++#include "MythXmlCommand.h"
++
++class GetProgramGuideCommand: public MythXmlCommand {
++public:
++ GetProgramGuideCommand() {};
++ virtual ~GetProgramGuideCommand() {};
++protected:
++ virtual const CStdString& getCommand() const {
++ static CStdString result = "GetProgramGuide";
++ return result;
++ };
++};
++
++
++#endif // XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETPROGRAMGUIDECOMMAND_H
+diff --git a/xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideParameters.cpp b/xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideParameters.cpp
+new file mode 100644
+index 0000000..4cab67f
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideParameters.cpp
+@@ -0,0 +1,43 @@
++/*
++ * GetProgramGuideParameters.cpp
++ *
++ * Created on: Oct 8, 2010
++ * Author: tafypz
++ */
++
++#include "GetProgramGuideParameters.h"
++#include "utils/TimeUtils.h"
++
++GetProgramGuideParameters::GetProgramGuideParameters(bool retrieveDetails) : MythXmlCommandParameters(true){
++ CDateTime now = CTimeUtils::GetLocalTime(time(NULL));
++ channelid_ = -1;
++ starttime_ = now;
++ endtime_ = now;
++ retrieveDetails_ = retrieveDetails_;
++}
++
++GetProgramGuideParameters::GetProgramGuideParameters(int channelid, CDateTime starttime, CDateTime endtime, bool retrieveDetails) : MythXmlCommandParameters(true){
++ channelid_ = channelid;
++ retrieveDetails_ = retrieveDetails;
++ starttime_ = starttime;
++ endtime_ = endtime;
++}
++
++GetProgramGuideParameters::~GetProgramGuideParameters() {
++}
++
++CStdString GetProgramGuideParameters::createParameterString() const{
++ CStdString result = "?StartTime=%s&EndTime=%s&NumOfChannels=%i";
++ CStdString start = MythXmlCommandParameters::convertTimeToString(starttime_);
++ CStdString end = MythXmlCommandParameters::convertTimeToString(endtime_);
++ int numChannels = 1;
++ if(channelid_ == -1)
++ numChannels = -1;
++ result.Format(result.c_str(), start.c_str(), end.c_str(),numChannels);
++ if(numChannels == 1){
++ CStdString chanid = "&StartChanId=%i";
++ chanid.Format(chanid.c_str(), channelid_);
++ result += chanid;
++ }
++ return result;
++};
+diff --git a/xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideParameters.h b/xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideParameters.h
+new file mode 100644
+index 0000000..5aaae54
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideParameters.h
+@@ -0,0 +1,38 @@
++/*
++ * GetProgramGuideParameters.h
++ *
++ * Created on: Oct 8, 2010
++ * Author: tafypz
++ */
++
++#ifndef XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETPROGRAMGUIDEPARAMETERS_H_
++#define XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETPROGRAMGUIDEPARAMETERS_H_
++
++#include "XBDateTime.h"
++
++#include "MythXmlCommandParameters.h"
++
++class GetProgramGuideParameters: public MythXmlCommandParameters {
++public:
++ GetProgramGuideParameters(int channelid, CDateTime starttime, CDateTime endtime, bool retrieveDetails);
++ virtual ~GetProgramGuideParameters();
++
++ virtual CStdString createParameterString() const;
++ inline const CDateTime& get_starttime() const {return starttime_;};
++ inline const CDateTime& get_endtime() const {return endtime_;};
++ inline void set_channelid(int channelid) {channelid_ = channelid;};
++ inline int get_channelid() const { return channelid_;};
++ inline void set_retrievedetailsflag(bool retrievedetails) {retrieveDetails_ = retrievedetails;};
++ inline bool get_retrievedetailsflag() const {return retrieveDetails_;};
++
++protected:
++ GetProgramGuideParameters(bool retrieveDetails);
++
++private:
++ CDateTime starttime_;
++ CDateTime endtime_;
++ int channelid_;
++ bool retrieveDetails_;
++};
++
++#endif /* XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETPROGRAMGUIDEPARAMETERS_H_ */
+diff --git a/xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideResult.cpp b/xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideResult.cpp
+new file mode 100644
+index 0000000..d7545eb
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideResult.cpp
+@@ -0,0 +1,331 @@
++#include "GetProgramGuideResult.h"
++#include <stdlib.h>
++
++#include "tinyXML/tinyxml.h"
++#include "utils/log.h"
++/*
++static const SContentType g_content_group[] =
++{ { 0x10, "Movie/Drama" }
++, { 0x20, "News/Current Affairs" }
++, { 0x30, "Show/Game show" }
++, { 0x40, "Sports" }
++, { 0x50, "Children's/Youth" }
++, { 0x60, "Music/Ballet/Dance" }
++, { 0x70, "Arts/Culture (without music)" }
++, { 0x80, "Social/Political issues/Economics" }
++, { 0x90, "Childrens/Youth Education/Science/Factual" }
++, { 0xa0, "Leisure hobbies" }
++, { 0xb0, "Misc" }
++, { 0xf0, "Unknown" }
++};
++
++static const SContentType g_content_type[] =
++{
++// movie/drama
++ { 0x11, "Detective/Thriller" }
++, { 0x12, "Adventure/Western/War" }
++, { 0x13, "Science Fiction/Fantasy/Horror" }
++, { 0x14, "Comedy" }
++, { 0x15, "Soap/Melodrama/Folkloric" }
++, { 0x16, "Romance" }
++, { 0x17, "Serious/ClassicalReligion/Historical" }
++, { 0x18, "Adult Movie/Drama" }
++
++// news/current affairs
++, { 0x21, "News/Weather Report" }
++, { 0x22, "Magazine" }
++, { 0x23, "Documentary" }
++, { 0x24, "Discussion/Interview/Debate" }
++
++// show/game show
++, { 0x31, "Game show/Quiz/Contest" }
++, { 0x32, "Variety" }
++, { 0x33, "Talk" }
++
++// sports
++, { 0x41, "Special Event (Olympics/World cup/...)" }
++, { 0x42, "Magazine" }
++, { 0x43, "Football/Soccer" }
++, { 0x44, "Tennis/Squash" }
++, { 0x45, "Team sports (excluding football)" }
++, { 0x46, "Athletics" }
++, { 0x47, "Motor Sport" }
++, { 0x48, "Water Sport" }
++, { 0x49, "Winter Sports" }
++, { 0x4a, "Equestrian" }
++, { 0x4b, "Martial sports" }
++
++// childrens/youth
++, { 0x51, "Pre-school" }
++, { 0x52, "Entertainment (6 to 14 year-olds)" }
++, { 0x53, "Entertainment (10 to 16 year-olds)" }
++, { 0x54, "Informational/Educational/Schools" }
++, { 0x55, "Cartoons/Puppets" }
++
++// music/ballet/dance
++, { 0x61, "Rock/Pop" }
++, { 0x62, "Serious music/Classical Music" }
++, { 0x63, "Folk/Traditional music" }
++, { 0x64, "Jazz" }
++, { 0x65, "Musical/Opera" }
++, { 0x66, "Ballet" }
++
++// arts/culture
++, { 0x71, "Performing Arts" }
++, { 0x72, "Fine Arts" }
++, { 0x73, "Religion" }
++, { 0x74, "Popular Culture/Tradital Arts" }
++, { 0x75, "Literature" }
++, { 0x76, "Film/Cinema" }
++, { 0x77, "Experimental Film/Video" }
++, { 0x78, "Broadcasting/Press" }
++, { 0x79, "New Media" }
++, { 0x7a, "Magazine" }
++, { 0x7b, "Fashion" }
++
++// social/political/economic
++, { 0x81, "Magazine/Report/Documentary" }
++, { 0x82, "Economics/Social Advisory" }
++, { 0x83, "Remarkable People" }
++
++// children's youth: educational/science/factual
++, { 0x91, "Nature/Animals/Environment" }
++, { 0x92, "Technology/Natural sciences" }
++, { 0x93, "Medicine/Physiology/Psychology" }
++, { 0x94, "Foreign Countries/Expeditions" }
++, { 0x95, "Social/Spiritual Sciences" }
++, { 0x96, "Further Education" }
++, { 0x97, "Languages" }
++
++// leisure hobbies
++, { 0xa1, "Tourism/Travel" }
++, { 0xa2, "Handicraft" }
++, { 0xa3, "Motoring" }
++, { 0xa4, "Fitness & Health" }
++, { 0xa5, "Cooking" }
++, { 0xa6, "Advertisement/Shopping" }
++, { 0xa7, "Gardening" }
++
++// misc
++, { 0xb0, "Original Language" }
++, { 0xb1, "Black and White" }
++, { 0xb2, "Unpublished" }
++, { 0xb3, "Live Broadcast" }
++};
++*/
++
++struct GenrePair
++{
++ GenrePair()
++ {
++ genretype_ = 0xf;
++ genresubtype_ = 0xb2;
++ };
++
++ GenrePair(int type, int subtype)
++ {
++ genretype_ = type;
++ genresubtype_ = subtype;
++ };
++
++ int genretype_;
++ int genresubtype_;
++};
++
++
++
++class GenreIdMapper
++{
++public:
++ GenreIdMapper()
++ {
++ genreTypeIdMap_["Auction"] = GenrePair (0xB0, 0xb2);
++ genreTypeIdMap_["Awards"] = GenrePair (0xB0, 0xb2);
++ genreTypeIdMap_["Biography"] = GenrePair (0x70, 0x74);
++ genreTypeIdMap_["Educational"] = GenrePair (0x90, 0x96);
++ genreTypeIdMap_["Entertainment"] = GenrePair (0xB0, 0xb2);
++ genreTypeIdMap_["Holiday"] = GenrePair (0xB0, 0xb2);
++ genreTypeIdMap_["Holiday special"] = GenrePair (0xB0, 0xb2);
++ genreTypeIdMap_["Home improvement"] = GenrePair (0xA0, 0xa7);
++ genreTypeIdMap_["How-to"] = GenrePair (0x90, 0x96);
++ genreTypeIdMap_["Music"] = GenrePair (0x60, 0x61);
++ genreTypeIdMap_["Music special"] = GenrePair (0x60, 0x61);
++ genreTypeIdMap_["Music talk"] = GenrePair (0x60, 0x61);
++ genreTypeIdMap_["Shopping"] = GenrePair (0xA0, 0xa6);
++ genreTypeIdMap_["Sitcom"] = GenrePair (0x10, 0x14);
++ genreTypeIdMap_["Soap"] = GenrePair (0x10, 0x15);
++ genreTypeIdMap_["Soap talk"] = GenrePair (0x10, 0x15);
++ genreTypeIdMap_["Golf"] = GenrePair (0x40, 0x41);
++ genreTypeIdMap_["Lacrosse"] = GenrePair (0x40, 0x45);
++ genreTypeIdMap_["Law"] = GenrePair (0xB0, 0xb2);
++ genreTypeIdMap_["Card games"] = GenrePair (0xB0, 0xb2);
++ genreTypeIdMap_["Collectibles"] = GenrePair (0xB0, 0xb2);
++ genreTypeIdMap_["Community"] = GenrePair (0xB0, 0xb2);
++ genreTypeIdMap_["Computers"] = GenrePair (0x90, 0x92);
++ genreTypeIdMap_["Consumer"] = GenrePair (0xB0, 0xb2);
++ genreTypeIdMap_["Fundraiser"] = GenrePair (0xB0, 0xb2);
++ genreTypeIdMap_["Gaming"] = GenrePair (0xB0, 0xb2);
++ genreTypeIdMap_["Gay/lesbian"] = GenrePair (0xB0, 0xb2);
++ genreTypeIdMap_["Military"] = GenrePair (0xB0, 0xb2);
++ genreTypeIdMap_["Miniseries"] = GenrePair (0xB0, 0xb2);
++ genreTypeIdMap_["Opera"] = GenrePair (0x60, 0x65);
++ genreTypeIdMap_["Parade"] = GenrePair (0xB0, 0xb2);
++ genreTypeIdMap_["Paranormal"] = GenrePair (0xB0, 0xb2);
++ genreTypeIdMap_["Parenting"] = GenrePair (0xB0, 0xb2);
++ genreTypeIdMap_["Poker"] = GenrePair (0xB0, 0xb2);
++ genreTypeIdMap_["Reality"] = GenrePair (0xB0, 0xb2);
++ genreTypeIdMap_["Self improvement"] = GenrePair (0xB0, 0xb2);
++ genreTypeIdMap_["Special"] = GenrePair (0xB0, 0xb2);
++ genreTypeIdMap_["Standup"] = GenrePair (0x10, 0x14);
++ genreTypeIdMap_["Politics"] = GenrePair (0x80, 0x82);
++ genreTypeIdMap_["Public affairs"] = GenrePair (0x80, 0x82);
++ genreTypeIdMap_["Historical drama"] = GenrePair (0x10, 0x17);
++ genreTypeIdMap_["History"] = GenrePair (0x10, 0x17);
++ genreTypeIdMap_["Boat"] = GenrePair (0xA0, 0xa3);
++ genreTypeIdMap_["Bus./financial"] = GenrePair (0x80, 0x82);
++ genreTypeIdMap_["Auto"] = GenrePair (0xA0, 0xa3);
++ genreTypeIdMap_["Aviation"] = GenrePair (0xA0, 0xa3);
++ genreTypeIdMap_["Nature"] = GenrePair (0x90, 0x91);
++ genreTypeIdMap_["Agriculture"] = GenrePair (0x90, 0x91);
++ genreTypeIdMap_["Animals"] = GenrePair (0x90, 0x91);
++ genreTypeIdMap_["Environment"] = GenrePair (0x90, 0x91);
++ genreTypeIdMap_["French"] = GenrePair (0x90, 0x97);
++ genreTypeIdMap_["Horse"] = GenrePair (0x90, 0x91);
++ genreTypeIdMap_["Outdoors"] = GenrePair (0x90, 0x91);
++ genreTypeIdMap_["Science"] = GenrePair (0x90, 0x92);
++ genreTypeIdMap_["Technology"] = GenrePair (0x90, 0x92);
++ genreTypeIdMap_["Medical"] = GenrePair (0x90, 0x93);
++ genreTypeIdMap_["Hunting"] = GenrePair (0x90, 0x91);
++ genreTypeIdMap_["Fishing"] = GenrePair (0x90, 0x91);
++ genreTypeIdMap_["Health"] = GenrePair (0xA0, 0xa4);
++ genreTypeIdMap_["Cooking"] = GenrePair (0xA0, 0xa5);
++ genreTypeIdMap_["House/garden"] = GenrePair (0xA0, 0xa7);
++ genreTypeIdMap_["Motorcycle"] = GenrePair (0xA0, 0xa3);
++ genreTypeIdMap_["Travel"] = GenrePair (0xA0, 0xa1);
++ genreTypeIdMap_["Aerobics"] = GenrePair (0xA0, 0xa4);
++ genreTypeIdMap_["Exercise"] = GenrePair (0xA0, 0xa4);
++ genreTypeIdMap_["Anthology"] = GenrePair (0x70, 0x74);
++ genreTypeIdMap_["Art"] = GenrePair (0x70, 0x72);
++ genreTypeIdMap_["Arts/crafts"] = GenrePair (0x70, 0x74);
++ genreTypeIdMap_["Fashion"] = GenrePair (0x70, 0x7b);
++ genreTypeIdMap_["Performing arts"] = GenrePair (0x70, 0x71);
++ genreTypeIdMap_["Spanish"] = GenrePair (0x90, 0x97);
++ genreTypeIdMap_["Religious"] = GenrePair (0x70, 0x73);
++ genreTypeIdMap_["Dance"] = GenrePair (0x60, 0x66);
++ genreTypeIdMap_["Animated"] = GenrePair (0x50, 0x55);
++ genreTypeIdMap_["Anime"] = GenrePair (0x50, 0x55);
++ genreTypeIdMap_["Children"] = GenrePair (0x50, 0x52);
++ genreTypeIdMap_["Children-music"] = GenrePair (0x50, 0x52);
++ genreTypeIdMap_["Children-special"] = GenrePair (0x50, 0x53);
++ genreTypeIdMap_["Holiday-children"] = GenrePair (0x50, 0x52);
++ genreTypeIdMap_["Holiday-children special"] = GenrePair (0x50, 0x52);
++ genreTypeIdMap_["Game show"] = GenrePair (0x30, 0x31);
++ genreTypeIdMap_["Talk"] = GenrePair (0x30, 0x33);
++ genreTypeIdMap_["Variety"] = GenrePair (0x30, 0x32);
++ genreTypeIdMap_["Debate"] = GenrePair (0x20, 0x24);
++ genreTypeIdMap_["Docudrama"] = GenrePair (0x20, 0x23);
++ genreTypeIdMap_["Documentary"] = GenrePair (0x20, 0x23);
++ genreTypeIdMap_["Interview"] = GenrePair (0x20, 0x24);
++ genreTypeIdMap_["News"] = GenrePair (0x20, 0x21);
++ genreTypeIdMap_["Newsmagazine"] = GenrePair (0x20, 0x21);
++ genreTypeIdMap_["Weather"] = GenrePair (0x20, 0x21);
++ genreTypeIdMap_["Action"] = GenrePair (0x10, 0x12);
++ genreTypeIdMap_["Adults only"] = GenrePair (0x10, 0x18);
++ genreTypeIdMap_["Adventure"] = GenrePair (0x10, 0x12);
++ genreTypeIdMap_["Comedy"] = GenrePair (0x10, 0x14);
++ genreTypeIdMap_["Comedy-drama"] = GenrePair (0x10, 0x14);
++ genreTypeIdMap_["Crime"] = GenrePair (0x10, 0x11);
++ genreTypeIdMap_["Crime drama"] = GenrePair (0x10, 0x11);
++ genreTypeIdMap_["Drama"] = GenrePair (0x10, 0x18);
++ genreTypeIdMap_["Fantasy"] = GenrePair (0x10, 0x13);
++ genreTypeIdMap_["Horror"] = GenrePair (0x10, 0x13);
++ genreTypeIdMap_["Musical"] = GenrePair (0x60, 0x65);
++ genreTypeIdMap_["Musical comedy"] = GenrePair (0x60, 0x65);
++ genreTypeIdMap_["Mystery"] = GenrePair (0x10, 0x12);
++ genreTypeIdMap_["Romance"] = GenrePair (0x10, 0x16);
++ genreTypeIdMap_["Romance-comedy"] = GenrePair (0x10, 0x16);
++ genreTypeIdMap_["Science fiction"] = GenrePair (0x10, 0x13);
++ genreTypeIdMap_["Suspense"] = GenrePair (0x10, 0x11);
++ genreTypeIdMap_["War"] = GenrePair (0x10, 0x12);
++ genreTypeIdMap_["Western"] = GenrePair (0x10, 0x12);
++ genreTypeIdMap_["Action sports"] = GenrePair (0x40, 0x4b);
++ };
++ ~GenreIdMapper()
++ {
++ };
++
++ const GenrePair& getGenreTypeId(CStdString& genre)
++ {
++ std::map<CStdString, GenrePair>::iterator it;
++ it = genreTypeIdMap_.find(genre);
++ if(it != genreTypeIdMap_.end())
++ return it->second;
++ return c_unknown_;
++ };
++
++private:
++ std::map<CStdString, GenrePair> genreTypeIdMap_;
++ const GenrePair c_unknown_;
++};
++
++GenreIdMapper GetProgramGuideResult::s_mapper_;
++
++GetProgramGuideResult::GetProgramGuideResult() {
++}
++
++GetProgramGuideResult::~GetProgramGuideResult() {
++}
++
++void GetProgramGuideResult::parseData(const CStdString& xmlData) {
++ TiXmlDocument xml;
++ xml.Parse(xmlData.c_str(), 0, TIXML_ENCODING_LEGACY);
++
++ TiXmlElement* rootXmlNode = xml.RootElement();
++
++ if (!rootXmlNode) {
++ errors_.push_back(" No root node parsed");
++ CLog::Log(LOGDEBUG, "MythXML GetProgramGuideResult - No root node parsed");
++ return;
++ }
++
++ TiXmlElement* programGuideResponseNode = NULL;
++ CStdString strValue = rootXmlNode->Value();
++ if (strValue.Find("GetProgramGuideResponse") >= 0 ) {
++ programGuideResponseNode = rootXmlNode;
++ }
++ else if (strValue.Find("detail") >= 0 ) {
++ // process the error.
++ TiXmlElement* errorCodeXmlNode = rootXmlNode->FirstChildElement("errorCode");
++ TiXmlElement* errorDescXmlNode = rootXmlNode->FirstChildElement("errorDescription");
++ CStdString error;
++ error.Format("ErrorCode [%i] - %s", errorCodeXmlNode->GetText(), errorDescXmlNode->GetText());
++ errors_.push_back(error);
++ return;
++ }
++ else
++ return;
++
++ TiXmlElement* programGuideNode = programGuideResponseNode->FirstChildElement("ProgramGuide");
++ TiXmlElement* channelsNode = programGuideNode->FirstChildElement("Channels");
++ TiXmlElement* channelNode = NULL;
++ TiXmlElement* programNode = NULL;
++ for( channelNode = channelsNode->FirstChildElement("Channel"); channelNode; channelNode = channelNode->NextSiblingElement("Channel")){
++ int chanId = atoi(channelNode->Attribute("chanId"));
++ for( programNode = channelNode->FirstChildElement("Program"); programNode; programNode = programNode->NextSiblingElement("Program")){
++ CStdString category = programNode->Attribute("category");
++ CStdString itemStart = programNode->Attribute("startTime");
++ CStdString itemEnd = programNode->Attribute("endTime");
++ const GenrePair& genres = s_mapper_.getGenreTypeId(category);
++ SEpg epg;
++ epg.chan_num = chanId;
++ epg.description = programNode->GetText();
++ epg.title = programNode->Attribute("title");
++ epg.subtitle = programNode->Attribute("subTitle");
++ epg.genre_type = genres.genretype_;
++ epg.genre_subtype = genres.genresubtype_;
++ epg.start_time = MythXmlCommandResult::convertTimeStringToObject(itemStart);
++ epg.end_time = MythXmlCommandResult::convertTimeStringToObject(itemEnd);
++ epg_.push_back(epg);
++ }
++ }
++}
+diff --git a/xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideResult.h b/xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideResult.h
+new file mode 100644
+index 0000000..b54e0ef
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideResult.h
+@@ -0,0 +1,25 @@
++#ifndef XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETPROGRAMGUIDERESULT_H
++#define XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETPROGRAMGUIDERESULT_H
++
++#include <map>
++
++#include "MythXmlCommandResult.h"
++#include "SEpg.h"
++
++class GenreIdMapper;
++
++class GetProgramGuideResult:public MythXmlCommandResult
++{
++public:
++ GetProgramGuideResult();
++ virtual ~GetProgramGuideResult();
++ virtual void parseData(const CStdString& xmlData);
++ inline const vector<SEpg>& getEpg() {return epg_;};
++
++private:
++ void initialize();
++ vector<SEpg> epg_;
++ static GenreIdMapper s_mapper_;
++};
++
++#endif // XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETPROGRAMGUIDERESULT_H
+diff --git a/xbmc/pvrclients/mythtv/libmythxml/Makefile b/xbmc/pvrclients/mythtv/libmythxml/Makefile
+new file mode 100644
+index 0000000..27e24d2
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv/libmythxml/Makefile
+@@ -0,0 +1,16 @@
++INCLUDES += -I. -I../../ -I../../../linux -I../../../ -I../../../../xbmc/addons/include -I../../../../guilib
++DEFINES += -D_LINUX -fPIC
++
++OBJS = GetProgramGuideResult.o \
++ GetChannelListResult.o \
++ GetNumChannelsResult.o \
++ GetProgramGuideParameters.o \
++ MythXmlCommand.o \
++ MythXmlCommandParameters.o
++
++LIB = libmythxml.a
++
++# all is the default rule
++all: $(LIB)
++
++include ../../../../Makefile.include
+diff --git a/xbmc/pvrclients/mythtv/libmythxml/MythXmlCommand.cpp b/xbmc/pvrclients/mythtv/libmythxml/MythXmlCommand.cpp
+new file mode 100644
+index 0000000..36d1269
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv/libmythxml/MythXmlCommand.cpp
+@@ -0,0 +1,42 @@
++/*
++ * MythXmlCommand.cpp
++ *
++ * Created on: Oct 7, 2010
++ * Author: mythtv
++ */
++
++#include "MythXmlCommand.h"
++
++#include "filesystem/FileCurl.h"
++
++#include "utils/log.h"
++
++using namespace XFILE;
++
++MythXmlCommand::MythXmlCommand() {
++}
++
++MythXmlCommand::~MythXmlCommand() {
++}
++
++void MythXmlCommand::execute(const CStdString& hostname, int port, const MythXmlCommandParameters& params, MythXmlCommandResult& result, int timeout){
++ CStdString strUrl = createRequestUrl(hostname, port, params);
++ CStdString strXML;
++ CFileCurl http;
++ http.SetTimeout(timeout);
++ if (http.Get(strUrl, strXML))
++ {
++ CLog::Log(LOGDEBUG, "Got response from mythtv backend: %s", strUrl.c_str());
++ }
++ http.Cancel();
++ result.parseData(strXML);
++}
++
++CStdString MythXmlCommand::createRequestUrl(const CStdString& hostname, int port, const MythXmlCommandParameters& params){
++ CStdString requestURL;
++ requestURL.Format("http://%s:%i/Myth/%s", hostname.c_str(), port, getCommand().c_str());
++ if(params.hasParameters()){
++ requestURL += params.createParameterString();
++ }
++ return requestURL;
++}
+diff --git a/xbmc/pvrclients/mythtv/libmythxml/MythXmlCommand.h b/xbmc/pvrclients/mythtv/libmythxml/MythXmlCommand.h
+new file mode 100644
+index 0000000..4d6766f
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv/libmythxml/MythXmlCommand.h
+@@ -0,0 +1,49 @@
++/*
++ * MythXmlCommand.h
++ *
++ * Created on: Oct 7, 2010
++ * Author: tafypz
++ */
++
++#ifndef XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_MYTHXMLCOMMAND_H_
++#define XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_MYTHXMLCOMMAND_H_
++
++
++#include "MythXmlCommandResult.h"
++#include "MythXmlCommandParameters.h"
++
++#include "utils/StdString.h"
++
++/*! \class MythXmlCommand
++ \brief The base class for all MythXML Commands.
++ This class will be subclassed by all MythXML commands; it provides a few basic services:
++ - creation of the request url (complete with parameters).
++ - execution of the created request.
++ */
++class MythXmlCommand {
++public:
++ MythXmlCommand();
++ virtual ~MythXmlCommand();
++ /*! \brief Execute the command with the given request parameters.
++ \param hostname the mythtv backend hostname to connect to.
++ \param port the mythtv backend port to connect to.
++ \param params the parameters specific to the command.
++ \param result the result instance to use to handle the data returned by the request.
++ \param timeout the timeout value for this request.
++ */
++ void execute(const CStdString& hostname, int port, const MythXmlCommandParameters& params, MythXmlCommandResult& result, int timeout);
++protected:
++ /*! \brief The MythXML Command to use.
++ \return The MythXML Command to use.
++ */
++ virtual const CStdString& getCommand() const = 0;
++private:
++ /*! \brief creates the url to use to issue the request.
++ \param hostname the mythtv backend hostname to connect to.
++ \param port the mythtv backend port to connect to.
++ \param params the parameters specific to the command.
++ */
++ CStdString createRequestUrl(const CStdString& hostname, int port, const MythXmlCommandParameters& params);
++};
++
++#endif /* XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_MYTHXMLCOMMAND_H_ */
+diff --git a/xbmc/pvrclients/mythtv/libmythxml/MythXmlCommandParameters.cpp b/xbmc/pvrclients/mythtv/libmythxml/MythXmlCommandParameters.cpp
+new file mode 100644
+index 0000000..89c86eb
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv/libmythxml/MythXmlCommandParameters.cpp
+@@ -0,0 +1,33 @@
++/*
++ * MythXmlCommandParameters.cpp
++ *
++ * Created on: Oct 8, 2010
++ * Author: tafypz
++ */
++
++#include "MythXmlCommandParameters.h"
++#include "XBDateTime.h"
++
++MythXmlCommandParameters::MythXmlCommandParameters(bool hasParameters) {
++ hasParameters_ = hasParameters;
++}
++
++MythXmlCommandParameters::~MythXmlCommandParameters() {
++}
++
++CStdString MythXmlCommandParameters::convertTimeToString(const CDateTime& time){
++ CStdString result = "%i-%02.2i-%02.2iT%02.2i:%02.2i";
++ result.Format(result.c_str(), time.GetYear(), time.GetMonth(), time.GetDay(), time.GetHour(), time.GetMinute());
++ return result;
++}
++
++MythXmlEmptyCommandParameters::MythXmlEmptyCommandParameters() : MythXmlCommandParameters(false){
++}
++
++MythXmlEmptyCommandParameters::~MythXmlEmptyCommandParameters(){
++}
++
++CStdString MythXmlEmptyCommandParameters::createParameterString() const{
++ static CStdString result = "";
++ return result;
++};
+diff --git a/xbmc/pvrclients/mythtv/libmythxml/MythXmlCommandParameters.h b/xbmc/pvrclients/mythtv/libmythxml/MythXmlCommandParameters.h
+new file mode 100644
+index 0000000..3283fc8
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv/libmythxml/MythXmlCommandParameters.h
+@@ -0,0 +1,43 @@
++/*
++ * MythXmlCommandParameters.h
++ *
++ * Created on: Oct 8, 2010
++ * Author: tafypz
++ */
++
++#ifndef XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_MYTHXMLCOMMANDPARAMETERS_H_
++#define XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_MYTHXMLCOMMANDPARAMETERS_H_
++
++#include "XBDateTime.h"
++
++#include "utils/StdString.h"
++
++/*! \class MythXmlCommandParameters
++ \brief This is the base abstract class for all MythXML command parameters.
++ This class should be subclassed in order to provide parameters to the mythxml commands.
++ It has the responsibility to create a list of HTTP parameters to use for the request.
++ A subclass called \ref MythXmlEmptyCommandParameters MythXmlEmptyCommandParameters has been provided for convenience
++ in case the mythXML command doesn't need parameters.
++ */
++class MythXmlCommandParameters {
++public:
++ static CStdString convertTimeToString(const CDateTime& time);
++
++ MythXmlCommandParameters(bool hasParameters);
++ virtual ~MythXmlCommandParameters();
++ virtual CStdString createParameterString() const = 0;
++ inline bool hasParameters() const {return hasParameters_;};
++private:
++ bool hasParameters_;
++};
++
++/*! \class MythXmlEmptyCommandParameters
++ \brief This class is to be used with commands that do not take parameters.
++ */
++class MythXmlEmptyCommandParameters : public MythXmlCommandParameters {
++ MythXmlEmptyCommandParameters();
++ virtual ~MythXmlEmptyCommandParameters();
++ virtual CStdString createParameterString() const;
++};
++
++#endif /* MYTHXMLCOMMANDPARAMETERS_H_ */
+diff --git a/xbmc/pvrclients/mythtv/libmythxml/MythXmlCommandResult.h b/xbmc/pvrclients/mythtv/libmythxml/MythXmlCommandResult.h
+new file mode 100644
+index 0000000..c8f09e3
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv/libmythxml/MythXmlCommandResult.h
+@@ -0,0 +1,35 @@
++/*
++ * MythXmlCommandResult.h
++ *
++ * Created on: Oct 7, 2010
++ * Author: tafypz
++ */
++
++#ifndef XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_MYTHXMLCOMMANDRESULT_H_
++#define XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_MYTHXMLCOMMANDRESULT_H_
++
++#include <vector>
++#include "XBDateTime.h"
++#include "utils/StdString.h"
++
++using std::vector;
++class MythXmlCommandResult {
++public:
++ static CDateTime convertTimeStringToObject(const CStdString& time)
++ {
++ int year = 0, month = 0, day = 0;
++ int hour = 0, minute = 0, second = 0;
++ sscanf(time.c_str(), "%d-%d-%dT%d:%d:%d", &year, &month, &day, &hour, &minute, &second);
++ return CDateTime(year, month, day,hour,minute, second);
++ }
++
++ MythXmlCommandResult(){};
++ virtual ~MythXmlCommandResult(){};
++ inline bool isSuccess() const { return errors_.empty(); };
++ inline vector<CStdString> getErrors() const {return errors_;};
++ virtual void parseData(const CStdString& xmlData) = 0;
++protected:
++ vector<CStdString> errors_;
++};
++
++#endif /* XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_MYTHXMLCOMMANDRESULT_H_ */
+diff --git a/xbmc/pvrclients/mythtv/libmythxml/SChannel.h b/xbmc/pvrclients/mythtv/libmythxml/SChannel.h
+new file mode 100644
+index 0000000..19fab61
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv/libmythxml/SChannel.h
+@@ -0,0 +1,18 @@
++#ifndef XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_SCHANNEL_H_
++#define XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_SCHANNEL_H_
++
++#include "utils/StdString.h"
++
++struct SChannel
++{
++ int id;
++ CStdString name;
++ CStdString callsign;
++ CStdString number;
++
++ SChannel() {
++ id = 0;
++ }
++};
++
++#endif /* XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_SCHANNEL_H_ */
+diff --git a/xbmc/pvrclients/mythtv/libmythxml/SEpg.h b/xbmc/pvrclients/mythtv/libmythxml/SEpg.h
+new file mode 100644
+index 0000000..e53d0a4
+--- /dev/null
++++ b/xbmc/pvrclients/mythtv/libmythxml/SEpg.h
+@@ -0,0 +1,29 @@
++#ifndef XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_SEPG_H_
++#define XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_SEPG_H_
++
++#include "utils/StdString.h"
++#include "XBDateTime.h"
++
++struct SEpg
++{
++ int id;
++ int chan_num;
++ int genre_type;
++ int genre_subtype;
++ int parental_rating;
++ CStdString title;
++ CStdString subtitle;
++ CStdString description;
++ CDateTime start_time;
++ CDateTime end_time;
++
++ SEpg() {
++ id = 0;
++ chan_num = 0;
++ genre_type = 0;
++ genre_subtype = 0;
++ parental_rating = 0;
++ }
++};
++
++#endif /* XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_SEPG_H_ */
+diff --git a/xbmc/pvrclients/pvr-demo/Makefile.in b/xbmc/pvrclients/pvr-demo/Makefile.in
+new file mode 100644
+index 0000000..fa724f5
+--- /dev/null
++++ b/xbmc/pvrclients/pvr-demo/Makefile.in
+@@ -0,0 +1,23 @@
++#
++# Makefile for the XBMC PVR Demo add-on
++#
++# See the README for copyright information and
++# how to reach the author.
++#
++
++LIBS = @abs_top_srcdir@/lib/tinyXML/tinyxml.a -ldl
++LIBDIR = @abs_top_srcdir@/addons/pvr.demo
++LIB = @abs_top_srcdir@/addons/pvr.demo/XBMC_demo.pvr
++
++SRCS=client.cpp \
++ PVRDemoData.cpp
++
++include ../Makefile.include
++
++clean:
++ -rm -f $(OBJS) $(LIB) *.P *~
++ ${MAKE} -C @abs_top_srcdir@/lib/tinyXML clean
++
++$(LIB): $(OBJS)
++ ${MAKE} -C @abs_top_srcdir@/lib/tinyXML
++ $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared -g $(OBJS) $(LIBS) $(LIBDIRS) $(SILIB) -o $(LIB)
+diff --git a/xbmc/pvrclients/pvr-demo/PVRDemoData.cpp b/xbmc/pvrclients/pvr-demo/PVRDemoData.cpp
+new file mode 100644
+index 0000000..6cf61b9
+--- /dev/null
++++ b/xbmc/pvrclients/pvr-demo/PVRDemoData.cpp
+@@ -0,0 +1,360 @@
++/*
++ * Copyright (C) 2011 Pulse-Eight
++ * http://www.pulse-eight.com/
++ *
++ * This Program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2, or (at your option)
++ * any later version.
++ *
++ * This Program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "utils/XMLUtils.h"
++#include "PVRDemoData.h"
++/* hack hack */
++#include "filesystem/SpecialProtocol.h"
++
++using namespace std;
++using namespace ADDON;
++
++PVRDemoData::PVRDemoData(void)
++{
++ m_iEpgStart = -1;
++ m_strDefaultIcon = "http://www.royalty-free.tv/news/wp-content/uploads/2011/06/cc-logo1.jpg";
++ m_strDefaultMovie = "";
++
++ LoadDemoData();
++}
++
++PVRDemoData::~PVRDemoData(void)
++{
++ m_channels.clear();
++ m_groups.clear();
++}
++
++std::string PVRDemoData::GetSettingsFile() const
++{
++ return _P("special://xbmc/system/PVRDemoAddonSettings.xml");
++}
++
++bool PVRDemoData::LoadDemoData(void)
++{
++ TiXmlDocument xmlDoc;
++
++ if (!xmlDoc.LoadFile(GetSettingsFile()))
++ {
++ XBMC->Log(LOG_ERROR, "invalid demo data (no/invalid data file found)");
++ return false;
++ }
++
++ TiXmlElement *pRootElement = xmlDoc.RootElement();
++ if (strcmp(pRootElement->Value(), "demo") != 0)
++ {
++ XBMC->Log(LOG_ERROR, "invalid demo data (no <demo> tag found)");
++ return false;
++ }
++
++ /* load channels */
++ int iUniqueChannelId = 0;
++ TiXmlElement *pElement = pRootElement->FirstChildElement("channels");
++ if (pElement)
++ {
++ TiXmlNode *pChannelNode = NULL;
++ while ((pChannelNode = pElement->IterateChildren(pChannelNode)) != NULL)
++ {
++ CStdString strTmp;
++ PVRDemoChannel channel;
++ channel.iUniqueId = ++iUniqueChannelId;
++
++ /* channel name */
++ if (!XMLUtils::GetString(pChannelNode, "name", strTmp))
++ continue;
++ channel.strChannelName = strTmp;
++
++ /* radio/TV */
++ XMLUtils::GetBoolean(pChannelNode, "radio", channel.bRadio);
++
++ /* channel number */
++ if (!XMLUtils::GetInt(pChannelNode, "number", channel.iChannelNumber))
++ channel.iChannelNumber = iUniqueChannelId;
++
++ /* CAID */
++ if (!XMLUtils::GetInt(pChannelNode, "encryption", channel.iEncryptionSystem))
++ channel.iEncryptionSystem = 0;
++
++ /* icon path */
++ if (!XMLUtils::GetString(pChannelNode, "icon", strTmp))
++ channel.strIconPath = m_strDefaultIcon;
++ else
++ channel.strIconPath = strTmp;
++
++ /* stream url */
++ if (!XMLUtils::GetString(pChannelNode, "stream", strTmp))
++ channel.strStreamURL = m_strDefaultMovie;
++ else
++ channel.strStreamURL = strTmp;
++
++ m_channels.push_back(channel);
++ }
++ }
++
++ /* load channel groups */
++ int iUniqueGroupId = 0;
++ pElement = pRootElement->FirstChildElement("channelgroups");
++ if (pElement)
++ {
++ TiXmlNode *pGroupNode = NULL;
++ while ((pGroupNode = pElement->IterateChildren(pGroupNode)) != NULL)
++ {
++ CStdString strTmp;
++ PVRDemoChannelGroup group;
++ group.iGroupId = ++iUniqueGroupId;
++
++ /* group name */
++ if (!XMLUtils::GetString(pGroupNode, "name", strTmp))
++ continue;
++ group.strGroupName = strTmp;
++
++ /* radio/TV */
++ XMLUtils::GetBoolean(pGroupNode, "radio", group.bRadio);
++
++ /* members */
++ TiXmlNode* pMembers = pGroupNode->FirstChild("members");
++ TiXmlNode *pMemberNode = NULL;
++ while (pMembers != NULL && (pMemberNode = pMembers->IterateChildren(pMemberNode)) != NULL)
++ {
++ int iChannelId = atoi(pMemberNode->FirstChild()->Value());
++ if (iChannelId > -1)
++ group.members.push_back(iChannelId);
++ }
++
++ m_groups.push_back(group);
++ }
++ }
++
++ /* load EPG entries */
++ pElement = pRootElement->FirstChildElement("epg");
++ if (pElement)
++ {
++ TiXmlNode *pEpgNode = NULL;
++ while ((pEpgNode = pElement->IterateChildren(pEpgNode)) != NULL)
++ {
++ CStdString strTmp;
++ int iTmp;
++ PVRDemoEpgEntry entry;
++
++ /* broadcast id */
++ if (!XMLUtils::GetInt(pEpgNode, "broadcastid", entry.iBroadcastId))
++ continue;
++
++ /* channel id */
++ if (!XMLUtils::GetInt(pEpgNode, "channelid", iTmp))
++ continue;
++ PVRDemoChannel &channel = m_channels.at(iTmp - 1);
++ entry.iChannelId = channel.iUniqueId;
++
++ /* title */
++ if (!XMLUtils::GetString(pEpgNode, "title", strTmp))
++ continue;
++ entry.strTitle = strTmp;
++
++ /* start */
++ if (!XMLUtils::GetInt(pEpgNode, "start", iTmp))
++ continue;
++ entry.startTime = iTmp;
++
++ /* end */
++ if (!XMLUtils::GetInt(pEpgNode, "end", iTmp))
++ continue;
++ entry.endTime = iTmp;
++
++ /* plot */
++ if (XMLUtils::GetString(pEpgNode, "plot", strTmp))
++ entry.strPlot = strTmp;
++
++ /* plot outline */
++ if (XMLUtils::GetString(pEpgNode, "plotoutline", strTmp))
++ entry.strPlotOutline = strTmp;
++
++ /* icon path */
++ if (XMLUtils::GetString(pEpgNode, "icon", strTmp))
++ entry.strIconPath = strTmp;
++
++ /* genre type */
++ XMLUtils::GetInt(pEpgNode, "genretype", entry.iGenreType);
++
++ /* genre subtype */
++ XMLUtils::GetInt(pEpgNode, "genresubtype", entry.iGenreSubType);
++
++ XBMC->Log(LOG_DEBUG, "loaded EPG entry '%s' channel '%d' start '%d' end '%d'", entry.strTitle.c_str(), entry.iChannelId, entry.startTime, entry.endTime);
++ channel.epg.push_back(entry);
++ }
++ }
++
++ return true;
++}
++
++int PVRDemoData::GetChannelsAmount(void)
++{
++ return m_channels.size();
++}
++
++PVR_ERROR PVRDemoData::GetChannels(PVR_HANDLE handle, bool bRadio)
++{
++ for (unsigned int iChannelPtr = 0; iChannelPtr < m_channels.size(); iChannelPtr++)
++ {
++ PVRDemoChannel &channel = m_channels.at(iChannelPtr);
++ if (channel.bRadio == bRadio)
++ {
++ PVR_CHANNEL xbmcChannel;
++ memset(&xbmcChannel, 0, sizeof(PVR_CHANNEL));
++
++ xbmcChannel.iUniqueId = channel.iUniqueId;
++ xbmcChannel.bIsRadio = channel.bRadio;
++ xbmcChannel.iChannelNumber = channel.iChannelNumber;
++ xbmcChannel.strChannelName = channel.strChannelName.c_str();
++ xbmcChannel.strInputFormat = ""; // unused
++ xbmcChannel.strStreamURL = channel.strStreamURL.c_str();
++ xbmcChannel.iEncryptionSystem = channel.iEncryptionSystem;
++ xbmcChannel.strIconPath = channel.strIconPath.c_str();
++ xbmcChannel.bIsHidden = false;
++
++ PVR->TransferChannelEntry(handle, &xbmcChannel);
++ }
++ }
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++bool PVRDemoData::GetChannel(const PVR_CHANNEL &channel, PVRDemoChannel &myChannel)
++{
++ for (unsigned int iChannelPtr = 0; iChannelPtr < m_channels.size(); iChannelPtr++)
++ {
++ PVRDemoChannel &thisChannel = m_channels.at(iChannelPtr);
++ if (thisChannel.iUniqueId == (int) channel.iUniqueId)
++ {
++ myChannel.iUniqueId = thisChannel.iUniqueId;
++ myChannel.bRadio = thisChannel.bRadio;
++ myChannel.iChannelNumber = thisChannel.iChannelNumber;
++ myChannel.iEncryptionSystem = thisChannel.iEncryptionSystem;
++ myChannel.strChannelName = thisChannel.strChannelName;
++ myChannel.strIconPath = thisChannel.strIconPath;
++ myChannel.strStreamURL = thisChannel.strStreamURL;
++
++ return true;
++ }
++ }
++
++ return false;
++}
++
++int PVRDemoData::GetChannelGroupsAmount(void)
++{
++ return m_groups.size();
++}
++
++PVR_ERROR PVRDemoData::GetChannelGroups(PVR_HANDLE handle, bool bRadio)
++{
++ for (unsigned int iGroupPtr = 0; iGroupPtr < m_groups.size(); iGroupPtr++)
++ {
++ PVRDemoChannelGroup &group = m_groups.at(iGroupPtr);
++ if (group.bRadio == bRadio)
++ {
++ PVR_CHANNEL_GROUP xbmcGroup;
++ memset(&xbmcGroup, 0, sizeof(PVR_CHANNEL_GROUP));
++
++ xbmcGroup.bIsRadio = bRadio;
++ xbmcGroup.strGroupName = group.strGroupName.c_str();
++
++ PVR->TransferChannelGroup(handle, &xbmcGroup);
++ }
++ }
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++PVR_ERROR PVRDemoData::GetChannelGroupMembers(PVR_HANDLE handle, const PVR_CHANNEL_GROUP &group)
++{
++ for (unsigned int iGroupPtr = 0; iGroupPtr < m_groups.size(); iGroupPtr++)
++ {
++ PVRDemoChannelGroup &myGroup = m_groups.at(iGroupPtr);
++ if (myGroup.strGroupName == group.strGroupName)
++ {
++ for (unsigned int iChannelPtr = 0; iChannelPtr < myGroup.members.size(); iChannelPtr++)
++ {
++ int iId = myGroup.members.at(iChannelPtr) - 1;
++ if (iId < 0 || iId > (int)m_channels.size() - 1)
++ continue;
++ PVRDemoChannel &channel = m_channels.at(iId);
++ PVR_CHANNEL_GROUP_MEMBER xbmcGroupMember;
++ memset(&xbmcGroupMember, 0, sizeof(PVR_CHANNEL_GROUP_MEMBER));
++
++ xbmcGroupMember.strGroupName = group.strGroupName;
++ xbmcGroupMember.iChannelUniqueId = channel.iUniqueId;
++ xbmcGroupMember.iChannelNumber = channel.iChannelNumber;
++
++ PVR->TransferChannelGroupMember(handle, &xbmcGroupMember);
++ }
++ }
++ }
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++PVR_ERROR PVRDemoData::GetEPGForChannel(PVR_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd)
++{
++ if (m_iEpgStart == -1)
++ m_iEpgStart = iStart;
++
++ time_t iLastEndTime = m_iEpgStart + 1;
++ int iAddBroadcastId = 0;
++
++ for (unsigned int iChannelPtr = 0; iChannelPtr < m_channels.size(); iChannelPtr++)
++ {
++ PVRDemoChannel &myChannel = m_channels.at(iChannelPtr);
++ if (myChannel.iUniqueId != (int) channel.iUniqueId)
++ continue;
++
++ while (iLastEndTime < iEnd && myChannel.epg.size() > 0)
++ {
++ time_t iLastEndTimeTmp = 0;
++ for (unsigned int iEntryPtr = 0; iEntryPtr < myChannel.epg.size(); iEntryPtr++)
++ {
++ PVRDemoEpgEntry &myTag = myChannel.epg.at(iEntryPtr);
++
++ EPG_TAG tag;
++ memset(&tag, 0, sizeof(EPG_TAG));
++
++ tag.iUniqueBroadcastId = myTag.iBroadcastId + iAddBroadcastId;
++ tag.strTitle = myTag.strTitle.c_str();
++ tag.iChannelNumber = myTag.iChannelId;
++ tag.startTime = myTag.startTime + iLastEndTime;
++ tag.endTime = myTag.endTime + iLastEndTime;
++ tag.strPlotOutline = myTag.strPlotOutline.c_str();
++ tag.strPlot = myTag.strPlot.c_str();
++ tag.strIconPath = myTag.strIconPath.c_str();
++ tag.iGenreType = myTag.iGenreType;
++ tag.iGenreSubType = myTag.iGenreSubType;
++
++ iLastEndTimeTmp = tag.endTime;
++
++ PVR->TransferEpgEntry(handle, &tag);
++ }
++
++ iLastEndTime = iLastEndTimeTmp;
++ iAddBroadcastId += myChannel.epg.size();
++ }
++ }
++
++ return PVR_ERROR_NO_ERROR;
++}
+diff --git a/xbmc/pvrclients/pvr-demo/PVRDemoData.h b/xbmc/pvrclients/pvr-demo/PVRDemoData.h
+new file mode 100644
+index 0000000..623744f
+--- /dev/null
++++ b/xbmc/pvrclients/pvr-demo/PVRDemoData.h
+@@ -0,0 +1,94 @@
++#pragma once
++/*
++ * Copyright (C) 2011 Pulse-Eight
++ * http://www.pulse-eight.com/
++ *
++ * This Program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2, or (at your option)
++ * any later version.
++ *
++ * This Program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <vector>
++#include "utils/StdString.h"
++#include "client.h"
++
++struct PVRDemoEpgEntry
++{
++ int iBroadcastId;
++ std::string strTitle;
++ int iChannelId;
++ time_t startTime;
++ time_t endTime;
++ std::string strPlotOutline;
++ std::string strPlot;
++ std::string strIconPath;
++ int iGenreType;
++ int iGenreSubType;
++// time_t firstAired;
++// int iParentalRating;
++// int iStarRating;
++// bool bNotify;
++// int iSeriesNumber;
++// int iEpisodeNumber;
++// int iEpisodePartNumber;
++// std::string strEpisodeName;
++};
++
++struct PVRDemoChannel
++{
++ bool bRadio;
++ int iUniqueId;
++ int iChannelNumber;
++ int iEncryptionSystem;
++ std::string strChannelName;
++ std::string strIconPath;
++ std::string strStreamURL;
++ std::vector<PVRDemoEpgEntry> epg;
++};
++
++struct PVRDemoChannelGroup
++{
++ bool bRadio;
++ int iGroupId;
++ std::string strGroupName;
++ std::vector<int> members;
++};
++
++class PVRDemoData
++{
++public:
++ PVRDemoData(void);
++ virtual ~PVRDemoData(void);
++
++ virtual int GetChannelsAmount(void);
++ virtual PVR_ERROR GetChannels(PVR_HANDLE handle, bool bRadio);
++ virtual bool GetChannel(const PVR_CHANNEL &channel, PVRDemoChannel &myChannel);
++
++ virtual int GetChannelGroupsAmount(void);
++ virtual PVR_ERROR GetChannelGroups(PVR_HANDLE handle, bool bRadio);
++ virtual PVR_ERROR GetChannelGroupMembers(PVR_HANDLE handle, const PVR_CHANNEL_GROUP &group);
++
++ virtual PVR_ERROR GetEPGForChannel(PVR_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd);
++
++ virtual std::string GetSettingsFile() const;
++protected:
++ virtual bool LoadDemoData(void);
++private:
++ std::vector<PVRDemoChannelGroup> m_groups;
++ std::vector<PVRDemoChannel> m_channels;
++ time_t m_iEpgStart;
++ CStdString m_strDefaultIcon;
++ CStdString m_strDefaultMovie;
++};
+diff --git a/xbmc/pvrclients/pvr-demo/client.cpp b/xbmc/pvrclients/pvr-demo/client.cpp
+new file mode 100644
+index 0000000..e6c6d43
+--- /dev/null
++++ b/xbmc/pvrclients/pvr-demo/client.cpp
+@@ -0,0 +1,290 @@
++/*
++ * Copyright (C) 2011 Pulse-Eight
++ * http://www.pulse-eight.com/
++ *
++ * This Program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2, or (at your option)
++ * any later version.
++ *
++ * This Program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "client.h"
++#include "xbmc_pvr_dll.h"
++#include "PVRDemoData.h"
++
++using namespace std;
++using namespace ADDON;
++
++bool m_bCreated = false;
++ADDON_STATUS m_CurStatus = ADDON_STATUS_UNKNOWN;
++int g_iClientId = -1;
++PVRDemoData *m_data = NULL;
++bool m_bIsPlaying = false;
++PVRDemoChannel m_currentChannel;
++
++/* User adjustable settings are saved here.
++ * Default values are defined inside client.h
++ * and exported to the other source files.
++ */
++std::string g_strUserPath = "";
++std::string g_strClientPath = "";
++
++CHelper_libXBMC_addon *XBMC = NULL;
++CHelper_libXBMC_pvr *PVR = NULL;
++
++extern "C" {
++
++void ADDON_ReadSettings(void)
++{
++ //STUB
++}
++
++ADDON_STATUS ADDON_Create(void* hdl, void* props)
++{
++ if (!hdl || !props)
++ return ADDON_STATUS_UNKNOWN;
++
++ PVR_PROPERTIES* pvrprops = (PVR_PROPERTIES*)props;
++
++ XBMC = new CHelper_libXBMC_addon;
++ if (!XBMC->RegisterMe(hdl))
++ return ADDON_STATUS_UNKNOWN;
++
++ PVR = new CHelper_libXBMC_pvr;
++ if (!PVR->RegisterMe(hdl))
++ return ADDON_STATUS_UNKNOWN;
++
++ XBMC->Log(LOG_DEBUG, "%s - Creating the PVR demo add-on", __FUNCTION__);
++
++ m_CurStatus = ADDON_STATUS_UNKNOWN;
++ g_iClientId = pvrprops->iClientId;
++ g_strUserPath = pvrprops->strUserPath;
++ g_strClientPath = pvrprops->strClientPath;
++
++ ADDON_ReadSettings();
++
++ m_data = new PVRDemoData;
++ m_CurStatus = ADDON_STATUS_OK;
++ m_bCreated = true;
++ return m_CurStatus;
++}
++
++ADDON_STATUS ADDON_GetStatus()
++{
++ return m_CurStatus;
++}
++
++void ADDON_Destroy()
++{
++ delete m_data;
++ m_bCreated = false;
++ m_CurStatus = ADDON_STATUS_UNKNOWN;
++}
++
++bool ADDON_HasSettings()
++{
++ return true;
++}
++
++unsigned int ADDON_GetSettings(ADDON_StructSetting ***sSet)
++{
++ return 0;
++}
++
++ADDON_STATUS ADDON_SetSetting(const char *settingName, const void *settingValue)
++{
++ return ADDON_STATUS_OK;
++}
++
++void ADDON_Stop()
++{
++}
++
++void ADDON_FreeSettings()
++{
++}
++
++/***********************************************************
++ * PVR Client AddOn specific public library functions
++ ***********************************************************/
++
++PVR_ERROR GetAddonCapabilities(PVR_ADDON_CAPABILITIES* pCapabilities)
++{
++ pCapabilities->bSupportsChannelSettings = false;
++ pCapabilities->bSupportsTimeshift = false;
++ pCapabilities->bSupportsEPG = true;
++ pCapabilities->bSupportsTV = true;
++ pCapabilities->bSupportsRadio = true;
++ pCapabilities->bSupportsRecordings = false;
++ pCapabilities->bSupportsTimers = false;
++ pCapabilities->bSupportsChannelGroups = true;
++ pCapabilities->bSupportsChannelScan = false;
++ pCapabilities->bHandlesInputStream = false;
++ pCapabilities->bHandlesDemuxing = false;
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++const char *GetBackendName(void)
++{
++ static const char *strBackendName = "pulse-eight demo pvr add-on";
++ return strBackendName;
++}
++
++const char *GetBackendVersion(void)
++{
++ static CStdString strBackendVersion = "0.1";
++ return strBackendVersion.c_str();
++}
++
++const char *GetConnectionString(void)
++{
++ static CStdString strConnectionString = "connected";
++ return strConnectionString.c_str();
++}
++
++PVR_ERROR GetDriveSpace(long long *iTotal, long long *iUsed)
++{
++ *iTotal = 1024 * 1024 * 1024;
++ *iUsed = 0;
++ return PVR_ERROR_NO_ERROR;
++}
++
++PVR_ERROR GetEPGForChannel(PVR_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd)
++{
++ if (m_data)
++ return m_data->GetEPGForChannel(handle, channel, iStart, iEnd);
++
++ return PVR_ERROR_SERVER_ERROR;
++}
++
++int GetChannelsAmount(void)
++{
++ if (m_data)
++ return m_data->GetChannelsAmount();
++
++ return -1;
++}
++
++PVR_ERROR GetChannels(PVR_HANDLE handle, bool bRadio)
++{
++ if (m_data)
++ return m_data->GetChannels(handle, bRadio);
++
++ return PVR_ERROR_SERVER_ERROR;
++}
++
++bool OpenLiveStream(const PVR_CHANNEL &channel)
++{
++ if (m_data)
++ {
++ CloseLiveStream();
++
++ if (m_data->GetChannel(channel, m_currentChannel))
++ {
++ m_bIsPlaying = true;
++ return true;
++ }
++ }
++
++ return false;
++}
++
++void CloseLiveStream(void)
++{
++ m_bIsPlaying = false;
++}
++
++int GetCurrentClientChannel(void)
++{
++ return m_currentChannel.iUniqueId;
++}
++
++bool SwitchChannel(const PVR_CHANNEL &channel)
++{
++ CloseLiveStream();
++
++ return OpenLiveStream(channel);
++}
++
++PVR_ERROR GetStreamProperties(PVR_STREAM_PROPERTIES* pProperties)
++{
++ return PVR_ERROR_NOT_IMPLEMENTED;
++}
++
++int GetChannelGroupsAmount(void)
++{
++ if (m_data)
++ return m_data->GetChannelGroupsAmount();
++
++ return -1;
++}
++
++PVR_ERROR GetChannelGroups(PVR_HANDLE handle, bool bRadio)
++{
++ if (m_data)
++ return m_data->GetChannelGroups(handle, bRadio);
++
++ return PVR_ERROR_SERVER_ERROR;
++}
++
++PVR_ERROR GetChannelGroupMembers(PVR_HANDLE handle, const PVR_CHANNEL_GROUP &group)
++{
++ if (m_data)
++ return m_data->GetChannelGroupMembers(handle, group);
++
++ return PVR_ERROR_SERVER_ERROR;
++}
++
++PVR_ERROR SignalStatus(PVR_SIGNAL_STATUS &signalStatus)
++{
++ snprintf(signalStatus.strAdapterName, sizeof(signalStatus.strAdapterName), "pvr demo adapter 1");
++ snprintf(signalStatus.strAdapterStatus, sizeof(signalStatus.strAdapterStatus), "OK");
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++/** UNUSED API FUNCTIONS */
++PVR_ERROR DialogChannelScan(void) { return PVR_ERROR_NOT_IMPLEMENTED; }
++PVR_ERROR CallMenuHook(const PVR_MENUHOOK &menuhook) { return PVR_ERROR_NOT_IMPLEMENTED; }
++PVR_ERROR DeleteChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
++PVR_ERROR RenameChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
++PVR_ERROR MoveChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
++PVR_ERROR DialogChannelSettings(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
++PVR_ERROR DialogAddChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
++bool OpenRecordedStream(const PVR_RECORDING &recording) { return false; }
++void CloseRecordedStream(void) {}
++int ReadRecordedStream(unsigned char *pBuffer, unsigned int iBufferSize) { return 0; }
++long long SeekRecordedStream(long long iPosition, int iWhence /* = SEEK_SET */) { return 0; }
++long long PositionRecordedStream(void) { return -1; }
++long long LengthRecordedStream(void) { return 0; }
++void DemuxReset(void) {}
++void DemuxFlush(void) {}
++int ReadLiveStream(unsigned char *pBuffer, unsigned int iBufferSize) { return 0; }
++long long SeekLiveStream(long long iPosition, int iWhence /* = SEEK_SET */) { return -1; }
++long long PositionLiveStream(void) { return -1; }
++long long LengthLiveStream(void) { return -1; }
++const char * GetLiveStreamURL(const PVR_CHANNEL &channel) { return ""; }
++int GetRecordingsAmount(void) { return -1; }
++PVR_ERROR GetRecordings(PVR_HANDLE handle) { return PVR_ERROR_NOT_IMPLEMENTED; }
++PVR_ERROR DeleteRecording(const PVR_RECORDING &recording) { return PVR_ERROR_NOT_IMPLEMENTED; }
++PVR_ERROR RenameRecording(const PVR_RECORDING &recording) { return PVR_ERROR_NOT_IMPLEMENTED; }
++int GetTimersAmount(void) { return -1; }
++PVR_ERROR GetTimers(PVR_HANDLE handle) { return PVR_ERROR_NOT_IMPLEMENTED; }
++PVR_ERROR AddTimer(const PVR_TIMER &timer) { return PVR_ERROR_NOT_IMPLEMENTED; }
++PVR_ERROR DeleteTimer(const PVR_TIMER &timer, bool bForceDelete) { return PVR_ERROR_NOT_IMPLEMENTED; }
++PVR_ERROR UpdateTimer(const PVR_TIMER &timer) { return PVR_ERROR_NOT_IMPLEMENTED; }
++void DemuxAbort(void) {}
++DemuxPacket* DemuxRead(void) { return NULL; }
++}
+diff --git a/xbmc/pvrclients/pvr-demo/client.h b/xbmc/pvrclients/pvr-demo/client.h
+new file mode 100644
+index 0000000..830a936
+--- /dev/null
++++ b/xbmc/pvrclients/pvr-demo/client.h
+@@ -0,0 +1,30 @@
++#pragma once
++/*
++ * Copyright (C) 2011 Pulse-Eight
++ * http://www.pulse-eight.com/
++ *
++ * This Program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2, or (at your option)
++ * any later version.
++ *
++ * This Program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "../../../addons/library.xbmc.addon/libXBMC_addon.h"
++#include "../../../addons/library.xbmc.pvr/libXBMC_pvr.h"
++
++extern bool m_bCreated;
++extern std::string g_szUserPath;
++extern std::string g_szClientPath;
++extern ADDON::CHelper_libXBMC_addon *XBMC;
++extern CHelper_libXBMC_pvr *PVR;
+diff --git a/xbmc/pvrclients/tvheadend/HTSPConnection.cpp b/xbmc/pvrclients/tvheadend/HTSPConnection.cpp
+new file mode 100644
+index 0000000..7935ba0
+--- /dev/null
++++ b/xbmc/pvrclients/tvheadend/HTSPConnection.cpp
+@@ -0,0 +1,364 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "HTSPConnection.h"
++#include "../../../lib/platform/threads/mutex.h"
++#include "../../../lib/platform/util/timeutils.h"
++#include "../../../lib/platform/sockets/tcp.h"
++#include "client.h"
++
++extern "C" {
++#include "cmyth/include/refmem/atomic.h"
++#include "libhts/htsmsg_binary.h"
++#include "libhts/sha1.h"
++}
++
++using namespace std;
++using namespace ADDON;
++using namespace PLATFORM;
++
++CHTSPConnection::CHTSPConnection() :
++ m_socket(new CTcpConnection(g_strHostname, g_iPortHTSP)),
++ m_challenge(NULL),
++ m_iChallengeLength(0),
++ m_iProtocol(0),
++ m_iPortnumber(g_iPortHTSP),
++ m_iConnectTimeout(g_iConnectTimeout * 1000),
++ m_strUsername(g_strUsername),
++ m_strPassword(g_strPassword),
++ m_strHostname(g_strHostname),
++ m_bIsConnected(false),
++ m_iQueueSize(1000)
++{
++}
++
++CHTSPConnection::~CHTSPConnection()
++{
++ Close();
++ delete m_socket;
++ for (deque<htsmsg_t*>::iterator it = m_queue.begin(); it != m_queue.end();)
++ delete *(it++);
++ m_queue.clear();
++}
++
++bool CHTSPConnection::Connect()
++{
++ {
++ CLockObject lock(m_mutex);
++
++ if (m_bIsConnected)
++ return true;
++
++ if (!m_socket)
++ {
++ XBMC->Log(LOG_ERROR, "%s - failed to connect to the backend (couldn't create a socket)", __FUNCTION__);
++ return false;
++ }
++
++ XBMC->Log(LOG_DEBUG, "%s - connecting to '%s', port '%d'", __FUNCTION__, m_strHostname.c_str(), m_iPortnumber);
++
++ CTimeout timeout(m_iConnectTimeout);
++ while (!m_socket->IsOpen() && timeout.TimeLeft() > 0)
++ {
++ if (!m_socket->Open(timeout.TimeLeft()))
++ CEvent::Sleep(100);
++ }
++
++ if (!m_socket->IsOpen())
++ {
++ XBMC->Log(LOG_ERROR, "%s - failed to connect to the backend (%s)", __FUNCTION__, m_socket->GetError().c_str());
++ return false;
++ }
++
++ m_bIsConnected = true;
++ XBMC->Log(LOG_DEBUG, "%s - connected to '%s', port '%d'", __FUNCTION__, m_strHostname.c_str(), m_iPortnumber);
++ }
++
++ if (!SendGreeting())
++ {
++ XBMC->Log(LOG_ERROR, "%s - failed to read greeting from the backend", __FUNCTION__);
++ Close();
++ return false;
++ }
++
++ if(m_iProtocol < 2)
++ {
++ XBMC->Log(LOG_ERROR, "%s - incompatible protocol version %d", __FUNCTION__, m_iProtocol);
++ Close();
++ return false;
++ }
++
++ if (!Auth())
++ {
++ XBMC->Log(LOG_ERROR, "%s - failed to authenticate", __FUNCTION__);
++ Close();
++ return false;
++ }
++
++ return true;
++}
++
++void CHTSPConnection::Close()
++{
++ CLockObject lock(m_mutex);
++ m_bIsConnected = false;
++
++ if(m_socket && m_socket->IsOpen())
++ m_socket->Close();
++
++ if(m_challenge)
++ {
++ free(m_challenge);
++ m_challenge = NULL;
++ m_iChallengeLength = 0;
++ }
++}
++
++void CHTSPConnection::Abort(void)
++{
++ CLockObject lock(m_mutex);
++ m_bIsConnected = false;
++
++ if(m_socket && m_socket->IsOpen())
++ m_socket->Shutdown();
++}
++
++htsmsg_t* CHTSPConnection::ReadMessage(int iInitialTimeout /* = 10000 */, int iDatapacketTimeout /* = 10000 */)
++{
++ void* buf;
++ uint32_t l;
++
++ if(m_queue.size())
++ {
++ htsmsg_t* m = m_queue.front();
++ m_queue.pop_front();
++ return m;
++ }
++
++ {
++ CLockObject lock(m_mutex);
++ if (!IsConnected())
++ {
++ XBMC->Log(LOG_ERROR, "%s - not connected", __FUNCTION__);
++ return NULL;
++ }
++
++ if (m_socket->Read(&l, 4, iInitialTimeout) != 4)
++ {
++ if(m_socket->GetErrorNumber() == ETIMEDOUT)
++ return htsmsg_create_map();
++
++ XBMC->Log(LOG_ERROR, "%s - Failed to read packet size (%s)", __FUNCTION__, m_socket->GetError().c_str());
++ return NULL;
++ }
++
++ l = ntohl(l);
++ if(l == 0)
++ return htsmsg_create_map();
++
++ buf = malloc(l);
++
++ if(m_socket->Read(buf, l, iDatapacketTimeout) != (ssize_t)l)
++ {
++ XBMC->Log(LOG_ERROR, "%s - Failed to read packet (%s)", __FUNCTION__, m_socket->GetError().c_str());
++ free(buf);
++ Close();
++ return NULL;
++ }
++ }
++
++ return htsmsg_binary_deserialize(buf, l, buf); /* consumes 'buf' */
++}
++
++bool CHTSPConnection::TransmitMessage(htsmsg_t* m)
++{
++ void* buf;
++ size_t len;
++
++ if (!IsConnected())
++ {
++ XBMC->Log(LOG_ERROR, "%s - not connected", __FUNCTION__);
++ return NULL;
++ }
++
++ if(htsmsg_binary_serialize(m, &buf, &len, -1) < 0)
++ {
++ htsmsg_destroy(m);
++ return false;
++ }
++ htsmsg_destroy(m);
++
++ CLockObject lock(m_mutex);
++ ssize_t iWriteResult = m_socket->Write(buf, len);
++ if (iWriteResult != (ssize_t)len)
++ {
++ XBMC->Log(LOG_ERROR, "%s - Failed to write packet (%s)", __FUNCTION__, m_socket->GetError().c_str());
++ free(buf);
++ Close();
++ return false;
++ }
++ free(buf);
++ return true;
++}
++
++htsmsg_t* CHTSPConnection::ReadResult(htsmsg_t* m, bool sequence)
++{
++ uint32_t iSequence = 0;
++ if(sequence)
++ {
++ iSequence = mvp_atomic_inc(&g_iPacketSequence);
++ htsmsg_add_u32(m, "seq", iSequence);
++ }
++
++ if(!TransmitMessage(m))
++ return NULL;
++
++ std::deque<htsmsg_t*> queue;
++ m_queue.swap(queue);
++
++ while((m = ReadMessage()))
++ {
++ uint32_t seq;
++ if(!sequence)
++ break;
++ if(!htsmsg_get_u32(m, "seq", &seq) && seq == iSequence)
++ break;
++
++ queue.push_back(m);
++ if(queue.size() >= m_iQueueSize)
++ {
++ XBMC->Log(LOG_ERROR, "%s - maximum queue size (%u) reached", __FUNCTION__, m_iQueueSize);
++ m_queue.swap(queue);
++ return NULL;
++ }
++ }
++
++ m_queue.swap(queue);
++
++ const char* error;
++ if(m && (error = htsmsg_get_str(m, "error")))
++ {
++ XBMC->Log(LOG_ERROR, "%s - error (%s)", __FUNCTION__, error);
++ htsmsg_destroy(m);
++ return NULL;
++ }
++ uint32_t noaccess;
++ if(m && !htsmsg_get_u32(m, "noaccess", &noaccess) && noaccess)
++ {
++
++ XBMC->Log(LOG_ERROR, "%s - access denied (%d)", __FUNCTION__, noaccess);
++ XBMC->QueueNotification(QUEUE_ERROR, "access denied (%d)", noaccess);
++ htsmsg_destroy(m);
++ return NULL;
++ }
++
++ return m;
++}
++
++bool CHTSPConnection::ReadSuccess(htsmsg_t* m, bool sequence, std::string action)
++{
++ if((m = ReadResult(m, sequence)) == NULL)
++ {
++ XBMC->Log(LOG_DEBUG, "%s - failed to %s", __FUNCTION__, action.c_str());
++ return false;
++ }
++ htsmsg_destroy(m);
++ return true;
++}
++
++bool CHTSPConnection::SendGreeting(void)
++{
++ htsmsg_t *m;
++ const char *method, *server, *version;
++ const void * chall = NULL;
++ size_t chall_len = 0;
++ int32_t proto = 0;
++
++ /* send hello */
++ m = htsmsg_create_map();
++ htsmsg_add_str(m, "method", "hello");
++ htsmsg_add_str(m, "clientname", "XBMC Media Center");
++ htsmsg_add_u32(m, "htspversion", 1);
++
++ /* read welcome */
++ if((m = ReadResult(m)) == NULL)
++ return false;
++
++ method = htsmsg_get_str(m, "method");
++ htsmsg_get_s32(m, "htspversion", &proto);
++ server = htsmsg_get_str(m, "servername");
++ version = htsmsg_get_str(m, "serverversion");
++ htsmsg_get_bin(m, "challenge", &chall, &chall_len);
++
++ m_strServerName = server;
++ m_strVersion = version;
++ m_iProtocol = proto;
++
++ if(chall && chall_len)
++ {
++ m_challenge = malloc(chall_len);
++ m_iChallengeLength = chall_len;
++ memcpy(m_challenge, chall, chall_len);
++ }
++
++ htsmsg_destroy(m);
++
++ return true;
++}
++
++bool CHTSPConnection::Auth(void)
++{
++ if (m_strUsername.empty())
++ {
++ XBMC->Log(LOG_DEBUG, "CHTSPConnection - %s - no username set. not authenticating", __FUNCTION__);
++ return true;
++ }
++
++ htsmsg_t *m = htsmsg_create_map();
++ htsmsg_add_str(m, "method" , "authenticate");
++ htsmsg_add_str(m, "username", m_strUsername.c_str());
++
++ if(m_strPassword != "" && m_challenge)
++ {
++ XBMC->Log(LOG_DEBUG, "CHTSPConnection - %s - authenticating as user '%s' with a password", __FUNCTION__, m_strUsername.c_str());
++
++ struct HTSSHA1* shactx = (struct HTSSHA1*) malloc(hts_sha1_size);
++ uint8_t d[20];
++ hts_sha1_init(shactx);
++ hts_sha1_update(shactx, (const uint8_t *) m_strPassword.c_str(), m_strPassword.length());
++ hts_sha1_update(shactx, (const uint8_t *) m_challenge, m_iChallengeLength);
++ hts_sha1_final(shactx, d);
++ htsmsg_add_bin(m, "digest", d, 20);
++ free(shactx);
++ }
++ else
++ {
++ XBMC->Log(LOG_DEBUG, "CHTSPConnection - %s - authenticating as user '%s' without a password", __FUNCTION__, m_strUsername.c_str());
++ }
++
++ return ReadSuccess(m, false, "get reply from authentication with server");
++}
++
++bool CHTSPConnection::IsConnected(void)
++{
++ CLockObject lock(m_mutex);
++ return m_bIsConnected && m_socket && m_socket->IsOpen();
++}
+diff --git a/xbmc/pvrclients/tvheadend/HTSPConnection.h b/xbmc/pvrclients/tvheadend/HTSPConnection.h
+new file mode 100644
+index 0000000..6dfeb2f
+--- /dev/null
++++ b/xbmc/pvrclients/tvheadend/HTSPConnection.h
+@@ -0,0 +1,76 @@
++#pragma once
++
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "HTSPTypes.h"
++#include "../../../lib/platform/threads/mutex.h"
++
++extern "C" {
++#include "libhts/net.h"
++#include "libhts/htsmsg.h"
++}
++
++namespace PLATFORM
++{
++ class CTcpConnection;
++}
++
++class CHTSPConnection
++{
++public:
++ CHTSPConnection();
++ ~CHTSPConnection();
++
++ bool Connect(void);
++ void Close();
++ void Abort();
++ bool IsConnected(void);
++ int GetProtocol() const { return m_iProtocol; }
++ const char *GetServerName() const { return m_strServerName.c_str(); }
++ const char *GetVersion() const { return m_strVersion.c_str(); }
++
++ htsmsg_t * ReadMessage(int iInitialTimeout = 10000, int iDatapacketTimeout = 10000);
++ bool TransmitMessage(htsmsg_t* m);
++ htsmsg_t * ReadResult (htsmsg_t* m, bool sequence = true);
++ bool ReadSuccess(htsmsg_t* m, bool sequence = true, std::string action = "");
++
++private:
++ bool SendGreeting(void);
++ bool Auth(void);
++
++ PLATFORM::CMutex m_mutex;
++ PLATFORM::CTcpConnection* m_socket;
++ void* m_challenge;
++ int m_iChallengeLength;
++ int m_iProtocol;
++ int m_iPortnumber;
++ int m_iConnectTimeout;
++ std::string m_strServerName;
++ std::string m_strUsername;
++ std::string m_strPassword;
++ std::string m_strVersion;
++ std::string m_strHostname;
++ bool m_bIsConnected;
++
++ std::deque<htsmsg_t*> m_queue;
++ const unsigned int m_iQueueSize;
++};
+diff --git a/xbmc/pvrclients/tvheadend/HTSPData.cpp b/xbmc/pvrclients/tvheadend/HTSPData.cpp
+new file mode 100644
+index 0000000..90cb196
+--- /dev/null
++++ b/xbmc/pvrclients/tvheadend/HTSPData.cpp
+@@ -0,0 +1,1117 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "HTSPData.h"
++
++extern "C" {
++#include "cmyth/include/refmem/atomic.h"
++#include "libhts/htsmsg.h"
++#include "libhts/htsmsg_binary.h"
++}
++
++typedef enum {
++ DVR_PRIO_IMPORTANT,
++ DVR_PRIO_HIGH,
++ DVR_PRIO_NORMAL,
++ DVR_PRIO_LOW,
++ DVR_PRIO_UNIMPORTANT,
++} dvr_prio_t;
++
++using namespace std;
++using namespace ADDON;
++using namespace PLATFORM;
++
++CHTSPData::CHTSPData()
++{
++ m_session = new CHTSPConnection();
++ m_bDisconnectWarningDisplayed = false;
++ m_bIsStarted = false;
++}
++
++CHTSPData::~CHTSPData()
++{
++ Close();
++ delete m_session;
++}
++
++bool CHTSPData::Open()
++{
++ CLockObject lock(m_mutex);
++ if(!m_session->Connect())
++ {
++ /* failed to connect */
++ return false;
++ }
++
++ if(!SendEnableAsync())
++ {
++ XBMC->Log(LOG_ERROR, "%s - couldn't send EnableAsync().", __FUNCTION__);
++ return false;
++ }
++
++ CreateThread();
++ m_started.Wait(m_mutex, m_bIsStarted, g_iConnectTimeout * 1000);
++
++ return IsRunning();
++}
++
++void CHTSPData::Close()
++{
++ m_session->Close();
++ StopThread();
++}
++
++bool CHTSPData::CheckConnection(void)
++{
++ bool bReturn(true);
++
++ if (!m_session->IsConnected() && m_bCreated && !IsStopped())
++ {
++ if (!m_bDisconnectWarningDisplayed)
++ {
++ m_bDisconnectWarningDisplayed = true;
++ CStdString strNotification(XBMC->GetLocalizedString(30500));
++ XBMC->QueueNotification(QUEUE_ERROR, strNotification, m_session->GetServerName());
++ }
++
++ if ((bReturn = m_session->Connect() && SendEnableAsync()))
++ {
++ m_bDisconnectWarningDisplayed = false;
++ /* notify the user that the connection has been restored */
++ CStdString strNotification(XBMC->GetLocalizedString(30501));
++ XBMC->QueueNotification(QUEUE_INFO, strNotification, m_session->GetServerName());
++ }
++ }
++
++ return bReturn;
++}
++
++void CHTSPData::ReadResult(htsmsg_t *m, CHTSResult &result)
++{
++ if (!m_session || !m_session->IsConnected())
++ {
++ htsmsg_destroy(m);
++ result.status = PVR_ERROR_NOT_POSSIBLE;
++ return;
++ }
++
++ uint32_t seq = mvp_atomic_inc(&g_iPacketSequence);
++
++ SMessage &message(m_queue[seq]);
++ message.event = new CEvent;
++ message.msg = NULL;
++
++ htsmsg_add_u32(m, "seq", seq);
++ if(!m_session->TransmitMessage(m))
++ {
++ XBMC->Log(LOG_ERROR, "%s - failed to send command", __FUNCTION__);
++ result.status = PVR_ERROR_UNKNOWN;
++ }
++ else if(!message.event->Wait(g_iResponseTimeout * 1000))
++ {
++ XBMC->Log(LOG_ERROR, "%s - request timed out after %d seconds", __FUNCTION__, g_iResponseTimeout);
++ result.status = PVR_ERROR_SERVER_TIMEOUT;
++ }
++ else
++ {
++ result.message = message.msg;
++ }
++
++ {
++ CLockObject lock(m_mutex);
++ delete message.event;
++ m_queue.erase(seq);
++ }
++}
++
++bool CHTSPData::GetDriveSpace(long long *total, long long *used)
++{
++ htsmsg_t *msg = htsmsg_create_map();
++ htsmsg_add_str(msg, "method", "getDiskSpace");
++
++ CHTSResult result;
++ ReadResult(msg, result);
++ if (result.status != PVR_ERROR_NO_ERROR)
++ {
++ XBMC->Log(LOG_DEBUG, "%s - failed to get getDiskSpace", __FUNCTION__);
++ return false;
++ }
++
++ int64_t freespace;
++ if (htsmsg_get_s64(result.message, "freediskspace", &freespace) != 0)
++ return false;
++
++ int64_t totalspace;
++ if (htsmsg_get_s64(result.message, "totaldiskspace", &totalspace) != 0)
++ return false;
++
++ *total = totalspace / 1024;
++ *used = (totalspace - freespace) / 1024;
++ return true;
++}
++
++bool CHTSPData::GetBackendTime(time_t *utcTime, int *gmtOffset)
++{
++ htsmsg_t *msg = htsmsg_create_map();
++ htsmsg_add_str(msg, "method", "getSysTime");
++
++ CHTSResult result;
++ ReadResult(msg, result);
++ if (result.status != PVR_ERROR_NO_ERROR)
++ {
++ XBMC->Log(LOG_ERROR, "%s - failed to get sysTime", __FUNCTION__);
++ return false;
++ }
++
++ unsigned int secs;
++ if (htsmsg_get_u32(result.message, "time", &secs) != 0)
++ return false;
++
++ int offset;
++ if (htsmsg_get_s32(result.message, "timezone", &offset) != 0)
++ return false;
++
++ XBMC->Log(LOG_DEBUG, "%s - tvheadend reported time=%u, timezone=%d, correction=%d"
++ , __FUNCTION__, secs, offset);
++
++ *utcTime = secs;
++ *gmtOffset = offset;
++
++ return true;
++}
++
++unsigned int CHTSPData::GetNumChannels()
++{
++ return GetChannels().size();
++}
++
++PVR_ERROR CHTSPData::GetChannels(PVR_HANDLE handle, bool bRadio)
++{
++ SChannels channels = GetChannels();
++ for(SChannels::iterator it = channels.begin(); it != channels.end(); ++it)
++ {
++ SChannel& channel = it->second;
++ if(bRadio != channel.radio)
++ continue;
++
++ PVR_CHANNEL tag;
++ memset(&tag, 0 , sizeof(PVR_CHANNEL));
++
++ tag.iUniqueId = channel.id;
++ tag.bIsRadio = channel.radio;
++ tag.iChannelNumber = channel.num;
++ tag.strChannelName = channel.name.c_str();
++ tag.strInputFormat = ""; // unused
++ tag.strStreamURL = ""; // unused
++ tag.iEncryptionSystem = channel.caid;
++ tag.strIconPath = channel.icon.c_str();
++ tag.bIsHidden = false;
++
++ PVR->TransferChannelEntry(handle, &tag);
++ }
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++PVR_ERROR CHTSPData::GetEpg(PVR_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd)
++{
++ PVR_ERROR retVal = PVR_ERROR_NO_ERROR;
++ SChannels channels = GetChannels();
++
++ if (channels.find(channel.iUniqueId) != channels.end())
++ {
++ time_t stop;
++
++ SEvent ev;
++ ev.id = channels[channel.iUniqueId].event;
++ if (ev.id == 0)
++ return retVal;
++
++ do
++ {
++ PVR_ERROR result = GetEvent(ev, ev.id);
++ if (result == PVR_ERROR_NO_ERROR)
++ {
++ EPG_TAG broadcast;
++ memset(&broadcast, 0, sizeof(EPG_TAG));
++
++ broadcast.iUniqueBroadcastId = ev.id;
++ broadcast.strTitle = ev.title.c_str();
++ broadcast.iChannelNumber = ev.chan_id >= 0 ? ev.chan_id : channel.iUniqueId;
++ broadcast.startTime = ev.start;
++ broadcast.endTime = ev.stop;
++ broadcast.strPlotOutline = ""; // unused
++ broadcast.strPlot = ev.descs.c_str();
++ broadcast.strIconPath = ""; // unused
++ broadcast.iGenreType = (ev.content & 0x0F) << 4;
++ broadcast.iGenreSubType = ev.content & 0xF0;
++ broadcast.strGenreDescription = "";
++ broadcast.firstAired = 0; // unused
++ broadcast.iParentalRating = 0; // unused
++ broadcast.iStarRating = 0; // unused
++ broadcast.bNotify = false;
++ broadcast.iSeriesNumber = 0; // unused
++ broadcast.iEpisodeNumber = 0; // unused
++ broadcast.iEpisodePartNumber = 0; // unused
++ broadcast.strEpisodeName = ""; // unused
++
++ PVR->TransferEpgEntry(handle, &broadcast);
++
++ ev.id = ev.next;
++ stop = ev.stop;
++ }
++ else
++ {
++ retVal = result;
++ break;
++ }
++
++ } while(iEnd > stop && ev.id != 0);
++ }
++ else
++ {
++ retVal = PVR_ERROR_UNKNOWN;
++ }
++
++ return retVal;
++}
++
++SRecordings CHTSPData::GetDVREntries(bool recorded, bool scheduled)
++{
++ CLockObject lock(m_mutex);
++ SRecordings recordings;
++
++ for(SRecordings::const_iterator it = m_recordings.begin(); it != m_recordings.end(); ++it)
++ {
++ SRecording recording = it->second;
++
++ if ((recorded && (recording.state == ST_COMPLETED || recording.state == ST_ABORTED || recording.state == ST_RECORDING)) ||
++ (scheduled && (recording.state == ST_SCHEDULED || recording.state == ST_RECORDING)))
++ recordings[recording.id] = recording;
++ }
++
++ return recordings;
++}
++
++unsigned int CHTSPData::GetNumRecordings()
++{
++ SRecordings recordings = GetDVREntries(true, false);
++ return recordings.size();
++}
++
++PVR_ERROR CHTSPData::GetRecordings(PVR_HANDLE handle)
++{
++ SRecordings recordings = GetDVREntries(true, false);
++
++ for(SRecordings::const_iterator it = recordings.begin(); it != recordings.end(); ++it)
++ {
++ SRecording recording = it->second;
++
++ CStdString strStreamURL = "http://";
++ CStdString strRecordingId;
++ std::string strChannelName = "";
++
++ /* lock */
++ {
++ CLockObject lock(m_mutex);
++ SChannels::const_iterator itr = m_channels.find(recording.channel);
++ if (itr != m_channels.end())
++ strChannelName = itr->second.name.c_str();
++
++ if (g_strUsername != "")
++ {
++ strStreamURL += g_strUsername;
++ if (g_strPassword != "")
++ {
++ strStreamURL += ":";
++ strStreamURL += g_strPassword;
++ }
++ strStreamURL += "@";
++ }
++ strStreamURL.Format("%s%s:%i/dvrfile/%i", strStreamURL.c_str(), g_strHostname.c_str(), g_iPortHTTP, recording.id);
++ }
++
++ strRecordingId.Format("%i", recording.id);
++
++ PVR_RECORDING tag;
++ memset(&tag, 0, sizeof(PVR_RECORDING));
++
++ tag.strRecordingId = strRecordingId.c_str();
++ tag.strTitle = recording.title.c_str();
++ tag.strStreamURL = strStreamURL.c_str();
++ tag.strDirectory = "/";
++ tag.strPlotOutline = "";
++ tag.strPlot = recording.description.c_str();
++ tag.strChannelName = strChannelName.c_str();
++ tag.recordingTime = recording.start;
++ tag.iDuration = recording.stop - recording.start;
++ tag.iPriority = 0;
++ tag.iLifetime = 0;
++ tag.iGenreType = 0;
++ tag.iGenreSubType = 0;
++
++ PVR->TransferRecordingEntry(handle, &tag);
++ }
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++PVR_ERROR CHTSPData::DeleteRecording(const PVR_RECORDING &recording)
++{
++ XBMC->Log(LOG_DEBUG, "%s", __FUNCTION__);
++
++ htsmsg_t *msg = htsmsg_create_map();
++ htsmsg_add_str(msg, "method", "deleteDvrEntry");
++ htsmsg_add_u32(msg, "id", atoi(recording.strRecordingId));
++
++ CHTSResult result;
++ ReadResult(msg, result);
++ if (result.status != PVR_ERROR_NO_ERROR)
++ {
++ XBMC->Log(LOG_DEBUG, "%s - Failed to get deleteDvrEntry", __FUNCTION__);
++ return result.status;
++ }
++
++ unsigned int success;
++ if (htsmsg_get_u32(result.message, "success", &success) != 0)
++ {
++ XBMC->Log(LOG_DEBUG, "%s - Failed to parse param", __FUNCTION__);
++ return PVR_ERROR_SERVER_ERROR;
++ }
++
++ return success > 0 ? PVR_ERROR_NO_ERROR : PVR_ERROR_NOT_DELETED;
++}
++
++unsigned int CHTSPData::GetNumTimers()
++{
++ SRecordings recordings = GetDVREntries(false, true);
++ return recordings.size();
++}
++
++unsigned int CHTSPData::GetNumChannelGroups(void)
++{
++ return m_tags.size();
++}
++
++PVR_ERROR CHTSPData::GetChannelGroups(PVR_HANDLE handle)
++{
++ for(unsigned int iTagPtr = 0; iTagPtr < m_tags.size(); iTagPtr++)
++ {
++ PVR_CHANNEL_GROUP tag;
++ memset(&tag, 0 , sizeof(PVR_CHANNEL_GROUP));
++
++ tag.bIsRadio = false;
++ tag.strGroupName = m_tags[iTagPtr].name.c_str();
++
++ PVR->TransferChannelGroup(handle, &tag);
++ }
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++PVR_ERROR CHTSPData::GetChannelGroupMembers(PVR_HANDLE handle, const PVR_CHANNEL_GROUP &group)
++{
++ XBMC->Log(LOG_DEBUG, "%s - group '%s'", __FUNCTION__, group.strGroupName);
++
++ for(unsigned int iTagPtr = 0; iTagPtr < m_tags.size(); iTagPtr++)
++ {
++ if (m_tags[iTagPtr].name != group.strGroupName)
++ continue;
++
++ SChannels channels = GetChannels(m_tags[iTagPtr].id);
++
++ for(SChannels::iterator it = channels.begin(); it != channels.end(); ++it)
++ {
++ SChannel& channel = it->second;
++ if (channel.radio != group.bIsRadio)
++ continue;
++
++ PVR_CHANNEL_GROUP_MEMBER tag;
++ memset(&tag,0 , sizeof(PVR_CHANNEL_GROUP_MEMBER));
++
++ tag.strGroupName = group.strGroupName;
++ tag.iChannelUniqueId = channel.id;
++ tag.iChannelNumber = channel.num;
++
++ XBMC->Log(LOG_DEBUG, "%s - add channel %s (%d) to group '%s' channel number %d",
++ __FUNCTION__, channel.name.c_str(), tag.iChannelUniqueId, group.strGroupName, channel.num);
++
++ PVR->TransferChannelGroupMember(handle, &tag);
++ }
++ }
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++PVR_ERROR CHTSPData::GetTimers(PVR_HANDLE handle)
++{
++ SRecordings recordings = GetDVREntries(false, true);
++
++ for(SRecordings::const_iterator it = recordings.begin(); it != recordings.end(); ++it)
++ {
++ SRecording recording = it->second;
++
++ PVR_TIMER tag;
++ memset(&tag, 0, sizeof(PVR_TIMER));
++
++ tag.iClientIndex = recording.id;
++ tag.iClientChannelUid = recording.channel;
++ tag.startTime = recording.start;
++ tag.endTime = recording.stop;
++ tag.strTitle = recording.title.c_str();
++ tag.strDirectory = "/"; // unused
++ tag.strSummary = recording.description.c_str();
++ tag.state = (PVR_TIMER_STATE) recording.state;
++ tag.iPriority = 0; // unused
++ tag.iLifetime = 0; // unused
++ tag.bIsRepeating = false; // unused
++ tag.firstDay = 0; // unused
++ tag.iWeekdays = 0; // unused
++ tag.iEpgUid = 0; // unused
++ tag.iMarginStart = 0; // unused
++ tag.iMarginEnd = 0; // unused
++ tag.iGenreType = 0; // unused
++ tag.iGenreSubType = 0; // unused
++
++ PVR->TransferTimerEntry(handle, &tag);
++ }
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++PVR_ERROR CHTSPData::DeleteTimer(const PVR_TIMER &timer, bool bForce)
++{
++ XBMC->Log(LOG_DEBUG, "%s", __FUNCTION__);
++
++ htsmsg_t *msg = htsmsg_create_map();
++ htsmsg_add_str(msg, "method", "cancelDvrEntry");
++ htsmsg_add_u32(msg, "id", timer.iClientIndex);
++
++ CHTSResult result;
++ ReadResult(msg, result);
++ if (result.status != PVR_ERROR_NO_ERROR)
++ {
++ XBMC->Log(LOG_DEBUG, "%s - Failed to get cancelDvrEntry", __FUNCTION__);
++ return result.status;
++ }
++
++ const char *strError = NULL;
++ if ((strError = htsmsg_get_str(result.message, "error")))
++ {
++ XBMC->Log(LOG_DEBUG, "%s - Error deleting timer: '%s'", __FUNCTION__, strError);
++ return PVR_ERROR_SERVER_ERROR;
++ }
++
++ unsigned int success;
++ if (htsmsg_get_u32(result.message, "success", &success) != 0)
++ {
++ XBMC->Log(LOG_DEBUG, "%s - Failed to parse param", __FUNCTION__);
++ return PVR_ERROR_SERVER_ERROR;
++ }
++
++ return success > 0 ? PVR_ERROR_NO_ERROR : PVR_ERROR_NOT_DELETED;
++}
++
++PVR_ERROR CHTSPData::AddTimer(const PVR_TIMER &timer)
++{
++ XBMC->Log(LOG_DEBUG, "%s - channelUid=%d title=%s epgid=%d", __FUNCTION__, timer.iClientChannelUid, timer.strTitle, timer.iEpgUid);
++
++ time_t startTime = timer.startTime;
++ if (startTime <= 0)
++ {
++ int iGmtOffset;
++ GetBackendTime(&startTime, &iGmtOffset);
++ }
++
++ dvr_prio_t prio = DVR_PRIO_UNIMPORTANT;
++ if (timer.iPriority <= 20)
++ prio = DVR_PRIO_UNIMPORTANT;
++ else if (timer.iPriority <= 40)
++ prio = DVR_PRIO_LOW;
++ else if (timer.iPriority <= 60)
++ prio = DVR_PRIO_NORMAL;
++ else if (timer.iPriority <= 80)
++ prio = DVR_PRIO_HIGH;
++ else
++ prio = DVR_PRIO_IMPORTANT;
++
++ htsmsg_t *msg = htsmsg_create_map();
++ htsmsg_add_str(msg, "method", "addDvrEntry");
++ htsmsg_add_u32(msg, "eventId", -1); // XXX tvheadend doesn't correct epg tags with wrong start and end times, so we'll use xbmc's values
++ htsmsg_add_str(msg, "title", timer.strTitle);
++ htsmsg_add_u32(msg, "start", startTime);
++ htsmsg_add_u32(msg, "stop", timer.endTime);
++ htsmsg_add_u32(msg, "channelId", timer.iClientChannelUid);
++ htsmsg_add_u32(msg, "priority", prio);
++ htsmsg_add_str(msg, "description", timer.strSummary);
++ htsmsg_add_str(msg, "creator", "XBMC");
++
++ CHTSResult result;
++ ReadResult(msg, result);
++ if (result.status != PVR_ERROR_NO_ERROR)
++ {
++ XBMC->Log(LOG_DEBUG, "%s - Failed to get addDvrEntry", __FUNCTION__);
++ return result.status;
++ }
++
++ const char *strError = NULL;
++ if ((strError = htsmsg_get_str(result.message, "error")))
++ {
++ XBMC->Log(LOG_DEBUG, "%s - Error adding timer: '%s'", __FUNCTION__, strError);
++ return PVR_ERROR_SERVER_ERROR;
++ }
++
++ unsigned int success;
++ if (htsmsg_get_u32(result.message, "success", &success) != 0)
++ {
++ XBMC->Log(LOG_DEBUG, "%s - Failed to parse param", __FUNCTION__);
++ return PVR_ERROR_SERVER_ERROR;
++ }
++
++ return success > 0 ? PVR_ERROR_NO_ERROR : PVR_ERROR_NOT_DELETED;
++}
++
++PVR_ERROR CHTSPData::UpdateTimer(const PVR_TIMER &timer)
++{
++ XBMC->Log(LOG_DEBUG, "%s - channelUid=%d title=%s epgid=%d", __FUNCTION__, timer.iClientChannelUid, timer.strTitle, timer.iEpgUid);
++
++ htsmsg_t *msg = htsmsg_create_map();
++ htsmsg_add_str(msg, "method", "updateDvrEntry");
++ htsmsg_add_u32(msg, "id", timer.iClientIndex);
++ htsmsg_add_str(msg, "title", timer.strTitle);
++ htsmsg_add_u32(msg, "start", timer.startTime);
++ htsmsg_add_u32(msg, "stop", timer.endTime);
++
++ CHTSResult result;
++ ReadResult(msg, result);
++ if (result.status != PVR_ERROR_NO_ERROR)
++ {
++ XBMC->Log(LOG_DEBUG, "%s - Failed to get updateDvrEntry", __FUNCTION__);
++ return result.status;
++ }
++
++ unsigned int success;
++ if (htsmsg_get_u32(result.message, "success", &success) != 0)
++ {
++ XBMC->Log(LOG_DEBUG, "%s - Failed to parse param", __FUNCTION__);
++ return PVR_ERROR_SERVER_ERROR;
++ }
++
++ return success > 0 ? PVR_ERROR_NO_ERROR : PVR_ERROR_NOT_SAVED;
++}
++
++PVR_ERROR CHTSPData::RenameRecording(const PVR_RECORDING &recording, const char *strNewName)
++{
++ XBMC->Log(LOG_DEBUG, "%s - id=%s", __FUNCTION__, recording.strRecordingId);
++
++ htsmsg_t *msg = htsmsg_create_map();
++ htsmsg_add_str(msg, "method", "updateDvrEntry");
++ htsmsg_add_u32(msg, "id", atoi(recording.strRecordingId));
++ htsmsg_add_str(msg, "title", recording.strTitle);
++
++ CHTSResult result;
++ ReadResult(msg, result);
++ if (result.status != PVR_ERROR_NO_ERROR)
++ {
++ XBMC->Log(LOG_DEBUG, "%s - Failed to get updateDvrEntry", __FUNCTION__);
++ return result.status;
++ }
++
++ unsigned int success;
++ if (htsmsg_get_u32(result.message, "success", &success) != 0)
++ {
++ XBMC->Log(LOG_DEBUG, "%s - Failed to parse param", __FUNCTION__);
++ return PVR_ERROR_SERVER_ERROR;
++ }
++
++ return success > 0 ? PVR_ERROR_NO_ERROR : PVR_ERROR_NOT_SAVED;
++}
++
++
++void *CHTSPData::Process()
++{
++ XBMC->Log(LOG_DEBUG, "%s - starting", __FUNCTION__);
++
++ bool bInitialised(false);
++ htsmsg_t* msg;
++ while (!IsStopped())
++ {
++ if (!bInitialised && !m_session->IsConnected())
++ break;
++
++ if (!CheckConnection())
++ {
++ Sleep(1000);
++ continue;
++ }
++
++ /* if there's anything in the buffer, read it */
++ msg = m_session->ReadMessage(5);
++ if(msg == NULL || msg->hm_data == NULL)
++ {
++ if (msg)
++ htsmsg_destroy(msg);
++ Sleep(5);
++ continue;
++ }
++
++ uint32_t seq;
++ if(htsmsg_get_u32(msg, "seq", &seq) == 0)
++ {
++ CLockObject lock(m_mutex);
++ SMessages::iterator it = m_queue.find(seq);
++ if(it != m_queue.end())
++ {
++ it->second.msg = msg;
++ it->second.event->Broadcast();
++ continue;
++ }
++ }
++
++ const char* method;
++ if((method = htsmsg_get_str(msg, "method")) == NULL)
++ {
++ htsmsg_destroy(msg);
++ continue;
++ }
++
++ CLockObject lock(m_mutex);
++ if (strstr(method, "channelAdd"))
++ ParseChannelUpdate(msg);
++ else if(strstr(method, "channelUpdate"))
++ ParseChannelUpdate(msg);
++ else if(strstr(method, "channelDelete"))
++ ParseChannelRemove(msg);
++ else if(strstr(method, "tagAdd"))
++ ParseTagUpdate(msg);
++ else if(strstr(method, "tagUpdate"))
++ ParseTagUpdate(msg);
++ else if(strstr(method, "tagDelete"))
++ ParseTagRemove(msg);
++ else if(strstr(method, "initialSyncCompleted"))
++ {
++ CLockObject lock(m_mutex);
++ bInitialised = true;
++ m_bIsStarted = true;
++ m_started.Broadcast();
++ }
++ else if(strstr(method, "dvrEntryAdd"))
++ ParseDVREntryUpdate(msg);
++ else if(strstr(method, "dvrEntryUpdate"))
++ ParseDVREntryUpdate(msg);
++ else if(strstr(method, "dvrEntryDelete"))
++ ParseDVREntryDelete(msg);
++ else
++ XBMC->Log(LOG_DEBUG, "%s - Unmapped action recieved '%s'", __FUNCTION__, method);
++
++ htsmsg_destroy(msg);
++ }
++
++ CLockObject lock(m_mutex);
++ m_started.Broadcast();
++ XBMC->Log(LOG_DEBUG, "%s - exiting", __FUNCTION__);
++ return NULL;
++}
++
++SChannels CHTSPData::GetChannels()
++{
++ return GetChannels(0);
++}
++
++SChannels CHTSPData::GetChannels(int tag)
++{
++ CLockObject lock(m_mutex);
++ if(tag == 0)
++ return m_channels;
++
++ STags::iterator it = m_tags.find(tag);
++ if(it == m_tags.end())
++ {
++ SChannels channels;
++ return channels;
++ }
++ return GetChannels(it->second);
++}
++
++SChannels CHTSPData::GetChannels(STag& tag)
++{
++ CLockObject lock(m_mutex);
++ SChannels channels;
++
++ std::vector<int>::iterator it;
++ for(it = tag.channels.begin(); it != tag.channels.end(); it++)
++ {
++ SChannels::iterator it2 = m_channels.find(*it);
++ if(it2 == m_channels.end())
++ {
++ XBMC->Log(LOG_ERROR, "%s - tag points to unknown channel %d", __FUNCTION__, *it);
++ continue;
++ }
++ channels[*it] = it2->second;
++ }
++ return channels;
++}
++
++STags CHTSPData::GetTags()
++{
++ CLockObject lock(m_mutex);
++ return m_tags;
++}
++
++PVR_ERROR CHTSPData::GetEvent(SEvent& ev, uint32_t id)
++{
++ if(id == 0)
++ {
++ ev.Clear();
++ return PVR_ERROR_UNKNOWN;
++ }
++
++ SEvents::iterator it = m_events.find(id);
++ if(it != m_events.end())
++ {
++ ev = it->second;
++ return PVR_ERROR_NO_ERROR;
++ }
++
++ htsmsg_t *msg = htsmsg_create_map();
++ htsmsg_add_str(msg, "method", "getEvent");
++ htsmsg_add_u32(msg, "eventId", id);
++
++ CHTSResult result;
++ ReadResult(msg, result);
++ if(result.status != PVR_ERROR_NO_ERROR)
++ {
++ XBMC->Log(LOG_DEBUG, "%s - failed to get event %d", __FUNCTION__, id);
++ return result.status;
++ }
++
++ if (ParseEvent(result.message, id, ev))
++ {
++ m_events[id] = ev;
++ return PVR_ERROR_NO_ERROR;
++ }
++
++ return PVR_ERROR_SERVER_ERROR;
++}
++
++bool CHTSPData::SendEnableAsync()
++{
++ htsmsg_t *m = htsmsg_create_map();
++ htsmsg_add_str(m, "method", "enableAsyncMetadata");
++ return m_session->ReadSuccess(m, true, "enableAsyncMetadata failed");
++}
++
++void CHTSPData::ParseChannelRemove(htsmsg_t* msg)
++{
++ uint32_t id;
++ if(htsmsg_get_u32(msg, "channelId", &id))
++ {
++ XBMC->Log(LOG_ERROR, "%s - malformed message received", __FUNCTION__);
++ htsmsg_print(msg);
++ return;
++ }
++ XBMC->Log(LOG_DEBUG, "%s - id:%u", __FUNCTION__, id);
++
++ m_channels.erase(id);
++
++ PVR->TriggerChannelUpdate();
++}
++
++void CHTSPData::ParseChannelUpdate(htsmsg_t* msg)
++{
++ bool bChanged(false);
++ uint32_t iChannelId, iEventId = 0, iChannelNumber = 0, iCaid = 0;
++ const char *strName, *strIconPath;
++ if(htsmsg_get_u32(msg, "channelId", &iChannelId))
++ {
++ XBMC->Log(LOG_ERROR, "%s - malformed message received", __FUNCTION__);
++ htsmsg_print(msg);
++ return;
++ }
++
++ SChannel &channel = m_channels[iChannelId];
++ channel.id = iChannelId;
++
++ if(htsmsg_get_u32(msg, "eventId", &iEventId) == 0)
++ channel.event = iEventId;
++
++ if((strName = htsmsg_get_str(msg, "channelName")))
++ {
++ bChanged = (channel.name != strName);
++ channel.name = strName;
++ }
++
++ if((strIconPath = htsmsg_get_str(msg, "channelIcon")))
++ {
++ bChanged = (channel.icon != strIconPath);
++ channel.icon = strIconPath;
++ }
++
++ if(htsmsg_get_u32(msg, "channelNumber", &iChannelNumber) == 0)
++ {
++ int iNewChannelNumber = (iChannelNumber == 0) ? iChannelId + 1000 : iChannelNumber;
++ bChanged = (channel.num != iNewChannelNumber);
++ channel.num = iNewChannelNumber;
++ }
++
++ htsmsg_t *tags;
++
++ if((tags = htsmsg_get_list(msg, "tags")))
++ {
++ bChanged = true;
++ channel.tags.clear();
++
++ htsmsg_field_t *f;
++ HTSMSG_FOREACH(f, tags)
++ {
++ if(f->hmf_type != HMF_S64)
++ continue;
++ channel.tags.push_back((int)f->hmf_s64);
++ }
++ }
++
++ htsmsg_t *services;
++
++ if((services = htsmsg_get_list(msg, "services")))
++ {
++ bChanged = true;
++ htsmsg_field_t *f;
++ HTSMSG_FOREACH(f, services)
++ {
++ if(f->hmf_type != HMF_MAP)
++ continue;
++
++ htsmsg_t *service = &f->hmf_msg;
++ const char *service_type = htsmsg_get_str(service, "type");
++ if(service_type != NULL)
++ {
++ channel.radio = !strcmp(service_type, "Radio");
++ }
++
++ if(!htsmsg_get_u32(service, "caid", &iCaid))
++ channel.caid = (int) iCaid;
++ }
++ }
++
++ XBMC->Log(LOG_DEBUG, "%s - id:%u, name:'%s', icon:'%s', event:%u",
++ __FUNCTION__, iChannelId, strName ? strName : "(null)", strIconPath ? strIconPath : "(null)", iEventId);
++
++ if (bChanged)
++ PVR->TriggerChannelUpdate();
++}
++
++void CHTSPData::ParseDVREntryDelete(htsmsg_t* msg)
++{
++ uint32_t id;
++
++ if(htsmsg_get_u32(msg, "id", &id))
++ {
++ XBMC->Log(LOG_ERROR, "%s - malformed message received", __FUNCTION__);
++ htsmsg_print(msg);
++ return;
++ }
++
++ XBMC->Log(LOG_DEBUG, "%s - Recording %i was deleted", __FUNCTION__, id);
++
++ m_recordings.erase(id);
++
++ PVR->TriggerTimerUpdate();
++ PVR->TriggerRecordingUpdate();
++}
++
++void CHTSPData::ParseDVREntryUpdate(htsmsg_t* msg)
++{
++ SRecording recording;
++ const char *state;
++
++ if(htsmsg_get_u32(msg, "id", &recording.id)
++ || htsmsg_get_u32(msg, "channel", &recording.channel)
++ || htsmsg_get_u32(msg, "start", &recording.start)
++ || htsmsg_get_u32(msg, "stop", &recording.stop)
++ || (state = htsmsg_get_str(msg, "state")) == NULL)
++ {
++ XBMC->Log(LOG_ERROR, "%s - malformed message received", __FUNCTION__);
++ htsmsg_print(msg);
++ return;
++ }
++
++ /* parse the dvr entry's state */
++ if (strstr(state, "scheduled"))
++ recording.state = ST_SCHEDULED;
++ else if(strstr(state, "recording"))
++ recording.state = ST_RECORDING;
++ else if(strstr(state, "completed"))
++ recording.state = ST_COMPLETED;
++ else if(strstr(state, "invalid"))
++ recording.state = ST_INVALID;
++
++ const char* str;
++ if((str = htsmsg_get_str(msg, "title")) == NULL)
++ recording.title = "";
++ else
++ recording.title = str;
++
++ if((str = htsmsg_get_str(msg, "description")) == NULL)
++ recording.description = "";
++ else
++ recording.description = str;
++
++ if((str = htsmsg_get_str(msg, "error")) == NULL)
++ recording.error = "";
++ else
++ recording.error = str;
++
++ // if the user has aborted the recording then the recording.error will be set to 300 by tvheadend
++ if (recording.error == "300")
++ {
++ recording.state = ST_ABORTED;
++ recording.error.clear();
++ }
++
++ XBMC->Log(LOG_DEBUG, "%s - id:%u, state:'%s', title:'%s', description: '%s'"
++ , __FUNCTION__, recording.id, state, recording.title.c_str()
++ , recording.description.c_str());
++
++ m_recordings[recording.id] = recording;
++
++ PVR->TriggerTimerUpdate();
++
++ if (recording.state == ST_RECORDING)
++ PVR->TriggerRecordingUpdate();
++}
++
++bool CHTSPData::ParseEvent(htsmsg_t* msg, uint32_t id, SEvent &event)
++{
++ uint32_t start, stop, next, chan_id, content;
++ const char *title, *desc, *ext_desc;
++ if( htsmsg_get_u32(msg, "start", &start)
++ || htsmsg_get_u32(msg, "stop" , &stop)
++ || (title = htsmsg_get_str(msg, "title")) == NULL)
++ {
++ XBMC->Log(LOG_DEBUG, "%s - malformed event", __FUNCTION__);
++ htsmsg_print(msg);
++ return false;
++ }
++ event.Clear();
++ event.id = id;
++ event.start = start;
++ event.stop = stop;
++ event.title = title;
++
++ desc = htsmsg_get_str(msg, "description");
++ ext_desc = htsmsg_get_str(msg, "ext_text");
++
++ if (desc && ext_desc)
++ {
++ string strBuf = desc;
++ strBuf.append(ext_desc);
++ event.descs = strBuf;
++ }
++ else if (desc)
++ event.descs = desc;
++ else if (ext_desc)
++ event.descs = ext_desc;
++ else
++ event.descs = "";
++
++ if(htsmsg_get_u32(msg, "nextEventId", &next))
++ event.next = 0;
++ else
++ event.next = next;
++ if(htsmsg_get_u32(msg, "channelId", &chan_id))
++ event.chan_id = -1;
++ else
++ event.chan_id = chan_id;
++ if(htsmsg_get_u32(msg, "contentType", &content))
++ event.content = -1;
++ else
++ event.content = content;
++
++ XBMC->Log(LOG_DEBUG, "%s - id:%u, chan_id:%u, title:'%s', genre_type:%u, genre_sub_type:%u, desc:'%s', start:%u, stop:%u, next:%u"
++ , __FUNCTION__
++ , event.id
++ , event.chan_id
++ , event.title.c_str()
++ , event.content & 0x0F
++ , event.content & 0xF0
++ , event.descs.c_str()
++ , event.start
++ , event.stop
++ , event.next);
++
++ return true;
++}
++
++void CHTSPData::ParseTagRemove(htsmsg_t* msg)
++{
++ uint32_t id;
++ if(htsmsg_get_u32(msg, "tagId", &id))
++ {
++ XBMC->Log(LOG_ERROR, "%s - malformed message received", __FUNCTION__);
++ htsmsg_print(msg);
++ return;
++ }
++ XBMC->Log(LOG_DEBUG, "%s - id:%u", __FUNCTION__, id);
++
++ m_tags.erase(id);
++
++ PVR->TriggerChannelGroupsUpdate();
++}
++
++void CHTSPData::ParseTagUpdate(htsmsg_t* msg)
++{
++ uint32_t id;
++ const char *name, *icon;
++ if(htsmsg_get_u32(msg, "tagId", &id))
++ {
++ XBMC->Log(LOG_ERROR, "%s - malformed message received", __FUNCTION__);
++ htsmsg_print(msg);
++ return;
++ }
++ STag &tag = m_tags[id];
++ tag.id = id;
++
++ if((icon = htsmsg_get_str(msg, "tagIcon")))
++ tag.icon = icon;
++
++ if((name = htsmsg_get_str(msg, "tagName")))
++ tag.name = name;
++
++ htsmsg_t *channels;
++
++ if((channels = htsmsg_get_list(msg, "members")))
++ {
++ tag.channels.clear();
++
++ htsmsg_field_t *f;
++ HTSMSG_FOREACH(f, channels)
++ {
++ if(f->hmf_type != HMF_S64)
++ continue;
++ tag.channels.push_back((int)f->hmf_s64);
++ }
++ }
++
++ XBMC->Log(LOG_DEBUG, "%s - id:%u, name:'%s', icon:'%s'"
++ , __FUNCTION__, id, name ? name : "(null)", icon ? icon : "(null)");
++
++ PVR->TriggerChannelGroupsUpdate();
++}
+diff --git a/xbmc/pvrclients/tvheadend/HTSPData.h b/xbmc/pvrclients/tvheadend/HTSPData.h
+new file mode 100644
+index 0000000..e61dd6e
+--- /dev/null
++++ b/xbmc/pvrclients/tvheadend/HTSPData.h
+@@ -0,0 +1,121 @@
++#pragma once
++
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "client.h"
++#include "../../../lib/platform/threads/threads.h"
++#include "HTSPConnection.h"
++
++class CHTSResult
++{
++public:
++ CHTSResult(void) : message(NULL), status(PVR_ERROR_NO_ERROR) {}
++ ~CHTSResult(void)
++ {
++ if (message != NULL)
++ htsmsg_destroy(message);
++ }
++
++ // the actual message
++ htsmsg *message;
++ // the return code
++ PVR_ERROR status;
++};
++
++class CHTSPData : public PLATFORM::CThread
++{
++public:
++ CHTSPData();
++ ~CHTSPData();
++
++ bool Open();
++ void Close();
++ bool CheckConnection(void);
++ bool IsConnected(void) const { return m_session->IsConnected(); }
++
++ /*!
++ * @brief Send a message to the backend and read the result.
++ * @param message The message to send.
++ * @return The returned message or NULL if an error occured or nothing was received.
++ */
++ void ReadResult(htsmsg_t *message, CHTSResult &result);
++ int GetProtocol(void) const { return m_session->GetProtocol(); }
++ const char * GetServerName(void) const { return m_session->GetServerName(); }
++ const char * GetVersion(void) const { return m_session->GetVersion(); }
++ bool GetDriveSpace(long long *total, long long *used);
++ bool GetBackendTime(time_t *utcTime, int *gmtOffset);
++ unsigned int GetNumChannels(void);
++ PVR_ERROR GetChannels(PVR_HANDLE handle, bool bRadio);
++ PVR_ERROR GetEpg(PVR_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd);
++ unsigned int GetNumRecordings();
++ PVR_ERROR GetRecordings(PVR_HANDLE handle);
++ PVR_ERROR DeleteRecording(const PVR_RECORDING &recinfo);
++ PVR_ERROR AddTimer(const PVR_TIMER &timerinfo);
++ PVR_ERROR UpdateTimer(const PVR_TIMER &timerinfo);
++ PVR_ERROR RenameRecording(const PVR_RECORDING &recinfo, const char* newname);
++ unsigned int GetNumTimers();
++ PVR_ERROR GetTimers(PVR_HANDLE handle);
++ PVR_ERROR DeleteTimer(const PVR_TIMER &timerinfo, bool force);
++ unsigned int GetNumChannelGroups(void);
++ PVR_ERROR GetChannelGroups(PVR_HANDLE handle);
++ PVR_ERROR GetChannelGroupMembers(PVR_HANDLE handle, const PVR_CHANNEL_GROUP &group);
++
++protected:
++ virtual void *Process(void);
++
++private:
++ struct SMessage
++ {
++ PLATFORM::CEvent* event;
++ htsmsg_t* msg;
++ };
++ typedef std::map<int, SMessage> SMessages;
++
++ SChannels GetChannels();
++ SChannels GetChannels(int tag);
++ SChannels GetChannels(STag &tag);
++ STags GetTags();
++ PVR_ERROR GetEvent(SEvent& event, uint32_t id);
++ bool SendEnableAsync();
++ SRecordings GetDVREntries(bool recorded, bool scheduled);
++
++ void ParseChannelRemove(htsmsg_t* msg);
++ void ParseChannelUpdate(htsmsg_t* msg);
++ void ParseDVREntryDelete(htsmsg_t* msg);
++ void ParseDVREntryUpdate(htsmsg_t* msg);
++ static bool ParseEvent(htsmsg_t* msg, uint32_t id, SEvent &event);
++ void ParseTagRemove(htsmsg_t* msg);
++ void ParseTagUpdate(htsmsg_t* msg);
++
++ CHTSPConnection * m_session;
++ bool m_bIsStarted;
++ PLATFORM::CCondition<bool> m_started;
++ PLATFORM::CMutex m_mutex;
++ SChannels m_channels;
++ STags m_tags;
++ SEvents m_events;
++ SMessages m_queue;
++ SRecordings m_recordings;
++ int m_iReconnectRetries;
++ bool m_bDisconnectWarningDisplayed;
++};
++
+diff --git a/xbmc/pvrclients/tvheadend/HTSPDemux.cpp b/xbmc/pvrclients/tvheadend/HTSPDemux.cpp
+new file mode 100644
+index 0000000..e193125
+--- /dev/null
++++ b/xbmc/pvrclients/tvheadend/HTSPDemux.cpp
+@@ -0,0 +1,539 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "client.h"
++#include "HTSPConnection.h"
++#include <libavcodec/avcodec.h> // For codec id's
++#include "HTSPDemux.h"
++
++using namespace ADDON;
++
++CHTSPDemux::CHTSPDemux() :
++ m_bIsRadio(false),
++ m_bAbort(false),
++ m_subs(0),
++ m_channel(0),
++ m_tag(0),
++ m_StatusCount(0)
++{
++ m_session = new CHTSPConnection();
++ m_Streams.iStreamCount = 0;
++}
++
++CHTSPDemux::~CHTSPDemux()
++{
++ Close();
++ delete m_session;
++}
++
++bool CHTSPDemux::Open(const PVR_CHANNEL &channelinfo)
++{
++ m_channel = channelinfo.iUniqueId;
++ m_bIsRadio = channelinfo.bIsRadio;
++
++ if (!m_session)
++ m_session = new CHTSPConnection();
++
++ if(!m_session->Connect())
++ return false;
++
++ if(!SendSubscribe(m_subs, m_channel))
++ return false;
++
++ m_Streams.iStreamCount = 0;
++ m_StatusCount = 0;
++ return true;
++}
++
++void CHTSPDemux::Close()
++{
++ m_bAbort = true;
++ m_session->Close();
++}
++
++bool CHTSPDemux::GetStreamProperties(PVR_STREAM_PROPERTIES* props)
++{
++ props->iStreamCount = m_Streams.iStreamCount;
++ for (unsigned int i = 0; i < m_Streams.iStreamCount; i++)
++ {
++ props->stream[i].iStreamIndex = m_Streams.stream[i].iStreamIndex;
++ props->stream[i].iPhysicalId = m_Streams.stream[i].iPhysicalId;
++ props->stream[i].iCodecType = m_Streams.stream[i].iCodecType;
++ props->stream[i].iCodecId = m_Streams.stream[i].iCodecId;
++ props->stream[i].iHeight = m_Streams.stream[i].iHeight;
++ props->stream[i].iWidth = m_Streams.stream[i].iWidth;
++ props->stream[i].strLanguage[0] = m_Streams.stream[i].strLanguage[0];
++ props->stream[i].strLanguage[1] = m_Streams.stream[i].strLanguage[1];
++ props->stream[i].strLanguage[2] = m_Streams.stream[i].strLanguage[2];
++ props->stream[i].strLanguage[3] = m_Streams.stream[i].strLanguage[3];
++ props->stream[i].iIdentifier = m_Streams.stream[i].iIdentifier;
++ }
++ return (props->iStreamCount > 0);
++}
++
++void CHTSPDemux::Abort()
++{
++ m_Streams.iStreamCount = 0;
++ m_bAbort = true;
++ m_session->Abort();
++}
++
++DemuxPacket* CHTSPDemux::Read()
++{
++ htsmsg_t * msg;
++ const char* method;
++ while((msg = m_session->ReadMessage(1000)) && !m_bAbort)
++ {
++ method = htsmsg_get_str(msg, "method");
++ if(method == NULL)
++ break;
++
++ uint32_t subs;
++ if(htsmsg_get_u32(msg, "subscriptionId", &subs) || subs != m_subs)
++ {
++ htsmsg_destroy(msg);
++ continue;
++ }
++
++ if (strcmp("subscriptionStart", method) == 0)
++ {
++ ParseSubscriptionStart(msg);
++ DemuxPacket* pkt = PVR->AllocateDemuxPacket(0);
++ pkt->iStreamId = DMX_SPECIALID_STREAMCHANGE;
++ htsmsg_destroy(msg);
++ return pkt;
++ }
++ else if(strcmp("subscriptionStop", method) == 0)
++ ParseSubscriptionStop (msg);
++ else if(strcmp("subscriptionStatus", method) == 0)
++ ParseSubscriptionStatus(msg);
++ else if(strcmp("queueStatus" , method) == 0)
++ ParseQueueStatus(msg);
++ else if(strcmp("signalStatus" , method) == 0)
++ ParseSignalStatus(msg);
++ else if(strcmp("muxpkt" , method) == 0)
++ {
++ DemuxPacket *pkt = ParseMuxPacket(msg);
++ htsmsg_destroy(msg);
++ return pkt;
++ }
++
++ break;
++ }
++
++ if(msg)
++ {
++ htsmsg_destroy(msg);
++ DemuxPacket* pkt = PVR->AllocateDemuxPacket(0);
++ return pkt;
++ }
++ return NULL;
++}
++
++DemuxPacket *CHTSPDemux::ParseMuxPacket(htsmsg_t *msg)
++{
++ DemuxPacket* pkt = NULL;
++ uint32_t index, duration;
++ const void* bin;
++ size_t binlen;
++ int64_t ts;
++
++ if(htsmsg_get_u32(msg, "stream" , &index) ||
++ htsmsg_get_bin(msg, "payload", &bin, &binlen))
++ {
++ XBMC->Log(LOG_ERROR, "%s - malformed message", __FUNCTION__);
++ return PVR->AllocateDemuxPacket(0);
++ }
++
++ pkt = PVR->AllocateDemuxPacket(binlen);
++ memcpy(pkt->pData, bin, binlen);
++
++ pkt->iSize = binlen;
++
++ if(!htsmsg_get_u32(msg, "duration", &duration))
++ pkt->duration = (double)duration * DVD_TIME_BASE / 1000000;
++
++ if(!htsmsg_get_s64(msg, "dts", &ts))
++ pkt->dts = (double)ts * DVD_TIME_BASE / 1000000;
++ else
++ pkt->dts = DVD_NOPTS_VALUE;
++
++ if(!htsmsg_get_s64(msg, "pts", &ts))
++ pkt->pts = (double)ts * DVD_TIME_BASE / 1000000;
++ else
++ pkt->pts = DVD_NOPTS_VALUE;
++
++ pkt->iStreamId = -1;
++ for(unsigned int i = 0; i < m_Streams.iStreamCount; i++)
++ {
++ if(m_Streams.stream[i].iPhysicalId == (unsigned int)index)
++ {
++ pkt->iStreamId = i;
++ break;
++ }
++ }
++
++ return pkt;
++}
++
++bool CHTSPDemux::SwitchChannel(const PVR_CHANNEL &channelinfo)
++{
++ XBMC->Log(LOG_DEBUG, "%s - changing to channel '%s'", __FUNCTION__, channelinfo.strChannelName);
++
++ if (!SendUnsubscribe(m_subs))
++ XBMC->Log(LOG_ERROR, "%s - failed to unsubscribe from previous channel", __FUNCTION__);
++
++ if (!SendSubscribe(m_subs+1, channelinfo.iUniqueId))
++ XBMC->Log(LOG_ERROR, "%s - failed to set channel", __FUNCTION__);
++ else
++ {
++ m_channel = channelinfo.iChannelNumber;
++ m_subs = m_subs+1;
++ m_Streams.iStreamCount = 0;
++ m_StatusCount = 0;
++
++ return true;
++ }
++ return false;
++}
++
++bool CHTSPDemux::GetSignalStatus(PVR_SIGNAL_STATUS &qualityinfo)
++{
++ if (m_SourceInfo.si_adapter.empty() || m_Quality.fe_status.empty())
++ return false;
++
++ strncpy(qualityinfo.strAdapterName, m_SourceInfo.si_adapter.c_str(), sizeof(qualityinfo.strAdapterName));
++ strncpy(qualityinfo.strAdapterStatus, m_Quality.fe_status.c_str(), sizeof(qualityinfo.strAdapterStatus));
++ qualityinfo.iSignal = (uint16_t)m_Quality.fe_signal;
++ qualityinfo.iSNR = (uint16_t)m_Quality.fe_snr;
++ qualityinfo.iBER = (uint32_t)m_Quality.fe_ber;
++ qualityinfo.iUNC = (uint32_t)m_Quality.fe_unc;
++ qualityinfo.dVideoBitrate = 0;
++ qualityinfo.dAudioBitrate = 0;
++ qualityinfo.dDolbyBitrate = 0;
++
++ return true;
++}
++
++inline void HTSPResetDemuxStreamInfo(PVR_STREAM_PROPERTIES::PVR_STREAM &stream)
++{
++ stream.iFPSScale = 0;
++ stream.iFPSRate = 0;
++ stream.iHeight = 0;
++ stream.iWidth = 0;
++ stream.fAspect = 0.0;
++ stream.iChannels = 0;
++ stream.iSampleRate = 0;
++ stream.iBlockAlign = 0;
++ stream.iBitRate = 0;
++ stream.iBitsPerSample = 0;
++ stream.strLanguage[0] = 0;
++ stream.strLanguage[1] = 0;
++ stream.strLanguage[2] = 0;
++ stream.strLanguage[3] = 0;
++ stream.iIdentifier = -1;
++}
++
++inline void HTSPSetDemuxStreamInfoAudio(PVR_STREAM_PROPERTIES::PVR_STREAM &stream, htsmsg_t *msg)
++{
++ stream.iChannels = htsmsg_get_u32_or_default(msg, "channels" , 0);
++ stream.iSampleRate = htsmsg_get_u32_or_default(msg, "rate" , 0);
++}
++
++inline void HTSPSetDemuxStreamInfoVideo(PVR_STREAM_PROPERTIES::PVR_STREAM &stream, htsmsg_t *msg)
++{
++ stream.iWidth = htsmsg_get_u32_or_default(msg, "width" , 0);
++ stream.iHeight = htsmsg_get_u32_or_default(msg, "height" , 0);
++ stream.fAspect = (float) (htsmsg_get_u32_or_default(msg, "aspect_num", 1) / htsmsg_get_u32_or_default(msg, "aspect_den", 1));
++}
++
++inline void HTSPSetDemuxStreamInfoLanguage(PVR_STREAM_PROPERTIES::PVR_STREAM &stream, htsmsg_t *msg)
++{
++ if (const char *strLanguage = htsmsg_get_str(msg, "language"))
++ {
++ stream.strLanguage[0] = strLanguage[0];
++ stream.strLanguage[1] = strLanguage[1];
++ stream.strLanguage[2] = strLanguage[2];
++ stream.strLanguage[3] = 0;
++ }
++}
++
++void CHTSPDemux::ParseSubscriptionStart(htsmsg_t *m)
++{
++ htsmsg_t *streams;
++ htsmsg_field_t *f;
++ if((streams = htsmsg_get_list(m, "streams")) == NULL)
++ {
++ XBMC->Log(LOG_ERROR, "%s - malformed message", __FUNCTION__);
++ return;
++ }
++
++ m_Streams.iStreamCount = 0;
++
++ HTSMSG_FOREACH(f, streams)
++ {
++ uint32_t index;
++ const char* type;
++ htsmsg_t* sub;
++
++ if (f->hmf_type != HMF_MAP)
++ continue;
++
++ sub = &f->hmf_msg;
++
++ if ((type = htsmsg_get_str(sub, "type")) == NULL)
++ continue;
++
++ if (htsmsg_get_u32(sub, "index", &index))
++ continue;
++
++ XBMC->Log(LOG_DEBUG, "%s - id: %d, type: %s", __FUNCTION__, index, type);
++
++ bool bValidStream(true);
++ HTSPResetDemuxStreamInfo(m_Streams.stream[m_Streams.iStreamCount]);
++
++ if(!strcmp(type, "AC3"))
++ {
++ m_Streams.stream[m_Streams.iStreamCount].iCodecType = AVMEDIA_TYPE_AUDIO;
++ m_Streams.stream[m_Streams.iStreamCount].iCodecId = CODEC_ID_AC3;
++ }
++ else if(!strcmp(type, "EAC3"))
++ {
++ m_Streams.stream[m_Streams.iStreamCount].iCodecType = AVMEDIA_TYPE_AUDIO;
++ m_Streams.stream[m_Streams.iStreamCount].iCodecId = CODEC_ID_EAC3;
++ }
++ else if(!strcmp(type, "MPEG2AUDIO"))
++ {
++ m_Streams.stream[m_Streams.iStreamCount].iCodecType = AVMEDIA_TYPE_AUDIO;
++ m_Streams.stream[m_Streams.iStreamCount].iCodecId = CODEC_ID_MP2;
++ }
++ else if(!strcmp(type, "AAC"))
++ {
++ m_Streams.stream[m_Streams.iStreamCount].iCodecType = AVMEDIA_TYPE_AUDIO;
++ m_Streams.stream[m_Streams.iStreamCount].iCodecId = CODEC_ID_AAC;
++ }
++ else if(!strcmp(type, "MPEG2VIDEO"))
++ {
++ m_Streams.stream[m_Streams.iStreamCount].iCodecType = AVMEDIA_TYPE_VIDEO;
++ m_Streams.stream[m_Streams.iStreamCount].iCodecId = CODEC_ID_MPEG2VIDEO;
++ }
++ else if(!strcmp(type, "H264"))
++ {
++ m_Streams.stream[m_Streams.iStreamCount].iCodecType = AVMEDIA_TYPE_VIDEO;
++ m_Streams.stream[m_Streams.iStreamCount].iCodecId = CODEC_ID_H264;
++ }
++ else if(!strcmp(type, "DVBSUB"))
++ {
++ uint32_t composition_id = 0, ancillary_id = 0;
++ htsmsg_get_u32(sub, "composition_id", &composition_id);
++ htsmsg_get_u32(sub, "ancillary_id" , &ancillary_id);
++
++ m_Streams.stream[m_Streams.iStreamCount].iCodecType = AVMEDIA_TYPE_SUBTITLE;
++ m_Streams.stream[m_Streams.iStreamCount].iCodecId = CODEC_ID_DVB_SUBTITLE;
++ m_Streams.stream[m_Streams.iStreamCount].iIdentifier = (composition_id & 0xffff) | ((ancillary_id & 0xffff) << 16);
++ HTSPSetDemuxStreamInfoLanguage(m_Streams.stream[m_Streams.iStreamCount], sub);
++ }
++ else if(!strcmp(type, "TEXTSUB"))
++ {
++ m_Streams.stream[m_Streams.iStreamCount].iCodecType = AVMEDIA_TYPE_SUBTITLE;
++ m_Streams.stream[m_Streams.iStreamCount].iCodecId = CODEC_ID_TEXT;
++ HTSPSetDemuxStreamInfoLanguage(m_Streams.stream[m_Streams.iStreamCount], sub);
++ }
++ else if(!strcmp(type, "TELETEXT"))
++ {
++ m_Streams.stream[m_Streams.iStreamCount].iCodecType = AVMEDIA_TYPE_SUBTITLE;
++ m_Streams.stream[m_Streams.iStreamCount].iCodecId = CODEC_ID_DVB_TELETEXT;
++ }
++ else
++ {
++ bValidStream = false;
++ }
++
++ if (bValidStream)
++ {
++ m_Streams.stream[m_Streams.iStreamCount].iStreamIndex = m_Streams.iStreamCount;
++ m_Streams.stream[m_Streams.iStreamCount].iPhysicalId = index;
++ if (m_Streams.stream[m_Streams.iStreamCount].iCodecType == AVMEDIA_TYPE_AUDIO)
++ {
++ HTSPSetDemuxStreamInfoAudio(m_Streams.stream[m_Streams.iStreamCount], sub);
++ HTSPSetDemuxStreamInfoLanguage(m_Streams.stream[m_Streams.iStreamCount], sub);
++ }
++ else if (m_Streams.stream[m_Streams.iStreamCount].iCodecType == AVMEDIA_TYPE_VIDEO)
++ HTSPSetDemuxStreamInfoVideo(m_Streams.stream[m_Streams.iStreamCount], sub);
++ ++m_Streams.iStreamCount;
++ }
++
++ if (m_Streams.iStreamCount >= PVR_STREAM_MAX_STREAMS)
++ {
++ XBMC->Log(LOG_ERROR, "%s - max amount of streams reached", __FUNCTION__);
++ break;
++ }
++ }
++
++ if (ParseSourceInfo(m))
++ {
++ XBMC->Log(LOG_DEBUG, "%s - subscription started on adapter %s, mux %s, network %s, provider %s, service %s"
++ , __FUNCTION__, m_SourceInfo.si_adapter.c_str(), m_SourceInfo.si_mux.c_str(),
++ m_SourceInfo.si_network.c_str(), m_SourceInfo.si_provider.c_str(),
++ m_SourceInfo.si_service.c_str());
++ }
++ else
++ {
++ XBMC->Log(LOG_DEBUG, "%s - subscription started on an unknown device", __FUNCTION__);
++ }
++}
++
++void CHTSPDemux::ParseSubscriptionStop (htsmsg_t *m)
++{
++ XBMC->Log(LOG_DEBUG, "%s - subscription ended on adapter %s", __FUNCTION__, m_SourceInfo.si_adapter.c_str());
++ m_Streams.iStreamCount = 0;
++
++ /* reset the signal status */
++ m_Quality.fe_status = "";
++ m_Quality.fe_ber = -2;
++ m_Quality.fe_signal = -2;
++ m_Quality.fe_snr = -2;
++ m_Quality.fe_unc = -2;
++
++ /* reset the source info */
++ m_SourceInfo.si_adapter = "";
++ m_SourceInfo.si_mux = "";
++ m_SourceInfo.si_network = "";
++ m_SourceInfo.si_provider = "";
++ m_SourceInfo.si_service = "";
++}
++
++void CHTSPDemux::ParseSubscriptionStatus(htsmsg_t *m)
++{
++ const char* status;
++ status = htsmsg_get_str(m, "status");
++ if(status == NULL)
++ m_Status = "";
++ else
++ {
++ m_StatusCount++;
++ m_Status = status;
++ XBMC->Log(LOG_DEBUG, "%s - %s", __FUNCTION__, status);
++ XBMC->QueueNotification(QUEUE_INFO, status);
++ }
++}
++
++bool CHTSPDemux::SendUnsubscribe(int subscription)
++{
++ htsmsg_t *m = htsmsg_create_map();
++ htsmsg_add_str(m, "method" , "unsubscribe");
++ htsmsg_add_s32(m, "subscriptionId", subscription);
++ return m_session->ReadSuccess(m, true, "unsubscribe from channel");
++}
++
++bool CHTSPDemux::SendSubscribe(int subscription, int channel)
++{
++ htsmsg_t *m = htsmsg_create_map();
++ htsmsg_add_str(m, "method" , "subscribe");
++ htsmsg_add_s32(m, "channelId" , channel);
++ htsmsg_add_s32(m, "subscriptionId", subscription);
++ return m_session->ReadSuccess(m, true, "subscribe to channel");
++}
++
++bool CHTSPDemux::ParseQueueStatus(htsmsg_t* msg)
++{
++ if(htsmsg_get_u32(msg, "packets", &m_QueueStatus.packets)
++ || htsmsg_get_u32(msg, "bytes", &m_QueueStatus.bytes)
++ || htsmsg_get_u32(msg, "Bdrops", &m_QueueStatus.bdrops)
++ || htsmsg_get_u32(msg, "Pdrops", &m_QueueStatus.pdrops)
++ || htsmsg_get_u32(msg, "Idrops", &m_QueueStatus.idrops))
++ {
++ XBMC->Log(LOG_ERROR, "%s - malformed message received", __FUNCTION__);
++ htsmsg_print(msg);
++ return false;
++ }
++
++ /* delay isn't always transmitted */
++ if(htsmsg_get_u32(msg, "delay", &m_QueueStatus.delay))
++ m_QueueStatus.delay = 0;
++
++ return true;
++}
++
++bool CHTSPDemux::ParseSignalStatus(htsmsg_t* msg)
++{
++ if(htsmsg_get_u32(msg, "feSNR", &m_Quality.fe_snr))
++ m_Quality.fe_snr = -2;
++
++ if(htsmsg_get_u32(msg, "feSignal", &m_Quality.fe_signal))
++ m_Quality.fe_signal = -2;
++
++ if(htsmsg_get_u32(msg, "feBER", &m_Quality.fe_ber))
++ m_Quality.fe_ber = -2;
++
++ if(htsmsg_get_u32(msg, "feUNC", &m_Quality.fe_unc))
++ m_Quality.fe_unc = -2;
++
++ const char* status;
++ if((status = htsmsg_get_str(msg, "feStatus")))
++ m_Quality.fe_status = status;
++ else
++ m_Quality.fe_status = "(unknown)";
++
++// XBMC->Log(LOG_DEBUG, "%s - updated signal status: snr=%d, signal=%d, ber=%d, unc=%d, status=%s"
++// , __FUNCTION__, quality.fe_snr, quality.fe_signal, quality.fe_ber
++// , quality.fe_unc, quality.fe_status.c_str());
++
++ return true;
++}
++
++bool CHTSPDemux::ParseSourceInfo(htsmsg_t* msg)
++{
++ htsmsg_t *sourceinfo;
++ if((sourceinfo = htsmsg_get_map(msg, "sourceinfo")) == NULL)
++ {
++ XBMC->Log(LOG_ERROR, "%s - malformed message", __FUNCTION__);
++ return false;
++ }
++
++ const char* str;
++ if((str = htsmsg_get_str(sourceinfo, "adapter")) == NULL)
++ m_SourceInfo.si_adapter = "";
++ else
++ m_SourceInfo.si_adapter = str;
++
++ if((str = htsmsg_get_str(sourceinfo, "mux")) == NULL)
++ m_SourceInfo.si_mux = "";
++ else
++ m_SourceInfo.si_mux = str;
++
++ if((str = htsmsg_get_str(sourceinfo, "network")) == NULL)
++ m_SourceInfo.si_network = "";
++ else
++ m_SourceInfo.si_network = str;
++
++ if((str = htsmsg_get_str(sourceinfo, "provider")) == NULL)
++ m_SourceInfo.si_provider = "";
++ else
++ m_SourceInfo.si_provider = str;
++
++ if((str = htsmsg_get_str(sourceinfo, "service")) == NULL)
++ m_SourceInfo.si_service = "";
++ else
++ m_SourceInfo.si_service = str;
++
++ return true;
++}
+diff --git a/xbmc/pvrclients/tvheadend/HTSPDemux.h b/xbmc/pvrclients/tvheadend/HTSPDemux.h
+new file mode 100644
+index 0000000..7c7f106
+--- /dev/null
++++ b/xbmc/pvrclients/tvheadend/HTSPDemux.h
+@@ -0,0 +1,69 @@
++#pragma once
++
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "client.h"
++#include "HTSPConnection.h"
++
++class CHTSPDemux
++{
++public:
++ CHTSPDemux();
++ ~CHTSPDemux();
++
++ bool Open(const PVR_CHANNEL &channelinfo);
++ void Close();
++ bool GetStreamProperties(PVR_STREAM_PROPERTIES* props);
++ void Abort();
++ DemuxPacket* Read();
++ bool SwitchChannel(const PVR_CHANNEL &channelinfo);
++ int CurrentChannel() { return m_channel; }
++ bool GetSignalStatus(PVR_SIGNAL_STATUS &qualityinfo);
++
++protected:
++ void ParseSubscriptionStart (htsmsg_t *m);
++ void ParseSubscriptionStop (htsmsg_t *m);
++ void ParseSubscriptionStatus(htsmsg_t *m);
++ bool SendSubscribe (int subscription, int channel);
++ bool SendUnsubscribe(int subscription);
++ DemuxPacket *ParseMuxPacket(htsmsg_t *m);
++
++private:
++ bool ParseQueueStatus(htsmsg_t* msg);
++ bool ParseSignalStatus(htsmsg_t* msg);
++ bool ParseSourceInfo(htsmsg_t* msg);
++
++ CHTSPConnection *m_session;
++ bool m_bIsRadio;
++ bool m_bAbort;
++ bool m_bResetNeeded;
++ unsigned m_subs;
++ int m_channel;
++ int m_tag;
++ int m_StatusCount;
++ std::string m_Status;
++ PVR_STREAM_PROPERTIES m_Streams;
++ SChannels m_channels;
++ SQueueStatus m_QueueStatus;
++ SQuality m_Quality;
++ SSourceInfo m_SourceInfo;
++};
+diff --git a/xbmc/pvrclients/tvheadend/HTSPTypes.h b/xbmc/pvrclients/tvheadend/HTSPTypes.h
+new file mode 100644
+index 0000000..cf6c2d2
+--- /dev/null
++++ b/xbmc/pvrclients/tvheadend/HTSPTypes.h
+@@ -0,0 +1,234 @@
++#pragma once
++
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <deque>
++#include <algorithm>
++#include <vector>
++#include <map>
++
++#include "utils/StdString.h"
++
++typedef struct htsmsg htsmsg_t;
++
++template<typename T>
++class const_circular_iter :
++ public std::iterator< typename std::iterator_traits<T>::iterator_category,
++ typename std::iterator_traits<T>::value_type,
++ typename std::iterator_traits<T>::difference_type,
++ typename std::iterator_traits<T>::pointer,
++ typename std::iterator_traits<T>::reference>
++{
++protected:
++ T begin;
++ T end;
++ T iter;
++
++public:
++ typedef typename std::iterator_traits<T>::value_type value_type;
++ typedef typename std::iterator_traits<T>::difference_type difference_type;
++ typedef typename std::iterator_traits<T>::pointer pointer;
++ typedef typename std::iterator_traits<T>::reference reference;
++
++ const_circular_iter(const const_circular_iter& src) : begin(src.begin), end(src.end), iter(src.iter) {};
++ const_circular_iter(const T& b, const T& e) : begin(b), end(e), iter(b) {};
++ const_circular_iter(const T& b, const T& e, const T& c) : begin(b), end(e), iter(c) {};
++ const_circular_iter<T>& operator++()
++ {
++ if(begin == end)
++ return(*this);
++ ++iter;
++ if (iter == end)
++ iter = begin;
++ return(*this);
++ }
++
++ const_circular_iter<T>& operator--()
++ {
++ if(begin == end)
++ return(*this);
++ if (iter == begin)
++ iter = end;
++ iter--;
++ return(*this);
++ }
++
++ reference operator*() const { return (*iter); }
++ const pointer operator->() const { return &(*iter); }
++ bool operator==(const const_circular_iter<T>& rhs) const { return (iter == rhs.iter); }
++ bool operator==(const T& rhs) const { return (iter == rhs); }
++ bool operator!=(const const_circular_iter<T>& rhs) const { return ! operator==(rhs); }
++ bool operator!=(const T& rhs) const { return ! operator==(rhs); }
++};
++
++struct STag
++{
++ int id;
++ std::string name;
++ std::string icon;
++ std::vector<int> channels;
++
++ STag() { Clear(); }
++ void Clear()
++ {
++ id = 0;
++ name.clear();
++ icon.clear();
++ channels.clear();
++ }
++ bool BelongsTo(int channel) const
++ {
++ return std::find(channels.begin(), channels.end(), channel) != channels.end();
++ }
++
++};
++
++struct SChannel
++{
++ int id;
++ std::string name;
++ std::string icon;
++ int event;
++ int num;
++ bool radio;
++ int caid;
++ std::vector<int> tags;
++
++ SChannel() { Clear(); }
++ void Clear()
++ {
++ id = 0;
++ event = 0;
++ num = 0;
++ radio = false;
++ caid = 0;
++ name.clear();
++ icon.clear();
++ tags.clear();
++ }
++ bool MemberOf(int tag) const
++ {
++ return std::find(tags.begin(), tags.end(), tag) != tags.end();
++ }
++ bool operator<(const SChannel &right) const
++ {
++ return num < right.num;
++ }
++};
++
++struct SEvent
++{
++ int id;
++ int next;
++ int chan_id;
++
++ int content;
++ int start;
++ int stop;
++ std::string title;
++ std::string descs;
++
++ SEvent() { Clear(); }
++ void Clear()
++ {
++ id = 0;
++ next = 0;
++ start = 0;
++ stop = 0;
++ title.clear();
++ descs.clear();
++ }
++};
++
++typedef enum recording_state {
++ ST_INVALID = 0,
++ ST_SCHEDULED = 1,
++ ST_RECORDING = 2,
++ ST_COMPLETED = 3,
++ ST_ABORTED = 4
++} ERecordingState;
++
++struct SRecording
++{
++ uint32_t id;
++ uint32_t channel;
++ uint32_t start;
++ uint32_t stop;
++ std::string title;
++ std::string description;
++ ERecordingState state;
++ std::string error;
++
++ SRecording() { Clear(); }
++ void Clear()
++ {
++ id = channel = start = stop = 0;
++ title.clear();
++ description.clear();
++ state = ST_INVALID;
++ error.clear();
++ }
++};
++
++struct SQueueStatus
++{
++ uint32_t packets; // Number of data packets in queue.
++ uint32_t bytes; // Number of bytes in queue.
++ uint32_t delay; // Estimated delay of queue (in µs)
++ uint32_t bdrops; // Number of B-frames dropped
++ uint32_t pdrops; // Number of P-frames dropped
++ uint32_t idrops; // Number of I-frames dropped
++
++ SQueueStatus() { Clear(); }
++ void Clear()
++ {
++ packets = 0;
++ bytes = 0;
++ delay = 0;
++ bdrops = 0;
++ pdrops = 0;
++ idrops = 0;
++ }
++};
++
++struct SQuality
++{
++ std::string fe_status;
++ uint32_t fe_snr;
++ uint32_t fe_signal;
++ uint32_t fe_ber;
++ uint32_t fe_unc;
++};
++
++struct SSourceInfo
++{
++ std::string si_adapter;
++ std::string si_network;
++ std::string si_mux;
++ std::string si_provider;
++ std::string si_service;
++};
++
++typedef std::map<int, SChannel> SChannels;
++typedef std::map<int, STag> STags;
++typedef std::map<int, SEvent> SEvents;
++typedef std::map<int, SRecording> SRecordings;
+diff --git a/xbmc/pvrclients/tvheadend/Makefile.in b/xbmc/pvrclients/tvheadend/Makefile.in
+new file mode 100644
+index 0000000..331ce10
+--- /dev/null
++++ b/xbmc/pvrclients/tvheadend/Makefile.in
+@@ -0,0 +1,25 @@
++#
++# Makefile for the XBMC HTS Tvheadend PVR AddOn
++#
++# See the README for copyright information and
++# how to reach the author.
++#
++
++LIBS = @abs_top_srcdir@/lib/libhts/libhts.a -ldl
++LIBDIR = @abs_top_srcdir@/addons/pvr.hts
++LIB = @abs_top_srcdir@/addons/pvr.hts/XBMC_Tvheadend.pvr
++
++SRCS=client.cpp \
++ HTSPConnection.cpp \
++ HTSPData.cpp \
++ HTSPDemux.cpp
++
++include ../Makefile.include
++
++clean:
++ -rm -f $(OBJS) $(LIB) *.P *~
++ $(MAKE) -C @abs_top_srcdir@/lib/libhts clean
++
++$(LIB): $(OBJS)
++ ${MAKE} -C @abs_top_srcdir@/lib/libhts
++ $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared -g $(OBJS) $(LIBS) $(LIBDIRS) $(SILIB) -o $(LIB)
+diff --git a/xbmc/pvrclients/tvheadend/client.cpp b/xbmc/pvrclients/tvheadend/client.cpp
+new file mode 100644
+index 0000000..a1ddda5
+--- /dev/null
++++ b/xbmc/pvrclients/tvheadend/client.cpp
+@@ -0,0 +1,554 @@
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "client.h"
++#include "xbmc_pvr_dll.h"
++#include "HTSPData.h"
++#include "HTSPDemux.h"
++
++using namespace std;
++using namespace ADDON;
++
++bool m_bCreated = false;
++ADDON_STATUS m_CurStatus = ADDON_STATUS_UNKNOWN;
++int g_iClientId = -1;
++unsigned int g_iPacketSequence = 0;
++
++/* User adjustable settings are saved here.
++ * Default values are defined inside client.h
++ * and exported to the other source files.
++ */
++std::string g_strHostname = DEFAULT_HOST;
++int g_iPortHTSP = DEFAULT_HTSP_PORT;
++int g_iPortHTTP = DEFAULT_HTTP_PORT;
++int g_iConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
++int g_iResponseTimeout = DEFAULT_RESPONSE_TIMEOUT;
++std::string g_strUsername = "";
++std::string g_strPassword = "";
++std::string g_strUserPath = "";
++std::string g_strClientPath = "";
++
++CHelper_libXBMC_addon *XBMC = NULL;
++CHelper_libXBMC_pvr *PVR = NULL;
++CHTSPDemux * HTSPDemuxer = NULL;
++CHTSPData * HTSPData = NULL;
++
++extern "C" {
++
++void ADDON_ReadSettings(void)
++{
++ /* read setting "host" from settings.xml */
++ char * buffer;
++ buffer = (char*) malloc (1024);
++ buffer[0] = 0; /* Set the end of string */
++
++ if (XBMC->GetSetting("host", buffer))
++ g_strHostname = buffer;
++ else
++ g_strHostname = DEFAULT_HOST;
++ buffer[0] = 0; /* Set the end of string */
++
++ /* read setting "user" from settings.xml */
++ if (XBMC->GetSetting("user", buffer))
++ g_strUsername = buffer;
++ else
++ g_strUsername = "";
++ buffer[0] = 0; /* Set the end of string */
++
++ /* read setting "pass" from settings.xml */
++ if (XBMC->GetSetting("pass", buffer))
++ g_strPassword = buffer;
++ else
++ g_strPassword = "";
++
++ free (buffer);
++
++ /* read setting "htsp_port" from settings.xml */
++ if (!XBMC->GetSetting("htsp_port", &g_iPortHTSP))
++ g_iPortHTSP = DEFAULT_HTSP_PORT;
++
++ /* read setting "http_port" from settings.xml */
++ if (!XBMC->GetSetting("http_port", &g_iPortHTTP))
++ g_iPortHTTP = DEFAULT_HTTP_PORT;
++
++ /* read setting "connect_timeout" from settings.xml */
++ if (!XBMC->GetSetting("connect_timeout", &g_iConnectTimeout))
++ g_iConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
++
++ /* read setting "read_timeout" from settings.xml */
++ if (!XBMC->GetSetting("response_timeout", &g_iResponseTimeout))
++ g_iResponseTimeout = DEFAULT_RESPONSE_TIMEOUT;
++}
++
++ADDON_STATUS ADDON_Create(void* hdl, void* props)
++{
++ if (!hdl || !props)
++ return ADDON_STATUS_UNKNOWN;
++
++ PVR_PROPERTIES* pvrprops = (PVR_PROPERTIES*)props;
++
++ XBMC = new CHelper_libXBMC_addon;
++ if (!XBMC->RegisterMe(hdl))
++ {
++ delete XBMC;
++ XBMC = NULL;
++ return ADDON_STATUS_UNKNOWN;
++ }
++
++ PVR = new CHelper_libXBMC_pvr;
++ if (!PVR->RegisterMe(hdl))
++ {
++ delete PVR;
++ delete XBMC;
++ PVR = NULL;
++ XBMC = NULL;
++ return ADDON_STATUS_UNKNOWN;
++ }
++
++ XBMC->Log(LOG_DEBUG, "%s - Creating Tvheadend PVR-Client", __FUNCTION__);
++
++ m_CurStatus = ADDON_STATUS_UNKNOWN;
++ g_iClientId = pvrprops->iClientId;
++ g_strUserPath = pvrprops->strUserPath;
++ g_strClientPath = pvrprops->strClientPath;
++
++ ADDON_ReadSettings();
++
++ HTSPData = new CHTSPData;
++ if (!HTSPData->Open())
++ {
++ delete HTSPData;
++ delete PVR;
++ delete XBMC;
++ HTSPData = NULL;
++ PVR = NULL;
++ XBMC = NULL;
++ m_CurStatus = ADDON_STATUS_LOST_CONNECTION;
++ return m_CurStatus;
++ }
++
++ m_CurStatus = ADDON_STATUS_OK;
++ m_bCreated = true;
++ return m_CurStatus;
++}
++
++ADDON_STATUS ADDON_GetStatus()
++{
++ /* check whether we're still connected */
++ if (m_CurStatus == ADDON_STATUS_OK && !HTSPData->IsConnected())
++ m_CurStatus = ADDON_STATUS_LOST_CONNECTION;
++
++ return m_CurStatus;
++}
++
++void ADDON_Destroy()
++{
++ if (m_bCreated)
++ {
++ if (HTSPData)
++ {
++ delete HTSPData;
++ HTSPData = NULL;
++ }
++ m_bCreated = false;
++ }
++
++ if (PVR)
++ {
++ delete PVR;
++ PVR = NULL;
++ }
++
++ if (XBMC)
++ {
++ delete XBMC;
++ XBMC = NULL;
++ }
++
++ m_CurStatus = ADDON_STATUS_UNKNOWN;
++}
++
++bool ADDON_HasSettings()
++{
++ return true;
++}
++
++unsigned int ADDON_GetSettings(ADDON_StructSetting ***sSet)
++{
++ return 0;
++}
++
++ADDON_STATUS ADDON_SetSetting(const char *settingName, const void *settingValue)
++{
++ string str = settingName;
++ if (str == "host")
++ {
++ string tmp_sHostname;
++ XBMC->Log(LOG_INFO, "%s - Changed Setting 'host' from %s to %s", __FUNCTION__, g_strHostname.c_str(), (const char*) settingValue);
++ tmp_sHostname = g_strHostname;
++ g_strHostname = (const char*) settingValue;
++ if (tmp_sHostname != g_strHostname)
++ return ADDON_STATUS_NEED_RESTART;
++ }
++ else if (str == "user")
++ {
++ string tmp_sUsername = g_strUsername;
++ g_strUsername = (const char*) settingValue;
++ if (tmp_sUsername != g_strUsername)
++ {
++ XBMC->Log(LOG_INFO, "%s - Changed Setting 'user'", __FUNCTION__);
++ return ADDON_STATUS_NEED_RESTART;
++ }
++ }
++ else if (str == "pass")
++ {
++ string tmp_sPassword = g_strPassword;
++ g_strPassword = (const char*) settingValue;
++ if (tmp_sPassword != g_strPassword)
++ {
++ XBMC->Log(LOG_INFO, "%s - Changed Setting 'pass'", __FUNCTION__);
++ return ADDON_STATUS_NEED_RESTART;
++ }
++ }
++ else if (str == "htsp_port")
++ {
++ if (g_iPortHTSP != *(int*) settingValue)
++ {
++ XBMC->Log(LOG_INFO, "%s - Changed Setting 'htsp_port' from %u to %u", __FUNCTION__, g_iPortHTSP, *(int*) settingValue);
++ g_iPortHTSP = *(int*) settingValue;
++ return ADDON_STATUS_NEED_RESTART;
++ }
++ }
++ else if (str == "http_port")
++ {
++ if (g_iPortHTTP != *(int*) settingValue)
++ {
++ XBMC->Log(LOG_INFO, "%s - Changed Setting 'port' from %u to %u", __FUNCTION__, g_iPortHTTP, *(int*) settingValue);
++ g_iPortHTTP = *(int*) settingValue;
++ return ADDON_STATUS_NEED_RESTART;
++ }
++ }
++ else if (str == "connect_timeout")
++ {
++ int iNewValue = *(int*) settingValue + 1;
++ if (g_iConnectTimeout != iNewValue)
++ {
++ XBMC->Log(LOG_INFO, "%s - Changed Setting 'connect_timeout' from %u to %u", __FUNCTION__, g_iConnectTimeout, iNewValue);
++ g_iConnectTimeout = iNewValue;
++ return ADDON_STATUS_OK;
++ }
++ }
++ else if (str == "response_timeout")
++ {
++ int iNewValue = *(int*) settingValue + 1;
++ if (g_iResponseTimeout != iNewValue)
++ {
++ XBMC->Log(LOG_INFO, "%s - Changed Setting 'response_timeout' from %u to %u", __FUNCTION__, g_iResponseTimeout, iNewValue);
++ g_iResponseTimeout = iNewValue;
++ return ADDON_STATUS_OK;
++ }
++ }
++ return ADDON_STATUS_OK;
++}
++
++void ADDON_Stop()
++{
++}
++
++void ADDON_FreeSettings()
++{
++}
++
++/***********************************************************
++ * PVR Client AddOn specific public library functions
++ ***********************************************************/
++
++PVR_ERROR GetAddonCapabilities(PVR_ADDON_CAPABILITIES* pCapabilities)
++{
++ pCapabilities->bSupportsChannelSettings = false;
++ pCapabilities->bSupportsTimeshift = false;
++ pCapabilities->bSupportsEPG = true;
++ pCapabilities->bSupportsTV = true;
++ pCapabilities->bSupportsRadio = true;
++ pCapabilities->bSupportsRecordings = true;
++ pCapabilities->bSupportsTimers = true;
++ pCapabilities->bSupportsChannelGroups = true;
++ pCapabilities->bSupportsChannelScan = false;
++ pCapabilities->bHandlesInputStream = true;
++ pCapabilities->bHandlesDemuxing = true;
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++const char *GetBackendName(void)
++{
++ static const char *strBackendName = HTSPData ? HTSPData->GetServerName() : "unknown";
++ return strBackendName;
++}
++
++const char *GetBackendVersion(void)
++{
++ static CStdString strBackendVersion;
++ if (HTSPData)
++ strBackendVersion.Format("%s (Protocol: %i)", HTSPData->GetVersion(), HTSPData->GetProtocol());
++ return strBackendVersion.c_str();
++}
++
++const char *GetConnectionString(void)
++{
++ static CStdString strConnectionString;
++ if (HTSPData)
++ strConnectionString.Format("%s:%i%s", g_strHostname.c_str(), g_iPortHTSP, HTSPData->IsConnected() ? "" : " (Not connected!)");
++ else
++ strConnectionString.Format("%s:%i (addon error!)", g_strHostname.c_str(), g_iPortHTSP);
++ return strConnectionString.c_str();
++}
++
++PVR_ERROR GetDriveSpace(long long *iTotal, long long *iUsed)
++{
++ if (!HTSPData || !HTSPData->IsConnected())
++ return PVR_ERROR_SERVER_ERROR;
++
++ if (HTSPData->GetDriveSpace(iTotal, iUsed))
++ return PVR_ERROR_NO_ERROR;
++
++ return PVR_ERROR_SERVER_ERROR;
++}
++
++PVR_ERROR GetEPGForChannel(PVR_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd)
++{
++ if (!HTSPData || !HTSPData->IsConnected())
++ return PVR_ERROR_SERVER_ERROR;
++
++ return HTSPData->GetEpg(handle, channel, iStart, iEnd);
++}
++
++int GetChannelsAmount(void)
++{
++ if (!HTSPData || !HTSPData->IsConnected())
++ return 0;
++
++ return HTSPData->GetNumChannels();
++}
++
++PVR_ERROR GetChannels(PVR_HANDLE handle, bool bRadio)
++{
++ if (!HTSPData || !HTSPData->IsConnected())
++ return PVR_ERROR_SERVER_ERROR;
++
++ return HTSPData->GetChannels(handle, bRadio);
++}
++
++int GetRecordingsAmount(void)
++{
++ if (!HTSPData || !HTSPData->IsConnected())
++ return 0;
++
++ return HTSPData->GetNumRecordings();
++}
++
++PVR_ERROR GetRecordings(PVR_HANDLE handle)
++{
++ if (!HTSPData || !HTSPData->IsConnected())
++ return PVR_ERROR_SERVER_ERROR;
++
++ return HTSPData->GetRecordings(handle);
++}
++
++PVR_ERROR DeleteRecording(const PVR_RECORDING &recording)
++{
++ if (!HTSPData || !HTSPData->IsConnected())
++ return PVR_ERROR_SERVER_ERROR;
++
++ return HTSPData->DeleteRecording(recording);
++}
++
++PVR_ERROR RenameRecording(const PVR_RECORDING &recording)
++{
++ if (!HTSPData || !HTSPData->IsConnected())
++ return PVR_ERROR_SERVER_ERROR;
++
++ return HTSPData->RenameRecording(recording, recording.strTitle);
++}
++
++int GetTimersAmount(void)
++{
++ if (!HTSPData || !HTSPData->IsConnected())
++ return 0;
++
++ return HTSPData->GetNumTimers();
++}
++
++PVR_ERROR GetTimers(PVR_HANDLE handle)
++{
++ if (!HTSPData || !HTSPData->IsConnected())
++ return PVR_ERROR_SERVER_ERROR;
++
++ return HTSPData->GetTimers(handle);
++}
++
++PVR_ERROR AddTimer(const PVR_TIMER &timer)
++{
++ if (!HTSPData || !HTSPData->IsConnected())
++ return PVR_ERROR_SERVER_ERROR;
++
++ return HTSPData->AddTimer(timer);
++}
++
++PVR_ERROR DeleteTimer(const PVR_TIMER &timer, bool bForceDelete)
++{
++ if (!HTSPData || !HTSPData->IsConnected())
++ return PVR_ERROR_SERVER_ERROR;
++
++ return HTSPData->DeleteTimer(timer, bForceDelete);
++}
++
++PVR_ERROR UpdateTimer(const PVR_TIMER &timer)
++{
++ if (!HTSPData || !HTSPData->IsConnected())
++ return PVR_ERROR_SERVER_ERROR;
++
++ return timer.state == PVR_TIMER_STATE_CANCELLED || timer.state == PVR_TIMER_STATE_ABORTED ?
++ HTSPData->DeleteTimer(timer, false) :
++ HTSPData->UpdateTimer(timer);
++}
++
++bool OpenLiveStream(const PVR_CHANNEL &channel)
++{
++ CloseLiveStream();
++
++ if (!HTSPData || !HTSPData->IsConnected())
++ return false;
++
++ HTSPDemuxer = new CHTSPDemux;
++ return HTSPDemuxer->Open(channel);
++}
++
++void CloseLiveStream(void)
++{
++ if (HTSPDemuxer)
++ {
++ HTSPDemuxer->Close();
++ delete HTSPDemuxer;
++ HTSPDemuxer = NULL;
++ }
++}
++
++int GetCurrentClientChannel(void)
++{
++ if (HTSPDemuxer)
++ return HTSPDemuxer->CurrentChannel();
++
++ return -1;
++}
++
++bool SwitchChannel(const PVR_CHANNEL &channel)
++{
++ if (HTSPDemuxer)
++ return HTSPDemuxer->SwitchChannel(channel);
++
++ return false;
++}
++
++PVR_ERROR GetStreamProperties(PVR_STREAM_PROPERTIES* pProperties)
++{
++ if (HTSPDemuxer && HTSPDemuxer->GetStreamProperties(pProperties))
++ return PVR_ERROR_NO_ERROR;
++
++ return PVR_ERROR_SERVER_ERROR;
++}
++
++PVR_ERROR SignalStatus(PVR_SIGNAL_STATUS &signalStatus)
++{
++ if (HTSPDemuxer && HTSPDemuxer->GetSignalStatus(signalStatus))
++ return PVR_ERROR_NO_ERROR;
++
++ return PVR_ERROR_SERVER_ERROR;
++}
++
++void DemuxAbort(void)
++{
++ if (HTSPDemuxer)
++ HTSPDemuxer->Abort();
++}
++
++DemuxPacket* DemuxRead(void)
++{
++ if (HTSPDemuxer)
++ return HTSPDemuxer->Read();
++ else
++ return NULL;
++}
++
++int GetChannelGroupsAmount(void)
++{
++ if (!HTSPData || !HTSPData->IsConnected())
++ return PVR_ERROR_SERVER_ERROR;
++
++ return HTSPData->GetNumChannelGroups();
++}
++
++PVR_ERROR GetChannelGroups(PVR_HANDLE handle, bool bRadio)
++{
++ /* tvheadend doesn't support separated groups, so we only support TV groups */
++ if (bRadio)
++ return PVR_ERROR_NO_ERROR;
++
++ if (!HTSPData || !HTSPData->IsConnected())
++ return PVR_ERROR_SERVER_ERROR;
++
++ return HTSPData->GetChannelGroups(handle);
++}
++
++PVR_ERROR GetChannelGroupMembers(PVR_HANDLE handle, const PVR_CHANNEL_GROUP &group)
++{
++ /* tvheadend doesn't support separated groups, so we only support TV groups */
++ if (group.bIsRadio)
++ return PVR_ERROR_NO_ERROR;
++
++ if (!HTSPData || !HTSPData->IsConnected())
++ return PVR_ERROR_SERVER_ERROR;
++
++ return HTSPData->GetChannelGroupMembers(handle, group);
++}
++
++/** UNUSED API FUNCTIONS */
++PVR_ERROR DialogChannelScan(void) { return PVR_ERROR_NOT_IMPLEMENTED; }
++PVR_ERROR CallMenuHook(const PVR_MENUHOOK &menuhook) { return PVR_ERROR_NOT_IMPLEMENTED; }
++PVR_ERROR DeleteChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
++PVR_ERROR RenameChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
++PVR_ERROR MoveChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
++PVR_ERROR DialogChannelSettings(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
++PVR_ERROR DialogAddChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
++bool OpenRecordedStream(const PVR_RECORDING &recording) { return false; }
++void CloseRecordedStream(void) {}
++int ReadRecordedStream(unsigned char *pBuffer, unsigned int iBufferSize) { return 0; }
++long long SeekRecordedStream(long long iPosition, int iWhence /* = SEEK_SET */) { return 0; }
++long long PositionRecordedStream(void) { return -1; }
++long long LengthRecordedStream(void) { return 0; }
++void DemuxReset(void) {}
++void DemuxFlush(void) {}
++int ReadLiveStream(unsigned char *pBuffer, unsigned int iBufferSize) { return 0; }
++long long SeekLiveStream(long long iPosition, int iWhence /* = SEEK_SET */) { return -1; }
++long long PositionLiveStream(void) { return -1; }
++long long LengthLiveStream(void) { return -1; }
++const char * GetLiveStreamURL(const PVR_CHANNEL &channel) { return ""; }
++}
+diff --git a/xbmc/pvrclients/tvheadend/client.h b/xbmc/pvrclients/tvheadend/client.h
+new file mode 100644
+index 0000000..27c8a81
+--- /dev/null
++++ b/xbmc/pvrclients/tvheadend/client.h
+@@ -0,0 +1,46 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2011 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "../../../addons/library.xbmc.addon/libXBMC_addon.h"
++#include "../../../addons/library.xbmc.pvr/libXBMC_pvr.h"
++
++#define DEFAULT_HOST "127.0.0.1"
++#define DEFAULT_HTTP_PORT 9981
++#define DEFAULT_HTSP_PORT 9982
++#define DEFAULT_CONNECT_TIMEOUT 30
++#define DEFAULT_RESPONSE_TIMEOUT 3
++
++extern bool m_bCreated;
++extern std::string g_strHostname;
++extern int g_iPortHTSP;
++extern int g_iPortHTTP;
++extern std::string g_strUsername;
++extern std::string g_strPassword;
++extern int g_iConnectTimeout;
++extern int g_iResponseTimeout;
++extern int g_iClientId;
++extern unsigned int g_iPacketSequence;
++extern bool g_bShowTimerNotifications;
++extern std::string g_szUserPath;
++extern std::string g_szClientPath;
++extern ADDON::CHelper_libXBMC_addon * XBMC;
++extern CHelper_libXBMC_pvr * PVR;
+diff --git a/xbmc/pvrclients/tvheadend/project/VS2010Express/XBMC_tvheadend.vcxproj b/xbmc/pvrclients/tvheadend/project/VS2010Express/XBMC_tvheadend.vcxproj
+new file mode 100644
+index 0000000..6d4a281
+--- /dev/null
++++ b/xbmc/pvrclients/tvheadend/project/VS2010Express/XBMC_tvheadend.vcxproj
+@@ -0,0 +1,112 @@
++<?xml version="1.0" encoding="utf-8"?>
++<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
++ <ItemGroup Label="ProjectConfigurations">
++ <ProjectConfiguration Include="Debug|Win32">
++ <Configuration>Debug</Configuration>
++ <Platform>Win32</Platform>
++ </ProjectConfiguration>
++ <ProjectConfiguration Include="Release|Win32">
++ <Configuration>Release</Configuration>
++ <Platform>Win32</Platform>
++ </ProjectConfiguration>
++ </ItemGroup>
++ <ItemGroup>
++ <ClCompile Include="..\..\..\..\..\addons\library.xbmc.addon\dlfcn-win32.cpp" />
++ <ClCompile Include="..\..\..\..\..\lib\platform\windows\os-threads.cpp" />
++ <ClCompile Include="..\..\client.cpp" />
++ <ClCompile Include="..\..\HTSPConnection.cpp" />
++ <ClCompile Include="..\..\HTSPData.cpp" />
++ <ClCompile Include="..\..\HTSPDemux.cpp" />
++ </ItemGroup>
++ <ItemGroup>
++ <ClInclude Include="..\..\..\..\..\addons\library.xbmc.addon\dlfcn-win32.h" />
++ <ClInclude Include="..\..\client.h" />
++ <ClInclude Include="..\..\HTSPConnection.h" />
++ <ClInclude Include="..\..\HTSPData.h" />
++ <ClInclude Include="..\..\HTSPDemux.h" />
++ </ItemGroup>
++ <PropertyGroup Label="Globals">
++ <ProjectGuid>{C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}</ProjectGuid>
++ <RootNamespace>XBMC_tvheadend</RootNamespace>
++ <ProjectName>pvrclient_tvheadend</ProjectName>
++ </PropertyGroup>
++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
++ <ConfigurationType>DynamicLibrary</ConfigurationType>
++ <UseDebugLibraries>true</UseDebugLibraries>
++ <CharacterSet>MultiByte</CharacterSet>
++ </PropertyGroup>
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
++ <ConfigurationType>DynamicLibrary</ConfigurationType>
++ <UseDebugLibraries>false</UseDebugLibraries>
++ <WholeProgramOptimization>true</WholeProgramOptimization>
++ <CharacterSet>MultiByte</CharacterSet>
++ </PropertyGroup>
++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
++ <ImportGroup Label="ExtensionSettings">
++ </ImportGroup>
++ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
++ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
++ </ImportGroup>
++ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
++ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
++ </ImportGroup>
++ <PropertyGroup Label="UserMacros" />
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
++ <OutDir>..\..\..\..\..\addons\pvr.hts\</OutDir>
++ </PropertyGroup>
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
++ <TargetName>XBMC_Tvheadend_win32</TargetName>
++ </PropertyGroup>
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
++ <TargetExt>.pvr</TargetExt>
++ </PropertyGroup>
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
++ <ExtensionsToDeleteOnClean>*.cdf;*.cache;*.obj;*.ilk;*.resources;*.tlb;*.tli;*.tlh;*.tmp;*.rsp;*.pgc;*.pgd;*.meta;*.tlog;*.manifest;*.res;*.pch;*.exp;*.idb;*.rep;*.xdc;*.pdb;*_manifest.rc;*.bsc;*.sbr;*.xml;*.pvr</ExtensionsToDeleteOnClean>
++ <IncludePath>$(SolutionDir)\..\..\xbmc;$(IncludePath)</IncludePath>
++ </PropertyGroup>
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
++ <OutDir>..\..\..\..\..\addons\pvr.hts\</OutDir>
++ <TargetName>XBMC_Tvheadend_win32</TargetName>
++ <TargetExt>.pvr</TargetExt>
++ <ExtensionsToDeleteOnClean>*.cdf;*.cache;*.obj;*.ilk;*.resources;*.tlb;*.tli;*.tlh;*.tmp;*.rsp;*.pgc;*.pgd;*.meta;*.tlog;*.manifest;*.res;*.pch;*.exp;*.idb;*.rep;*.xdc;*.pdb;*_manifest.rc;*.bsc;*.sbr;*.xml;*.pvr</ExtensionsToDeleteOnClean>
++ <IncludePath>$(SolutionDir)\..\..\xbmc;$(IncludePath)</IncludePath>
++ </PropertyGroup>
++ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
++ <ClCompile>
++ <WarningLevel>Level3</WarningLevel>
++ <Optimization>Disabled</Optimization>
++ <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_USE_32BIT_TIME_T;_WINSOCKAPI_;USE_DEMUX;__STDC_CONSTANT_MACROS;__WINDOWS__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++ <AdditionalIncludeDirectories>..\..\..\..\..\lib\ffmpeg;..\..\..\..\..\lib\ffmpeg\include;..\..\..\..\..\lib\ffmpeg\include-xbmc-win32;..\..\;..\..\windows;..\..\..\..\addons\include;..\..\..\..\cores\dvdplayer\DVDDemuxers;..\..\..\..\..\lib;..\..\..\..\..\lib\libhts\Win32\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
++ <DisableSpecificWarnings>4996</DisableSpecificWarnings>
++ </ClCompile>
++ <Link>
++ <GenerateDebugInformation>true</GenerateDebugInformation>
++ <OutputFile>..\..\..\..\..\addons\pvr.hts\XBMC_Tvheadend_win32.pvr</OutputFile>
++ <AdditionalDependencies>ws2_32.lib;$(SolutionDir)\libs\libhts\$(Configuration)\libhts.lib;%(AdditionalDependencies)</AdditionalDependencies>
++ <IgnoreSpecificDefaultLibraries>libcmtd</IgnoreSpecificDefaultLibraries>
++ </Link>
++ </ItemDefinitionGroup>
++ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
++ <ClCompile>
++ <WarningLevel>Level3</WarningLevel>
++ <Optimization>MaxSpeed</Optimization>
++ <FunctionLevelLinking>true</FunctionLevelLinking>
++ <IntrinsicFunctions>true</IntrinsicFunctions>
++ <AdditionalIncludeDirectories>..\..\..\..\..\lib\ffmpeg;..\..\..\..\..\lib\ffmpeg\include;..\..\..\..\..\lib\ffmpeg\include-xbmc-win32;..\..\;..\..\windows;..\..\..\..\addons\include;..\..\..\..\cores\dvdplayer\DVDDemuxers;..\..\..\..\..\lib;..\..\..\..\..\lib\libhts\Win32\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
++ <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_USE_32BIT_TIME_T;_WINSOCKAPI_;USE_DEMUX;__STDC_CONSTANT_MACROS;__WINDOWS__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++ <DisableSpecificWarnings>4996</DisableSpecificWarnings>
++ </ClCompile>
++ <Link>
++ <GenerateDebugInformation>true</GenerateDebugInformation>
++ <EnableCOMDATFolding>true</EnableCOMDATFolding>
++ <OptimizeReferences>true</OptimizeReferences>
++ <OutputFile>..\..\..\..\..\addons\pvr.hts\XBMC_Tvheadend_win32.pvr</OutputFile>
++ <AdditionalDependencies>ws2_32.lib;$(SolutionDir)\libs\libhts\$(Configuration)\libhts.lib;%(AdditionalDependencies)</AdditionalDependencies>
++ <IgnoreSpecificDefaultLibraries>libcmt</IgnoreSpecificDefaultLibraries>
++ </Link>
++ </ItemDefinitionGroup>
++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
++ <ImportGroup Label="ExtensionTargets">
++ </ImportGroup>
++</Project>
+diff --git a/xbmc/pvrclients/tvheadend/project/VS2010Express/XBMC_tvheadend.vcxproj.filters b/xbmc/pvrclients/tvheadend/project/VS2010Express/XBMC_tvheadend.vcxproj.filters
+new file mode 100644
+index 0000000..c85055a
+--- /dev/null
++++ b/xbmc/pvrclients/tvheadend/project/VS2010Express/XBMC_tvheadend.vcxproj.filters
+@@ -0,0 +1,54 @@
++<?xml version="1.0" encoding="utf-8"?>
++<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
++ <ItemGroup>
++ <Filter Include="Source Files">
++ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
++ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
++ </Filter>
++ <Filter Include="Header Files">
++ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
++ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
++ </Filter>
++ <Filter Include="Resource Files">
++ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
++ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
++ </Filter>
++ </ItemGroup>
++ <ItemGroup>
++ <ClCompile Include="..\..\client.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\HTSPData.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\HTSPDemux.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\..\..\..\addons\library.xbmc.addon\dlfcn-win32.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\..\..\..\lib\platform\windows\os-threads.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\HTSPConnection.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ </ItemGroup>
++ <ItemGroup>
++ <ClInclude Include="..\..\..\..\..\addons\library.xbmc.addon\dlfcn-win32.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\client.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\HTSPData.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\HTSPDemux.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\HTSPConnection.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ </ItemGroup>
++</Project>
+diff --git a/xbmc/pvrclients/vdr-vnsi/COPYING b/xbmc/pvrclients/vdr-vnsi/COPYING
+new file mode 100644
+index 0000000..f90922e
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/COPYING
+@@ -0,0 +1,340 @@
++ GNU GENERAL PUBLIC LICENSE
++ Version 2, June 1991
++
++ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ Everyone is permitted to copy and distribute verbatim copies
++ of this license document, but changing it is not allowed.
++
++ Preamble
++
++ The licenses for most software are designed to take away your
++freedom to share and change it. By contrast, the GNU General Public
++License is intended to guarantee your freedom to share and change free
++software--to make sure the software is free for all its users. This
++General Public License applies to most of the Free Software
++Foundation's software and to any other program whose authors commit to
++using it. (Some other Free Software Foundation software is covered by
++the GNU Lesser General Public License instead.) You can apply it to
++your programs, too.
++
++ When we speak of free software, we are referring to freedom, not
++price. Our General Public Licenses are designed to make sure that you
++have the freedom to distribute copies of free software (and charge for
++this service if you wish), that you receive source code or can get it
++if you want it, that you can change the software or use pieces of it
++in new free programs; and that you know you can do these things.
++
++ To protect your rights, we need to make restrictions that forbid
++anyone to deny you these rights or to ask you to surrender the rights.
++These restrictions translate to certain responsibilities for you if you
++distribute copies of the software, or if you modify it.
++
++ For example, if you distribute copies of such a program, whether
++gratis or for a fee, you must give the recipients all the rights that
++you have. You must make sure that they, too, receive or can get the
++source code. And you must show them these terms so they know their
++rights.
++
++ We protect your rights with two steps: (1) copyright the software, and
++(2) offer you this license which gives you legal permission to copy,
++distribute and/or modify the software.
++
++ Also, for each author's protection and ours, we want to make certain
++that everyone understands that there is no warranty for this free
++software. If the software is modified by someone else and passed on, we
++want its recipients to know that what they have is not the original, so
++that any problems introduced by others will not reflect on the original
++authors' reputations.
++
++ Finally, any free program is threatened constantly by software
++patents. We wish to avoid the danger that redistributors of a free
++program will individually obtain patent licenses, in effect making the
++program proprietary. To prevent this, we have made it clear that any
++patent must be licensed for everyone's free use or not licensed at all.
++
++ The precise terms and conditions for copying, distribution and
++modification follow.
++
++ GNU GENERAL PUBLIC LICENSE
++ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
++
++ 0. This License applies to any program or other work which contains
++a notice placed by the copyright holder saying it may be distributed
++under the terms of this General Public License. The "Program", below,
++refers to any such program or work, and a "work based on the Program"
++means either the Program or any derivative work under copyright law:
++that is to say, a work containing the Program or a portion of it,
++either verbatim or with modifications and/or translated into another
++language. (Hereinafter, translation is included without limitation in
++the term "modification".) Each licensee is addressed as "you".
++
++Activities other than copying, distribution and modification are not
++covered by this License; they are outside its scope. The act of
++running the Program is not restricted, and the output from the Program
++is covered only if its contents constitute a work based on the
++Program (independent of having been made by running the Program).
++Whether that is true depends on what the Program does.
++
++ 1. You may copy and distribute verbatim copies of the Program's
++source code as you receive it, in any medium, provided that you
++conspicuously and appropriately publish on each copy an appropriate
++copyright notice and disclaimer of warranty; keep intact all the
++notices that refer to this License and to the absence of any warranty;
++and give any other recipients of the Program a copy of this License
++along with the Program.
++
++You may charge a fee for the physical act of transferring a copy, and
++you may at your option offer warranty protection in exchange for a fee.
++
++ 2. You may modify your copy or copies of the Program or any portion
++of it, thus forming a work based on the Program, and copy and
++distribute such modifications or work under the terms of Section 1
++above, provided that you also meet all of these conditions:
++
++ a) You must cause the modified files to carry prominent notices
++ stating that you changed the files and the date of any change.
++
++ b) You must cause any work that you distribute or publish, that in
++ whole or in part contains or is derived from the Program or any
++ part thereof, to be licensed as a whole at no charge to all third
++ parties under the terms of this License.
++
++ c) If the modified program normally reads commands interactively
++ when run, you must cause it, when started running for such
++ interactive use in the most ordinary way, to print or display an
++ announcement including an appropriate copyright notice and a
++ notice that there is no warranty (or else, saying that you provide
++ a warranty) and that users may redistribute the program under
++ these conditions, and telling the user how to view a copy of this
++ License. (Exception: if the Program itself is interactive but
++ does not normally print such an announcement, your work based on
++ the Program is not required to print an announcement.)
++
++These requirements apply to the modified work as a whole. If
++identifiable sections of that work are not derived from the Program,
++and can be reasonably considered independent and separate works in
++themselves, then this License, and its terms, do not apply to those
++sections when you distribute them as separate works. But when you
++distribute the same sections as part of a whole which is a work based
++on the Program, the distribution of the whole must be on the terms of
++this License, whose permissions for other licensees extend to the
++entire whole, and thus to each and every part regardless of who wrote it.
++
++Thus, it is not the intent of this section to claim rights or contest
++your rights to work written entirely by you; rather, the intent is to
++exercise the right to control the distribution of derivative or
++collective works based on the Program.
++
++In addition, mere aggregation of another work not based on the Program
++with the Program (or with a work based on the Program) on a volume of
++a storage or distribution medium does not bring the other work under
++the scope of this License.
++
++ 3. You may copy and distribute the Program (or a work based on it,
++under Section 2) in object code or executable form under the terms of
++Sections 1 and 2 above provided that you also do one of the following:
++
++ a) Accompany it with the complete corresponding machine-readable
++ source code, which must be distributed under the terms of Sections
++ 1 and 2 above on a medium customarily used for software interchange; or,
++
++ b) Accompany it with a written offer, valid for at least three
++ years, to give any third party, for a charge no more than your
++ cost of physically performing source distribution, a complete
++ machine-readable copy of the corresponding source code, to be
++ distributed under the terms of Sections 1 and 2 above on a medium
++ customarily used for software interchange; or,
++
++ c) Accompany it with the information you received as to the offer
++ to distribute corresponding source code. (This alternative is
++ allowed only for noncommercial distribution and only if you
++ received the program in object code or executable form with such
++ an offer, in accord with Subsection b above.)
++
++The source code for a work means the preferred form of the work for
++making modifications to it. For an executable work, complete source
++code means all the source code for all modules it contains, plus any
++associated interface definition files, plus the scripts used to
++control compilation and installation of the executable. However, as a
++special exception, the source code distributed need not include
++anything that is normally distributed (in either source or binary
++form) with the major components (compiler, kernel, and so on) of the
++operating system on which the executable runs, unless that component
++itself accompanies the executable.
++
++If distribution of executable or object code is made by offering
++access to copy from a designated place, then offering equivalent
++access to copy the source code from the same place counts as
++distribution of the source code, even though third parties are not
++compelled to copy the source along with the object code.
++
++ 4. You may not copy, modify, sublicense, or distribute the Program
++except as expressly provided under this License. Any attempt
++otherwise to copy, modify, sublicense or distribute the Program is
++void, and will automatically terminate your rights under this License.
++However, parties who have received copies, or rights, from you under
++this License will not have their licenses terminated so long as such
++parties remain in full compliance.
++
++ 5. You are not required to accept this License, since you have not
++signed it. However, nothing else grants you permission to modify or
++distribute the Program or its derivative works. These actions are
++prohibited by law if you do not accept this License. Therefore, by
++modifying or distributing the Program (or any work based on the
++Program), you indicate your acceptance of this License to do so, and
++all its terms and conditions for copying, distributing or modifying
++the Program or works based on it.
++
++ 6. Each time you redistribute the Program (or any work based on the
++Program), the recipient automatically receives a license from the
++original licensor to copy, distribute or modify the Program subject to
++these terms and conditions. You may not impose any further
++restrictions on the recipients' exercise of the rights granted herein.
++You are not responsible for enforcing compliance by third parties to
++this License.
++
++ 7. If, as a consequence of a court judgment or allegation of patent
++infringement or for any other reason (not limited to patent issues),
++conditions are imposed on you (whether by court order, agreement or
++otherwise) that contradict the conditions of this License, they do not
++excuse you from the conditions of this License. If you cannot
++distribute so as to satisfy simultaneously your obligations under this
++License and any other pertinent obligations, then as a consequence you
++may not distribute the Program at all. For example, if a patent
++license would not permit royalty-free redistribution of the Program by
++all those who receive copies directly or indirectly through you, then
++the only way you could satisfy both it and this License would be to
++refrain entirely from distribution of the Program.
++
++If any portion of this section is held invalid or unenforceable under
++any particular circumstance, the balance of the section is intended to
++apply and the section as a whole is intended to apply in other
++circumstances.
++
++It is not the purpose of this section to induce you to infringe any
++patents or other property right claims or to contest validity of any
++such claims; this section has the sole purpose of protecting the
++integrity of the free software distribution system, which is
++implemented by public license practices. Many people have made
++generous contributions to the wide range of software distributed
++through that system in reliance on consistent application of that
++system; it is up to the author/donor to decide if he or she is willing
++to distribute software through any other system and a licensee cannot
++impose that choice.
++
++This section is intended to make thoroughly clear what is believed to
++be a consequence of the rest of this License.
++
++ 8. If the distribution and/or use of the Program is restricted in
++certain countries either by patents or by copyrighted interfaces, the
++original copyright holder who places the Program under this License
++may add an explicit geographical distribution limitation excluding
++those countries, so that distribution is permitted only in or among
++countries not thus excluded. In such case, this License incorporates
++the limitation as if written in the body of this License.
++
++ 9. The Free Software Foundation may publish revised and/or new versions
++of the General Public License from time to time. Such new versions will
++be similar in spirit to the present version, but may differ in detail to
++address new problems or concerns.
++
++Each version is given a distinguishing version number. If the Program
++specifies a version number of this License which applies to it and "any
++later version", you have the option of following the terms and conditions
++either of that version or of any later version published by the Free
++Software Foundation. If the Program does not specify a version number of
++this License, you may choose any version ever published by the Free Software
++Foundation.
++
++ 10. If you wish to incorporate parts of the Program into other free
++programs whose distribution conditions are different, write to the author
++to ask for permission. For software which is copyrighted by the Free
++Software Foundation, write to the Free Software Foundation; we sometimes
++make exceptions for this. Our decision will be guided by the two goals
++of preserving the free status of all derivatives of our free software and
++of promoting the sharing and reuse of software generally.
++
++ NO WARRANTY
++
++ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
++FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
++OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
++PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
++OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
++MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
++TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
++PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
++REPAIR OR CORRECTION.
++
++ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
++WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
++REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
++INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
++OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
++TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
++YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
++PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
++POSSIBILITY OF SUCH DAMAGES.
++
++ END OF TERMS AND CONDITIONS
++
++ How to Apply These Terms to Your New Programs
++
++ If you develop a new program, and you want it to be of the greatest
++possible use to the public, the best way to achieve this is to make it
++free software which everyone can redistribute and change under these terms.
++
++ To do so, attach the following notices to the program. It is safest
++to attach them to the start of each source file to most effectively
++convey the exclusion of warranty; and each file should have at least
++the "copyright" line and a pointer to where the full notice is found.
++
++ <one line to give the program's name and a brief idea of what it does.>
++ Copyright (C) <year> <name of author>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 2 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++
++
++Also add information on how to contact you by electronic and paper mail.
++
++If the program is interactive, make it output a short notice like this
++when it starts in an interactive mode:
++
++ Gnomovision version 69, Copyright (C) year name of author
++ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
++ This is free software, and you are welcome to redistribute it
++ under certain conditions; type `show c' for details.
++
++The hypothetical commands `show w' and `show c' should show the appropriate
++parts of the General Public License. Of course, the commands you use may
++be called something other than `show w' and `show c'; they could even be
++mouse-clicks or menu items--whatever suits your program.
++
++You should also get your employer (if you work as a programmer) or your
++school, if any, to sign a "copyright disclaimer" for the program, if
++necessary. Here is a sample; alter the names:
++
++ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
++ `Gnomovision' (which makes passes at compilers) written by James Hacker.
++
++ <signature of Ty Coon>, 1 April 1989
++ Ty Coon, President of Vice
++
++This General Public License does not permit incorporating your program into
++proprietary programs. If your program is a subroutine library, you may
++consider it more useful to permit linking proprietary applications with the
++library. If this is what you want to do, use the GNU Lesser General
++Public License instead of this License.
+diff --git a/xbmc/pvrclients/vdr-vnsi/Makefile.in b/xbmc/pvrclients/vdr-vnsi/Makefile.in
+new file mode 100644
+index 0000000..b02878e
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/Makefile.in
+@@ -0,0 +1,29 @@
++#
++# Makefile for the XBMC Video Disk Recorder VNSI PVR AddOn
++#
++# See the README for copyright information and
++# how to reach the author.
++#
++
++LIBS = -ldl
++LIBDIR = @abs_top_srcdir@/addons/pvr.vdr.vnsi
++LIB = @abs_top_srcdir@/addons/pvr.vdr.vnsi/XBMC_VDR_vnsi.pvr
++
++SRCS = client.cpp \
++ VNSIChannelScan.cpp \
++ VNSIData.cpp \
++ VNSIDemux.cpp \
++ VNSIRecording.cpp \
++ VNSISession.cpp \
++ requestpacket.cpp \
++ responsepacket.cpp \
++ tools.cpp
++
++include ../Makefile.include
++
++clean:
++ -rm -f $(OBJS) $(LIB) *.P *~
++
++$(LIB): $(OBJS)
++ $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared -g $(OBJS) $(LIBS) $(LIBDIRS) $(SILIB) -o $(LIB)
++
+diff --git a/xbmc/pvrclients/vdr-vnsi/README b/xbmc/pvrclients/vdr-vnsi/README
+new file mode 100644
+index 0000000..c4a8676
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/README
+@@ -0,0 +1,71 @@
++XBMC Video Disk Recorder ('VDR') PVR Add-on
++------------------------------------------
++
++THIS IS A PRELIMINARY README AND IS SUBJECT TO CHANGE!!!
++
++Written by: Alwin Esch (Team XBMC)
++
++Project's homepage:
++
++Latest version available at:
++
++This program is free software; you can redistribute 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.
++See the file COPYING for more information.
++
++------------------------------------------
++
++This is a PVR Add-on for XBMC to add VDR (http://www.cadsoft.de/vdr) as a TV/PVR Backend to
++XBMC based upon the new VNSI Protocol.
++
++It want to add support for Live TV watching, replaying of Recordings, programming Timers and.
++EPG TV Guide to use on same computer or over the Network.
++
++The connection of this AddOn depend upon a installed VNSI-Server-Plugin on the VDR which
++is included inside the "vdr-plugin-vnsiserver" directory of this Addon.
++VDR itself need no patches or modification to use all current features.
++
++VDR Versions older as 1.6.0 are not supported by this plugin.
++
++------------------------------------------
++PLUGIN INSTALLATION INSTRUCTIONS:
++
++Copy the "vdr-plugin-vnsiserver" directory to your "VDR/plugins/src" directory and
++rename it to "vnsiserver".
++
++Run a "make plugins" and depented on your environment a "make install".
++
++And add "-P'vnsiserver'" to your startup options.
++
++
++------------------------------------------
++CHANNEL SCANNING
++
++For channel scan's a modified version of the wirbelscan plugin for VDR (Version dev-0.0.5-pre11e) is
++required. You can download the orginal source from here "http://wirbel.htpc-forum.de/wirbelscan/index2.html".
++
++The VNSI communicate with wirbelscan over VDR's plugin service interface, to add this feature
++you must patch wirbelscan with the file in "patches/vdr-wirbelscan-0.0.5-pre11e-AddServiceInterface.diff".
++
++Please load VDR with VNSI plugin together with the modified wirbelscan plugin and you can access the
++"Search Channels" option inside "Settings->TV->General"
++
++Scanning can take up to 50 minutes dependet on signal type and signal quality.
++ * DVB-T ~ 5 min
++ * DVB-C ~ 30 min (Symbolrate=AUTO, QAM=AUTO)
++ * DVB-S/S2 ~ 50 min (depend on Satellite, Beam, Hardware)
++ * Analog ~ 5 min
++
++Note: Please notice the warning on the wirbelscan plugin homepage:
++ "Development Version - kein Support. Benutzung auf eigene Verantwortung."
++ "Development Version - no support. Use at your own risk."
++ This means, the time how long the scan run and problems on VDR side are dependet on wirbelscan
++ and not part of VNSI.
++
++
++------------------------------------------
++
++Links:
++VDR: http://www.cadsoft.de/vdr
+diff --git a/xbmc/pvrclients/vdr-vnsi/VNSIChannelScan.cpp b/xbmc/pvrclients/vdr-vnsi/VNSIChannelScan.cpp
+new file mode 100644
+index 0000000..f84914f
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/VNSIChannelScan.cpp
+@@ -0,0 +1,598 @@
++/*
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <limits.h>
++#include "VNSIChannelScan.h"
++#include "responsepacket.h"
++#include "requestpacket.h"
++#include "vnsicommand.h"
++
++#include <sstream>
++
++
++#define BUTTON_START 5
++#define BUTTON_BACK 6
++#define BUTTON_CANCEL 7
++#define HEADER_LABEL 8
++
++#define SPIN_CONTROL_SOURCE_TYPE 10
++#define CONTROL_RADIO_BUTTON_TV 11
++#define CONTROL_RADIO_BUTTON_RADIO 12
++#define CONTROL_RADIO_BUTTON_FTA 13
++#define CONTROL_RADIO_BUTTON_SCRAMBLED 14
++#define CONTROL_RADIO_BUTTON_HD 15
++#define CONTROL_SPIN_COUNTRIES 16
++#define CONTROL_SPIN_SATELLITES 17
++#define CONTROL_SPIN_DVBC_INVERSION 18
++#define CONTROL_SPIN_DVBC_SYMBOLRATE 29
++#define CONTROL_SPIN_DVBC_QAM 20
++#define CONTROL_SPIN_DVBT_INVERSION 21
++#define CONTROL_SPIN_ATSC_TYPE 22
++
++#define LABEL_TYPE 30
++#define LABEL_DEVICE 31
++#define PROGRESS_DONE 32
++#define LABEL_TRANSPONDER 33
++#define LABEL_SIGNAL 34
++#define PROGRESS_SIGNAL 35
++#define LABEL_STATUS 36
++
++using namespace ADDON;
++
++cVNSIChannelScan::cVNSIChannelScan()
++{
++}
++
++cVNSIChannelScan::~cVNSIChannelScan()
++{
++}
++
++bool cVNSIChannelScan::Open(const std::string& hostname, int port, const char* name)
++{
++ m_running = false;
++ m_Canceled = false;
++ m_stopped = true;
++ m_progressDone = NULL;
++ m_progressSignal = NULL;
++
++ if(!cVNSIData::Open(hostname, port, "XBMC channel scanner"))
++ return false;
++
++ /* Load the Window as Dialog */
++ m_window = GUI->Window_create("ChannelScan.xml", "Confluence", false, true);
++ m_window->m_cbhdl = this;
++ m_window->CBOnInit = OnInitCB;
++ m_window->CBOnFocus = OnFocusCB;
++ m_window->CBOnClick = OnClickCB;
++ m_window->CBOnAction= OnActionCB;
++ m_window->DoModal();
++
++ GUI->Window_destroy(m_window);
++ Close();
++
++ return true;
++}
++
++void cVNSIChannelScan::StartScan()
++{
++ m_header = XBMC->GetLocalizedString(30025);
++ m_Signal = XBMC->GetLocalizedString(30029);
++ SetProgress(0);
++ SetSignal(0, false);
++
++ int source = m_spinSourceType->GetValue();
++ switch (source)
++ {
++ case DVB_TERR:
++ m_window->SetControlLabel(LABEL_TYPE, "DVB-T");
++ break;
++ case DVB_CABLE:
++ m_window->SetControlLabel(LABEL_TYPE, "DVB-C");
++ break;
++ case DVB_SAT:
++ m_window->SetControlLabel(LABEL_TYPE, "DVB-S/S2");
++ break;
++ case PVRINPUT:
++ m_window->SetControlLabel(LABEL_TYPE, XBMC->GetLocalizedString(30032));
++ break;
++ case PVRINPUT_FM:
++ m_window->SetControlLabel(LABEL_TYPE, XBMC->GetLocalizedString(30033));
++ break;
++ case DVB_ATSC:
++ m_window->SetControlLabel(LABEL_TYPE, "ATSC");
++ break;
++ }
++
++ cRequestPacket vrp;
++ cResponsePacket* vresp = NULL;
++ uint32_t retCode = VNSI_RET_ERROR;
++ if (!vrp.init(VNSI_SCAN_START)) goto SCANError;
++ if (!vrp.add_U32(source)) goto SCANError;
++ if (!vrp.add_U8(m_radioButtonTV->IsSelected())) goto SCANError;
++ if (!vrp.add_U8(m_radioButtonRadio->IsSelected())) goto SCANError;
++ if (!vrp.add_U8(m_radioButtonFTA->IsSelected())) goto SCANError;
++ if (!vrp.add_U8(m_radioButtonScrambled->IsSelected())) goto SCANError;
++ if (!vrp.add_U8(m_radioButtonHD->IsSelected())) goto SCANError;
++ if (!vrp.add_U32(m_spinCountries->GetValue())) goto SCANError;
++ if (!vrp.add_U32(m_spinDVBCInversion->GetValue())) goto SCANError;
++ if (!vrp.add_U32(m_spinDVBCSymbolrates->GetValue())) goto SCANError;
++ if (!vrp.add_U32(m_spinDVBCqam->GetValue())) goto SCANError;
++ if (!vrp.add_U32(m_spinDVBTInversion->GetValue())) goto SCANError;
++ if (!vrp.add_U32(m_spinSatellites->GetValue())) goto SCANError;
++ if (!vrp.add_U32(m_spinATSCType->GetValue())) goto SCANError;
++
++ vresp = ReadResult(&vrp);
++ if (!vresp)
++ goto SCANError;
++
++ retCode = vresp->extract_U32();
++ if (retCode != VNSI_RET_OK)
++ goto SCANError;
++
++ return;
++
++SCANError:
++ XBMC->Log(LOG_ERROR, "%s - Return error after start (%i)", __FUNCTION__, retCode);
++ m_window->SetControlLabel(LABEL_STATUS, XBMC->GetLocalizedString(24071));
++ m_window->SetControlLabel(BUTTON_START, XBMC->GetLocalizedString(30024));
++ m_window->SetControlLabel(HEADER_LABEL, XBMC->GetLocalizedString(30043));
++ m_stopped = true;
++}
++
++void cVNSIChannelScan::StopScan()
++{
++ cRequestPacket vrp;
++ if (!vrp.init(VNSI_SCAN_STOP))
++ return;
++
++ cResponsePacket* vresp = ReadResult(&vrp);
++ if (!vresp)
++ return;
++
++ uint32_t retCode = vresp->extract_U32();
++ if (retCode != VNSI_RET_OK)
++ {
++ XBMC->Log(LOG_ERROR, "%s - Return error after stop (%i)", __FUNCTION__, retCode);
++ m_window->SetControlLabel(LABEL_STATUS, XBMC->GetLocalizedString(24071));
++ m_window->SetControlLabel(BUTTON_START, XBMC->GetLocalizedString(30024));
++ m_window->SetControlLabel(HEADER_LABEL, XBMC->GetLocalizedString(30043));
++ m_stopped = true;
++ }
++ return;
++}
++
++void cVNSIChannelScan::ReturnFromProcessView()
++{
++ if (m_running)
++ {
++ m_running = false;
++ m_window->ClearProperties();
++ m_window->SetControlLabel(BUTTON_START, XBMC->GetLocalizedString(30010));
++ m_window->SetControlLabel(HEADER_LABEL, XBMC->GetLocalizedString(30009));
++
++ if (m_progressDone)
++ {
++ GUI->Control_releaseProgress(m_progressDone);
++ m_progressDone = NULL;
++ }
++ if (m_progressSignal)
++ {
++ GUI->Control_releaseProgress(m_progressSignal);
++ m_progressSignal = NULL;
++ }
++ }
++}
++
++void cVNSIChannelScan::SetProgress(int percent)
++{
++ if (!m_progressDone)
++ m_progressDone = GUI->Control_getProgress(m_window, PROGRESS_DONE);
++
++ std::stringstream header;
++ header << percent;
++
++ m_window->SetControlLabel(HEADER_LABEL, header.str().c_str());
++ m_progressDone->SetPercentage((float)percent);
++}
++
++void cVNSIChannelScan::SetSignal(int percent, bool locked)
++{
++ if (!m_progressSignal)
++ m_progressSignal = GUI->Control_getProgress(m_window, PROGRESS_SIGNAL);
++
++ std::stringstream signal;
++ signal << percent;
++
++ m_window->SetControlLabel(LABEL_SIGNAL, signal.str().c_str());
++ m_progressSignal->SetPercentage((float)percent);
++
++ if (locked)
++ m_window->SetProperty("Locked", "true");
++ else
++ m_window->SetProperty("Locked", "");
++}
++
++bool cVNSIChannelScan::OnClick(int controlId)
++{
++ if (controlId == SPIN_CONTROL_SOURCE_TYPE)
++ {
++ int value = m_spinSourceType->GetValue();
++ SetControlsVisible((scantype_t)value);
++ }
++ else if (controlId == BUTTON_BACK)
++ {
++ m_window->Close();
++ GUI->Control_releaseSpin(m_spinSourceType);
++ GUI->Control_releaseSpin(m_spinCountries);
++ GUI->Control_releaseSpin(m_spinSatellites);
++ GUI->Control_releaseSpin(m_spinDVBCInversion);
++ GUI->Control_releaseSpin(m_spinDVBCSymbolrates);
++ GUI->Control_releaseSpin(m_spinDVBCqam);
++ GUI->Control_releaseSpin(m_spinDVBTInversion);
++ GUI->Control_releaseSpin(m_spinATSCType);
++ GUI->Control_releaseRadioButton(m_radioButtonTV);
++ GUI->Control_releaseRadioButton(m_radioButtonRadio);
++ GUI->Control_releaseRadioButton(m_radioButtonFTA);
++ GUI->Control_releaseRadioButton(m_radioButtonScrambled);
++ GUI->Control_releaseRadioButton(m_radioButtonHD);
++ if (m_progressDone)
++ {
++ GUI->Control_releaseProgress(m_progressDone);
++ m_progressDone = NULL;
++ }
++ if (m_progressSignal)
++ {
++ GUI->Control_releaseProgress(m_progressSignal);
++ m_progressSignal = NULL;
++ }
++ }
++ else if (controlId == BUTTON_START)
++ {
++ if (!m_running)
++ {
++ m_running = true;
++ m_stopped = false;
++ m_Canceled = false;
++ m_window->SetProperty("Scanning", "running");
++ m_window->SetControlLabel(BUTTON_START, XBMC->GetLocalizedString(222));
++ StartScan();
++ }
++ else if (!m_stopped)
++ {
++ m_stopped = true;
++ m_Canceled = true;
++ StopScan();
++ }
++ else
++ ReturnFromProcessView();
++ }
++ return true;
++}
++
++bool cVNSIChannelScan::OnFocus(int controlId)
++{
++ return true;
++}
++
++bool cVNSIChannelScan::OnInit()
++{
++ m_spinSourceType = GUI->Control_getSpin(m_window, SPIN_CONTROL_SOURCE_TYPE);
++ m_spinSourceType->Clear();
++ m_spinSourceType->AddLabel("DVB-T", DVB_TERR);
++ m_spinSourceType->AddLabel("DVB-C", DVB_CABLE);
++ m_spinSourceType->AddLabel("DVB-S/S2", DVB_SAT);
++ m_spinSourceType->AddLabel("Analog TV", PVRINPUT);
++ m_spinSourceType->AddLabel("Analog Radio", PVRINPUT_FM);
++ m_spinSourceType->AddLabel("ATSC", DVB_ATSC);
++
++ m_spinDVBCInversion = GUI->Control_getSpin(m_window, CONTROL_SPIN_DVBC_INVERSION);
++ m_spinDVBCInversion->Clear();
++ m_spinDVBCInversion->AddLabel("Auto", 0);
++ m_spinDVBCInversion->AddLabel("On", 1);
++ m_spinDVBCInversion->AddLabel("Off", 2);
++
++ m_spinDVBCSymbolrates = GUI->Control_getSpin(m_window, CONTROL_SPIN_DVBC_SYMBOLRATE);
++ m_spinDVBCSymbolrates->Clear();
++ m_spinDVBCSymbolrates->AddLabel("AUTO", 0);
++ m_spinDVBCSymbolrates->AddLabel("6900", 1);
++ m_spinDVBCSymbolrates->AddLabel("6875", 2);
++ m_spinDVBCSymbolrates->AddLabel("6111", 3);
++ m_spinDVBCSymbolrates->AddLabel("6250", 4);
++ m_spinDVBCSymbolrates->AddLabel("6790", 5);
++ m_spinDVBCSymbolrates->AddLabel("6811", 6);
++ m_spinDVBCSymbolrates->AddLabel("5900", 7);
++ m_spinDVBCSymbolrates->AddLabel("5000", 8);
++ m_spinDVBCSymbolrates->AddLabel("3450", 9);
++ m_spinDVBCSymbolrates->AddLabel("4000", 10);
++ m_spinDVBCSymbolrates->AddLabel("6950", 11);
++ m_spinDVBCSymbolrates->AddLabel("7000", 12);
++ m_spinDVBCSymbolrates->AddLabel("6952", 13);
++ m_spinDVBCSymbolrates->AddLabel("5156", 14);
++ m_spinDVBCSymbolrates->AddLabel("4583", 15);
++ m_spinDVBCSymbolrates->AddLabel("ALL (slow)", 16);
++
++ m_spinDVBCqam = GUI->Control_getSpin(m_window, CONTROL_SPIN_DVBC_QAM);
++ m_spinDVBCqam->Clear();
++ m_spinDVBCqam->AddLabel("AUTO", 0);
++ m_spinDVBCqam->AddLabel("64", 1);
++ m_spinDVBCqam->AddLabel("128", 2);
++ m_spinDVBCqam->AddLabel("256", 3);
++ m_spinDVBCqam->AddLabel("ALL (slow)", 4);
++
++ m_spinDVBTInversion = GUI->Control_getSpin(m_window, CONTROL_SPIN_DVBT_INVERSION);
++ m_spinDVBTInversion->Clear();
++ m_spinDVBTInversion->AddLabel("Auto", 0);
++ m_spinDVBTInversion->AddLabel("On", 1);
++ m_spinDVBTInversion->AddLabel("Off", 2);
++
++ m_spinATSCType = GUI->Control_getSpin(m_window, CONTROL_SPIN_ATSC_TYPE);
++ m_spinATSCType->Clear();
++ m_spinATSCType->AddLabel("VSB (aerial)", 0);
++ m_spinATSCType->AddLabel("QAM (cable)", 1);
++ m_spinATSCType->AddLabel("VSB + QAM (aerial + cable)", 2);
++
++ m_radioButtonTV = GUI->Control_getRadioButton(m_window, CONTROL_RADIO_BUTTON_TV);
++ m_radioButtonTV->SetSelected(true);
++
++ m_radioButtonRadio = GUI->Control_getRadioButton(m_window, CONTROL_RADIO_BUTTON_RADIO);
++ m_radioButtonRadio->SetSelected(true);
++
++ m_radioButtonFTA = GUI->Control_getRadioButton(m_window, CONTROL_RADIO_BUTTON_FTA);
++ m_radioButtonFTA->SetSelected(true);
++
++ m_radioButtonScrambled = GUI->Control_getRadioButton(m_window, CONTROL_RADIO_BUTTON_SCRAMBLED);
++ m_radioButtonScrambled->SetSelected(true);
++
++ m_radioButtonHD = GUI->Control_getRadioButton(m_window, CONTROL_RADIO_BUTTON_HD);
++ m_radioButtonHD->SetSelected(true);
++
++ if (!ReadCountries())
++ return false;
++
++ if (!ReadSatellites())
++ return false;
++
++ SetControlsVisible(DVB_TERR);
++ return true;
++}
++
++bool cVNSIChannelScan::OnAction(int actionId)
++{
++ if (actionId == ADDON_ACTION_CLOSE_DIALOG || actionId == ADDON_ACTION_PREVIOUS_MENU)
++ OnClick(BUTTON_BACK);
++
++ return true;
++}
++
++bool cVNSIChannelScan::OnInitCB(GUIHANDLE cbhdl)
++{
++ cVNSIChannelScan* scanner = static_cast<cVNSIChannelScan*>(cbhdl);
++ return scanner->OnInit();
++}
++
++bool cVNSIChannelScan::OnClickCB(GUIHANDLE cbhdl, int controlId)
++{
++ cVNSIChannelScan* scanner = static_cast<cVNSIChannelScan*>(cbhdl);
++ return scanner->OnClick(controlId);
++}
++
++bool cVNSIChannelScan::OnFocusCB(GUIHANDLE cbhdl, int controlId)
++{
++ cVNSIChannelScan* scanner = static_cast<cVNSIChannelScan*>(cbhdl);
++ return scanner->OnFocus(controlId);
++}
++
++bool cVNSIChannelScan::OnActionCB(GUIHANDLE cbhdl, int actionId)
++{
++ cVNSIChannelScan* scanner = static_cast<cVNSIChannelScan*>(cbhdl);
++ return scanner->OnAction(actionId);
++}
++
++bool cVNSIChannelScan::ReadCountries()
++{
++ m_spinCountries = GUI->Control_getSpin(m_window, CONTROL_SPIN_COUNTRIES);
++ m_spinCountries->Clear();
++
++ std::string dvdlang = XBMC->GetDVDMenuLanguage();
++ //dvdlang = dvdlang.ToUpper();
++
++ cRequestPacket vrp;
++ if (!vrp.init(VNSI_SCAN_GETCOUNTRIES))
++ return false;
++
++ cResponsePacket* vresp = ReadResult(&vrp);
++ if (!vresp)
++ return false;
++
++ int startIndex = -1;
++ uint32_t retCode = vresp->extract_U32();
++ if (retCode == VNSI_RET_OK)
++ {
++ while (!vresp->end())
++ {
++ uint32_t index = vresp->extract_U32();
++ const char *isoName = vresp->extract_String();
++ const char *longName = vresp->extract_String();
++ m_spinCountries->AddLabel(longName, index);
++ if (dvdlang == isoName)
++ startIndex = index;
++
++ delete[] longName;
++ delete[] isoName;
++ }
++ if (startIndex >= 0)
++ m_spinCountries->SetValue(startIndex);
++ }
++ else
++ {
++ XBMC->Log(LOG_ERROR, "%s - Return error after reading countries (%i)", __FUNCTION__, retCode);
++ }
++ delete vresp;
++ return retCode == VNSI_RET_OK;
++}
++
++bool cVNSIChannelScan::ReadSatellites()
++{
++ m_spinSatellites = GUI->Control_getSpin(m_window, CONTROL_SPIN_SATELLITES);
++ m_spinSatellites->Clear();
++
++ cRequestPacket vrp;
++ if (!vrp.init(VNSI_SCAN_GETSATELLITES))
++ return false;
++
++ cResponsePacket* vresp = ReadResult(&vrp);
++ if (!vresp)
++ return false;
++
++ uint32_t retCode = vresp->extract_U32();
++ if (retCode == VNSI_RET_OK)
++ {
++ while (!vresp->end())
++ {
++ uint32_t index = vresp->extract_U32();
++ const char *shortName = vresp->extract_String();
++ const char *longName = vresp->extract_String();
++ m_spinSatellites->AddLabel(longName, index);
++ delete[] longName;
++ delete[] shortName;
++ }
++ m_spinSatellites->SetValue(6); /* default to Astra 19.2 */
++ }
++ else
++ {
++ XBMC->Log(LOG_ERROR, "%s - Return error after reading satellites (%i)", __FUNCTION__, retCode);
++ }
++ delete vresp;
++ return retCode == VNSI_RET_OK;
++}
++
++void cVNSIChannelScan::SetControlsVisible(scantype_t type)
++{
++ m_spinCountries->SetVisible(type == DVB_TERR || type == DVB_CABLE || type == PVRINPUT);
++ m_spinSatellites->SetVisible(type == DVB_SAT || type == DVB_ATSC);
++ m_spinDVBCInversion->SetVisible(type == DVB_CABLE);
++ m_spinDVBCSymbolrates->SetVisible(type == DVB_CABLE);
++ m_spinDVBCqam->SetVisible(type == DVB_CABLE);
++ m_spinDVBTInversion->SetVisible(type == DVB_TERR);
++ m_spinATSCType->SetVisible(type == DVB_ATSC);
++ m_radioButtonTV->SetVisible(type == DVB_TERR || type == DVB_CABLE || type == DVB_SAT || type == DVB_ATSC);
++ m_radioButtonRadio->SetVisible(type == DVB_TERR || type == DVB_CABLE || type == DVB_SAT || type == DVB_ATSC);
++ m_radioButtonFTA->SetVisible(type == DVB_TERR || type == DVB_CABLE || type == DVB_SAT || type == DVB_ATSC);
++ m_radioButtonScrambled->SetVisible(type == DVB_TERR || type == DVB_CABLE || type == DVB_SAT || type == DVB_ATSC);
++ m_radioButtonHD->SetVisible(type == DVB_TERR || type == DVB_CABLE || type == DVB_SAT || type == DVB_ATSC);
++}
++
++bool cVNSIChannelScan::OnResponsePacket(cResponsePacket* resp)
++{
++ uint32_t requestID = resp->getRequestID();
++
++ if (requestID == VNSI_SCANNER_PERCENTAGE)
++ {
++ uint32_t percent = resp->extract_U32();
++ if (percent >= 0 && percent <= 100)
++ SetProgress(percent);
++ }
++ else if (requestID == VNSI_SCANNER_SIGNAL)
++ {
++ uint32_t strength = resp->extract_U32();
++ uint32_t locked = resp->extract_U32();
++ SetSignal(strength, locked);
++ }
++ else if (requestID == VNSI_SCANNER_DEVICE)
++ {
++ char* str = resp->extract_String();
++ m_window->SetControlLabel(LABEL_DEVICE, str);
++ delete[] str;
++ }
++ else if (requestID == VNSI_SCANNER_TRANSPONDER)
++ {
++ char* str = resp->extract_String();
++ m_window->SetControlLabel(LABEL_TRANSPONDER, str);
++ delete[] str;
++ }
++ else if (requestID == VNSI_SCANNER_NEWCHANNEL)
++ {
++ uint32_t isRadio = resp->extract_U32();
++ uint32_t isEncrypted = resp->extract_U32();
++ uint32_t isHD = resp->extract_U32();
++ char* str = resp->extract_String();
++
++ CAddonListItem* item = GUI->ListItem_create(str, NULL, NULL, NULL, NULL);
++ if (isEncrypted)
++ item->SetProperty("IsEncrypted", "yes");
++ if (isRadio)
++ item->SetProperty("IsRadio", "yes");
++ if (isHD)
++ item->SetProperty("IsHD", "yes");
++
++ m_window->AddItem(item, 0);
++ GUI->ListItem_destroy(item);
++
++ delete[] str;
++ }
++ else if (requestID == VNSI_SCANNER_FINISHED)
++ {
++ if (!m_Canceled)
++ {
++ m_window->SetControlLabel(HEADER_LABEL, XBMC->GetLocalizedString(30036));
++ m_window->SetControlLabel(BUTTON_START, XBMC->GetLocalizedString(30024));
++ m_window->SetControlLabel(LABEL_STATUS, XBMC->GetLocalizedString(30041));
++ }
++ else
++ {
++ m_window->SetControlLabel(HEADER_LABEL, XBMC->GetLocalizedString(30042));
++ }
++ }
++ else if (requestID == VNSI_SCANNER_STATUS)
++ {
++ uint32_t status = resp->extract_U32();
++ if (status == 0)
++ {
++ if (m_Canceled)
++ m_window->SetControlLabel(LABEL_STATUS, XBMC->GetLocalizedString(16200));
++ else
++ m_window->SetControlLabel(LABEL_STATUS, XBMC->GetLocalizedString(30040));
++
++ m_window->SetControlLabel(BUTTON_START, XBMC->GetLocalizedString(30024));
++ m_stopped = true;
++ }
++ else if (status == 1)
++ {
++ m_window->SetControlLabel(LABEL_STATUS, XBMC->GetLocalizedString(30039));
++ }
++ else if (status == 2)
++ {
++ m_window->SetControlLabel(LABEL_STATUS, XBMC->GetLocalizedString(30037));
++ m_window->SetControlLabel(BUTTON_START, XBMC->GetLocalizedString(30024));
++ m_window->SetControlLabel(HEADER_LABEL, XBMC->GetLocalizedString(30043));
++ m_stopped = true;
++ }
++ else if (status == 3)
++ {
++ m_window->SetControlLabel(LABEL_STATUS, XBMC->GetLocalizedString(30038));
++ }
++ }
++ else {
++ return false;
++ }
++
++ return true;
++}
+diff --git a/xbmc/pvrclients/vdr-vnsi/VNSIChannelScan.h b/xbmc/pvrclients/vdr-vnsi/VNSIChannelScan.h
+new file mode 100644
+index 0000000..754c416
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/VNSIChannelScan.h
+@@ -0,0 +1,95 @@
++#pragma once
++/*
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "VNSIData.h"
++#include "client.h"
++#include <string>
++#include <map>
++
++typedef enum scantype
++{
++ DVB_TERR = 0,
++ DVB_CABLE = 1,
++ DVB_SAT = 2,
++ PVRINPUT = 3,
++ PVRINPUT_FM = 4,
++ DVB_ATSC = 5,
++} scantype_t;
++
++
++class cVNSIChannelScan : public cVNSIData
++{
++public:
++
++ cVNSIChannelScan();
++ ~cVNSIChannelScan();
++
++ bool Open(const std::string& hostname, int port, const char* name = "XBMC channel scanner");
++
++ bool OnClick(int controlId);
++ bool OnFocus(int controlId);
++ bool OnInit();
++ bool OnAction(int actionId);
++
++ static bool OnClickCB(GUIHANDLE cbhdl, int controlId);
++ static bool OnFocusCB(GUIHANDLE cbhdl, int controlId);
++ static bool OnInitCB(GUIHANDLE cbhdl);
++ static bool OnActionCB(GUIHANDLE cbhdl, int actionId);
++
++protected:
++
++ bool OnResponsePacket(cResponsePacket* resp);
++
++private:
++
++ bool ReadCountries();
++ bool ReadSatellites();
++ void SetControlsVisible(scantype_t type);
++ void StartScan();
++ void StopScan();
++ void ReturnFromProcessView();
++ void SetProgress(int procent);
++ void SetSignal(int procent, bool locked);
++
++ std::string m_header;
++ std::string m_Signal;
++ bool m_running;
++ bool m_stopped;
++ bool m_Canceled;
++
++ CAddonGUIWindow *m_window;
++ CAddonGUISpinControl *m_spinSourceType;
++ CAddonGUISpinControl *m_spinCountries;
++ CAddonGUISpinControl *m_spinSatellites;
++ CAddonGUISpinControl *m_spinDVBCInversion;
++ CAddonGUISpinControl *m_spinDVBCSymbolrates;
++ CAddonGUISpinControl *m_spinDVBCqam;
++ CAddonGUISpinControl *m_spinDVBTInversion;
++ CAddonGUISpinControl *m_spinATSCType;
++ CAddonGUIRadioButton *m_radioButtonTV;
++ CAddonGUIRadioButton *m_radioButtonRadio;
++ CAddonGUIRadioButton *m_radioButtonFTA;
++ CAddonGUIRadioButton *m_radioButtonScrambled;
++ CAddonGUIRadioButton *m_radioButtonHD;
++ CAddonGUIProgressControl *m_progressDone;
++ CAddonGUIProgressControl *m_progressSignal;
++};
+diff --git a/xbmc/pvrclients/vdr-vnsi/VNSIData.cpp b/xbmc/pvrclients/vdr-vnsi/VNSIData.cpp
+new file mode 100644
+index 0000000..64311e6
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/VNSIData.cpp
+@@ -0,0 +1,986 @@
++/*
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "VNSIData.h"
++#include "responsepacket.h"
++#include "requestpacket.h"
++#include "vnsicommand.h"
++#include "utils/StdString.h"
++
++using namespace ADDON;
++using namespace PLATFORM;
++
++cVNSIData::cVNSIData()
++ : m_aborting(false)
++{
++}
++
++cVNSIData::~cVNSIData()
++{
++ Abort();
++ StopThread();
++ Close();
++}
++
++bool cVNSIData::Open(const std::string& hostname, int port, const char* name)
++{
++ m_aborting = false;
++
++ if(!cVNSISession::Open(hostname, port, name))
++ return false;
++
++ return true;
++}
++
++bool cVNSIData::Login()
++{
++ if(!cVNSISession::Login())
++ return false;
++
++ CreateThread();
++ return true;
++}
++
++void cVNSIData::Abort()
++{
++ CLockObject lock(m_mutex);
++ m_aborting = true;
++ cVNSISession::Abort();
++}
++
++void cVNSIData::SignalConnectionLost()
++{
++ CLockObject lock(m_mutex);
++
++ if(m_aborting)
++ return;
++
++ cVNSISession::SignalConnectionLost();
++}
++
++void cVNSIData::OnDisconnect()
++{
++ XBMC->QueueNotification(QUEUE_ERROR, XBMC->GetLocalizedString(30044));
++ PVR->TriggerTimerUpdate();
++}
++
++void cVNSIData::OnReconnect()
++{
++ XBMC->QueueNotification(QUEUE_INFO, XBMC->GetLocalizedString(30045));
++
++ EnableStatusInterface(g_bHandleMessages);
++
++ PVR->TriggerChannelUpdate();
++ PVR->TriggerTimerUpdate();
++ PVR->TriggerRecordingUpdate();
++}
++
++cResponsePacket* cVNSIData::ReadResult(cRequestPacket* vrp)
++{
++ m_mutex.Lock();
++
++ SMessage &message(m_queue[vrp->getSerial()]);
++ message.event = new CEvent;
++ message.pkt = NULL;
++
++ m_mutex.Unlock();
++
++ if(!cVNSISession::TransmitMessage(vrp))
++ {
++ m_queue.erase(vrp->getSerial());
++ return NULL;
++ }
++
++ if (!message.event->Wait(g_iConnectTimeout * 1000))
++ {
++ XBMC->Log(LOG_ERROR, "%s - request timed out after %d seconds", __FUNCTION__, g_iConnectTimeout);
++ }
++
++ m_mutex.Lock();
++
++ cResponsePacket* vresp = message.pkt;
++ delete message.event;
++
++ m_queue.erase(vrp->getSerial());
++
++ m_mutex.Unlock();
++
++ return vresp;
++}
++
++bool cVNSIData::GetDriveSpace(long long *total, long long *used)
++{
++ cRequestPacket vrp;
++ if (!vrp.init(VNSI_RECORDINGS_DISKSIZE))
++ {
++ XBMC->Log(LOG_ERROR, "%s - Can't init cRequestPacket", __FUNCTION__);
++ return false;
++ }
++
++ cResponsePacket* vresp = ReadResult(&vrp);
++ if (!vresp)
++ {
++ XBMC->Log(LOG_ERROR, "%s - Can't get response packed", __FUNCTION__);
++ return false;
++ }
++
++ uint32_t totalspace = vresp->extract_U32();
++ uint32_t freespace = vresp->extract_U32();
++ /* vresp->extract_U32(); percent not used */
++
++ *total = totalspace;
++ *used = (totalspace - freespace);
++
++ /* Convert from kBytes to Bytes */
++ *total *= 1024;
++ *used *= 1024;
++
++ delete vresp;
++ return true;
++}
++
++bool cVNSIData::SupportChannelScan()
++{
++ cRequestPacket vrp;
++ if (!vrp.init(VNSI_SCAN_SUPPORTED))
++ {
++ XBMC->Log(LOG_ERROR, "%s - Can't init cRequestPacket", __FUNCTION__);
++ return false;
++ }
++
++ cResponsePacket* vresp = ReadResult(&vrp);
++ if (!vresp)
++ {
++ XBMC->Log(LOG_ERROR, "%s - Can't get response packed", __FUNCTION__);
++ return false;
++ }
++
++ uint32_t ret = vresp->extract_U32();
++ delete vresp;
++ return ret == VNSI_RET_OK ? true : false;
++}
++
++bool cVNSIData::EnableStatusInterface(bool onOff)
++{
++ cRequestPacket vrp;
++ if (!vrp.init(VNSI_ENABLESTATUSINTERFACE)) return false;
++ if (!vrp.add_U8(onOff)) return false;
++
++ cResponsePacket* vresp = ReadResult(&vrp);
++ if (!vresp)
++ {
++ XBMC->Log(LOG_ERROR, "%s - Can't get response packed", __FUNCTION__);
++ return false;
++ }
++
++ uint32_t ret = vresp->extract_U32();
++ delete vresp;
++ return ret == VNSI_RET_OK ? true : false;
++}
++
++int cVNSIData::GetChannelsCount()
++{
++ cRequestPacket vrp;
++ if (!vrp.init(VNSI_CHANNELS_GETCOUNT))
++ {
++ XBMC->Log(LOG_ERROR, "%s - Can't init cRequestPacket", __FUNCTION__);
++ return -1;
++ }
++
++ cResponsePacket* vresp = ReadResult(&vrp);
++ if (!vresp)
++ {
++ XBMC->Log(LOG_ERROR, "%s - Can't get response packed", __FUNCTION__);
++ return -1;
++ }
++
++ uint32_t count = vresp->extract_U32();
++
++ delete vresp;
++ return count;
++}
++
++bool cVNSIData::GetChannelsList(PVR_HANDLE handle, bool radio)
++{
++ cRequestPacket vrp;
++ if (!vrp.init(VNSI_CHANNELS_GETCHANNELS))
++ {
++ XBMC->Log(LOG_ERROR, "%s - Can't init cRequestPacket", __FUNCTION__);
++ return false;
++ }
++ if (!vrp.add_U32(radio))
++ {
++ XBMC->Log(LOG_ERROR, "%s - Can't add parameter to cRequestPacket", __FUNCTION__);
++ return false;
++ }
++
++ cResponsePacket* vresp = ReadResult(&vrp);
++ if (!vresp)
++ {
++ XBMC->Log(LOG_ERROR, "%s - Can't get response packed", __FUNCTION__);
++ return false;
++ }
++
++ while (!vresp->end())
++ {
++ PVR_CHANNEL tag;
++ memset(&tag, 0 , sizeof(tag));
++
++ tag.iChannelNumber = vresp->extract_U32();
++ tag.strChannelName = vresp->extract_String();
++ tag.iUniqueId = vresp->extract_U32();
++ vresp->extract_U32(); // still here for compatibility
++ tag.iEncryptionSystem = vresp->extract_U32();
++ vresp->extract_U32(); // uint32_t vtype - currently unused
++ tag.bIsRadio = radio;
++ tag.strInputFormat = "";
++ tag.strStreamURL = "";
++ tag.strIconPath = "";
++ tag.bIsHidden = false;
++
++ PVR->TransferChannelEntry(handle, &tag);
++ delete[] tag.strChannelName;
++ }
++
++ delete vresp;
++ return true;
++}
++
++bool cVNSIData::GetEPGForChannel(PVR_HANDLE handle, const PVR_CHANNEL &channel, time_t start, time_t end)
++{
++ cRequestPacket vrp;
++ if (!vrp.init(VNSI_EPG_GETFORCHANNEL))
++ {
++ XBMC->Log(LOG_ERROR, "%s - Can't init cRequestPacket", __FUNCTION__);
++ return false;
++ }
++ if (!vrp.add_U32(channel.iUniqueId) || !vrp.add_U32(start) || !vrp.add_U32(end - start))
++ {
++ XBMC->Log(LOG_ERROR, "%s - Can't add parameter to cRequestPacket", __FUNCTION__);
++ return false;
++ }
++
++ cResponsePacket* vresp = ReadResult(&vrp);
++ if (!vresp)
++ {
++ XBMC->Log(LOG_ERROR, "%s - Can't get response packed", __FUNCTION__);
++ return false;
++ }
++
++ while (!vresp->end())
++ {
++ EPG_TAG tag;
++ memset(&tag, 0 , sizeof(tag));
++
++ tag.iChannelNumber = channel.iChannelNumber;
++ tag.iUniqueBroadcastId = vresp->extract_U32();
++ tag.startTime = vresp->extract_U32();
++ tag.endTime = tag.startTime + vresp->extract_U32();
++ uint32_t content = vresp->extract_U32();
++ tag.iGenreType = content & 0xF0;
++ tag.iGenreSubType = content & 0x0F;
++ tag.strGenreDescription = "";
++ tag.iParentalRating = vresp->extract_U32();
++ tag.strTitle = vresp->extract_String();
++ tag.strPlotOutline = vresp->extract_String();
++ tag.strPlot = vresp->extract_String();
++
++ PVR->TransferEpgEntry(handle, &tag);
++ delete[] tag.strTitle;
++ delete[] tag.strPlotOutline;
++ delete[] tag.strPlot;
++ }
++
++ delete vresp;
++ return true;
++}
++
++
++/** OPCODE's 60 - 69: VNSI network functions for timer access */
++
++int cVNSIData::GetTimersCount()
++{
++ cRequestPacket vrp;
++ if (!vrp.init(VNSI_TIMER_GETCOUNT))
++ {
++ XBMC->Log(LOG_ERROR, "%s - Can't init cRequestPacket", __FUNCTION__);
++ return -1;
++ }
++
++ cResponsePacket* vresp = ReadResult(&vrp);
++ if (!vresp)
++ {
++ XBMC->Log(LOG_ERROR, "%s - Can't get response packed", __FUNCTION__);
++ return -1;
++ }
++
++ uint32_t count = vresp->extract_U32();
++
++ delete vresp;
++ return count;
++}
++
++PVR_ERROR cVNSIData::GetTimerInfo(unsigned int timernumber, PVR_TIMER &tag)
++{
++ cRequestPacket vrp;
++ if (!vrp.init(VNSI_TIMER_GET))
++ {
++ XBMC->Log(LOG_ERROR, "%s - Can't init cRequestPacket", __FUNCTION__);
++ return PVR_ERROR_UNKNOWN;
++ }
++
++ if (!vrp.add_U32(timernumber))
++ return PVR_ERROR_UNKNOWN;
++
++ cResponsePacket* vresp = ReadResult(&vrp);
++ if (!vresp)
++ {
++ XBMC->Log(LOG_ERROR, "%s - Can't get response packed", __FUNCTION__);
++ delete vresp;
++ return PVR_ERROR_UNKNOWN;
++ }
++
++ uint32_t returnCode = vresp->extract_U32();
++ if (returnCode != VNSI_RET_OK)
++ {
++ delete vresp;
++ if (returnCode == VNSI_RET_DATAUNKNOWN)
++ return PVR_ERROR_NOT_POSSIBLE;
++ else if (returnCode == VNSI_RET_ERROR)
++ return PVR_ERROR_SERVER_ERROR;
++ }
++
++ tag.iClientIndex = vresp->extract_U32();
++ int iActive = vresp->extract_U32();
++ int iRecording = vresp->extract_U32();
++ int iPending = vresp->extract_U32();
++ if (iRecording)
++ tag.state = PVR_TIMER_STATE_RECORDING;
++ else if (iPending || iActive)
++ tag.state = PVR_TIMER_STATE_SCHEDULED;
++ else
++ tag.state = PVR_TIMER_STATE_CANCELLED;
++ tag.iPriority = vresp->extract_U32();
++ tag.iLifetime = vresp->extract_U32();
++ vresp->extract_U32(); // channel number - unused
++ tag.iClientChannelUid = vresp->extract_U32();
++ tag.startTime = vresp->extract_U32();
++ tag.endTime = vresp->extract_U32();
++ tag.firstDay = vresp->extract_U32();
++ tag.iWeekdays = vresp->extract_U32();
++ tag.bIsRepeating = tag.iWeekdays == 0 ? false : true;
++ tag.strTitle = vresp->extract_String();
++ tag.strDirectory = "";
++
++ delete[] tag.strTitle;
++ delete vresp;
++ return PVR_ERROR_NO_ERROR;
++}
++
++bool cVNSIData::GetTimersList(PVR_HANDLE handle)
++{
++ cRequestPacket vrp;
++ if (!vrp.init(VNSI_TIMER_GETLIST))
++ {
++ XBMC->Log(LOG_ERROR, "%s - Can't init cRequestPacket", __FUNCTION__);
++ return false;
++ }
++
++ cResponsePacket* vresp = ReadResult(&vrp);
++ if (!vresp)
++ {
++ delete vresp;
++ XBMC->Log(LOG_ERROR, "%s - Can't get response packed", __FUNCTION__);
++ return false;
++ }
++
++ uint32_t numTimers = vresp->extract_U32();
++ if (numTimers > 0)
++ {
++ while (!vresp->end())
++ {
++ PVR_TIMER tag;
++ tag.iClientIndex = vresp->extract_U32();
++ int iActive = vresp->extract_U32();
++ int iRecording = vresp->extract_U32();
++ int iPending = vresp->extract_U32();
++ if (iRecording)
++ tag.state = PVR_TIMER_STATE_RECORDING;
++ else if (iPending || iActive)
++ tag.state = PVR_TIMER_STATE_SCHEDULED;
++ else
++ tag.state = PVR_TIMER_STATE_CANCELLED;
++ tag.iPriority = vresp->extract_U32();
++ tag.iLifetime = vresp->extract_U32();
++ vresp->extract_U32(); // channel number - unused
++ tag.iClientChannelUid = vresp->extract_U32();
++ tag.startTime = vresp->extract_U32();
++ tag.endTime = vresp->extract_U32();
++ tag.firstDay = vresp->extract_U32();
++ tag.iWeekdays = vresp->extract_U32();
++ tag.bIsRepeating = tag.iWeekdays == 0 ? false : true;
++ tag.strTitle = vresp->extract_String();
++ tag.strDirectory = "";
++ tag.iMarginStart = 0;
++ tag.iMarginEnd = 0;
++
++ PVR->TransferTimerEntry(handle, &tag);
++ delete[] tag.strTitle;
++ }
++ }
++ delete vresp;
++ return true;
++}
++
++PVR_ERROR cVNSIData::AddTimer(const PVR_TIMER &timerinfo)
++{
++ cRequestPacket vrp;
++ if (!vrp.init(VNSI_TIMER_ADD))
++ {
++ XBMC->Log(LOG_ERROR, "%s - Can't init cRequestPacket", __FUNCTION__);
++ return PVR_ERROR_UNKNOWN;
++ }
++
++ // add directory in front of the title
++ std::string path;
++ if(timerinfo.strDirectory != NULL && strlen(timerinfo.strDirectory) > 0) {
++ path += timerinfo.strDirectory;
++ if(path == "/") {
++ path.clear();
++ }
++ else if(path.size() > 1) {
++ if(path[0] == '/') {
++ path = path.substr(1);
++ }
++ }
++
++ if(path.size() > 0 && path[path.size()-1] != '/') {
++ path += "/";
++ }
++ }
++
++ if(timerinfo.strTitle != NULL) {
++ path += timerinfo.strTitle;
++ }
++
++ // replace directory separators
++ for(std::size_t i=0; i<path.size(); i++) {
++ if(path[i] == '/' || path[i] == '\\') {
++ path[i] = '~';
++ }
++ }
++
++ if(path.empty()) {
++ XBMC->Log(LOG_ERROR, "%s - Empty filename !", __FUNCTION__);
++ return PVR_ERROR_UNKNOWN;
++ }
++
++ // use timer margin to calculate start/end times
++ uint32_t starttime = timerinfo.startTime - timerinfo.iMarginStart*60;
++ uint32_t endtime = timerinfo.endTime + timerinfo.iMarginEnd*60;
++
++ if (!vrp.add_U32(timerinfo.state == PVR_TIMER_STATE_SCHEDULED)) return PVR_ERROR_UNKNOWN;
++ if (!vrp.add_U32(timerinfo.iPriority)) return PVR_ERROR_UNKNOWN;
++ if (!vrp.add_U32(timerinfo.iLifetime)) return PVR_ERROR_UNKNOWN;
++ if (!vrp.add_U32(timerinfo.iClientChannelUid)) return PVR_ERROR_UNKNOWN;
++ if (!vrp.add_U32(starttime)) return PVR_ERROR_UNKNOWN;
++ if (!vrp.add_U32(endtime)) return PVR_ERROR_UNKNOWN;
++ if (!vrp.add_U32(timerinfo.bIsRepeating ? timerinfo.firstDay : 0)) return PVR_ERROR_UNKNOWN;
++ if (!vrp.add_U32(timerinfo.iWeekdays))return PVR_ERROR_UNKNOWN;
++ if (!vrp.add_String(path.c_str())) return PVR_ERROR_UNKNOWN;
++ if (!vrp.add_String("")) return PVR_ERROR_UNKNOWN;
++
++ cResponsePacket* vresp = ReadResult(&vrp);
++ if (vresp == NULL || vresp->noResponse())
++ {
++ delete vresp;
++ XBMC->Log(LOG_ERROR, "%s - Can't get response packed", __FUNCTION__);
++ return PVR_ERROR_UNKNOWN;
++ }
++ uint32_t returnCode = vresp->extract_U32();
++ delete vresp;
++ if (returnCode == VNSI_RET_DATALOCKED)
++ return PVR_ERROR_ALREADY_PRESENT;
++ else if (returnCode == VNSI_RET_DATAINVALID)
++ return PVR_ERROR_NOT_SAVED;
++ else if (returnCode == VNSI_RET_ERROR)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++PVR_ERROR cVNSIData::DeleteTimer(const PVR_TIMER &timerinfo, bool force)
++{
++ cRequestPacket vrp;
++ if (!vrp.init(VNSI_TIMER_DELETE))
++ return PVR_ERROR_UNKNOWN;
++
++ if (!vrp.add_U32(timerinfo.iClientIndex))
++ return PVR_ERROR_UNKNOWN;
++
++ if (!vrp.add_U32(force))
++ return PVR_ERROR_UNKNOWN;
++
++ cResponsePacket* vresp = ReadResult(&vrp);
++ if (vresp == NULL || vresp->noResponse())
++ {
++ delete vresp;
++ return PVR_ERROR_UNKNOWN;
++ }
++
++ uint32_t returnCode = vresp->extract_U32();
++ delete vresp;
++
++ if (returnCode == VNSI_RET_DATALOCKED)
++ return PVR_ERROR_NOT_DELETED;
++ if (returnCode == VNSI_RET_RECRUNNING)
++ return PVR_ERROR_RECORDING_RUNNING;
++ else if (returnCode == VNSI_RET_DATAINVALID)
++ return PVR_ERROR_NOT_POSSIBLE;
++ else if (returnCode == VNSI_RET_ERROR)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++PVR_ERROR cVNSIData::RenameTimer(const PVR_TIMER &timerinfo, const char *newname)
++{
++ PVR_TIMER timerinfo1;
++ PVR_ERROR ret = GetTimerInfo(timerinfo.iClientIndex, timerinfo1);
++ if (ret != PVR_ERROR_NO_ERROR)
++ return ret;
++
++ timerinfo1.strTitle = newname;
++ return UpdateTimer(timerinfo1);
++}
++
++PVR_ERROR cVNSIData::UpdateTimer(const PVR_TIMER &timerinfo)
++{
++ // use timer margin to calculate start/end times
++ uint32_t starttime = timerinfo.startTime - timerinfo.iMarginStart*60;
++ uint32_t endtime = timerinfo.endTime + timerinfo.iMarginEnd*60;
++
++ cRequestPacket vrp;
++ if (!vrp.init(VNSI_TIMER_UPDATE)) return PVR_ERROR_UNKNOWN;
++ if (!vrp.add_U32(timerinfo.iClientIndex)) return PVR_ERROR_UNKNOWN;
++ if (!vrp.add_U32(timerinfo.state == PVR_TIMER_STATE_SCHEDULED)) return PVR_ERROR_UNKNOWN;
++ if (!vrp.add_U32(timerinfo.iPriority)) return PVR_ERROR_UNKNOWN;
++ if (!vrp.add_U32(timerinfo.iLifetime)) return PVR_ERROR_UNKNOWN;
++ if (!vrp.add_U32(timerinfo.iClientChannelUid)) return PVR_ERROR_UNKNOWN;
++ if (!vrp.add_U32(starttime)) return PVR_ERROR_UNKNOWN;
++ if (!vrp.add_U32(endtime)) return PVR_ERROR_UNKNOWN;
++ if (!vrp.add_U32(timerinfo.bIsRepeating ? timerinfo.firstDay : 0)) return PVR_ERROR_UNKNOWN;
++ if (!vrp.add_U32(timerinfo.iWeekdays))return PVR_ERROR_UNKNOWN;
++ if (!vrp.add_String(timerinfo.strTitle)) return PVR_ERROR_UNKNOWN;
++ if (!vrp.add_String("")) return PVR_ERROR_UNKNOWN;
++
++ cResponsePacket* vresp = ReadResult(&vrp);
++ if (vresp == NULL || vresp->noResponse())
++ {
++ delete vresp;
++ return PVR_ERROR_UNKNOWN;
++ }
++ uint32_t returnCode = vresp->extract_U32();
++ delete vresp;
++ if (returnCode == VNSI_RET_DATAUNKNOWN)
++ return PVR_ERROR_NOT_POSSIBLE;
++ else if (returnCode == VNSI_RET_DATAINVALID)
++ return PVR_ERROR_NOT_SAVED;
++ else if (returnCode == VNSI_RET_ERROR)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++int cVNSIData::GetRecordingsCount()
++{
++ cRequestPacket vrp;
++ if (!vrp.init(VNSI_RECORDINGS_GETCOUNT))
++ {
++ XBMC->Log(LOG_ERROR, "%s - Can't init cRequestPacket", __FUNCTION__);
++ return -1;
++ }
++
++ cResponsePacket* vresp = ReadResult(&vrp);
++ if (!vresp)
++ {
++ XBMC->Log(LOG_ERROR, "%s - Can't get response packed", __FUNCTION__);
++ return -1;
++ }
++
++ uint32_t count = vresp->extract_U32();
++
++ delete vresp;
++ return count;
++}
++
++PVR_ERROR cVNSIData::GetRecordingsList(PVR_HANDLE handle)
++{
++ cRequestPacket vrp;
++ if (!vrp.init(VNSI_RECORDINGS_GETLIST))
++ {
++ XBMC->Log(LOG_ERROR, "%s - Can't init cRequestPacket", __FUNCTION__);
++ return PVR_ERROR_UNKNOWN;
++ }
++
++ cResponsePacket* vresp = ReadResult(&vrp);
++ if (!vresp)
++ {
++ XBMC->Log(LOG_ERROR, "%s - Can't get response packed", __FUNCTION__);
++ return PVR_ERROR_UNKNOWN;
++ }
++
++ CStdString strRecordingId;
++ while (!vresp->end())
++ {
++ PVR_RECORDING tag;
++ tag.recordingTime = vresp->extract_U32();
++ tag.iDuration = vresp->extract_U32();
++ tag.iPriority = vresp->extract_U32();
++ tag.iLifetime = vresp->extract_U32();
++ tag.strChannelName = vresp->extract_String();
++ tag.strTitle = vresp->extract_String();
++ tag.strPlotOutline = vresp->extract_String();
++ tag.strPlot = vresp->extract_String();
++ tag.strDirectory = vresp->extract_String();
++ strRecordingId.Format("%i", vresp->extract_U32());
++ tag.strRecordingId = strRecordingId.c_str();
++ tag.strStreamURL = "";
++
++ PVR->TransferRecordingEntry(handle, &tag);
++
++ delete[] tag.strChannelName;
++ delete[] tag.strTitle;
++ delete[] tag.strPlotOutline;
++ delete[] tag.strPlot;
++ delete[] tag.strDirectory;
++ }
++
++ delete vresp;
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++PVR_ERROR cVNSIData::RenameRecording(const PVR_RECORDING& recinfo, const char* newname)
++{
++ cRequestPacket vrp;
++ if (!vrp.init(VNSI_RECORDINGS_RENAME))
++ {
++ XBMC->Log(LOG_ERROR, "%s - Can't init cRequestPacket", __FUNCTION__);
++ return PVR_ERROR_UNKNOWN;
++ }
++
++ // add uid
++ XBMC->Log(LOG_DEBUG, "%s - uid: %s", __FUNCTION__, recinfo.strRecordingId);
++ if (!vrp.add_U32(atoi(recinfo.strRecordingId)))
++ return PVR_ERROR_UNKNOWN;
++
++ // add new title
++ if (!vrp.add_String(newname))
++ return PVR_ERROR_UNKNOWN;
++
++ cResponsePacket* vresp = ReadResult(&vrp);
++ if (vresp == NULL || vresp->noResponse())
++ {
++ delete vresp;
++ return PVR_ERROR_SERVER_ERROR;
++ }
++
++ uint32_t returnCode = vresp->extract_U32();
++ delete vresp;
++
++ if(returnCode != 0)
++ return PVR_ERROR_NOT_POSSIBLE;
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++PVR_ERROR cVNSIData::DeleteRecording(const PVR_RECORDING& recinfo)
++{
++ cRequestPacket vrp;
++ if (!vrp.init(VNSI_RECORDINGS_DELETE))
++ {
++ XBMC->Log(LOG_ERROR, "%s - Can't init cRequestPacket", __FUNCTION__);
++ return PVR_ERROR_UNKNOWN;
++ }
++
++ if (!vrp.add_U32(atoi(recinfo.strRecordingId)))
++ return PVR_ERROR_UNKNOWN;
++
++ cResponsePacket* vresp = ReadResult(&vrp);
++ if (vresp == NULL || vresp->noResponse())
++ {
++ delete vresp;
++ return PVR_ERROR_UNKNOWN;
++ }
++
++ uint32_t returnCode = vresp->extract_U32();
++ delete vresp;
++
++ switch(returnCode)
++ {
++ case VNSI_RET_DATALOCKED:
++ return PVR_ERROR_NOT_DELETED;
++
++ case VNSI_RET_RECRUNNING:
++ return PVR_ERROR_RECORDING_RUNNING;
++
++ case VNSI_RET_DATAINVALID:
++ return PVR_ERROR_NOT_POSSIBLE;
++
++ case VNSI_RET_ERROR:
++ return PVR_ERROR_SERVER_ERROR;
++ }
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++bool cVNSIData::OnResponsePacket(cResponsePacket* pkt)
++{
++ return false;
++}
++
++bool cVNSIData::SendPing()
++{
++ XBMC->Log(LOG_DEBUG, "%s", __FUNCTION__);
++
++ cRequestPacket vrp;
++ if (!vrp.init(VNSI_PING))
++ {
++ XBMC->Log(LOG_ERROR, "%s - Can't init cRequestPacket", __FUNCTION__);
++ return false;
++ }
++
++ cResponsePacket* vresp = cVNSISession::ReadResult(&vrp);
++ delete vresp;
++
++ return (vresp != NULL);
++}
++
++void *cVNSIData::Process()
++{
++ uint32_t lastPing = 0;
++
++ cResponsePacket* vresp;
++
++ while (!IsStopped())
++ {
++ // try to reconnect
++ if(ConnectionLost() && !TryReconnect())
++ {
++ Sleep(1000);
++ continue;
++ }
++
++ // if there's anything in the buffer, read it
++ if ((vresp = cVNSISession::ReadMessage(5)) == NULL)
++ {
++ Sleep(5);
++ continue;
++ }
++
++ // check if the connection is still up
++ if (vresp == NULL)
++ {
++ if(time(NULL) - lastPing > 5)
++ {
++ lastPing = time(NULL);
++
++// if(!SendPing())
++// SignalConnectionLost();
++ }
++ continue;
++ }
++
++ // CHANNEL_REQUEST_RESPONSE
++
++ if (vresp->getChannelID() == VNSI_CHANNEL_REQUEST_RESPONSE)
++ {
++ CLockObject lock(m_mutex);
++ SMessages::iterator it = m_queue.find(vresp->getRequestID());
++ if (it != m_queue.end())
++ {
++ it->second.pkt = vresp;
++ it->second.event->Broadcast();
++ }
++ else
++ {
++ delete vresp;
++ }
++ }
++
++ // CHANNEL_STATUS
++
++ else if (vresp->getChannelID() == VNSI_CHANNEL_STATUS)
++ {
++ if (vresp->getRequestID() == VNSI_STATUS_MESSAGE)
++ {
++ uint32_t type = vresp->extract_U32();
++ char* msgstr = vresp->extract_String();
++ std::string text = msgstr;
++
++ if (g_bCharsetConv)
++ XBMC->UnknownToUTF8(text);
++
++ if (type == 2)
++ XBMC->QueueNotification(QUEUE_ERROR, text.c_str());
++ if (type == 1)
++ XBMC->QueueNotification(QUEUE_WARNING, text.c_str());
++ else
++ XBMC->QueueNotification(QUEUE_INFO, text.c_str());
++
++ delete[] msgstr;
++ }
++ else if (vresp->getRequestID() == VNSI_STATUS_RECORDING)
++ {
++ vresp->extract_U32(); // device currently unused
++ uint32_t on = vresp->extract_U32();
++ char* str1 = vresp->extract_String();
++ char* str2 = vresp->extract_String();
++
++ PVR->Recording(str1, str2, on!=0?true:false);
++ PVR->TriggerTimerUpdate();
++
++ delete[] str1;
++ delete[] str2;
++ }
++ else if (vresp->getRequestID() == VNSI_STATUS_TIMERCHANGE)
++ {
++ XBMC->Log(LOG_DEBUG, "Server requested timer update");
++ PVR->TriggerTimerUpdate();
++ }
++ else if (vresp->getRequestID() == VNSI_STATUS_CHANNELCHANGE)
++ {
++ XBMC->Log(LOG_DEBUG, "Server requested channel update");
++ PVR->TriggerChannelUpdate();
++ }
++ else if (vresp->getRequestID() == VNSI_STATUS_RECORDINGSCHANGE)
++ {
++ XBMC->Log(LOG_DEBUG, "Server requested recordings update");
++ PVR->TriggerRecordingUpdate();
++ }
++
++ delete vresp;
++ }
++
++ // UNKOWN CHANNELID
++
++ else if (!OnResponsePacket(vresp))
++ {
++ XBMC->Log(LOG_ERROR, "%s - Rxd a response packet on channel %lu !!", __FUNCTION__, vresp->getChannelID());
++ delete vresp;
++ }
++ }
++ return NULL;
++}
++
++int cVNSIData::GetChannelGroupCount(bool automatic)
++{
++ cRequestPacket vrp;
++ if (!vrp.init(VNSI_CHANNELGROUP_GETCOUNT))
++ {
++ XBMC->Log(LOG_ERROR, "%s - Can't init cRequestPacket", __FUNCTION__);
++ return 0;
++ }
++
++ if (!vrp.add_U32(automatic))
++ {
++ return 0;
++ }
++
++ cResponsePacket* vresp = ReadResult(&vrp);
++ if (vresp == NULL || vresp->noResponse())
++ {
++ delete vresp;
++ return 0;
++ }
++
++ uint32_t count = vresp->extract_U32();
++
++ delete vresp;
++ return count;
++}
++
++bool cVNSIData::GetChannelGroupList(PVR_HANDLE handle, bool bRadio)
++{
++ cRequestPacket vrp;
++ if (!vrp.init(VNSI_CHANNELGROUP_LIST))
++ {
++ XBMC->Log(LOG_ERROR, "%s - Can't init cRequestPacket", __FUNCTION__);
++ return false;
++ }
++
++ vrp.add_U8(bRadio);
++
++ cResponsePacket* vresp = ReadResult(&vrp);
++ if (vresp == NULL || vresp->noResponse())
++ {
++ delete vresp;
++ return false;
++ }
++
++ while (!vresp->end())
++ {
++ PVR_CHANNEL_GROUP tag;
++
++ tag.strGroupName = vresp->extract_String();
++ tag.bIsRadio = vresp->extract_U8()!=0?true:false;
++ PVR->TransferChannelGroup(handle, &tag);
++
++ delete[] tag.strGroupName;
++ }
++
++ delete vresp;
++ return true;
++}
++
++bool cVNSIData::GetChannelGroupMembers(PVR_HANDLE handle, const PVR_CHANNEL_GROUP &group)
++{
++ cRequestPacket vrp;
++ if (!vrp.init(VNSI_CHANNELGROUP_MEMBERS))
++ {
++ XBMC->Log(LOG_ERROR, "%s - Can't init cRequestPacket", __FUNCTION__);
++ return false;
++ }
++
++ vrp.add_String(group.strGroupName);
++ vrp.add_U8(group.bIsRadio);
++
++ cResponsePacket* vresp = ReadResult(&vrp);
++ if (vresp == NULL || vresp->noResponse())
++ {
++ delete vresp;
++ return false;
++ }
++
++ while (!vresp->end())
++ {
++ PVR_CHANNEL_GROUP_MEMBER tag;
++ tag.strGroupName = group.strGroupName;
++ tag.iChannelUniqueId = vresp->extract_U32();
++ tag.iChannelNumber = vresp->extract_U32();
++
++ PVR->TransferChannelGroupMember(handle, &tag);
++ }
++
++ delete vresp;
++ return true;
++}
+diff --git a/xbmc/pvrclients/vdr-vnsi/VNSIData.h b/xbmc/pvrclients/vdr-vnsi/VNSIData.h
+new file mode 100644
+index 0000000..7b3dd2b
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/VNSIData.h
+@@ -0,0 +1,94 @@
++#pragma once
++/*
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "VNSISession.h"
++#include "client.h"
++
++#include <string>
++#include <map>
++
++class cResponsePacket;
++class cRequestPacket;
++
++class cVNSIData : public cVNSISession, public PLATFORM::CThread
++{
++public:
++
++ cVNSIData();
++ virtual ~cVNSIData();
++
++ bool Open(const std::string& hostname, int port, const char* name = NULL);
++ bool Login();
++ void Abort();
++
++ bool SupportChannelScan();
++ bool EnableStatusInterface(bool onOff);
++ bool GetDriveSpace(long long *total, long long *used);
++
++ int GetChannelsCount();
++ bool GetChannelsList(PVR_HANDLE handle, bool radio = false);
++ bool GetEPGForChannel(PVR_HANDLE handle, const PVR_CHANNEL &channel, time_t start, time_t end);
++
++ int GetChannelGroupCount(bool automatic);
++ bool GetChannelGroupList(PVR_HANDLE handle, bool bRadio);
++ bool GetChannelGroupMembers(PVR_HANDLE handle, const PVR_CHANNEL_GROUP &group);
++
++ bool GetTimersList(PVR_HANDLE handle);
++ int GetTimersCount();
++ PVR_ERROR AddTimer(const PVR_TIMER &timerinfo);
++ PVR_ERROR GetTimerInfo(unsigned int timernumber, PVR_TIMER &tag);
++ PVR_ERROR DeleteTimer(const PVR_TIMER &timerinfo, bool force = false);
++ PVR_ERROR RenameTimer(const PVR_TIMER &timerinfo, const char *newname);
++ PVR_ERROR UpdateTimer(const PVR_TIMER &timerinfo);
++
++ int GetRecordingsCount();
++ PVR_ERROR GetRecordingsList(PVR_HANDLE handle);
++ PVR_ERROR RenameRecording(const PVR_RECORDING& recinfo, const char* newname);
++ PVR_ERROR DeleteRecording(const PVR_RECORDING& recinfo);
++
++ cResponsePacket* ReadResult(cRequestPacket* vrp);
++
++protected:
++
++ virtual void *Process(void);
++ virtual bool OnResponsePacket(cResponsePacket *pkt);
++
++ void SignalConnectionLost();
++ void OnDisconnect();
++ void OnReconnect();
++
++private:
++
++ bool SendPing();
++
++ struct SMessage
++ {
++ PLATFORM::CEvent *event;
++ cResponsePacket *pkt;
++ };
++ typedef std::map<int, SMessage> SMessages;
++
++ SMessages m_queue;
++ std::string m_videodir;
++ bool m_aborting;
++ PLATFORM::CMutex m_mutex;
++};
+diff --git a/xbmc/pvrclients/vdr-vnsi/VNSIDemux.cpp b/xbmc/pvrclients/vdr-vnsi/VNSIDemux.cpp
+new file mode 100644
+index 0000000..1866375
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/VNSIDemux.cpp
+@@ -0,0 +1,487 @@
++/*
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <stdint.h>
++#include <limits.h>
++#include <string.h>
++#include <libavcodec/avcodec.h> // For codec id's
++#include "VNSIDemux.h"
++#include "responsepacket.h"
++#include "requestpacket.h"
++#include "vnsicommand.h"
++
++using namespace ADDON;
++
++cVNSIDemux::cVNSIDemux()
++{
++ m_Streams.iStreamCount = 0;
++}
++
++cVNSIDemux::~cVNSIDemux()
++{
++}
++
++bool cVNSIDemux::OpenChannel(const PVR_CHANNEL &channelinfo)
++{
++ m_channelinfo = channelinfo;
++ if(!cVNSISession::Open(g_szHostname, g_iPort))
++ return false;
++
++ if(!cVNSISession::Login())
++ return false;
++
++ return SwitchChannel(m_channelinfo);
++}
++
++bool cVNSIDemux::GetStreamProperties(PVR_STREAM_PROPERTIES* props)
++{
++ props->iStreamCount = m_Streams.iStreamCount;
++ for (unsigned int i = 0; i < m_Streams.iStreamCount; i++)
++ {
++ props->stream[i].iStreamIndex = m_Streams.stream[i].iStreamIndex;
++ props->stream[i].iPhysicalId = m_Streams.stream[i].iPhysicalId;
++ props->stream[i].iCodecType = m_Streams.stream[i].iCodecType;
++ props->stream[i].iCodecId = m_Streams.stream[i].iCodecId;
++ props->stream[i].iHeight = m_Streams.stream[i].iHeight;
++ props->stream[i].iWidth = m_Streams.stream[i].iWidth;
++ props->stream[i].strLanguage[0] = m_Streams.stream[i].strLanguage[0];
++ props->stream[i].strLanguage[1] = m_Streams.stream[i].strLanguage[1];
++ props->stream[i].strLanguage[2] = m_Streams.stream[i].strLanguage[2];
++ props->stream[i].strLanguage[3] = m_Streams.stream[i].strLanguage[3];
++ props->stream[i].iIdentifier = m_Streams.stream[i].iIdentifier;
++ }
++ return (props->iStreamCount > 0);
++}
++
++void cVNSIDemux::Abort()
++{
++ m_Streams.iStreamCount = 0;
++ cVNSISession::Abort();
++}
++
++DemuxPacket* cVNSIDemux::Read()
++{
++ if(ConnectionLost())
++ {
++ return NULL;
++ }
++
++ cResponsePacket *resp = ReadMessage();
++
++ if(resp == NULL)
++ return PVR->AllocateDemuxPacket(0);
++
++ if (resp->getChannelID() != VNSI_CHANNEL_STREAM)
++ {
++ delete resp;
++ return NULL;
++ }
++
++ if (resp->getOpCodeID() == VNSI_STREAM_CHANGE)
++ {
++ StreamChange(resp);
++ DemuxPacket* pkt = PVR->AllocateDemuxPacket(0);
++ pkt->iStreamId = DMX_SPECIALID_STREAMCHANGE;
++ delete resp;
++ return pkt;
++ }
++ else if (resp->getOpCodeID() == VNSI_STREAM_STATUS)
++ {
++ StreamStatus(resp);
++ }
++ else if (resp->getOpCodeID() == VNSI_STREAM_SIGNALINFO)
++ {
++ StreamSignalInfo(resp);
++ }
++ else if (resp->getOpCodeID() == VNSI_STREAM_CONTENTINFO)
++ {
++ // send stream updates only if there are changes
++ if(StreamContentInfo(resp))
++ {
++ DemuxPacket* pkt = PVR->AllocateDemuxPacket(sizeof(PVR_STREAM_PROPERTIES));
++ memcpy(pkt->pData, &m_Streams, sizeof(PVR_STREAM_PROPERTIES));
++ pkt->iStreamId = DMX_SPECIALID_STREAMINFO;
++ pkt->iSize = sizeof(PVR_STREAM_PROPERTIES);
++ delete resp;
++ return pkt;
++ }
++ }
++ else if (resp->getOpCodeID() == VNSI_STREAM_MUXPKT)
++ {
++ // figure out the stream id for this packet
++ int iStreamId = -1;
++ for(unsigned int i = 0; i < m_Streams.iStreamCount; i++)
++ {
++ if(m_Streams.stream[i].iPhysicalId == (unsigned int)resp->getStreamID())
++ {
++ iStreamId = i;
++ break;
++ }
++ }
++
++ // stream found ?
++ if(iStreamId != -1)
++ {
++ DemuxPacket* p = (DemuxPacket*)resp->getUserData();
++ p->iSize = resp->getUserDataLength();
++ p->duration = (double)resp->getDuration() * DVD_TIME_BASE / 1000000;
++ p->dts = (double)resp->getDTS() * DVD_TIME_BASE / 1000000;
++ p->pts = (double)resp->getPTS() * DVD_TIME_BASE / 1000000;
++ p->iStreamId = iStreamId;
++ delete resp;
++ return p;
++ }
++ else
++ {
++ XBMC->Log(LOG_DEBUG, "stream id %i not found", resp->getStreamID());
++ }
++ }
++
++ delete resp;
++ return PVR->AllocateDemuxPacket(0);
++}
++
++bool cVNSIDemux::SwitchChannel(const PVR_CHANNEL &channelinfo)
++{
++ XBMC->Log(LOG_DEBUG, "changing to channel %d", channelinfo.iChannelNumber);
++
++ cRequestPacket vrp;
++ if (!vrp.init(VNSI_CHANNELSTREAM_OPEN) || !vrp.add_U32(channelinfo.iUniqueId) || !ReadSuccess(&vrp))
++ {
++ XBMC->Log(LOG_ERROR, "%s - failed to set channel", __FUNCTION__);
++ return false;
++ }
++
++ m_channelinfo = channelinfo;
++ m_Streams.iStreamCount = 0;
++
++ return true;
++}
++
++bool cVNSIDemux::GetSignalStatus(PVR_SIGNAL_STATUS &qualityinfo)
++{
++ if (m_Quality.fe_name.empty())
++ return false;
++
++ strncpy(qualityinfo.strAdapterName, m_Quality.fe_name.c_str(), sizeof(qualityinfo.strAdapterName));
++ strncpy(qualityinfo.strAdapterStatus, m_Quality.fe_status.c_str(), sizeof(qualityinfo.strAdapterStatus));
++ qualityinfo.iSignal = (uint16_t)m_Quality.fe_signal;
++ qualityinfo.iSNR = (uint16_t)m_Quality.fe_snr;
++ qualityinfo.iBER = (uint32_t)m_Quality.fe_ber;
++ qualityinfo.iUNC = (uint32_t)m_Quality.fe_unc;
++ qualityinfo.dVideoBitrate = 0;
++ qualityinfo.dAudioBitrate = 0;
++ qualityinfo.dDolbyBitrate = 0;
++
++ return true;
++}
++
++void cVNSIDemux::StreamChange(cResponsePacket *resp)
++{
++ m_Streams.iStreamCount = 0;
++
++ while (!resp->end())
++ {
++ uint32_t index = resp->extract_U32();
++ const char* type = resp->extract_String();
++
++ m_Streams.stream[m_Streams.iStreamCount].iFPSScale = 0;
++ m_Streams.stream[m_Streams.iStreamCount].iFPSRate = 0;
++ m_Streams.stream[m_Streams.iStreamCount].iHeight = 0;
++ m_Streams.stream[m_Streams.iStreamCount].iWidth = 0;
++ m_Streams.stream[m_Streams.iStreamCount].fAspect = 0.0;
++
++ m_Streams.stream[m_Streams.iStreamCount].iChannels = 0;
++ m_Streams.stream[m_Streams.iStreamCount].iSampleRate = 0;
++ m_Streams.stream[m_Streams.iStreamCount].iBlockAlign = 0;
++ m_Streams.stream[m_Streams.iStreamCount].iBitRate = 0;
++ m_Streams.stream[m_Streams.iStreamCount].iBitsPerSample = 0;
++
++ if(!strcmp(type, "AC3"))
++ {
++ const char *language = resp->extract_String();
++
++ m_Streams.stream[m_Streams.iStreamCount].iStreamIndex = m_Streams.iStreamCount;
++ m_Streams.stream[m_Streams.iStreamCount].iPhysicalId = index;
++ m_Streams.stream[m_Streams.iStreamCount].iCodecType = AVMEDIA_TYPE_AUDIO;
++ m_Streams.stream[m_Streams.iStreamCount].iCodecId = CODEC_ID_AC3;
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[0]= language[0];
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[1]= language[1];
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[2]= language[2];
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[3]= 0;
++ m_Streams.stream[m_Streams.iStreamCount].iIdentifier = -1;
++ m_Streams.iStreamCount++;
++
++ delete[] language;
++ }
++ else if(!strcmp(type, "MPEG2AUDIO"))
++ {
++ const char *language = resp->extract_String();
++
++ m_Streams.stream[m_Streams.iStreamCount].iStreamIndex = m_Streams.iStreamCount;
++ m_Streams.stream[m_Streams.iStreamCount].iPhysicalId = index;
++ m_Streams.stream[m_Streams.iStreamCount].iCodecType = AVMEDIA_TYPE_AUDIO;
++ m_Streams.stream[m_Streams.iStreamCount].iCodecId = CODEC_ID_MP2;
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[0]= language[0];
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[1]= language[1];
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[2]= language[2];
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[3]= 0;
++ m_Streams.stream[m_Streams.iStreamCount].iIdentifier = -1;
++ m_Streams.iStreamCount++;
++
++ delete[] language;
++ }
++ else if(!strcmp(type, "AAC"))
++ {
++ const char *language = resp->extract_String();
++
++ m_Streams.stream[m_Streams.iStreamCount].iStreamIndex = m_Streams.iStreamCount;
++ m_Streams.stream[m_Streams.iStreamCount].iPhysicalId = index;
++ m_Streams.stream[m_Streams.iStreamCount].iCodecType = AVMEDIA_TYPE_AUDIO;
++ m_Streams.stream[m_Streams.iStreamCount].iCodecId = CODEC_ID_AAC;
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[0]= language[0];
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[1]= language[1];
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[2]= language[2];
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[3]= 0;
++ m_Streams.stream[m_Streams.iStreamCount].iIdentifier = -1;
++ m_Streams.iStreamCount++;
++
++ delete[] language;
++ }
++ else if(!strcmp(type, "DTS"))
++ {
++ const char *language = resp->extract_String();
++
++ m_Streams.stream[m_Streams.iStreamCount].iStreamIndex = m_Streams.iStreamCount;
++ m_Streams.stream[m_Streams.iStreamCount].iPhysicalId = index;
++ m_Streams.stream[m_Streams.iStreamCount].iCodecType = AVMEDIA_TYPE_AUDIO;
++ m_Streams.stream[m_Streams.iStreamCount].iCodecId = CODEC_ID_DTS;
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[0]= language[0];
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[1]= language[1];
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[2]= language[2];
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[3]= 0;
++ m_Streams.stream[m_Streams.iStreamCount].iIdentifier = -1;
++ m_Streams.iStreamCount++;
++
++ delete[] language;
++ }
++ else if(!strcmp(type, "EAC3"))
++ {
++ const char *language = resp->extract_String();
++
++ m_Streams.stream[m_Streams.iStreamCount].iStreamIndex = m_Streams.iStreamCount;
++ m_Streams.stream[m_Streams.iStreamCount].iPhysicalId = index;
++ m_Streams.stream[m_Streams.iStreamCount].iCodecType = AVMEDIA_TYPE_AUDIO;
++ m_Streams.stream[m_Streams.iStreamCount].iCodecId = CODEC_ID_EAC3;
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[0]= language[0];
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[1]= language[1];
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[2]= language[2];
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[3]= 0;
++ m_Streams.stream[m_Streams.iStreamCount].iIdentifier = -1;
++ m_Streams.iStreamCount++;
++
++ delete[] language;
++ }
++ else if(!strcmp(type, "MPEG2VIDEO"))
++ {
++ m_Streams.stream[m_Streams.iStreamCount].iStreamIndex = m_Streams.iStreamCount;
++ m_Streams.stream[m_Streams.iStreamCount].iPhysicalId = index;
++ m_Streams.stream[m_Streams.iStreamCount].iCodecType = AVMEDIA_TYPE_VIDEO;
++ m_Streams.stream[m_Streams.iStreamCount].iCodecId = CODEC_ID_MPEG2VIDEO;
++ m_Streams.stream[m_Streams.iStreamCount].iFPSScale = resp->extract_U32();
++ m_Streams.stream[m_Streams.iStreamCount].iFPSRate = resp->extract_U32();
++ m_Streams.stream[m_Streams.iStreamCount].iHeight = resp->extract_U32();
++ m_Streams.stream[m_Streams.iStreamCount].iWidth = resp->extract_U32();
++ m_Streams.stream[m_Streams.iStreamCount].fAspect = (float)resp->extract_Double();
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[0]= 0;
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[1]= 0;
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[2]= 0;
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[3]= 0;
++ m_Streams.stream[m_Streams.iStreamCount].iIdentifier = -1;
++ m_Streams.iStreamCount++;
++ }
++ else if(!strcmp(type, "H264"))
++ {
++ m_Streams.stream[m_Streams.iStreamCount].iStreamIndex = m_Streams.iStreamCount;
++ m_Streams.stream[m_Streams.iStreamCount].iPhysicalId = index;
++ m_Streams.stream[m_Streams.iStreamCount].iCodecType = AVMEDIA_TYPE_VIDEO;
++ m_Streams.stream[m_Streams.iStreamCount].iCodecId = CODEC_ID_H264;
++ m_Streams.stream[m_Streams.iStreamCount].iFPSScale = resp->extract_U32();
++ m_Streams.stream[m_Streams.iStreamCount].iFPSRate = resp->extract_U32();
++ m_Streams.stream[m_Streams.iStreamCount].iHeight = resp->extract_U32();
++ m_Streams.stream[m_Streams.iStreamCount].iWidth = resp->extract_U32();
++ m_Streams.stream[m_Streams.iStreamCount].fAspect = (float)resp->extract_Double();
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[0]= 0;
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[1]= 0;
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[2]= 0;
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[3]= 0;
++ m_Streams.stream[m_Streams.iStreamCount].iIdentifier = -1;
++ m_Streams.iStreamCount++;
++ }
++ else if(!strcmp(type, "DVBSUB"))
++ {
++ const char *language = resp->extract_String();
++ uint32_t composition_id = resp->extract_U32();
++ uint32_t ancillary_id = resp->extract_U32();
++
++ m_Streams.stream[m_Streams.iStreamCount].iStreamIndex = m_Streams.iStreamCount;
++ m_Streams.stream[m_Streams.iStreamCount].iPhysicalId = index;
++ m_Streams.stream[m_Streams.iStreamCount].iCodecType = AVMEDIA_TYPE_SUBTITLE;
++ m_Streams.stream[m_Streams.iStreamCount].iCodecId = CODEC_ID_DVB_SUBTITLE;
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[0]= language[0];
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[1]= language[1];
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[2]= language[2];
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[3]= 0;
++ m_Streams.stream[m_Streams.iStreamCount].iIdentifier = (composition_id & 0xffff) | ((ancillary_id & 0xffff) << 16);
++ m_Streams.iStreamCount++;
++
++ delete[] language;
++ }
++ else if(!strcmp(type, "TEXTSUB"))
++ {
++ const char *language = resp->extract_String();
++
++ m_Streams.stream[m_Streams.iStreamCount].iStreamIndex = m_Streams.iStreamCount;
++ m_Streams.stream[m_Streams.iStreamCount].iPhysicalId = index;
++ m_Streams.stream[m_Streams.iStreamCount].iCodecType = AVMEDIA_TYPE_SUBTITLE;
++ m_Streams.stream[m_Streams.iStreamCount].iCodecId = CODEC_ID_TEXT;
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[0]= language[0];
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[1]= language[1];
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[2]= language[2];
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[3]= 0;
++ m_Streams.stream[m_Streams.iStreamCount].iIdentifier = -1;
++ m_Streams.iStreamCount++;
++
++ delete[] language;
++ }
++ else if(!strcmp(type, "TELETEXT"))
++ {
++ m_Streams.stream[m_Streams.iStreamCount].iStreamIndex = m_Streams.iStreamCount;
++ m_Streams.stream[m_Streams.iStreamCount].iPhysicalId = index;
++ m_Streams.stream[m_Streams.iStreamCount].iCodecType = AVMEDIA_TYPE_SUBTITLE;
++ m_Streams.stream[m_Streams.iStreamCount].iCodecId = CODEC_ID_DVB_TELETEXT;
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[0]= 0;
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[1]= 0;
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[2]= 0;
++ m_Streams.stream[m_Streams.iStreamCount].strLanguage[3]= 0;
++ m_Streams.stream[m_Streams.iStreamCount].iIdentifier = -1;
++ m_Streams.iStreamCount++;
++ }
++
++ delete[] type;
++
++ if (m_Streams.iStreamCount >= PVR_STREAM_MAX_STREAMS)
++ {
++ XBMC->Log(LOG_ERROR, "%s - max amount of streams reached", __FUNCTION__);
++ break;
++ }
++ }
++}
++
++void cVNSIDemux::StreamStatus(cResponsePacket *resp)
++{
++ const char* status = resp->extract_String();
++ if(status != NULL)
++ {
++ XBMC->Log(LOG_DEBUG, "%s - %s", __FUNCTION__, status);
++ XBMC->QueueNotification(QUEUE_INFO, status);
++ }
++ delete[] status;
++}
++
++void cVNSIDemux::StreamSignalInfo(cResponsePacket *resp)
++{
++ const char* name = resp->extract_String();
++ const char* status = resp->extract_String();
++
++ m_Quality.fe_name = name;
++ m_Quality.fe_status = status;
++ m_Quality.fe_snr = resp->extract_U32();
++ m_Quality.fe_signal = resp->extract_U32();
++ m_Quality.fe_ber = resp->extract_U32();
++ m_Quality.fe_unc = resp->extract_U32();
++
++ delete[] name;
++ delete[] status;
++}
++
++bool cVNSIDemux::StreamContentInfo(cResponsePacket *resp)
++{
++ PVR_STREAM_PROPERTIES old = m_Streams;
++
++
++ while (!resp->end())
++ {
++ uint32_t index = resp->extract_U32();
++ unsigned int i;
++ for (i = 0; i < m_Streams.iStreamCount; i++)
++ {
++ if (index == m_Streams.stream[i].iPhysicalId)
++ {
++ if (m_Streams.stream[i].iCodecType == AVMEDIA_TYPE_AUDIO)
++ {
++ const char *language = resp->extract_String();
++
++ m_Streams.stream[i].iChannels = resp->extract_U32();
++ m_Streams.stream[i].iSampleRate = resp->extract_U32();
++ m_Streams.stream[i].iBlockAlign = resp->extract_U32();
++ m_Streams.stream[i].iBitRate = resp->extract_U32();
++ m_Streams.stream[i].iBitsPerSample = resp->extract_U32();
++ m_Streams.stream[i].strLanguage[0] = language[0];
++ m_Streams.stream[i].strLanguage[1] = language[1];
++ m_Streams.stream[i].strLanguage[2] = language[2];
++ m_Streams.stream[i].strLanguage[3] = 0;
++
++ delete[] language;
++ }
++ else if (m_Streams.stream[i].iCodecType == AVMEDIA_TYPE_VIDEO)
++ {
++ m_Streams.stream[i].iFPSScale = resp->extract_U32();
++ m_Streams.stream[i].iFPSRate = resp->extract_U32();
++ m_Streams.stream[i].iHeight = resp->extract_U32();
++ m_Streams.stream[i].iWidth = resp->extract_U32();
++ m_Streams.stream[i].fAspect = (float)resp->extract_Double();
++ }
++ else if (m_Streams.stream[i].iCodecType == AVMEDIA_TYPE_SUBTITLE)
++ {
++ const char *language = resp->extract_String();
++ uint32_t composition_id = resp->extract_U32();
++ uint32_t ancillary_id = resp->extract_U32();
++
++ m_Streams.stream[i].iIdentifier = (composition_id & 0xffff) | ((ancillary_id & 0xffff) << 16);
++ m_Streams.stream[i].strLanguage[0]= language[0];
++ m_Streams.stream[i].strLanguage[1]= language[1];
++ m_Streams.stream[i].strLanguage[2]= language[2];
++ m_Streams.stream[i].strLanguage[3]= 0;
++
++ delete[] language;
++ }
++ break;
++ }
++ }
++ if (i >= m_Streams.iStreamCount)
++ {
++ XBMC->Log(LOG_ERROR, "%s - unknown stream id", __FUNCTION__);
++ break;
++ }
++ }
++ return (memcmp(&old, &m_Streams, sizeof(m_Streams)) != 0);
++}
+diff --git a/xbmc/pvrclients/vdr-vnsi/VNSIDemux.h b/xbmc/pvrclients/vdr-vnsi/VNSIDemux.h
+new file mode 100644
+index 0000000..0ea1e15
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/VNSIDemux.h
+@@ -0,0 +1,66 @@
++#pragma once
++/*
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "VNSISession.h"
++#include "client.h"
++#include <string>
++
++class cResponsePacket;
++
++struct SQuality
++{
++ std::string fe_name;
++ std::string fe_status;
++ uint32_t fe_snr;
++ uint32_t fe_signal;
++ uint32_t fe_ber;
++ uint32_t fe_unc;
++};
++
++class cVNSIDemux : public cVNSISession
++{
++public:
++
++ cVNSIDemux();
++ ~cVNSIDemux();
++
++ bool OpenChannel(const PVR_CHANNEL &channelinfo);
++ void Abort();
++ bool GetStreamProperties(PVR_STREAM_PROPERTIES* props);
++ DemuxPacket* Read();
++ bool SwitchChannel(const PVR_CHANNEL &channelinfo);
++ int CurrentChannel() { return m_channelinfo.iChannelNumber; }
++ bool GetSignalStatus(PVR_SIGNAL_STATUS &qualityinfo);
++
++protected:
++
++ void StreamChange(cResponsePacket *resp);
++ void StreamStatus(cResponsePacket *resp);
++ void StreamSignalInfo(cResponsePacket *resp);
++ bool StreamContentInfo(cResponsePacket *resp);
++
++private:
++
++ PVR_STREAM_PROPERTIES m_Streams;
++ PVR_CHANNEL m_channelinfo;
++ SQuality m_Quality;
++};
+diff --git a/xbmc/pvrclients/vdr-vnsi/VNSIRecording.cpp b/xbmc/pvrclients/vdr-vnsi/VNSIRecording.cpp
+new file mode 100644
+index 0000000..feb72d6
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/VNSIRecording.cpp
+@@ -0,0 +1,180 @@
++/*
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <limits.h>
++#include "VNSIRecording.h"
++#include "responsepacket.h"
++#include "requestpacket.h"
++#include "vnsicommand.h"
++
++#define SEEK_POSSIBLE 0x10 // flag used to check if protocol allows seeks
++
++using namespace ADDON;
++
++cVNSIRecording::cVNSIRecording()
++{
++}
++
++cVNSIRecording::~cVNSIRecording()
++{
++ Close();
++}
++
++bool cVNSIRecording::OpenRecording(const PVR_RECORDING& recinfo)
++{
++ m_recinfo = recinfo;
++
++ if(!cVNSISession::Open(g_szHostname, g_iPort, "XBMC RecordingStream Receiver"))
++ return false;
++
++ if(!cVNSISession::Login())
++ return false;
++
++ cRequestPacket vrp;
++ if (!vrp.init(VNSI_RECSTREAM_OPEN) ||
++ !vrp.add_U32(atoi(recinfo.strRecordingId)))
++ {
++ return false;
++ }
++
++ cResponsePacket* vresp = ReadResult(&vrp);
++ if (!vresp)
++ return false;
++
++ uint32_t returnCode = vresp->extract_U32();
++ if (returnCode == VNSI_RET_OK)
++ {
++ m_currentPlayingRecordFrames = vresp->extract_U32();
++ m_currentPlayingRecordBytes = vresp->extract_U64();
++ m_currentPlayingRecordPosition = 0;
++ }
++ else
++ XBMC->Log(LOG_ERROR, "%s - Can't open recording '%s'", __FUNCTION__, recinfo.strTitle);
++
++ delete vresp;
++ return (returnCode == VNSI_RET_OK);
++}
++
++void cVNSIRecording::Close()
++{
++ if(!IsOpen())
++ return;
++
++ cRequestPacket vrp;
++ vrp.init(VNSI_RECSTREAM_CLOSE);
++ ReadSuccess(&vrp);
++ cVNSISession::Close();
++}
++
++int cVNSIRecording::Read(unsigned char* buf, uint32_t buf_size)
++{
++ if (ConnectionLost() && !TryReconnect())
++ {
++ *buf = 0;
++ SleepMs(100);
++ return 1;
++ }
++
++ if (m_currentPlayingRecordPosition >= m_currentPlayingRecordBytes)
++ return 0;
++
++ cRequestPacket vrp;
++ if (!vrp.init(VNSI_RECSTREAM_GETBLOCK) ||
++ !vrp.add_U64(m_currentPlayingRecordPosition) ||
++ !vrp.add_U32(buf_size))
++ {
++ return 0;
++ }
++
++ cResponsePacket* vresp = ReadResult(&vrp);
++ if (!vresp)
++ return -1;
++
++ uint32_t length = vresp->getUserDataLength();
++ uint8_t *data = vresp->getUserData();
++ if (length > buf_size)
++ {
++ XBMC->Log(LOG_ERROR, "%s: PANIC - Received more bytes as requested", __FUNCTION__);
++ free(data);
++ delete vresp;
++ return 0;
++ }
++
++ memcpy(buf, data, length);
++ m_currentPlayingRecordPosition += length;
++ free(data);
++ delete vresp;
++ return length;
++}
++
++long long cVNSIRecording::Seek(long long pos, uint32_t whence)
++{
++ uint64_t nextPos = m_currentPlayingRecordPosition;
++
++ switch (whence)
++ {
++ case SEEK_SET:
++ nextPos = pos;
++ break;
++
++ case SEEK_CUR:
++ nextPos += pos;
++ break;
++
++ case SEEK_END:
++ if (m_currentPlayingRecordBytes)
++ nextPos = m_currentPlayingRecordBytes - pos;
++ else
++ return -1;
++
++ break;
++
++ case SEEK_POSSIBLE:
++ return 1;
++
++ default:
++ return -1;
++ }
++
++ if (nextPos >= m_currentPlayingRecordBytes)
++ {
++ return 0;
++ }
++
++ m_currentPlayingRecordPosition = nextPos;
++
++ return m_currentPlayingRecordPosition;
++}
++
++long long cVNSIRecording::Position(void)
++{
++ return m_currentPlayingRecordPosition;
++}
++
++long long cVNSIRecording::Length(void)
++{
++ return m_currentPlayingRecordBytes;
++}
++
++void cVNSIRecording::OnReconnect()
++{
++ OpenRecording(m_recinfo);
++}
+diff --git a/xbmc/pvrclients/vdr-vnsi/VNSIRecording.h b/xbmc/pvrclients/vdr-vnsi/VNSIRecording.h
+new file mode 100644
+index 0000000..e1d92c3
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/VNSIRecording.h
+@@ -0,0 +1,51 @@
++#pragma once
++/*
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "VNSISession.h"
++#include "client.h"
++
++class cVNSIRecording : public cVNSISession
++{
++public:
++
++ cVNSIRecording();
++ ~cVNSIRecording();
++
++ bool OpenRecording(const PVR_RECORDING& recinfo);
++ void Close();
++
++ int Read(unsigned char* buf, uint32_t buf_size);
++ long long Seek(long long pos, uint32_t whence);
++ long long Position(void);
++ long long Length(void);
++
++protected:
++
++ void OnReconnect();
++
++private:
++
++ PVR_RECORDING m_recinfo;
++ uint64_t m_currentPlayingRecordBytes;
++ uint32_t m_currentPlayingRecordFrames;
++ uint64_t m_currentPlayingRecordPosition;
++};
+diff --git a/xbmc/pvrclients/vdr-vnsi/VNSISession.cpp b/xbmc/pvrclients/vdr-vnsi/VNSISession.cpp
+new file mode 100644
+index 0000000..a6becbd
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/VNSISession.cpp
+@@ -0,0 +1,371 @@
++/*
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "VNSISession.h"
++#include "client.h"
++
++#include <errno.h>
++#include <fcntl.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++
++#include "responsepacket.h"
++#include "requestpacket.h"
++#include "vnsicommand.h"
++#include "tools.h"
++#include "../../../lib/platform/sockets/tcp.h"
++#include "../../../lib/platform/util/timeutils.h"
++
++/* Needed on Mac OS/X */
++
++#ifndef SOL_TCP
++#define SOL_TCP IPPROTO_TCP
++#endif
++
++using namespace ADDON;
++using namespace PLATFORM;
++
++cVNSISession::cVNSISession()
++ : m_socket(NULL)
++ , m_protocol(0)
++ , m_connectionLost(false)
++{
++}
++
++cVNSISession::~cVNSISession()
++{
++ Close();
++}
++
++void cVNSISession::Abort()
++{
++ if (!m_socket)
++ return;
++
++ m_socket->Shutdown();
++}
++
++void cVNSISession::Close()
++{
++ if(IsOpen())
++ {
++ m_socket->Close();
++ }
++ if (m_socket)
++ {
++ delete m_socket;
++ m_socket = NULL;
++ }
++}
++
++bool cVNSISession::Open(const std::string& hostname, int port, const char *name)
++{
++ Close();
++
++ uint64_t iNow = GetTimeMs();
++ uint64_t iTarget = iNow + g_iConnectTimeout * 1000;
++ if (!m_socket)
++ m_socket = new CTcpConnection(hostname.c_str(), port);
++ while (!m_socket->IsOpen() && iNow < iTarget)
++ {
++ if (!m_socket->Open(iTarget - iNow))
++ CEvent::Sleep(100);
++ iNow = GetTimeMs();
++ }
++
++ if (!m_socket->IsOpen())
++ {
++ XBMC->Log(LOG_ERROR, "%s - failed to connect to the backend (%s)", __FUNCTION__, m_socket->GetError().c_str());
++ return false;
++ }
++
++ // store connection data
++ m_hostname = hostname;
++ m_port = port;
++
++ if(name != NULL)
++ m_name = name;
++
++ return true;
++}
++
++bool cVNSISession::Login()
++{
++ try
++ {
++ cRequestPacket vrp;
++ if (!vrp.init(VNSI_LOGIN)) throw "Can't init cRequestPacket";
++ if (!vrp.add_U32(VNSIPROTOCOLVERSION)) throw "Can't add protocol version to RequestPacket";
++ if (!vrp.add_U8(false)) throw "Can't add netlog flag";
++ if (!m_name.empty())
++ {
++ if (!vrp.add_String(m_name.c_str())) throw "Can't add client name to RequestPacket";
++ }
++ else
++ {
++ if (!vrp.add_String("XBMC Media Center")) throw "Can't add client name to RequestPacket";
++ }
++
++ // read welcome
++ cResponsePacket* vresp = ReadResult(&vrp);
++ if (!vresp)
++ throw "failed to read greeting from server";
++
++ uint32_t protocol = vresp->extract_U32();
++ uint32_t vdrTime = vresp->extract_U32();
++ int32_t vdrTimeOffset = vresp->extract_S32();
++ const char *ServerName = vresp->extract_String();
++ const char *ServerVersion = vresp->extract_String();
++
++ m_server = ServerName;
++ m_version = ServerVersion;
++ m_protocol = (int)protocol;
++
++ if (m_name.empty())
++ XBMC->Log(LOG_NOTICE, "Logged in at '%lu+%i' to '%s' Version: '%s' with protocol version '%d'",
++ vdrTime, vdrTimeOffset, ServerName, ServerVersion, protocol);
++
++ delete[] ServerName;
++ delete[] ServerVersion;
++
++ delete vresp;
++ }
++ catch (const char * str)
++ {
++ XBMC->Log(LOG_ERROR, "%s - %s", __FUNCTION__,str);
++ m_socket->Close();
++ delete m_socket;
++ m_socket = NULL;
++ return false;
++ }
++
++ return true;
++}
++
++cResponsePacket* cVNSISession::ReadMessage(int iInitialTimeout /*= 10000*/, int iDatapacketTimeout /*= 10000*/)
++{
++ uint32_t channelID = 0;
++ uint32_t requestID;
++ uint32_t userDataLength = 0;
++ uint8_t* userData = NULL;
++ uint32_t streamID;
++ uint32_t duration;
++ uint32_t opCodeID;
++ int64_t dts = 0;
++ int64_t pts = 0;
++
++ cResponsePacket* vresp = NULL;
++
++ CLockObject lock(m_readMutex);
++
++ if(!readData((uint8_t*)&channelID, sizeof(uint32_t), iInitialTimeout))
++ return NULL;
++
++ // Data was read
++
++ channelID = ntohl(channelID);
++ if (channelID == VNSI_CHANNEL_STREAM)
++ {
++ if (!readData((uint8_t*)&m_streamPacketHeader, sizeof(m_streamPacketHeader), iDatapacketTimeout)) return NULL;
++
++ opCodeID = ntohl(m_streamPacketHeader.opCodeID);
++ streamID = ntohl(m_streamPacketHeader.streamID);
++ duration = ntohl(m_streamPacketHeader.duration);
++ pts = ntohll(*(int64_t*)m_streamPacketHeader.pts);
++ dts = ntohll(*(int64_t*)m_streamPacketHeader.dts);
++ userDataLength = ntohl(m_streamPacketHeader.userDataLength);
++
++ if(opCodeID == VNSI_STREAM_MUXPKT)
++ {
++ DemuxPacket* p = PVR->AllocateDemuxPacket(userDataLength);
++ userData = (uint8_t*)p;
++ if (userDataLength > 0)
++ {
++ if (!userData) return NULL;
++ if (!readData(p->pData, userDataLength, iDatapacketTimeout))
++ {
++ PVR->FreeDemuxPacket(p);
++ return NULL;
++ }
++ }
++ }
++ else if (userDataLength > 0)
++ {
++ userData = (uint8_t*)malloc(userDataLength);
++ if (!userData) return NULL;
++ if (!readData(userData, userDataLength, iDatapacketTimeout))
++ {
++ free(userData);
++ return NULL;
++ }
++ }
++
++ vresp = new cResponsePacket();
++ vresp->setStream(opCodeID, streamID, duration, dts, pts, userData, userDataLength);
++ }
++ else
++ {
++ if (!readData((uint8_t*)&m_responsePacketHeader, sizeof(m_responsePacketHeader), iDatapacketTimeout)) return NULL;
++
++ requestID = ntohl(m_responsePacketHeader.requestID);
++ userDataLength = ntohl(m_responsePacketHeader.userDataLength);
++
++ if (userDataLength > 5000000) return NULL; // how big can these packets get?
++ userData = NULL;
++ if (userDataLength > 0)
++ {
++ userData = (uint8_t*)malloc(userDataLength);
++ if (!userData) return NULL;
++ if (!readData(userData, userDataLength, iDatapacketTimeout))
++ {
++ free(userData);
++ return NULL;
++ }
++ }
++
++ vresp = new cResponsePacket();
++ if (channelID == VNSI_CHANNEL_STATUS)
++ vresp->setStatus(requestID, userData, userDataLength);
++ else
++ vresp->setResponse(requestID, userData, userDataLength);
++ }
++
++ return vresp;
++}
++
++bool cVNSISession::TransmitMessage(cRequestPacket* vrp)
++{
++ if (!IsOpen())
++ return false;
++
++ ssize_t iWriteResult = m_socket->Write(vrp->getPtr(), vrp->getLen());
++ if (iWriteResult != (ssize_t)vrp->getLen())
++ {
++ XBMC->Log(LOG_ERROR, "%s - Failed to write packet (%s), bytes written: %d of total: %s", __FUNCTION__, m_socket->GetError().c_str(), iWriteResult, vrp->getLen());
++ return false;
++ }
++ return true;
++}
++
++cResponsePacket* cVNSISession::ReadResult(cRequestPacket* vrp)
++{
++ if(!TransmitMessage(vrp))
++ {
++ SignalConnectionLost();
++ return NULL;
++ }
++
++ cResponsePacket *pkt = NULL;
++
++ while((pkt = ReadMessage()))
++ {
++ /* Discard everything other as response packets until it is received */
++ if (pkt->getChannelID() == VNSI_CHANNEL_REQUEST_RESPONSE && pkt->getRequestID() == vrp->getSerial())
++ {
++ return pkt;
++ }
++ else
++ delete pkt;
++ }
++
++ SignalConnectionLost();
++ return NULL;
++}
++
++bool cVNSISession::ReadSuccess(cRequestPacket* vrp)
++{
++ cResponsePacket *pkt = NULL;
++ if((pkt = ReadResult(vrp)) == NULL)
++ {
++ return false;
++ }
++ uint32_t retCode = pkt->extract_U32();
++ delete pkt;
++
++ if(retCode != VNSI_RET_OK)
++ {
++ XBMC->Log(LOG_ERROR, "%s - failed with error code '%i'", __FUNCTION__, retCode);
++ return false;
++ }
++ return true;
++}
++
++void cVNSISession::OnReconnect() {
++}
++
++void cVNSISession::OnDisconnect() {
++}
++
++bool cVNSISession::TryReconnect() {
++ if(!Open(m_hostname, m_port))
++ return false;
++
++ if(!Login())
++ return false;
++
++ XBMC->Log(LOG_DEBUG, "%s - reconnected", __FUNCTION__);
++ m_connectionLost = false;
++
++ OnReconnect();
++
++ return true;
++}
++
++bool cVNSISession::IsOpen()
++{
++ bool bReturn(false);
++ if (m_socket && m_socket->IsOpen())
++ bReturn = true;
++ return bReturn;
++}
++
++void cVNSISession::SignalConnectionLost()
++{
++ if(m_connectionLost)
++ return;
++
++ XBMC->Log(LOG_ERROR, "%s - connection lost !!!", __FUNCTION__);
++
++ m_connectionLost = true;
++ Abort();
++ Close();
++
++ OnDisconnect();
++}
++
++bool cVNSISession::readData(uint8_t* buffer, int totalBytes, int timeout)
++{
++ int bytesRead = m_socket->Read(buffer, totalBytes, timeout);
++ if (bytesRead == totalBytes)
++ return true;
++ else if (m_socket->GetErrorNumber() != ETIMEDOUT)
++ {
++ SignalConnectionLost();
++ return false;
++ }
++ else
++ return false;
++}
++
++void cVNSISession::SleepMs(int ms)
++{
++ CEvent::Sleep(ms);
++}
+diff --git a/xbmc/pvrclients/vdr-vnsi/VNSISession.h b/xbmc/pvrclients/vdr-vnsi/VNSISession.h
+new file mode 100644
+index 0000000..783907b
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/VNSISession.h
+@@ -0,0 +1,100 @@
++#pragma once
++/*
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <stdint.h>
++#include <string>
++#include "../../../lib/platform/threads/threads.h"
++
++
++class cResponsePacket;
++class cRequestPacket;
++
++namespace PLATFORM
++{
++ class CTcpConnection;
++}
++
++class cVNSISession
++{
++public:
++ cVNSISession();
++ virtual ~cVNSISession();
++
++ virtual bool Open(const std::string& hostname, int port, const char *name = NULL);
++ virtual bool Login();
++ virtual void Close();
++ virtual void Abort();
++
++ cResponsePacket* ReadMessage(int iInitialTimeout = 10000, int iDatapacketTimeout = 10000);
++ bool TransmitMessage(cRequestPacket* vrp);
++
++ cResponsePacket* ReadResult(cRequestPacket* vrp);
++ bool ReadSuccess(cRequestPacket* m);
++
++ int GetProtocol() { return m_protocol; }
++ const std::string& GetServerName() { return m_server; }
++ const std::string& GetVersion() { return m_version; }
++
++protected:
++
++ void SleepMs(int ms);
++
++ bool TryReconnect();
++ bool IsOpen();
++
++ virtual void OnDisconnect();
++ virtual void OnReconnect();
++
++ virtual void SignalConnectionLost();
++ bool ConnectionLost() { return m_connectionLost; }
++
++ std::string m_hostname;
++ int m_port;
++ std::string m_name;
++ PLATFORM::CMutex m_mutex;
++
++private:
++
++ bool readData(uint8_t* buffer, int totalBytes, int timeout);
++
++ PLATFORM::CTcpConnection *m_socket;
++ PLATFORM::CMutex m_readMutex;
++ int m_protocol;
++ std::string m_server;
++ std::string m_version;
++ bool m_connectionLost;
++
++ struct {
++ uint32_t opCodeID;
++ uint32_t streamID;
++ uint32_t duration;
++ uint8_t pts[sizeof(int64_t)];
++ uint8_t dts[sizeof(int64_t)];
++ uint32_t userDataLength;
++ } m_streamPacketHeader;
++
++ struct {
++ uint32_t requestID;
++ uint32_t userDataLength;
++ } m_responsePacketHeader;
++
++};
+diff --git a/xbmc/pvrclients/vdr-vnsi/client.cpp b/xbmc/pvrclients/vdr-vnsi/client.cpp
+new file mode 100644
+index 0000000..0c891ed
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/client.cpp
+@@ -0,0 +1,642 @@
++/*
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "client.h"
++#include "xbmc_pvr_dll.h"
++#include "VNSIDemux.h"
++#include "VNSIRecording.h"
++#include "VNSIData.h"
++#include "VNSIChannelScan.h"
++
++#include <sstream>
++#include <string>
++#include <iostream>
++
++using namespace std;
++using namespace ADDON;
++
++bool m_bCreated = false;
++ADDON_STATUS m_CurStatus = ADDON_STATUS_UNKNOWN;
++
++/* User adjustable settings are saved here.
++ * Default values are defined inside client.h
++ * and exported to the other source files.
++ */
++std::string g_szHostname = DEFAULT_HOST;
++int g_iPort = DEFAULT_PORT;
++bool g_bCharsetConv = DEFAULT_CHARCONV; ///< Convert VDR's incoming strings to UTF8 character set
++bool g_bHandleMessages = DEFAULT_HANDLE_MSG; ///< Send VDR's OSD status messages to XBMC OSD
++int g_iConnectTimeout = DEFAULT_TIMEOUT; ///< The Socket connection timeout
++int g_iPriority = DEFAULT_PRIORITY; ///< The Priority this client have in response to other clients
++bool g_bAutoChannelGroups = DEFAULT_AUTOGROUPS;
++
++CHelper_libXBMC_addon *XBMC = NULL;
++CHelper_libXBMC_gui *GUI = NULL;
++CHelper_libXBMC_pvr *PVR = NULL;
++
++cVNSIDemux *VNSIDemuxer = NULL;
++cVNSIData *VNSIData = NULL;
++cVNSIRecording *VNSIRecording = NULL;
++
++extern "C" {
++
++/***********************************************************
++ * Standart AddOn related public library functions
++ ***********************************************************/
++
++ADDON_STATUS ADDON_Create(void* hdl, void* props)
++{
++ if (!hdl || !props)
++ return ADDON_STATUS_UNKNOWN;
++
++ XBMC = new CHelper_libXBMC_addon;
++ if (!XBMC->RegisterMe(hdl))
++ {
++ delete XBMC;
++ XBMC = NULL;
++ return ADDON_STATUS_UNKNOWN;
++ }
++
++ GUI = new CHelper_libXBMC_gui;
++ if (!GUI->RegisterMe(hdl))
++ return ADDON_STATUS_UNKNOWN;
++
++ PVR = new CHelper_libXBMC_pvr;
++ if (!PVR->RegisterMe(hdl))
++ {
++ delete PVR;
++ delete XBMC;
++ PVR = NULL;
++ XBMC = NULL;
++ return ADDON_STATUS_UNKNOWN;
++ }
++
++ XBMC->Log(LOG_DEBUG, "Creating VDR VNSI PVR-Client");
++
++ m_CurStatus = ADDON_STATUS_UNKNOWN;
++
++ /* Read setting "host" from settings.xml */
++ char * buffer = (char*) malloc(128);
++ buffer[0] = 0; /* Set the end of string */
++
++ if (XBMC->GetSetting("host", buffer))
++ g_szHostname = buffer;
++ else
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'host' setting, falling back to '%s' as default", DEFAULT_HOST);
++ g_szHostname = DEFAULT_HOST;
++ }
++ free(buffer);
++
++ /* Read setting "port" from settings.xml */
++ if (!XBMC->GetSetting("port", &g_iPort))
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'port' setting, falling back to '%i' as default", DEFAULT_PORT);
++ g_iPort = DEFAULT_PORT;
++ }
++
++ /* Read setting "priority" from settings.xml */
++ if (!XBMC->GetSetting("priority", &g_iPriority))
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'priority' setting, falling back to %i as default", DEFAULT_PRIORITY);
++ g_iPriority = DEFAULT_PRIORITY;
++ }
++
++ /* Read setting "convertchar" from settings.xml */
++ if (!XBMC->GetSetting("convertchar", &g_bCharsetConv))
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'convertchar' setting, falling back to 'false' as default");
++ g_bCharsetConv = DEFAULT_CHARCONV;
++ }
++
++ /* Read setting "timeout" from settings.xml */
++ if (!XBMC->GetSetting("timeout", &g_iConnectTimeout))
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'timeout' setting, falling back to %i seconds as default", DEFAULT_TIMEOUT);
++ g_iConnectTimeout = DEFAULT_TIMEOUT;
++ }
++
++ /* Read setting "handlemessages" from settings.xml */
++ if (!XBMC->GetSetting("handlemessages", &g_bHandleMessages))
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'handlemessages' setting, falling back to 'true' as default");
++ g_bHandleMessages = DEFAULT_HANDLE_MSG;
++ }
++
++ /* Read setting "autochannelgroups" from settings.xml */
++ if (!XBMC->GetSetting("autochannelgroups", &g_bAutoChannelGroups))
++ {
++ /* If setting is unknown fallback to defaults */
++ XBMC->Log(LOG_ERROR, "Couldn't get 'autochannelgroups' setting, falling back to 'false' as default");
++ g_bAutoChannelGroups = DEFAULT_AUTOGROUPS;
++ }
++
++ VNSIData = new cVNSIData;
++ if (!VNSIData->Open(g_szHostname, g_iPort))
++ {
++ delete VNSIData;
++ delete PVR;
++ delete XBMC;
++ VNSIData = NULL;
++ PVR = NULL;
++ XBMC = NULL;
++ m_CurStatus = ADDON_STATUS_LOST_CONNECTION;
++ return m_CurStatus;
++ }
++
++ if (!VNSIData->Login())
++ {
++ m_CurStatus = ADDON_STATUS_LOST_CONNECTION;
++ return m_CurStatus;
++ }
++
++ if (!VNSIData->EnableStatusInterface(g_bHandleMessages))
++ {
++ m_CurStatus = ADDON_STATUS_LOST_CONNECTION;
++ return m_CurStatus;
++ }
++
++ m_CurStatus = ADDON_STATUS_OK;
++ m_bCreated = true;
++ return m_CurStatus;
++}
++
++ADDON_STATUS ADDON_GetStatus()
++{
++ return m_CurStatus;
++}
++
++void ADDON_Destroy()
++{
++ if (m_bCreated)
++ {
++ delete VNSIData;
++ VNSIData = NULL;
++ }
++
++ if (PVR)
++ {
++ delete PVR;
++ PVR = NULL;
++ }
++
++ if (XBMC)
++ {
++ delete XBMC;
++ XBMC = NULL;
++ }
++
++ m_CurStatus = ADDON_STATUS_UNKNOWN;
++}
++
++bool ADDON_HasSettings()
++{
++ return true;
++}
++
++unsigned int ADDON_GetSettings(ADDON_StructSetting ***sSet)
++{
++ return 0;
++}
++
++ADDON_STATUS ADDON_SetSetting(const char *settingName, const void *settingValue)
++{
++ string str = settingName;
++ if (str == "host")
++ {
++ string tmp_sHostname;
++ XBMC->Log(LOG_INFO, "Changed Setting 'host' from %s to %s", g_szHostname.c_str(), (const char*) settingValue);
++ tmp_sHostname = g_szHostname;
++ g_szHostname = (const char*) settingValue;
++ if (tmp_sHostname != g_szHostname)
++ return ADDON_STATUS_NEED_RESTART;
++ }
++ else if (str == "port")
++ {
++ XBMC->Log(LOG_INFO, "Changed Setting 'port' from %u to %u", g_iPort, *(int*) settingValue);
++ if (g_iPort != *(int*) settingValue)
++ {
++ g_iPort = *(int*) settingValue;
++ return ADDON_STATUS_NEED_RESTART;
++ }
++ }
++ else if (str == "priority")
++ {
++ XBMC->Log(LOG_INFO, "Changed Setting 'priority' from %u to %u", g_iPriority, *(int*) settingValue);
++ g_iPriority = *(int*) settingValue;
++ }
++ else if (str == "convertchar")
++ {
++ XBMC->Log(LOG_INFO, "Changed Setting 'convertchar' from %u to %u", g_bCharsetConv, *(bool*) settingValue);
++ g_bCharsetConv = *(bool*) settingValue;
++ }
++ else if (str == "timeout")
++ {
++ XBMC->Log(LOG_INFO, "Changed Setting 'timeout' from %u to %u", g_iConnectTimeout, *(int*) settingValue);
++ g_iConnectTimeout = *(int*) settingValue;
++ }
++ else if (str == "handlemessages")
++ {
++ XBMC->Log(LOG_INFO, "Changed Setting 'handlemessages' from %u to %u", g_bHandleMessages, *(bool*) settingValue);
++ g_bHandleMessages = *(bool*) settingValue;
++ if (VNSIData) VNSIData->EnableStatusInterface(g_bHandleMessages);
++ }
++ else if (str == "autochannelgroups")
++ {
++ XBMC->Log(LOG_INFO, "Changed Setting 'autochannelgroups' from %u to %u", g_bAutoChannelGroups, *(bool*) settingValue);
++ if (g_bAutoChannelGroups != *(bool*) settingValue)
++ {
++ g_bAutoChannelGroups = *(bool*) settingValue;
++ return ADDON_STATUS_NEED_RESTART;
++ }
++ }
++
++ return ADDON_STATUS_OK;
++}
++
++void ADDON_Stop()
++{
++}
++
++void ADDON_FreeSettings()
++{
++
++}
++
++/***********************************************************
++ * PVR Client AddOn specific public library functions
++ ***********************************************************/
++
++PVR_ERROR GetAddonCapabilities(PVR_ADDON_CAPABILITIES* pCapabilities)
++{
++ pCapabilities->bSupportsTimeshift = false;
++ pCapabilities->bSupportsEPG = true;
++ pCapabilities->bSupportsRecordings = true;
++ pCapabilities->bSupportsTimers = true;
++ pCapabilities->bSupportsTV = true;
++ pCapabilities->bSupportsRadio = true;
++ pCapabilities->bSupportsChannelSettings = false;
++ pCapabilities->bSupportsChannelGroups = true;
++ pCapabilities->bHandlesInputStream = true;
++ pCapabilities->bHandlesDemuxing = true;
++ if (VNSIData && VNSIData->SupportChannelScan())
++ pCapabilities->bSupportsChannelScan = true;
++ else
++ pCapabilities->bSupportsChannelScan = false;
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++const char * GetBackendName(void)
++{
++ static std::string BackendName = VNSIData ? VNSIData->GetServerName() : "unknown";
++ return BackendName.c_str();
++}
++
++const char * GetBackendVersion(void)
++{
++ static std::string BackendVersion;
++ if (VNSIData) {
++ std::stringstream format;
++ format << VNSIData->GetVersion() << "(Protocol: " << VNSIData->GetProtocol() << ")";
++ BackendVersion = format.str();
++ }
++ return BackendVersion.c_str();
++}
++
++const char * GetConnectionString(void)
++{
++ static std::string ConnectionString;
++ std::stringstream format;
++
++ if (VNSIData) {
++ format << g_szHostname << ":" << g_iPort;
++ }
++ else {
++ format << g_szHostname << ":" << g_iPort << " (addon error!)";
++ }
++ ConnectionString = format.str();
++ return ConnectionString.c_str();
++}
++
++PVR_ERROR GetDriveSpace(long long *iTotal, long long *iUsed)
++{
++ if (!VNSIData)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return (VNSIData->GetDriveSpace(iTotal, iUsed) ? PVR_ERROR_NO_ERROR : PVR_ERROR_SERVER_ERROR);
++}
++
++PVR_ERROR DialogChannelScan(void)
++{
++ cVNSIChannelScan scanner;
++ scanner.Open(g_szHostname, g_iPort);
++ return PVR_ERROR_NO_ERROR;
++}
++
++/*******************************************/
++/** PVR EPG Functions **/
++
++PVR_ERROR GetEPGForChannel(PVR_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd)
++{
++ if (!VNSIData)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return (VNSIData->GetEPGForChannel(handle, channel, iStart, iEnd) ? PVR_ERROR_NO_ERROR: PVR_ERROR_SERVER_ERROR);
++}
++
++
++/*******************************************/
++/** PVR Channel Functions **/
++
++int GetChannelsAmount(void)
++{
++ if (!VNSIData)
++ return 0;
++
++ return VNSIData->GetChannelsCount();
++}
++
++PVR_ERROR GetChannels(PVR_HANDLE handle, bool bRadio)
++{
++ if (!VNSIData)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return (VNSIData->GetChannelsList(handle, bRadio) ? PVR_ERROR_NO_ERROR : PVR_ERROR_SERVER_ERROR);
++}
++
++
++/*******************************************/
++/** PVR Channelgroups Functions **/
++
++int GetChannelGroupsAmount()
++{
++ if (!VNSIData)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return VNSIData->GetChannelGroupCount(g_bAutoChannelGroups);
++}
++
++PVR_ERROR GetChannelGroups(PVR_HANDLE handle, bool bRadio)
++{
++ if (!VNSIData)
++ return PVR_ERROR_SERVER_ERROR;
++
++ if(VNSIData->GetChannelGroupCount(g_bAutoChannelGroups) > 0)
++ return VNSIData->GetChannelGroupList(handle, bRadio) ? PVR_ERROR_NO_ERROR : PVR_ERROR_SERVER_ERROR;
++
++ return PVR_ERROR_NO_ERROR;
++}
++
++PVR_ERROR GetChannelGroupMembers(PVR_HANDLE handle, const PVR_CHANNEL_GROUP &group)
++{
++ if (!VNSIData)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return VNSIData->GetChannelGroupMembers(handle, group) ? PVR_ERROR_NO_ERROR : PVR_ERROR_SERVER_ERROR;
++}
++
++
++/*******************************************/
++/** PVR Timer Functions **/
++
++int GetTimersAmount(void)
++{
++ if (!VNSIData)
++ return 0;
++
++ return VNSIData->GetTimersCount();
++}
++
++PVR_ERROR GetTimers(PVR_HANDLE handle)
++{
++ if (!VNSIData)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return (VNSIData->GetTimersList(handle) ? PVR_ERROR_NO_ERROR : PVR_ERROR_SERVER_ERROR);
++}
++
++PVR_ERROR AddTimer(const PVR_TIMER &timer)
++{
++ if (!VNSIData)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return VNSIData->AddTimer(timer);
++}
++
++PVR_ERROR DeleteTimer(const PVR_TIMER &timer, bool bForce)
++{
++ if (!VNSIData)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return VNSIData->DeleteTimer(timer, bForce);
++}
++
++PVR_ERROR UpdateTimer(const PVR_TIMER &timer)
++{
++ if (!VNSIData)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return VNSIData->UpdateTimer(timer);
++}
++
++
++/*******************************************/
++/** PVR Recording Functions **/
++
++int GetRecordingsAmount(void)
++{
++ if (!VNSIData)
++ return 0;
++
++ return VNSIData->GetRecordingsCount();
++}
++
++PVR_ERROR GetRecordings(PVR_HANDLE handle)
++{
++ if (!VNSIData)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return VNSIData->GetRecordingsList(handle);
++}
++
++PVR_ERROR RenameRecording(const PVR_RECORDING &recording)
++{
++ if (!VNSIData)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return VNSIData->RenameRecording(recording, recording.strTitle);
++}
++
++PVR_ERROR DeleteRecording(const PVR_RECORDING &recording)
++{
++ if (!VNSIData)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return VNSIData->DeleteRecording(recording);
++}
++
++/*******************************************/
++/** PVR Live Stream Functions **/
++
++bool OpenLiveStream(const PVR_CHANNEL &channel)
++{
++ CloseLiveStream();
++
++ VNSIDemuxer = new cVNSIDemux;
++ return VNSIDemuxer->OpenChannel(channel);
++}
++
++void CloseLiveStream(void)
++{
++ if (VNSIDemuxer)
++ {
++ VNSIDemuxer->Close();
++ delete VNSIDemuxer;
++ VNSIDemuxer = NULL;
++ }
++}
++
++PVR_ERROR GetStreamProperties(PVR_STREAM_PROPERTIES* pProperties)
++{
++ if (!VNSIDemuxer)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return (VNSIDemuxer->GetStreamProperties(pProperties) ? PVR_ERROR_NO_ERROR : PVR_ERROR_SERVER_ERROR);
++}
++
++void DemuxAbort(void)
++{
++ if (VNSIDemuxer) VNSIDemuxer->Abort();
++}
++
++DemuxPacket* DemuxRead(void)
++{
++ if (!VNSIDemuxer)
++ return NULL;
++
++ return VNSIDemuxer->Read();
++}
++
++int GetCurrentClientChannel(void)
++{
++ if (VNSIDemuxer)
++ return VNSIDemuxer->CurrentChannel();
++
++ return -1;
++}
++
++bool SwitchChannel(const PVR_CHANNEL &channel)
++{
++ if (VNSIDemuxer)
++ return VNSIDemuxer->SwitchChannel(channel);
++
++ return false;
++}
++
++PVR_ERROR SignalStatus(PVR_SIGNAL_STATUS &signalStatus)
++{
++ if (!VNSIDemuxer)
++ return PVR_ERROR_SERVER_ERROR;
++
++ return (VNSIDemuxer->GetSignalStatus(signalStatus) ? PVR_ERROR_NO_ERROR : PVR_ERROR_SERVER_ERROR);
++
++}
++
++
++/*******************************************/
++/** PVR Recording Stream Functions **/
++
++bool OpenRecordedStream(const PVR_RECORDING &recording)
++{
++ if(!VNSIData)
++ return false;
++
++ CloseRecordedStream();
++
++ VNSIRecording = new cVNSIRecording;
++ return VNSIRecording->OpenRecording(recording);
++}
++
++void CloseRecordedStream(void)
++{
++ if (VNSIRecording)
++ {
++ VNSIRecording->Close();
++ delete VNSIRecording;
++ VNSIRecording = NULL;
++ }
++}
++
++int ReadRecordedStream(unsigned char *pBuffer, unsigned int iBufferSize)
++{
++ if (!VNSIRecording)
++ return -1;
++
++ return VNSIRecording->Read(pBuffer, iBufferSize);
++}
++
++long long SeekRecordedStream(long long iPosition, int iWhence /* = SEEK_SET */)
++{
++ if (VNSIRecording)
++ return VNSIRecording->Seek(iPosition, iWhence);
++
++ return -1;
++}
++
++long long PositionRecordedStream(void)
++{
++ if (VNSIRecording)
++ return VNSIRecording->Position();
++
++ return 0;
++}
++
++long long LengthRecordedStream(void)
++{
++ if (VNSIRecording)
++ return VNSIRecording->Length();
++
++ return 0;
++}
++
++/** UNUSED API FUNCTIONS */
++PVR_ERROR CallMenuHook(const PVR_MENUHOOK &menuhook) { return PVR_ERROR_NOT_IMPLEMENTED; }
++PVR_ERROR DeleteChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
++PVR_ERROR RenameChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
++PVR_ERROR MoveChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
++PVR_ERROR DialogChannelSettings(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
++PVR_ERROR DialogAddChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
++void DemuxReset(void) {}
++void DemuxFlush(void) {}
++int ReadLiveStream(unsigned char *pBuffer, unsigned int iBufferSize) { return 0; }
++long long SeekLiveStream(long long iPosition, int iWhence /* = SEEK_SET */) { return -1; }
++long long PositionLiveStream(void) { return -1; }
++long long LengthLiveStream(void) { return -1; }
++const char * GetLiveStreamURL(const PVR_CHANNEL &channel) { return ""; }
++
++}
+diff --git a/xbmc/pvrclients/vdr-vnsi/client.h b/xbmc/pvrclients/vdr-vnsi/client.h
+new file mode 100644
+index 0000000..d24becc
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/client.h
+@@ -0,0 +1,45 @@
++#pragma once
++/*
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "../../../addons/library.xbmc.addon/libXBMC_addon.h"
++#include "../../../addons/library.xbmc.gui/libXBMC_gui.h"
++#include "../../../addons/library.xbmc.pvr/libXBMC_pvr.h"
++
++#define DEFAULT_HOST "127.0.0.1"
++#define DEFAULT_PORT 34890
++#define DEFAULT_CHARCONV false
++#define DEFAULT_HANDLE_MSG true
++#define DEFAULT_PRIORITY 99
++#define DEFAULT_TIMEOUT 3
++#define DEFAULT_AUTOGROUPS false
++
++extern bool m_bCreated;
++extern std::string g_szHostname; ///< hostname or ip-address of the server
++extern int g_iPort; ///< TCP port of the vnsi server
++extern int g_iConnectTimeout; ///< Network connection / read timeout in seconds
++extern int g_iPriority; ///< The Priority this client have in response to other clients
++extern bool g_bCharsetConv; ///< Convert VDR's incoming strings to UTF8 character set
++extern bool g_bHandleMessages; ///< Send VDR's OSD status messages to XBMC OSD
++
++extern ADDON::CHelper_libXBMC_addon *XBMC;
++extern CHelper_libXBMC_gui *GUI;
++extern CHelper_libXBMC_pvr *PVR;
+diff --git a/xbmc/pvrclients/vdr-vnsi/project/VS2010Express/XBMC_VDR_vnsi.vcxproj b/xbmc/pvrclients/vdr-vnsi/project/VS2010Express/XBMC_VDR_vnsi.vcxproj
+new file mode 100644
+index 0000000..23d657a
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/project/VS2010Express/XBMC_VDR_vnsi.vcxproj
+@@ -0,0 +1,126 @@
++<?xml version="1.0" encoding="utf-8"?>
++<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
++ <ItemGroup Label="ProjectConfigurations">
++ <ProjectConfiguration Include="Debug|Win32">
++ <Configuration>Debug</Configuration>
++ <Platform>Win32</Platform>
++ </ProjectConfiguration>
++ <ProjectConfiguration Include="Release|Win32">
++ <Configuration>Release</Configuration>
++ <Platform>Win32</Platform>
++ </ProjectConfiguration>
++ </ItemGroup>
++ <ItemGroup>
++ <ClCompile Include="..\..\requestpacket.cpp" />
++ <ClCompile Include="..\..\client.cpp" />
++ <ClCompile Include="..\..\responsepacket.cpp" />
++ <ClCompile Include="..\..\..\..\..\addons\library.xbmc.addon\dlfcn-win32.cpp" />
++ <ClCompile Include="..\..\..\..\..\lib\platform\windows\os-threads.cpp" />
++ <ClCompile Include="..\..\tools.cpp" />
++ <ClCompile Include="..\..\VNSIChannelScan.cpp" />
++ <ClCompile Include="..\..\VNSIData.cpp" />
++ <ClCompile Include="..\..\VNSIDemux.cpp" />
++ <ClCompile Include="..\..\VNSIRecording.cpp" />
++ <ClCompile Include="..\..\VNSISession.cpp" />
++ </ItemGroup>
++ <ItemGroup>
++ <ClInclude Include="..\..\requestpacket.h" />
++ <ClInclude Include="..\..\client.h" />
++ <ClInclude Include="..\..\responsepacket.h" />
++ <ClInclude Include="..\..\tools.h" />
++ <ClInclude Include="..\..\VNSIChannelScan.h" />
++ <ClInclude Include="..\..\vnsicommand.h" />
++ <ClInclude Include="..\..\VNSIData.h" />
++ <ClInclude Include="..\..\VNSIDemux.h" />
++ <ClInclude Include="..\..\VNSIRecording.h" />
++ <ClInclude Include="..\..\VNSISession.h" />
++ </ItemGroup>
++ <ItemGroup>
++ <None Include="..\..\README" />
++ </ItemGroup>
++ <PropertyGroup Label="Globals">
++ <ProjectName>pvrclient_vnsi</ProjectName>
++ <ProjectGuid>{3AD3147B-8E7F-4C70-B049-19FA1916BF12}</ProjectGuid>
++ <RootNamespace>XBMC_VDR_vnsi</RootNamespace>
++ <Keyword>Win32Proj</Keyword>
++ </PropertyGroup>
++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
++ <ConfigurationType>DynamicLibrary</ConfigurationType>
++ <UseDebugLibraries>true</UseDebugLibraries>
++ <CharacterSet>MultiByte</CharacterSet>
++ </PropertyGroup>
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
++ <ConfigurationType>DynamicLibrary</ConfigurationType>
++ <UseDebugLibraries>false</UseDebugLibraries>
++ <WholeProgramOptimization>true</WholeProgramOptimization>
++ <CharacterSet>MultiByte</CharacterSet>
++ </PropertyGroup>
++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
++ <ImportGroup Label="ExtensionSettings">
++ </ImportGroup>
++ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
++ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
++ </ImportGroup>
++ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
++ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
++ </ImportGroup>
++ <PropertyGroup Label="UserMacros" />
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
++ <OutDir>..\..\..\..\..\addons\pvr.vdr.vnsi\</OutDir>
++ </PropertyGroup>
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
++ <TargetName>XBMC_VDR_vnsi_win32</TargetName>
++ </PropertyGroup>
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
++ <TargetExt>.pvr</TargetExt>
++ </PropertyGroup>
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
++ <ExtensionsToDeleteOnClean>*.cdf;*.cache;*.obj;*.ilk;*.resources;*.tlb;*.tli;*.tlh;*.tmp;*.rsp;*.pgc;*.pgd;*.meta;*.tlog;*.manifest;*.res;*.pch;*.exp;*.idb;*.rep;*.xdc;*.pdb;*_manifest.rc;*.bsc;*.sbr;*.xml;*.pvr</ExtensionsToDeleteOnClean>
++ <IncludePath>..\..\..\..\..\lib\ffmpeg;..\..\..\..\..\lib\ffmpeg\include-xbmc-win32;..\..\..\..\cores\dvdplayer\DVDDemuxers;..\..\..\..\win32;$(SolutionDir)\..\..\lib;$(IncludePath)</IncludePath>
++ </PropertyGroup>
++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
++ <OutDir>..\..\..\..\..\addons\pvr.vdr.vnsi\</OutDir>
++ <TargetName>XBMC_VDR_vnsi_win32</TargetName>
++ <TargetExt>.pvr</TargetExt>
++ <ExtensionsToDeleteOnClean>*.cdf;*.cache;*.obj;*.ilk;*.resources;*.tlb;*.tli;*.tlh;*.tmp;*.rsp;*.pgc;*.pgd;*.meta;*.tlog;*.manifest;*.res;*.pch;*.exp;*.idb;*.rep;*.xdc;*.pdb;*_manifest.rc;*.bsc;*.sbr;*.xml;*.pvr</ExtensionsToDeleteOnClean>
++ <IncludePath>..\..\..\..\..\lib\ffmpeg;..\..\..\..\..\lib\ffmpeg\include-xbmc-win32;..\..\..\..\cores\dvdplayer\DVDDemuxers;..\..\..\..\win32;$(SolutionDir)\..\..\lib;$(IncludePath)</IncludePath>
++ </PropertyGroup>
++ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
++ <ClCompile>
++ <WarningLevel>Level3</WarningLevel>
++ <Optimization>Disabled</Optimization>
++ <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_USE_32BIT_TIME_T;_WINSOCKAPI_;_WIN32;__WINDOWS__;USE_DEMUX;__STDC_CONSTANT_MACROS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++ <AdditionalIncludeDirectories>..\..\..\..\addons\include;.;..\..\win32;..\..\windows;..\..\;..\..\..\..</AdditionalIncludeDirectories>
++ <DisableSpecificWarnings>4996</DisableSpecificWarnings>
++ </ClCompile>
++ <Link>
++ <GenerateDebugInformation>true</GenerateDebugInformation>
++ <OutputFile>..\..\..\..\..\addons\pvr.vdr.vnsi\XBMC_VDR_vnsi_win32.pvr</OutputFile>
++ <AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
++ <IgnoreSpecificDefaultLibraries>libcmtd</IgnoreSpecificDefaultLibraries>
++ </Link>
++ </ItemDefinitionGroup>
++ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
++ <ClCompile>
++ <WarningLevel>Level3</WarningLevel>
++ <Optimization>MaxSpeed</Optimization>
++ <FunctionLevelLinking>true</FunctionLevelLinking>
++ <IntrinsicFunctions>true</IntrinsicFunctions>
++ <AdditionalIncludeDirectories>..\..\..\..\addons\include;.;..\..\win32;..\..\windows;..\..\;..\..\..\..</AdditionalIncludeDirectories>
++ <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_USE_32BIT_TIME_T;_WINSOCKAPI_;_WIN32;__WINDOWS__;USE_DEMUX;__STDC_CONSTANT_MACROS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++ <DisableSpecificWarnings>4996</DisableSpecificWarnings>
++ </ClCompile>
++ <Link>
++ <GenerateDebugInformation>true</GenerateDebugInformation>
++ <EnableCOMDATFolding>true</EnableCOMDATFolding>
++ <OptimizeReferences>true</OptimizeReferences>
++ <OutputFile>..\..\..\..\..\addons\pvr.vdr.vnsi\XBMC_VDR_vnsi_win32.pvr</OutputFile>
++ <AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
++ <IgnoreSpecificDefaultLibraries>libcmt</IgnoreSpecificDefaultLibraries>
++ </Link>
++ </ItemDefinitionGroup>
++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
++ <ImportGroup Label="ExtensionTargets">
++ </ImportGroup>
++</Project>
+diff --git a/xbmc/pvrclients/vdr-vnsi/project/VS2010Express/XBMC_VDR_vnsi.vcxproj.filters b/xbmc/pvrclients/vdr-vnsi/project/VS2010Express/XBMC_VDR_vnsi.vcxproj.filters
+new file mode 100644
+index 0000000..ff132f8
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/project/VS2010Express/XBMC_VDR_vnsi.vcxproj.filters
+@@ -0,0 +1,79 @@
++<?xml version="1.0" encoding="utf-8"?>
++<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
++ <ItemGroup>
++ <ClCompile Include="..\..\..\..\..\addons\library.xbmc.addon\dlfcn-win32.cpp" />
++ <ClCompile Include="..\..\..\..\..\lib\platform\windows\os-threads.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\client.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\requestpacket.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\responsepacket.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\VNSIChannelScan.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\VNSIData.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\VNSIDemux.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\VNSIRecording.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\VNSISession.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ <ClCompile Include="..\..\tools.cpp">
++ <Filter>Source Files</Filter>
++ </ClCompile>
++ </ItemGroup>
++ <ItemGroup>
++ <ClInclude Include="..\..\client.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\requestpacket.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\responsepacket.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\VNSIChannelScan.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\VNSIData.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\VNSIDemux.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\VNSIRecording.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\VNSISession.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\vnsicommand.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ <ClInclude Include="..\..\tools.h">
++ <Filter>Header Files</Filter>
++ </ClInclude>
++ </ItemGroup>
++ <ItemGroup>
++ <None Include="..\..\README" />
++ </ItemGroup>
++ <ItemGroup>
++ <Filter Include="Header Files">
++ <UniqueIdentifier>{9eb3c821-9dcf-4c43-8432-0090aea9e145}</UniqueIdentifier>
++ </Filter>
++ <Filter Include="Source Files">
++ <UniqueIdentifier>{8f6400e9-2d4f-4132-a6d0-81f39e820f4b}</UniqueIdentifier>
++ </Filter>
++ </ItemGroup>
++</Project>
+diff --git a/xbmc/pvrclients/vdr-vnsi/requestpacket.cpp b/xbmc/pvrclients/vdr-vnsi/requestpacket.cpp
+new file mode 100644
+index 0000000..08828a1
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/requestpacket.cpp
+@@ -0,0 +1,146 @@
++/*
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include <stdio.h>
++
++#include "requestpacket.h"
++#include "vnsicommand.h"
++#include "tools.h"
++#include "../../../lib/platform/sockets/tcp.h"
++
++uint32_t cRequestPacket::serialNumberCounter = 1;
++
++cRequestPacket::cRequestPacket()
++{
++ buffer = NULL;
++ bufSize = 0;
++ bufUsed = 0;
++ lengthSet = false;
++ serialNumber = 0;
++ opcode = 0;
++}
++
++cRequestPacket::~cRequestPacket()
++{
++ free(buffer);
++}
++
++bool cRequestPacket::init(uint32_t topcode, bool stream, bool setUserDataLength, uint32_t userDataLength)
++{
++ if (buffer) return false;
++
++ if (setUserDataLength)
++ {
++ bufSize = headerLength + userDataLength;
++ lengthSet = true;
++ }
++ else
++ {
++ bufSize = 512;
++ userDataLength = 0; // so the below will write a zero
++ }
++
++ buffer = (uint8_t*)malloc(bufSize);
++ if (!buffer) return false;
++
++ if (!stream)
++ channel = VNSI_CHANNEL_REQUEST_RESPONSE;
++ else
++ channel = VNSI_CHANNEL_STREAM;
++ serialNumber = serialNumberCounter++;
++ opcode = topcode;
++
++ *(uint32_t*)&buffer[0] = htonl(channel);
++ *(uint32_t*)&buffer[4] = htonl(serialNumber);
++ *(uint32_t*)&buffer[8] = htonl(opcode);
++ *(uint32_t*)&buffer[userDataLenPos] = htonl(userDataLength);
++ bufUsed = headerLength;
++
++ return true;
++}
++
++bool cRequestPacket::add_String(const char* string)
++{
++ uint32_t len = strlen(string) + 1;
++ if (!checkExtend(len)) return false;
++ memcpy(buffer + bufUsed, string, len);
++ bufUsed += len;
++ if (!lengthSet) *(uint32_t*)&buffer[userDataLenPos] = htonl(bufUsed - headerLength);
++ return true;
++}
++
++bool cRequestPacket::add_U8(uint8_t c)
++{
++ if (!checkExtend(sizeof(uint8_t))) return false;
++ buffer[bufUsed] = c;
++ bufUsed += sizeof(uint8_t);
++ if (!lengthSet) *(uint32_t*)&buffer[userDataLenPos] = htonl(bufUsed - headerLength);
++ return true;
++}
++
++bool cRequestPacket::add_S32(int32_t l)
++{
++ if (!checkExtend(sizeof(int32_t))) return false;
++ *(int32_t*)&buffer[bufUsed] = htonl(l);
++ bufUsed += sizeof(int32_t);
++ if (!lengthSet) *(uint32_t*)&buffer[userDataLenPos] = htonl(bufUsed - headerLength);
++ return true;
++}
++
++bool cRequestPacket::add_U32(uint32_t ul)
++{
++ if (!checkExtend(sizeof(uint32_t))) return false;
++ *(uint32_t*)&buffer[bufUsed] = htonl(ul);
++ bufUsed += sizeof(uint32_t);
++ if (!lengthSet) *(uint32_t*)&buffer[userDataLenPos] = htonl(bufUsed - headerLength);
++ return true;
++}
++
++bool cRequestPacket::add_U64(uint64_t ull)
++{
++ if (!checkExtend(sizeof(uint64_t))) return false;
++ *(uint64_t*)&buffer[bufUsed] = htonll(ull);
++ bufUsed += sizeof(uint64_t);
++ if (!lengthSet) *(uint32_t*)&buffer[userDataLenPos] = htonl(bufUsed - headerLength);
++ return true;
++}
++
++bool cRequestPacket::checkExtend(uint32_t by)
++{
++ if (lengthSet) return true;
++ if ((bufUsed + by) <= bufSize) return true;
++ uint8_t* newBuf = (uint8_t*)realloc(buffer, bufUsed + by);
++ if (!newBuf)
++ {
++ newBuf = (uint8_t*)malloc(bufUsed + by);
++ if (!newBuf) {
++ return false;
++ }
++ memcpy(newBuf, buffer, bufUsed);
++ free(buffer);
++ }
++ buffer = newBuf;
++ bufSize = bufUsed + by;
++ return true;
++}
++
+diff --git a/xbmc/pvrclients/vdr-vnsi/requestpacket.h b/xbmc/pvrclients/vdr-vnsi/requestpacket.h
+new file mode 100644
+index 0000000..e544b20
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/requestpacket.h
+@@ -0,0 +1,62 @@
++#pragma once
++/*
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <stdint.h>
++
++class cRequestPacket
++{
++ public:
++ cRequestPacket();
++ ~cRequestPacket();
++
++ bool init(uint32_t opcode, bool stream = false, bool setUserDataLength = false, uint32_t userDataLength = 0);
++ bool add_String(const char* string);
++ bool add_U8(uint8_t c);
++ bool add_U32(uint32_t ul);
++ bool add_S32(int32_t l);
++ bool add_U64(uint64_t ull);
++
++ uint8_t* getPtr() { return buffer; }
++ uint32_t getLen() { return bufUsed; }
++ uint32_t getChannel() { return channel; }
++ uint32_t getSerial() { return serialNumber; }
++
++ uint32_t getOpcode() { return opcode; }
++
++ private:
++ static uint32_t serialNumberCounter;
++
++ uint8_t* buffer;
++ uint32_t bufSize;
++ uint32_t bufUsed;
++ bool lengthSet;
++
++ uint32_t channel;
++ uint32_t serialNumber;
++
++ uint32_t opcode;
++
++ bool checkExtend(uint32_t by);
++
++ const static uint32_t headerLength = 16;
++ const static uint32_t userDataLenPos = 12;
++};
+diff --git a/xbmc/pvrclients/vdr-vnsi/responsepacket.cpp b/xbmc/pvrclients/vdr-vnsi/responsepacket.cpp
+new file mode 100644
+index 0000000..6d730bc
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/responsepacket.cpp
+@@ -0,0 +1,161 @@
++/*
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <stdlib.h>
++#include <string.h>
++
++#include "responsepacket.h"
++#include "vnsicommand.h"
++#include "tools.h"
++#include "client.h"
++#include "../../../lib/platform/sockets/tcp.h"
++
++
++cResponsePacket::cResponsePacket()
++{
++ userDataLength = 0;
++ packetPos = 0;
++ userData = NULL;
++ ownBlock = true;
++ channelID = 0;
++ requestID = 0;
++ streamID = 0;
++}
++
++cResponsePacket::~cResponsePacket()
++{
++ if (!ownBlock) return; // don't free if it's a getblock
++
++ if (userData)
++ {
++ if (channelID == VNSI_CHANNEL_STREAM && opcodeID == VNSI_STREAM_MUXPKT)
++ PVR->FreeDemuxPacket((DemuxPacket*)userData);
++ else
++ free(userData);
++ }
++}
++
++void cResponsePacket::setResponse(uint32_t trequestID, uint8_t* tuserData, uint32_t tuserDataLength)
++{
++ channelID = VNSI_CHANNEL_REQUEST_RESPONSE;
++ requestID = trequestID;
++ userData = tuserData;
++ userDataLength = tuserDataLength;
++}
++
++void cResponsePacket::setStatus(uint32_t trequestID, uint8_t* tuserData, uint32_t tuserDataLength)
++{
++ channelID = VNSI_CHANNEL_STATUS;
++ requestID = trequestID;
++ userData = tuserData;
++ userDataLength = tuserDataLength;
++}
++
++void cResponsePacket::setStream(uint32_t topcodeID, uint32_t tstreamID, uint32_t tduration, int64_t tdts, int64_t tpts, uint8_t* tuserData, uint32_t tuserDataLength)
++{
++ channelID = VNSI_CHANNEL_STREAM;
++ opcodeID = topcodeID;
++ streamID = tstreamID;
++ duration = tduration;
++ dts = tdts;
++ pts = tpts;
++ userData = tuserData;
++ userDataLength = tuserDataLength;
++}
++
++bool cResponsePacket::end()
++{
++ return (packetPos >= userDataLength);
++}
++
++int cResponsePacket::serverError()
++{
++ if ((packetPos == 0) && (userDataLength == 4) && !ntohl(*(uint32_t*)userData)) return 1;
++ else return 0;
++}
++
++char* cResponsePacket::extract_String()
++{
++ if (serverError()) return NULL;
++
++ int length = strlen((char*)&userData[packetPos]);
++ if ((packetPos + length) > userDataLength) return NULL;
++ char* str = new char[length + 1];
++ strcpy(str, (char*)&userData[packetPos]);
++ packetPos += length + 1;
++ return str;
++}
++
++uint8_t cResponsePacket::extract_U8()
++{
++ if ((packetPos + sizeof(uint8_t)) > userDataLength) return 0;
++ uint8_t uc = userData[packetPos];
++ packetPos += sizeof(uint8_t);
++ return uc;
++}
++
++uint32_t cResponsePacket::extract_U32()
++{
++ if ((packetPos + sizeof(uint32_t)) > userDataLength) return 0;
++ uint32_t ul = ntohl(*(uint32_t*)&userData[packetPos]);
++ packetPos += sizeof(uint32_t);
++ return ul;
++}
++
++uint64_t cResponsePacket::extract_U64()
++{
++ if ((packetPos + sizeof(uint64_t)) > userDataLength) return 0;
++ uint64_t ull = ntohll(*(uint64_t*)&userData[packetPos]);
++ packetPos += sizeof(uint64_t);
++ return ull;
++}
++
++double cResponsePacket::extract_Double()
++{
++ if ((packetPos + sizeof(uint64_t)) > userDataLength) return 0;
++ uint64_t ull = ntohll(*(uint64_t*)&userData[packetPos]);
++ double d;
++ memcpy(&d,&ull,sizeof(double));
++ packetPos += sizeof(uint64_t);
++ return d;
++}
++
++int32_t cResponsePacket::extract_S32()
++{
++ if ((packetPos + sizeof(int32_t)) > userDataLength) return 0;
++ int32_t l = ntohl(*(int32_t*)&userData[packetPos]);
++ packetPos += sizeof(int32_t);
++ return l;
++}
++
++int64_t cResponsePacket::extract_S64()
++{
++ if ((packetPos + sizeof(int64_t)) > userDataLength) return 0;
++ int64_t ll = ntohll(*(int64_t*)&userData[packetPos]);
++ packetPos += sizeof(int64_t);
++ return ll;
++}
++
++uint8_t* cResponsePacket::getUserData()
++{
++ ownBlock = false;
++ return userData;
++}
+diff --git a/xbmc/pvrclients/vdr-vnsi/responsepacket.h b/xbmc/pvrclients/vdr-vnsi/responsepacket.h
+new file mode 100644
+index 0000000..109965e
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/responsepacket.h
+@@ -0,0 +1,79 @@
++#pragma once
++/*
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <stdio.h>
++#include <stdint.h>
++
++class cResponsePacket
++{
++ public:
++ cResponsePacket();
++ ~cResponsePacket();
++
++ void setResponse(uint32_t requestID, uint8_t* packet, uint32_t packetLength);
++ void setStatus(uint32_t requestID, uint8_t* packet, uint32_t packetLength);
++
++ void setStream(uint32_t opcodeID, uint32_t streamID, uint32_t duration, int64_t dts, int64_t pts, uint8_t* packet, uint32_t packetLength);
++
++ bool noResponse() { return (userData == NULL); };
++ int serverError();
++
++ uint32_t getUserDataLength() { return userDataLength; }
++ uint32_t getChannelID() { return channelID; }
++ uint32_t getRequestID() { return requestID; }
++ uint32_t getStreamID() { return streamID; }
++ uint32_t getOpCodeID() { return opcodeID; }
++ uint32_t getDuration() { return duration; }
++ int64_t getDTS() { return dts; }
++ int64_t getPTS() { return pts; }
++
++ uint32_t getPacketPos() { return packetPos; }
++
++ char* extract_String();
++ uint8_t extract_U8();
++ uint32_t extract_U32();
++ uint64_t extract_U64();
++ int32_t extract_S32();
++ int64_t extract_S64();
++ double extract_Double();
++
++ bool end();
++
++ // If you call this, the memory becomes yours. Free with free()
++ uint8_t* getUserData();
++
++ private:
++ uint8_t* userData;
++ uint32_t userDataLength;
++ uint32_t packetPos;
++
++ uint32_t channelID;
++
++ uint32_t requestID;
++ uint32_t streamID;
++ uint32_t opcodeID;
++ uint32_t duration;
++ int64_t dts;
++ int64_t pts;
++
++ bool ownBlock;
++};
+diff --git a/xbmc/pvrclients/vdr-vnsi/tools.cpp b/xbmc/pvrclients/vdr-vnsi/tools.cpp
+new file mode 100644
+index 0000000..620ace4
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/tools.cpp
+@@ -0,0 +1,52 @@
++/*
++ * Copyright (C) 2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "tools.h"
++
++#define TYP_INIT 0
++#define TYP_SMLE 1
++#define TYP_BIGE 2
++
++uint64_t ntohll(uint64_t a)
++{
++ return htonll(a);
++}
++
++uint64_t htonll(uint64_t a) {
++ static int typ = TYP_INIT;
++ unsigned char c;
++ union {
++ uint64_t ull;
++ unsigned char c[8];
++ } x;
++ if (typ == TYP_INIT) {
++ x.ull = 0x01;
++ typ = (x.c[7] == 0x01ULL) ? TYP_BIGE : TYP_SMLE;
++ }
++ if (typ == TYP_BIGE)
++ return a;
++ x.ull = a;
++ c = x.c[0]; x.c[0] = x.c[7]; x.c[7] = c;
++ c = x.c[1]; x.c[1] = x.c[6]; x.c[6] = c;
++ c = x.c[2]; x.c[2] = x.c[5]; x.c[5] = c;
++ c = x.c[3]; x.c[3] = x.c[4]; x.c[4] = c;
++ return x.ull;
++}
+diff --git a/xbmc/pvrclients/vdr-vnsi/tools.h b/xbmc/pvrclients/vdr-vnsi/tools.h
+new file mode 100644
+index 0000000..ccffad2
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/tools.h
+@@ -0,0 +1,26 @@
++#pragma once
++/*
++ * Copyright (C) 2010 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <inttypes.h>
++
++uint64_t ntohll(uint64_t a);
++uint64_t htonll(uint64_t a);
+\ No newline at end of file
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/COPYING b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/COPYING
+new file mode 100644
+index 0000000..f90922e
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/COPYING
+@@ -0,0 +1,340 @@
++ GNU GENERAL PUBLIC LICENSE
++ Version 2, June 1991
++
++ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ Everyone is permitted to copy and distribute verbatim copies
++ of this license document, but changing it is not allowed.
++
++ Preamble
++
++ The licenses for most software are designed to take away your
++freedom to share and change it. By contrast, the GNU General Public
++License is intended to guarantee your freedom to share and change free
++software--to make sure the software is free for all its users. This
++General Public License applies to most of the Free Software
++Foundation's software and to any other program whose authors commit to
++using it. (Some other Free Software Foundation software is covered by
++the GNU Lesser General Public License instead.) You can apply it to
++your programs, too.
++
++ When we speak of free software, we are referring to freedom, not
++price. Our General Public Licenses are designed to make sure that you
++have the freedom to distribute copies of free software (and charge for
++this service if you wish), that you receive source code or can get it
++if you want it, that you can change the software or use pieces of it
++in new free programs; and that you know you can do these things.
++
++ To protect your rights, we need to make restrictions that forbid
++anyone to deny you these rights or to ask you to surrender the rights.
++These restrictions translate to certain responsibilities for you if you
++distribute copies of the software, or if you modify it.
++
++ For example, if you distribute copies of such a program, whether
++gratis or for a fee, you must give the recipients all the rights that
++you have. You must make sure that they, too, receive or can get the
++source code. And you must show them these terms so they know their
++rights.
++
++ We protect your rights with two steps: (1) copyright the software, and
++(2) offer you this license which gives you legal permission to copy,
++distribute and/or modify the software.
++
++ Also, for each author's protection and ours, we want to make certain
++that everyone understands that there is no warranty for this free
++software. If the software is modified by someone else and passed on, we
++want its recipients to know that what they have is not the original, so
++that any problems introduced by others will not reflect on the original
++authors' reputations.
++
++ Finally, any free program is threatened constantly by software
++patents. We wish to avoid the danger that redistributors of a free
++program will individually obtain patent licenses, in effect making the
++program proprietary. To prevent this, we have made it clear that any
++patent must be licensed for everyone's free use or not licensed at all.
++
++ The precise terms and conditions for copying, distribution and
++modification follow.
++
++ GNU GENERAL PUBLIC LICENSE
++ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
++
++ 0. This License applies to any program or other work which contains
++a notice placed by the copyright holder saying it may be distributed
++under the terms of this General Public License. The "Program", below,
++refers to any such program or work, and a "work based on the Program"
++means either the Program or any derivative work under copyright law:
++that is to say, a work containing the Program or a portion of it,
++either verbatim or with modifications and/or translated into another
++language. (Hereinafter, translation is included without limitation in
++the term "modification".) Each licensee is addressed as "you".
++
++Activities other than copying, distribution and modification are not
++covered by this License; they are outside its scope. The act of
++running the Program is not restricted, and the output from the Program
++is covered only if its contents constitute a work based on the
++Program (independent of having been made by running the Program).
++Whether that is true depends on what the Program does.
++
++ 1. You may copy and distribute verbatim copies of the Program's
++source code as you receive it, in any medium, provided that you
++conspicuously and appropriately publish on each copy an appropriate
++copyright notice and disclaimer of warranty; keep intact all the
++notices that refer to this License and to the absence of any warranty;
++and give any other recipients of the Program a copy of this License
++along with the Program.
++
++You may charge a fee for the physical act of transferring a copy, and
++you may at your option offer warranty protection in exchange for a fee.
++
++ 2. You may modify your copy or copies of the Program or any portion
++of it, thus forming a work based on the Program, and copy and
++distribute such modifications or work under the terms of Section 1
++above, provided that you also meet all of these conditions:
++
++ a) You must cause the modified files to carry prominent notices
++ stating that you changed the files and the date of any change.
++
++ b) You must cause any work that you distribute or publish, that in
++ whole or in part contains or is derived from the Program or any
++ part thereof, to be licensed as a whole at no charge to all third
++ parties under the terms of this License.
++
++ c) If the modified program normally reads commands interactively
++ when run, you must cause it, when started running for such
++ interactive use in the most ordinary way, to print or display an
++ announcement including an appropriate copyright notice and a
++ notice that there is no warranty (or else, saying that you provide
++ a warranty) and that users may redistribute the program under
++ these conditions, and telling the user how to view a copy of this
++ License. (Exception: if the Program itself is interactive but
++ does not normally print such an announcement, your work based on
++ the Program is not required to print an announcement.)
++
++These requirements apply to the modified work as a whole. If
++identifiable sections of that work are not derived from the Program,
++and can be reasonably considered independent and separate works in
++themselves, then this License, and its terms, do not apply to those
++sections when you distribute them as separate works. But when you
++distribute the same sections as part of a whole which is a work based
++on the Program, the distribution of the whole must be on the terms of
++this License, whose permissions for other licensees extend to the
++entire whole, and thus to each and every part regardless of who wrote it.
++
++Thus, it is not the intent of this section to claim rights or contest
++your rights to work written entirely by you; rather, the intent is to
++exercise the right to control the distribution of derivative or
++collective works based on the Program.
++
++In addition, mere aggregation of another work not based on the Program
++with the Program (or with a work based on the Program) on a volume of
++a storage or distribution medium does not bring the other work under
++the scope of this License.
++
++ 3. You may copy and distribute the Program (or a work based on it,
++under Section 2) in object code or executable form under the terms of
++Sections 1 and 2 above provided that you also do one of the following:
++
++ a) Accompany it with the complete corresponding machine-readable
++ source code, which must be distributed under the terms of Sections
++ 1 and 2 above on a medium customarily used for software interchange; or,
++
++ b) Accompany it with a written offer, valid for at least three
++ years, to give any third party, for a charge no more than your
++ cost of physically performing source distribution, a complete
++ machine-readable copy of the corresponding source code, to be
++ distributed under the terms of Sections 1 and 2 above on a medium
++ customarily used for software interchange; or,
++
++ c) Accompany it with the information you received as to the offer
++ to distribute corresponding source code. (This alternative is
++ allowed only for noncommercial distribution and only if you
++ received the program in object code or executable form with such
++ an offer, in accord with Subsection b above.)
++
++The source code for a work means the preferred form of the work for
++making modifications to it. For an executable work, complete source
++code means all the source code for all modules it contains, plus any
++associated interface definition files, plus the scripts used to
++control compilation and installation of the executable. However, as a
++special exception, the source code distributed need not include
++anything that is normally distributed (in either source or binary
++form) with the major components (compiler, kernel, and so on) of the
++operating system on which the executable runs, unless that component
++itself accompanies the executable.
++
++If distribution of executable or object code is made by offering
++access to copy from a designated place, then offering equivalent
++access to copy the source code from the same place counts as
++distribution of the source code, even though third parties are not
++compelled to copy the source along with the object code.
++
++ 4. You may not copy, modify, sublicense, or distribute the Program
++except as expressly provided under this License. Any attempt
++otherwise to copy, modify, sublicense or distribute the Program is
++void, and will automatically terminate your rights under this License.
++However, parties who have received copies, or rights, from you under
++this License will not have their licenses terminated so long as such
++parties remain in full compliance.
++
++ 5. You are not required to accept this License, since you have not
++signed it. However, nothing else grants you permission to modify or
++distribute the Program or its derivative works. These actions are
++prohibited by law if you do not accept this License. Therefore, by
++modifying or distributing the Program (or any work based on the
++Program), you indicate your acceptance of this License to do so, and
++all its terms and conditions for copying, distributing or modifying
++the Program or works based on it.
++
++ 6. Each time you redistribute the Program (or any work based on the
++Program), the recipient automatically receives a license from the
++original licensor to copy, distribute or modify the Program subject to
++these terms and conditions. You may not impose any further
++restrictions on the recipients' exercise of the rights granted herein.
++You are not responsible for enforcing compliance by third parties to
++this License.
++
++ 7. If, as a consequence of a court judgment or allegation of patent
++infringement or for any other reason (not limited to patent issues),
++conditions are imposed on you (whether by court order, agreement or
++otherwise) that contradict the conditions of this License, they do not
++excuse you from the conditions of this License. If you cannot
++distribute so as to satisfy simultaneously your obligations under this
++License and any other pertinent obligations, then as a consequence you
++may not distribute the Program at all. For example, if a patent
++license would not permit royalty-free redistribution of the Program by
++all those who receive copies directly or indirectly through you, then
++the only way you could satisfy both it and this License would be to
++refrain entirely from distribution of the Program.
++
++If any portion of this section is held invalid or unenforceable under
++any particular circumstance, the balance of the section is intended to
++apply and the section as a whole is intended to apply in other
++circumstances.
++
++It is not the purpose of this section to induce you to infringe any
++patents or other property right claims or to contest validity of any
++such claims; this section has the sole purpose of protecting the
++integrity of the free software distribution system, which is
++implemented by public license practices. Many people have made
++generous contributions to the wide range of software distributed
++through that system in reliance on consistent application of that
++system; it is up to the author/donor to decide if he or she is willing
++to distribute software through any other system and a licensee cannot
++impose that choice.
++
++This section is intended to make thoroughly clear what is believed to
++be a consequence of the rest of this License.
++
++ 8. If the distribution and/or use of the Program is restricted in
++certain countries either by patents or by copyrighted interfaces, the
++original copyright holder who places the Program under this License
++may add an explicit geographical distribution limitation excluding
++those countries, so that distribution is permitted only in or among
++countries not thus excluded. In such case, this License incorporates
++the limitation as if written in the body of this License.
++
++ 9. The Free Software Foundation may publish revised and/or new versions
++of the General Public License from time to time. Such new versions will
++be similar in spirit to the present version, but may differ in detail to
++address new problems or concerns.
++
++Each version is given a distinguishing version number. If the Program
++specifies a version number of this License which applies to it and "any
++later version", you have the option of following the terms and conditions
++either of that version or of any later version published by the Free
++Software Foundation. If the Program does not specify a version number of
++this License, you may choose any version ever published by the Free Software
++Foundation.
++
++ 10. If you wish to incorporate parts of the Program into other free
++programs whose distribution conditions are different, write to the author
++to ask for permission. For software which is copyrighted by the Free
++Software Foundation, write to the Free Software Foundation; we sometimes
++make exceptions for this. Our decision will be guided by the two goals
++of preserving the free status of all derivatives of our free software and
++of promoting the sharing and reuse of software generally.
++
++ NO WARRANTY
++
++ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
++FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
++OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
++PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
++OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
++MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
++TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
++PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
++REPAIR OR CORRECTION.
++
++ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
++WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
++REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
++INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
++OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
++TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
++YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
++PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
++POSSIBILITY OF SUCH DAMAGES.
++
++ END OF TERMS AND CONDITIONS
++
++ How to Apply These Terms to Your New Programs
++
++ If you develop a new program, and you want it to be of the greatest
++possible use to the public, the best way to achieve this is to make it
++free software which everyone can redistribute and change under these terms.
++
++ To do so, attach the following notices to the program. It is safest
++to attach them to the start of each source file to most effectively
++convey the exclusion of warranty; and each file should have at least
++the "copyright" line and a pointer to where the full notice is found.
++
++ <one line to give the program's name and a brief idea of what it does.>
++ Copyright (C) <year> <name of author>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 2 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++
++
++Also add information on how to contact you by electronic and paper mail.
++
++If the program is interactive, make it output a short notice like this
++when it starts in an interactive mode:
++
++ Gnomovision version 69, Copyright (C) year name of author
++ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
++ This is free software, and you are welcome to redistribute it
++ under certain conditions; type `show c' for details.
++
++The hypothetical commands `show w' and `show c' should show the appropriate
++parts of the General Public License. Of course, the commands you use may
++be called something other than `show w' and `show c'; they could even be
++mouse-clicks or menu items--whatever suits your program.
++
++You should also get your employer (if you work as a programmer) or your
++school, if any, to sign a "copyright disclaimer" for the program, if
++necessary. Here is a sample; alter the names:
++
++ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
++ `Gnomovision' (which makes passes at compilers) written by James Hacker.
++
++ <signature of Ty Coon>, 1 April 1989
++ Ty Coon, President of Vice
++
++This General Public License does not permit incorporating your program into
++proprietary programs. If your program is a subroutine library, you may
++consider it more useful to permit linking proprietary applications with the
++library. If this is what you want to do, use the GNU Lesser General
++Public License instead of this License.
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/HISTORY b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/HISTORY
+new file mode 100644
+index 0000000..756fa44
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/HISTORY
+@@ -0,0 +1,6 @@
++VDR Plugin 'vnsiserver' Revision History
++----------------------------------------
++
++2010-03-23: Version 0.0.1
++
++- Initial revision.
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/Makefile b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/Makefile
+new file mode 100644
+index 0000000..a29ef04
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/Makefile
+@@ -0,0 +1,98 @@
++#
++# Makefile for a Video Disk Recorder plugin
++#
++# $Id$
++
++# The official name of this plugin.
++# This name will be used in the '-P...' option of VDR to load the plugin.
++# By default the main source file also carries this name.
++#
++PLUGIN = vnsiserver
++
++### The version number of this plugin (taken from the main source file):
++
++VERSION = $(shell grep 'static const char \*VERSION *=' vnsi.h | awk '{ print $$6 }' | sed -e 's/[";]//g')
++
++### The C++ compiler and options:
++
++OPTLEVEL ?= 2
++CXXFLAGS = -O$(OPTLEVEL) -g -Wall -Woverloaded-virtual -fPIC -DPIC
++
++### The directory environment:
++
++DVBDIR = ../../../../DVB
++VDRDIR = ../../..
++LIBDIR = ../../lib
++TMPDIR = /tmp
++
++### Allow user defined options to overwrite defaults:
++
++-include $(VDRDIR)/Make.config
++-include $(VDRDIR)/Make.global
++
++### The version number of VDR (taken from VDR's "config.h"):
++
++APIVERSION = $(shell grep 'define APIVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g')
++
++### The name of the distribution archive:
++
++ARCHIVE = $(PLUGIN)-$(VERSION)
++PACKAGE = vdr-$(ARCHIVE)
++
++### Includes and Defines (add further entries here):
++
++INCLUDES += -I$(VDRDIR)/include -I$(DVBDIR)/include -I$(VDRDIR)
++
++DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -DVNSI_SERVER_VERSION='"$(VERSION)"'
++ifeq ($(DEBUG),1)
++ DEFINES += -DDEBUG
++endif
++ifeq ($(CONSOLEDEBUG),1)
++ DEFINES += -DCONSOLEDEBUG
++endif
++
++### The object files (add further files here):
++
++OBJS = vnsi.o bitstream.o vnsiclient.o config.o cxsocket.o demuxer.o demuxer_AAC.o \
++ demuxer_AC3.o demuxer_DTS.o demuxer_h264.o demuxer_MPEGAudio.o demuxer_MPEGVideo.o \
++ demuxer_Subtitle.o demuxer_Teletext.o receiver.o recplayer.o requestpacket.o responsepacket.o \
++ vnsiserver.o hash.o recordingscache.o
++
++### Implicit rules:
++
++all-redirect: all
++
++%.o: %.c
++ $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $<
++
++# Dependencies:
++
++MAKEDEP = g++ -MM -MG
++DEPFILE = .dependencies
++$(DEPFILE): Makefile
++ @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
++
++-include $(DEPFILE)
++
++### Targets:
++
++all: libvdr-$(PLUGIN).so
++
++libvdr-$(PLUGIN).so: $(OBJS)
++ $(CXX) $(CXXFLAGS) -shared $(OBJS) -o $@
++ @cp $@ $(LIBDIR)/$@.$(APIVERSION)
++
++dist: clean
++ @-rm -rf $(TMPDIR)/$(ARCHIVE)
++ @mkdir $(TMPDIR)/$(ARCHIVE)
++ @cp -a * $(TMPDIR)/$(ARCHIVE)
++ @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE)
++ @-rm -rf $(TMPDIR)/$(ARCHIVE)
++ @echo Distribution package created as $(PACKAGE).tgz
++
++clean:
++ @-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core* *~
++
++install:
++ @install -d ../../man
++ @install README ../../man/$(PLUGIN).man
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/README b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/README
+new file mode 100644
+index 0000000..e9db1b7
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/README
+@@ -0,0 +1,26 @@
++This is a "plugin" for the Video Disk Recorder (VDR).
++
++Written by: Alexander Pipelka, Alwin Esch
++
++Project's homepage: https://github.com/pipelka/vdr-plugin-vnsiserver
++
++Latest version available at: https://github.com/pipelka/vdr-plugin-vnsiserver
++
++This program is free software; you can redistribute 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.
++See the file COPYING for more information.
++
++
++Description:
++------------
++
++VDR plugin to handle XBMC clients.
++The vdr-plugin-vnsiserver is able to handle serveral XBMC clients connecting via the VNSI addon.
++
++VNSI supports also dynamic PID switching of the received DVB-TS stream. Further it detect and demuxing several
++not by VDR implemented Audio Streams, this are:
++- Enhanced AC3 (not tested)
++- Advanced Audio Coding (AAC) (not tested)
++- DTS (demuxer not finished now, and does not work)
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/bitstream.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/bitstream.c
+new file mode 100644
+index 0000000..53effea
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/bitstream.c
+@@ -0,0 +1,131 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <stdio.h>
++#include <inttypes.h>
++#include "bitstream.h"
++
++cBitstream::cBitstream(uint8_t *data, int bits)
++{
++ m_data = data;
++ m_offset = 0;
++ m_len = bits;
++}
++
++void cBitstream::setBitstream(uint8_t *data, int bits)
++{
++ m_data = data;
++ m_offset = 0;
++ m_len = bits;
++}
++
++void cBitstream::skipBits(int num)
++{
++ m_offset += num;
++}
++
++unsigned int cBitstream::readBits(int num)
++{
++ int r = 0;
++
++ while(num > 0)
++ {
++ if(m_offset >= m_len)
++ return 0;
++
++ num--;
++
++ if(m_data[m_offset / 8] & (1 << (7 - (m_offset & 7))))
++ r |= 1 << num;
++
++ m_offset++;
++ }
++ return r;
++}
++
++unsigned int cBitstream::showBits(int num)
++{
++ int r = 0;
++ int offs = m_offset;
++
++ while(num > 0)
++ {
++ if(offs >= m_len)
++ return 0;
++
++ num--;
++
++ if(m_data[offs / 8] & (1 << (7 - (offs & 7))))
++ r |= 1 << num;
++
++ offs++;
++ }
++ return r;
++}
++
++unsigned int cBitstream::readGolombUE()
++{
++ int lzb = -1;
++
++ for(int b = 0; !b; lzb++)
++ b = readBits1();
++
++ return (1 << lzb) - 1 + readBits(lzb);
++}
++
++signed int cBitstream::readGolombSE()
++{
++ int v, neg;
++ v = readGolombUE();
++ if(v == 0)
++ return 0;
++
++ neg = v & 1;
++ v = (v + 1) >> 1;
++ return neg ? -v : v;
++}
++
++
++unsigned int cBitstream::remainingBits()
++{
++ return m_len - m_offset;
++}
++
++
++void cBitstream::putBits(int val, int num)
++{
++ while(num > 0) {
++ if(m_offset >= m_len)
++ return;
++
++ num--;
++
++ if(val & (1 << num))
++ m_data[m_offset / 8] |= 1 << (7 - (m_offset & 7));
++ else
++ m_data[m_offset / 8] &= ~(1 << (7 - (m_offset & 7)));
++
++ m_offset++;
++ }
++}
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/bitstream.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/bitstream.h
+new file mode 100644
+index 0000000..728ce52
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/bitstream.h
+@@ -0,0 +1,50 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#ifndef VNSI_BITSTREAM_H
++#define VNSI_BITSTREAM_H
++
++class cBitstream
++{
++private:
++ uint8_t *m_data;
++ int m_offset;
++ int m_len;
++
++public:
++ cBitstream(uint8_t *data, int bits);
++
++ void setBitstream(uint8_t *data, int bits);
++ void skipBits(int num);
++ unsigned int readBits(int num);
++ unsigned int showBits(int num);
++ unsigned int readBits1() { return readBits(1); }
++ unsigned int readGolombUE();
++ signed int readGolombSE();
++ unsigned int remainingBits();
++ void putBits(int val, int num);
++ int length() { return m_len; }
++};
++
++#endif // VNSI_BITSTREAM_H
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/config.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/config.c
+new file mode 100644
+index 0000000..5ba1ba8
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/config.c
+@@ -0,0 +1,42 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (Team XBMC)
++ * Copyright (C) 2010, 2011 Alexander Pipelka
++ *
++ * 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <stdio.h>
++#include <string.h>
++#include <stdlib.h>
++
++#include <vdr/plugin.h>
++
++#include "config.h"
++
++cVNSIServerConfig::cVNSIServerConfig()
++{
++ listen_port = LISTEN_PORT;
++ ConfigDirectory = NULL;
++ stream_timeout = 10;
++}
++
++/* Global instance */
++cVNSIServerConfig VNSIServerConfig;
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/config.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/config.h
+new file mode 100644
+index 0000000..d99deec
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/config.h
+@@ -0,0 +1,81 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (Team XBMC)
++ * Copyright (C) 2010, 2011 Alexander Pipelka
++ *
++ * 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#ifndef VNSI_CONFIG_H
++#define VNSI_CONFIG_H
++
++#include <string.h>
++#include <stdint.h>
++
++#include <vdr/config.h>
++
++// log output configuration
++
++#ifdef CONSOLEDEBUG
++#define DEBUGLOG(x...) printf("VNSI: "x)
++#elif defined DEBUG
++#define DEBUGLOG(x...) dsyslog("VNSI: "x)
++#else
++#define DEBUGLOG(x...)
++#endif
++
++#ifdef CONSOLEDEBUG
++#define INFOLOG(x...) printf("VNSI: "x)
++#define ERRORLOG(x...) printf("VNSI-Error: "x)
++#else
++#define INFOLOG(x...) isyslog("VNSI: "x)
++#define ERRORLOG(x...) esyslog("VNSI-Error: "x)
++#endif
++
++// default settings
++
++#define ALLOWED_HOSTS_FILE "allowed_hosts.conf"
++#define FRONTEND_DEVICE "/dev/dvb/adapter%d/frontend%d"
++
++#define LISTEN_PORT 34890
++#define LISTEN_PORT_S "34890"
++#define DISCOVERY_PORT 34890
++
++// backward compatibility
++
++#if APIVERSNUM < 10701
++#define FOLDERDELIMCHAR '~'
++#endif
++
++class cVNSIServerConfig
++{
++public:
++ cVNSIServerConfig();
++
++ // Remote server settings
++ cString ConfigDirectory; // config directory path
++ uint16_t listen_port; // Port of remote server
++ uint16_t stream_timeout; // timeout in seconds for stream data
++};
++
++// Global instance
++extern cVNSIServerConfig VNSIServerConfig;
++
++#endif // VNSI_CONFIG_H
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/cxsocket.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/cxsocket.c
+new file mode 100644
+index 0000000..dcb75c3
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/cxsocket.c
+@@ -0,0 +1,174 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2003-2006 Petri Hintukainen
++ * Copyright (C) 2010 Alwin Esch (Team XBMC)
++ * Copyright (C) 2011 Alexander Pipelka
++ *
++ * 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++/*
++ * Socket wrapper classes
++ *
++ * Code is taken from xineliboutput plugin.
++ *
++ */
++
++#define __STDC_FORMAT_MACROS
++#include <inttypes.h>
++
++#include <stdlib.h>
++#include <stdarg.h>
++#include <errno.h>
++#include <sys/types.h>
++#include <sys/socket.h>
++#include <sys/ioctl.h>
++#include <netinet/tcp.h>
++
++#include <vdr/config.h>
++#include <vdr/tools.h>
++
++#include "config.h"
++#include "cxsocket.h"
++
++cxSocket::~cxSocket()
++{
++ close();
++ delete m_pollerRead;
++ delete m_pollerWrite;
++}
++
++void cxSocket::close() {
++ if(m_fd >= 0) {
++ ::close(m_fd);
++ m_fd=-1;
++ }
++}
++
++ssize_t cxSocket::write(const void *buffer, size_t size, int timeout_ms, bool more_data)
++{
++ cMutexLock CmdLock((cMutex*)&m_MutexWrite);
++
++ if(m_fd == -1) return -1;
++
++ ssize_t written = (ssize_t)size;
++ const unsigned char *ptr = (const unsigned char *)buffer;
++
++ while (size > 0)
++ {
++ if(!m_pollerWrite->Poll(timeout_ms))
++ {
++ ERRORLOG("cxSocket::write: poll() failed");
++ return written-size;
++ }
++
++ ssize_t p = ::send(m_fd, ptr, size, MSG_NOSIGNAL | (more_data ? MSG_MORE : 0));
++
++ if (p <= 0)
++ {
++ if (errno == EINTR || errno == EAGAIN)
++ {
++ DEBUGLOG("cxSocket::write: EINTR during write(), retrying");
++ continue;
++ }
++ ERRORLOG("cxSocket::write: write() error");
++ return p;
++ }
++
++ ptr += p;
++ size -= p;
++ }
++
++ return written;
++}
++
++ssize_t cxSocket::read(void *buffer, size_t size, int timeout_ms)
++{
++ cMutexLock CmdLock((cMutex*)&m_MutexRead);
++
++ int retryCounter = 0;
++
++ if(m_fd == -1) return -1;
++
++ ssize_t missing = (ssize_t)size;
++ unsigned char *ptr = (unsigned char *)buffer;
++
++ while (missing > 0)
++ {
++ if(!m_pollerRead->Poll(timeout_ms))
++ {
++ ERRORLOG("cxSocket::read: poll() failed at %d/%d", (int)(size-missing), (int)size);
++ return size-missing;
++ }
++
++ ssize_t p = ::read(m_fd, ptr, missing);
++
++ if (p <= 0)
++ {
++ if (retryCounter < 10 && (errno == EINTR || errno == EAGAIN))
++ {
++ DEBUGLOG("cxSocket::read: EINTR/EAGAIN during read(), retrying");
++ retryCounter++;
++ continue;
++ }
++ ERRORLOG("cxSocket::read: read() error at %d/%d", (int)(size-missing), (int)size);
++ return size-missing;
++ }
++
++ retryCounter = 0;
++ ptr += p;
++ missing -= p;
++ }
++
++ return size;
++}
++
++void cxSocket::set_handle(int h) {
++ if(h != m_fd) {
++ close();
++ m_fd = h;
++ delete m_pollerRead;
++ delete m_pollerWrite;
++ m_pollerRead = new cPoller(m_fd);
++ m_pollerWrite = new cPoller(m_fd, true);
++ }
++}
++
++#include <sys/ioctl.h>
++#include <net/if.h>
++
++char *cxSocket::ip2txt(uint32_t ip, unsigned int port, char *str)
++{
++ // inet_ntoa is not thread-safe (?)
++ if(str) {
++ unsigned int iph =(unsigned int)ntohl(ip);
++ unsigned int porth =(unsigned int)ntohs(port);
++ if(!porth)
++ sprintf(str, "%d.%d.%d.%d",
++ ((iph>>24)&0xff), ((iph>>16)&0xff),
++ ((iph>>8)&0xff), ((iph)&0xff));
++ else
++ sprintf(str, "%u.%u.%u.%u:%u",
++ ((iph>>24)&0xff), ((iph>>16)&0xff),
++ ((iph>>8)&0xff), ((iph)&0xff),
++ porth);
++ }
++ return str;
++}
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/cxsocket.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/cxsocket.h
+new file mode 100644
+index 0000000..369054c
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/cxsocket.h
+@@ -0,0 +1,65 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2003-2006 Petri Hintukainen
++ * Copyright (C) 2010 Alwin Esch (Team XBMC)
++ * Copyright (C) 2011 Alexander Pipelka
++ *
++ * 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#ifndef VNSI_CXSOCKET_H
++#define VNSI_CXSOCKET_H
++
++#include <inttypes.h>
++#include <sys/types.h>
++#include <sys/socket.h>
++#include <vdr/thread.h>
++#include <vdr/tools.h>
++
++class cxSocket {
++ private:
++ int m_fd;
++ cMutex m_MutexWrite;
++ cMutex m_MutexRead;
++
++ cPoller *m_pollerRead;
++ cPoller *m_pollerWrite;
++
++ cxSocket(const cxSocket& s);
++ cxSocket &operator=(const cxSocket &S);
++
++ public:
++
++ cxSocket() : m_fd(-1), m_pollerRead(NULL), m_pollerWrite(NULL) {}
++
++ ~cxSocket();
++
++ void set_handle(int h);
++
++ void close(void);
++
++ ssize_t read(void *buffer, size_t size, int timeout_ms = -1);
++
++ ssize_t write(const void *buffer, size_t size, int timeout_ms = -1, bool more_data = false);
++
++ static char *ip2txt(uint32_t ip, unsigned int port, char *str);
++};
++
++#endif // VNSI_CXSOCKET_H
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer.c
+new file mode 100644
+index 0000000..e84c044
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer.c
+@@ -0,0 +1,362 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <stdlib.h>
++#include <assert.h>
++#include <vdr/remux.h>
++#include <vdr/channels.h>
++#include "config.h"
++#include "receiver.h"
++#include "demuxer.h"
++#include "demuxer_AAC.h"
++#include "demuxer_AC3.h"
++#include "demuxer_DTS.h"
++#include "demuxer_h264.h"
++#include "demuxer_MPEGAudio.h"
++#include "demuxer_MPEGVideo.h"
++#include "demuxer_Subtitle.h"
++#include "demuxer_Teletext.h"
++
++#define PTS_MASK 0x1ffffffffLL
++//#define PTS_MASK 0x7ffffLL
++
++#ifndef INT64_MIN
++#define INT64_MIN (-0x7fffffffffffffffLL-1)
++#endif
++
++int64_t PesGetPTS(const uint8_t *buf, int len)
++{
++ /* assume mpeg2 pes header ... */
++ if (PesIsVideoPacket(buf) || PesIsAudioPacket(buf)) {
++
++ if ((buf[6] & 0xC0) != 0x80)
++ return DVD_NOPTS_VALUE;
++ if ((buf[6] & 0x30) != 0)
++ return DVD_NOPTS_VALUE;
++
++ if ((len > 13) && (buf[7] & 0x80)) { /* pts avail */
++ int64_t pts;
++ pts = ((int64_t)(buf[ 9] & 0x0E)) << 29 ;
++ pts |= ((int64_t) buf[10]) << 22 ;
++ pts |= ((int64_t)(buf[11] & 0xFE)) << 14 ;
++ pts |= ((int64_t) buf[12]) << 7 ;
++ pts |= ((int64_t)(buf[13] & 0xFE)) >> 1 ;
++ return pts;
++ }
++ }
++ return DVD_NOPTS_VALUE;
++}
++
++int64_t PesGetDTS(const uint8_t *buf, int len)
++{
++ if (PesIsVideoPacket(buf) || PesIsAudioPacket(buf))
++ {
++ if ((buf[6] & 0xC0) != 0x80)
++ return DVD_NOPTS_VALUE;
++ if ((buf[6] & 0x30) != 0)
++ return DVD_NOPTS_VALUE;
++
++ if (len > 18 && (buf[7] & 0x40)) { /* dts avail */
++ int64_t dts;
++ dts = ((int64_t)( buf[14] & 0x0E)) << 29 ;
++ dts |= (int64_t)( buf[15] << 22 );
++ dts |= (int64_t)((buf[16] & 0xFE) << 14 );
++ dts |= (int64_t)( buf[17] << 7 );
++ dts |= (int64_t)((buf[18] & 0xFE) >> 1 );
++ return dts;
++ }
++ }
++ return DVD_NOPTS_VALUE;
++}
++
++// --- cParser -------------------------------------------------
++
++cParser::cParser(cLiveStreamer *streamer, int streamID)
++ : m_Streamer(streamer)
++ , m_streamID(streamID)
++ , m_FoundFrame(false)
++{
++ m_curPTS = DVD_NOPTS_VALUE;
++ m_curDTS = DVD_NOPTS_VALUE;
++ m_LastDTS = DVD_NOPTS_VALUE;
++ m_epochDTS = 0;
++ m_badDTS = 0;
++}
++
++int64_t cParser::Rescale(int64_t a)
++{
++ uint64_t b = DVD_TIME_BASE;
++ uint64_t c = 90000;
++ uint64_t r = c/2;
++
++ if (b<=INT_MAX && c<=INT_MAX){
++ if (a<=INT_MAX)
++ return (a * b + r)/c;
++ else
++ return a/c*b + (a%c*b + r)/c;
++ }
++ else
++ {
++ uint64_t a0= a&0xFFFFFFFF;
++ uint64_t a1= a>>32;
++ uint64_t b0= b&0xFFFFFFFF;
++ uint64_t b1= b>>32;
++ uint64_t t1= a0*b1 + a1*b0;
++ uint64_t t1a= t1<<32;
++
++ a0 = a0*b0 + t1a;
++ a1 = a1*b1 + (t1>>32) + (a0<t1a);
++ a0 += r;
++ a1 += a0<r;
++
++ for (int i=63; i>=0; i--)
++ {
++ a1+= a1 + ((a0>>i)&1);
++ t1+=t1;
++ if (c <= a1)
++ {
++ a1 -= c;
++ t1++;
++ }
++ }
++ return t1;
++ }
++}
++
++/*
++ * Extract DTS and PTS and update current values in stream
++ */
++int cParser::ParsePESHeader(uint8_t *buf, size_t len)
++{
++ /* parse PES header */
++ unsigned int hdr_len = PesHeaderLength(buf);
++
++ /* parse PTS */
++ int64_t pts = PesGetPTS(buf, len);
++ int64_t dts = PesGetDTS(buf, len);
++ if (dts == DVD_NOPTS_VALUE)
++ dts = pts;
++
++ dts = dts & PTS_MASK;
++ pts = pts & PTS_MASK;
++
++ if(pts != 0) m_curDTS = dts;
++ if(dts != 0) m_curPTS = pts;
++
++ return hdr_len;
++}
++
++void cParser::BufferPacket(sStreamPacket *pkt)
++{
++ // create new packet
++ sStreamPacket* p = new sStreamPacket(*pkt);
++
++ // copy payload
++ p->data = (uint8_t*)malloc(pkt->size);
++ memcpy(p->data, pkt->data, pkt->size);
++
++ // push to queue
++ m_queue.push(p);
++}
++
++void cParser::SendPacket(sStreamPacket *pkt)
++{
++ if(pkt->dts == DVD_NOPTS_VALUE) return;
++ if(pkt->pts == DVD_NOPTS_VALUE) return;
++
++ int64_t dts = pkt->dts;
++ int64_t pts = pkt->pts;
++
++ // Rescale for XBMC
++ pkt->dts = Rescale(dts);
++ pkt->pts = Rescale(pts);
++ pkt->duration = Rescale(pkt->duration);
++
++ // buffer packets if we are not ready to send
++ if (m_Streamer->IsStarting()) {
++ BufferPacket(pkt);
++ return;
++ }
++
++ // stream packet if queue is empty
++ if(m_queue.size() == 0) {
++ m_Streamer->sendStreamPacket(pkt);
++ return;
++ }
++
++ BufferPacket(pkt);
++
++ // send buffered data first
++ INFOLOG("sending %i buffered packets", (int)m_queue.size());
++
++ while(m_queue.size() > 0) {
++ sStreamPacket* p = m_queue.front();
++ if(p != NULL) {
++ m_Streamer->sendStreamPacket(p);
++ free(p->data);
++ delete p;
++ }
++ m_queue.pop();
++ }
++}
++
++
++// --- cTSDemuxer ----------------------------------------------------
++
++cTSDemuxer::cTSDemuxer(cLiveStreamer *streamer, int id, eStreamType type, int pid)
++ : m_Streamer(streamer)
++ , m_streamID(id)
++ , m_pID(pid)
++ , m_streamType(type)
++{
++ m_pesError = false;
++ m_pesParser = NULL;
++ m_language[0] = 0;
++ m_FpsScale = 0;
++ m_FpsRate = 0;
++ m_Height = 0;
++ m_Width = 0;
++ m_Aspect = 0.0f;
++ m_Channels = 0;
++ m_SampleRate = 0;
++ m_BitRate = 0;
++ m_BitsPerSample = 0;
++ m_BlockAlign = 0;
++
++ if (m_streamType == stMPEG2VIDEO)
++ m_pesParser = new cParserMPEG2Video(this, m_Streamer, m_streamID);
++ else if (m_streamType == stH264)
++ m_pesParser = new cParserH264(this, m_Streamer, m_streamID);
++ else if (m_streamType == stMPEG2AUDIO)
++ m_pesParser = new cParserMPEG2Audio(this, m_Streamer, m_streamID);
++ else if (m_streamType == stAAC)
++ m_pesParser = new cParserAAC(this, m_Streamer, m_streamID);
++ else if (m_streamType == stAC3)
++ m_pesParser = new cParserAC3(this, m_Streamer, m_streamID);
++ else if (m_streamType == stDTS)
++ m_pesParser = new cParserDTS(this, m_Streamer, m_streamID);
++ else if (m_streamType == stEAC3)
++ m_pesParser = new cParserAC3(this, m_Streamer, m_streamID);
++ else if (m_streamType == stTELETEXT)
++ m_pesParser = new cParserTeletext(this, m_Streamer, m_streamID);
++ else if (m_streamType == stDVBSUB)
++ m_pesParser = new cParserSubtitle(this, m_Streamer, m_streamID);
++ else
++ {
++ ERRORLOG("Unrecognised type %i inside stream %i", m_streamType, m_streamID);
++ return;
++ }
++}
++
++cTSDemuxer::~cTSDemuxer()
++{
++ if (m_pesParser)
++ {
++ delete m_pesParser;
++ m_pesParser = NULL;
++ }
++}
++
++bool cTSDemuxer::ProcessTSPacket(unsigned char *data)
++{
++ if (!data)
++ return false;
++
++ bool pusi = TsPayloadStart(data);
++ int bytes = TS_SIZE - TsPayloadOffset(data);
++
++ if(bytes < 0 || bytes > TS_SIZE)
++ return false;
++
++ if (TsError(data))
++ {
++ ERRORLOG("transport error");
++ return false;
++ }
++
++ if (!TsHasPayload(data))
++ {
++ DEBUGLOG("no payload, size %d", bytes);
++ return true;
++ }
++
++ /* drop broken PES packets */
++ if (m_pesError && !pusi)
++ {
++ return false;
++ }
++
++ /* strip ts header */
++ data += TS_SIZE - bytes;
++
++ /* handle new payload unit */
++ if (pusi)
++ {
++ if (!PesIsHeader(data))
++ {
++ m_pesError = true;
++ return false;
++ }
++ m_pesError = false;
++ }
++
++ /* Parse the data */
++ if (m_pesParser)
++ m_pesParser->Parse(data, bytes, pusi);
++
++ return true;
++}
++
++void cTSDemuxer::SetLanguage(const char *language)
++{
++ m_language[0] = language[0];
++ m_language[1] = language[1];
++ m_language[2] = language[2];
++ m_language[3] = 0;
++}
++
++void cTSDemuxer::SetVideoInformation(int FpsScale, int FpsRate, int Height, int Width, float Aspect)
++{
++ m_FpsScale = FpsScale;
++ m_FpsRate = FpsRate;
++ m_Height = Height;
++ m_Width = Width;
++ m_Aspect = Aspect;
++}
++
++void cTSDemuxer::SetAudioInformation(int Channels, int SampleRate, int BitRate, int BitsPerSample, int BlockAlign)
++{
++ m_Channels = Channels;
++ m_SampleRate = SampleRate;
++ m_BlockAlign = BlockAlign;
++ m_BitRate = BitRate;
++ m_BitsPerSample = BitsPerSample;
++}
++
++void cTSDemuxer::SetSubtitlingDescriptor(unsigned char SubtitlingType, uint16_t CompositionPageId, uint16_t AncillaryPageId)
++{
++ m_subtitlingType = SubtitlingType;
++ m_compositionPageId = CompositionPageId;
++ m_ancillaryPageId = AncillaryPageId;
++}
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer.h
+new file mode 100644
+index 0000000..06dbfaf
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer.h
+@@ -0,0 +1,307 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#ifndef VNSI_DEMUXER_H
++#define VNSI_DEMUXER_H
++
++#include <vdr/device.h>
++#include <queue>
++
++#define DVD_TIME_BASE 1000000
++#define DVD_NOPTS_VALUE (-1LL<<52) // should be possible to represent in both double and __int64
++
++/* PES PIDs */
++#define PRIVATE_STREAM1 0xBD
++#define PADDING_STREAM 0xBE
++#define PRIVATE_STREAM2 0xBF
++#define PRIVATE_STREAM3 0xFD
++#define AUDIO_STREAM_S 0xC0 /* 1100 0000 */
++#define AUDIO_STREAM_E 0xDF /* 1101 1111 */
++#define VIDEO_STREAM_S 0xE0 /* 1110 0000 */
++#define VIDEO_STREAM_E 0xEF /* 1110 1111 */
++
++#define AUDIO_STREAM_MASK 0x1F /* 0001 1111 */
++#define VIDEO_STREAM_MASK 0x0F /* 0000 1111 */
++#define AUDIO_STREAM 0xC0 /* 1100 0000 */
++#define VIDEO_STREAM 0xE0 /* 1110 0000 */
++
++#define ECM_STREAM 0xF0
++#define EMM_STREAM 0xF1
++#define DSM_CC_STREAM 0xF2
++#define ISO13522_STREAM 0xF3
++#define PROG_STREAM_DIR 0xFF
++
++inline bool PesIsHeader(const uchar *p)
++{
++ return !(p)[0] && !(p)[1] && (p)[2] == 1;
++}
++
++inline int PesHeaderLength(const uchar *p)
++{
++ return 8 + (p)[8] + 1;
++}
++
++inline bool PesIsVideoPacket(const uchar *p)
++{
++ return (((p)[3] & ~VIDEO_STREAM_MASK) == VIDEO_STREAM);
++}
++
++inline bool PesIsMPEGAudioPacket(const uchar *p)
++{
++ return (((p)[3] & ~AUDIO_STREAM_MASK) == AUDIO_STREAM);
++}
++
++inline bool PesIsPS1Packet(const uchar *p)
++{
++ return ((p)[3] == PRIVATE_STREAM1 || (p)[3] == PRIVATE_STREAM3 );
++}
++
++inline bool PesIsPaddingPacket(const uchar *p)
++{
++ return ((p)[3] == PADDING_STREAM);
++}
++
++inline bool PesIsAudioPacket(const uchar *p)
++{
++ return (PesIsMPEGAudioPacket(p) || PesIsPS1Packet(p));
++}
++
++#if APIVERSNUM < 10701
++
++#define TS_ERROR 0x80
++#define TS_PAYLOAD_START 0x40
++#define TS_TRANSPORT_PRIORITY 0x20
++#define TS_PID_MASK_HI 0x1F
++#define TS_SCRAMBLING_CONTROL 0xC0
++#define TS_ADAPT_FIELD_EXISTS 0x20
++#define TS_PAYLOAD_EXISTS 0x10
++#define TS_CONT_CNT_MASK 0x0F
++#define TS_ADAPT_DISCONT 0x80
++#define TS_ADAPT_RANDOM_ACC 0x40 // would be perfect for detecting independent frames, but unfortunately not used by all broadcasters
++#define TS_ADAPT_ELEM_PRIO 0x20
++#define TS_ADAPT_PCR 0x10
++#define TS_ADAPT_OPCR 0x08
++#define TS_ADAPT_SPLICING 0x04
++#define TS_ADAPT_TP_PRIVATE 0x02
++#define TS_ADAPT_EXTENSION 0x01
++
++inline bool TsHasPayload(const uchar *p)
++{
++ return p[3] & TS_PAYLOAD_EXISTS;
++}
++
++inline bool TsHasAdaptationField(const uchar *p)
++{
++ return p[3] & TS_ADAPT_FIELD_EXISTS;
++}
++
++inline bool TsPayloadStart(const uchar *p)
++{
++ return p[1] & TS_PAYLOAD_START;
++}
++
++inline bool TsError(const uchar *p)
++{
++ return p[1] & TS_ERROR;
++}
++
++inline int TsPid(const uchar *p)
++{
++ return (p[1] & TS_PID_MASK_HI) * 256 + p[2];
++}
++
++inline bool TsIsScrambled(const uchar *p)
++{
++ return p[3] & TS_SCRAMBLING_CONTROL;
++}
++
++inline int TsPayloadOffset(const uchar *p)
++{
++ return (p[3] & TS_ADAPT_FIELD_EXISTS) ? p[4] + 5 : 4;
++}
++
++inline int TsGetPayload(const uchar **p)
++{
++ int o = TsPayloadOffset(*p);
++ *p += o;
++ return TS_SIZE - o;
++}
++
++inline int TsContinuityCounter(const uchar *p)
++{
++ return p[3] & TS_CONT_CNT_MASK;
++}
++
++inline int TsGetAdaptationField(const uchar *p)
++{
++ return TsHasAdaptationField(p) ? p[5] : 0x00;
++}
++#endif
++
++enum eStreamContent
++{
++ scVIDEO,
++ scAUDIO,
++ scSUBTITLE,
++ scTELETEXT,
++ scPROGRAMM
++};
++
++enum eStreamType
++{
++ stNone,
++ stAC3,
++ stMPEG2AUDIO,
++ stEAC3,
++ stAAC,
++ stDTS,
++ stMPEG2VIDEO,
++ stH264,
++ stDVBSUB,
++ stTEXTSUB,
++ stTELETEXT,
++};
++
++#define PKT_I_FRAME 1
++#define PKT_P_FRAME 2
++#define PKT_B_FRAME 3
++#define PKT_NTYPES 4
++struct sStreamPacket
++{
++ sStreamPacket() {
++ frametype = 0;
++ }
++
++ int64_t id;
++ int64_t dts;
++ int64_t pts;
++ int duration;
++
++ uint8_t commercial;
++ uint8_t componentindex;
++ uint8_t frametype;
++
++ uint8_t *data;
++ int size;
++};
++
++class cLiveStreamer;
++
++class cParser
++{
++public:
++ cParser(cLiveStreamer *streamer, int streamID);
++ virtual ~cParser() {};
++
++ virtual void Parse(unsigned char *data, int size, bool pusi) = 0;
++
++ int ParsePESHeader(uint8_t *buf, size_t len);
++ void SendPacket(sStreamPacket *pkt);
++ void BufferPacket(sStreamPacket *pkt);
++ int64_t Rescale(int64_t a);
++
++ cLiveStreamer *m_Streamer;
++
++ int64_t m_LastDTS;
++ int64_t m_curPTS;
++ int64_t m_curDTS;
++ int64_t m_epochDTS;
++
++ int m_badDTS;
++ int m_streamID;
++
++protected:
++ bool m_FoundFrame;
++
++ std::queue<sStreamPacket*> m_queue;
++};
++
++
++class cTSDemuxer
++{
++private:
++ cLiveStreamer *m_Streamer;
++ const int m_streamID;
++ const int m_pID;
++ eStreamContent m_streamContent;
++ eStreamType m_streamType;
++
++ bool m_pesError;
++ cParser *m_pesParser;
++
++ char m_language[4]; // ISO 639 3-letter language code (empty string if undefined)
++
++ int m_FpsScale; // scale of 1000 and a rate of 29970 will result in 29.97 fps
++ int m_FpsRate;
++ int m_Height; // height of the stream reported by the demuxer
++ int m_Width; // width of the stream reported by the demuxer
++ float m_Aspect; // display aspect of stream
++
++ int m_Channels;
++ int m_SampleRate;
++ int m_BitRate;
++ int m_BitsPerSample;
++ int m_BlockAlign;
++
++ unsigned char m_subtitlingType;
++ uint16_t m_compositionPageId;
++ uint16_t m_ancillaryPageId;
++
++public:
++ cTSDemuxer(cLiveStreamer *streamer, int id, eStreamType type, int pid);
++ virtual ~cTSDemuxer();
++
++ bool ProcessTSPacket(unsigned char *data);
++
++ void SetLanguage(const char *language);
++ const char *GetLanguage() { return m_language; }
++ const eStreamContent Content() const { return m_streamContent; }
++ const eStreamType Type() const { return m_streamType; }
++ const int GetPID() const { return m_pID; }
++ const int GetStreamID() const { return m_streamID; }
++
++ /* Video Stream Information */
++ void SetVideoInformation(int FpsScale, int FpsRate, int Height, int Width, float Aspect);
++ uint32_t GetFpsScale() const { return m_FpsScale; }
++ uint32_t GetFpsRate() const { return m_FpsRate; }
++ uint32_t GetHeight() const { return m_Height; }
++ uint32_t GetWidth() const { return m_Width; }
++ double GetAspect() const { return m_Aspect; }
++
++ /* Audio Stream Information */
++ void SetAudioInformation(int Channels, int SampleRate, int BitRate, int BitsPerSample, int BlockAlign);
++ uint32_t GetChannels() const { return m_Channels; }
++ uint32_t GetSampleRate() const { return m_SampleRate; }
++ uint32_t GetBlockAlign() const { return m_BlockAlign; }
++ uint32_t GetBitRate() const { return m_BitRate; }
++ uint32_t GetBitsPerSample() const { return m_BitsPerSample; }
++
++ /* Subtitle related stream information */
++ void SetSubtitlingDescriptor(unsigned char SubtitlingType, uint16_t CompositionPageId, uint16_t AncillaryPageId);
++ unsigned char SubtitlingType() const { return m_subtitlingType; }
++ uint16_t CompositionPageId() const { return m_compositionPageId; }
++ uint16_t AncillaryPageId() const { return m_ancillaryPageId; }
++};
++
++#endif // VNSI_DEMUXER_H
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_AAC.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_AAC.c
+new file mode 100644
+index 0000000..c6f1883
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_AAC.c
+@@ -0,0 +1,281 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <stdlib.h>
++#include <assert.h>
++#include "config.h"
++
++#include "demuxer_AAC.h"
++
++static int aac_sample_rates[16] =
++{
++ 96000, 88200, 64000, 48000, 44100, 32000,
++ 24000, 22050, 16000, 12000, 11025, 8000, 7350
++};
++
++
++cParserAAC::cParserAAC(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID)
++ : cParser(streamer, streamID)
++{
++ m_demuxer = demuxer;
++ m_streamBuffer = NULL;
++ m_streamBufferSize = 0;
++ m_streamBufferPtr = 0;
++ m_streamParserPtr = 0;
++ m_firstPUSIseen = false;
++
++ m_Configured = false;
++ m_FrameLengthType = 0;
++ m_SampleRate = 0;
++}
++
++cParserAAC::~cParserAAC()
++{
++ if (m_streamBuffer)
++ free(m_streamBuffer);
++}
++
++void cParserAAC::Parse(unsigned char *data, int size, bool pusi)
++{
++ if (pusi)
++ {
++ /* Payload unit start */
++ m_firstPUSIseen = true;
++ m_streamBufferPtr = 0;
++ m_streamParserPtr = 0;
++ }
++
++ if (!m_firstPUSIseen)
++ return;
++
++ if (m_streamBuffer == NULL)
++ {
++ m_streamBufferSize = 4000;
++ m_streamBuffer = (uint8_t*)malloc(m_streamBufferSize);
++ }
++
++ if(m_streamBufferPtr + size >= m_streamBufferSize)
++ {
++ m_streamBufferSize += size * 4;
++ m_streamBuffer = (uint8_t*)realloc(m_streamBuffer, m_streamBufferSize);
++ }
++
++ memcpy(m_streamBuffer + m_streamBufferPtr, data, size);
++ m_streamBufferPtr += size;
++
++ if (m_streamParserPtr == 0)
++ {
++ if (m_streamBufferPtr < 9)
++ return;
++
++ int hlen = ParsePESHeader(data, size);
++ if (hlen < 0)
++ return;
++
++ m_streamParserPtr += hlen;
++ }
++
++ int p = m_streamParserPtr;
++
++ int l;
++ while ((l = m_streamBufferPtr - p) > 3)
++ {
++ if(m_streamBuffer[p] == 0x56 && (m_streamBuffer[p + 1] & 0xe0) == 0xe0)
++ {
++ int muxlen = (m_streamBuffer[p + 1] & 0x1f) << 8 | m_streamBuffer[p + 2];
++
++ if(l < muxlen + 3)
++ break;
++
++ ParseLATMAudioMuxElement(m_streamBuffer + p + 3, muxlen);
++ p += muxlen + 3;
++ }
++ else
++ {
++ p++;
++ }
++ }
++ m_streamParserPtr = p;
++}
++
++void cParserAAC::ParseLATMAudioMuxElement(uint8_t *data, int len)
++{
++ cBitstream bs(data, len * 8);
++
++ if (!bs.readBits1())
++ ReadStreamMuxConfig(&bs);
++
++ if (!m_Configured)
++ return;
++
++ if (m_FrameLengthType != 0)
++ return;
++
++ int tmp;
++ unsigned int slotLen = 0;
++ do
++ {
++ tmp = bs.readBits(8);
++ slotLen += tmp;
++ } while (tmp == 255);
++
++ if (slotLen * 8 > bs.remainingBits())
++ return;
++
++ if (m_curDTS == DVD_NOPTS_VALUE)
++ return;
++
++ sStreamPacket pkt;
++ pkt.id = m_streamID;
++ pkt.size = slotLen + 7;
++ pkt.data = (uint8_t*)malloc(pkt.size);
++ pkt.dts = m_curDTS;
++ pkt.pts = m_curDTS;
++ pkt.duration = m_FrameDuration;
++
++ /* 7 bytes of ADTS header */
++ cBitstream out(pkt.data, 56);
++
++ out.putBits(0xfff, 12); // Sync marker
++ out.putBits(0, 1); // ID 0 = MPEG 4
++ out.putBits(0, 2); // Layer
++ out.putBits(1, 1); // Protection absent
++ out.putBits(2, 2); // AOT
++ out.putBits(m_SampleRateIndex, 4);
++ out.putBits(1, 1); // Private bit
++ out.putBits(m_ChannelConfig, 3);
++ out.putBits(1, 1); // Original
++ out.putBits(1, 1); // Copy
++ out.putBits(1, 1); // Copyright identification bit
++ out.putBits(1, 1); // Copyright identification start
++ out.putBits(slotLen, 13);
++ out.putBits(0, 11); // Buffer fullness
++ out.putBits(0, 2); // RDB in frame
++
++ assert(out.remainingBits() == 0);
++
++ /* AAC RDB */
++ uint8_t *buf = pkt.data + 7;
++ for (unsigned int i = 0; i < slotLen; i++)
++ *buf++ = bs.readBits(8);
++
++ m_curDTS += m_FrameDuration;
++
++ SendPacket(&pkt);
++ return;
++}
++
++void cParserAAC::ReadStreamMuxConfig(cBitstream *bs)
++{
++ int AudioMuxVersion = bs->readBits(1);
++ m_AudioMuxVersion_A = 0;
++ if (AudioMuxVersion) // audioMuxVersion
++ m_AudioMuxVersion_A = bs->readBits(1);
++
++ if(m_AudioMuxVersion_A)
++ return;
++
++ if (AudioMuxVersion)
++ LATMGetValue(bs); // taraFullness
++
++ bs->skipBits(1); // allStreamSameTimeFraming = 1
++ bs->skipBits(6); // numSubFrames = 0
++ bs->skipBits(4); // numPrograms = 0
++
++ // for each program (which there is only on in DVB)
++ bs->skipBits(3); // numLayer = 0
++
++ // for each layer (which there is only on in DVB)
++ if (!AudioMuxVersion)
++ ReadAudioSpecificConfig(bs);
++ else
++ return;
++
++ // these are not needed... perhaps
++ m_FrameLengthType = bs->readBits(3);
++ switch (m_FrameLengthType)
++ {
++ case 0:
++ bs->readBits(8);
++ break;
++ case 1:
++ bs->readBits(9);
++ break;
++ case 3:
++ case 4:
++ case 5:
++ bs->readBits(6); // celp_table_index
++ break;
++ case 6:
++ case 7:
++ bs->readBits(1); // hvxc_table_index
++ break;
++ }
++
++ if (bs->readBits(1))
++ { // other data?
++ if (AudioMuxVersion)
++ {
++ LATMGetValue(bs); // other_data_bits
++ }
++ else
++ {
++ int esc;
++ do
++ {
++ esc = bs->readBits(1);
++ bs->skipBits(8);
++ } while (esc);
++ }
++ }
++
++ if (bs->readBits(1)) // crc present?
++ bs->skipBits(8); // config_crc
++ m_Configured = true;
++}
++
++void cParserAAC::ReadAudioSpecificConfig(cBitstream *bs)
++{
++ int aot = bs->readBits(5);
++ if (aot != 2)
++ return;
++
++ m_SampleRateIndex = bs->readBits(4);
++
++ if (m_SampleRateIndex == 0xf)
++ return;
++
++ m_SampleRate = aac_sample_rates[m_SampleRateIndex];
++ m_FrameDuration = 1024 * 90000 / m_SampleRate;
++ m_ChannelConfig = bs->readBits(4);
++
++ bs->skipBits(1); //framelen_flag
++ if (bs->readBits1()) // depends_on_coder
++ bs->skipBits(14);
++
++ if (bs->readBits(1)) // ext_flag
++ bs->skipBits(1); // ext3_flag
++
++ m_demuxer->SetAudioInformation(m_ChannelConfig, m_SampleRate, 0, 0, 0);
++}
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_AAC.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_AAC.h
+new file mode 100644
+index 0000000..0f807e8
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_AAC.h
+@@ -0,0 +1,62 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#ifndef VNSI_DEMUXER_AAC_H
++#define VNSI_DEMUXER_AAC_H
++
++#include "demuxer.h"
++#include "bitstream.h"
++
++// --- cParserAAC -------------------------------------------------
++
++class cParserAAC : public cParser
++{
++private:
++ cTSDemuxer *m_demuxer;
++ uint8_t *m_streamBuffer;
++ int m_streamBufferSize;
++ int m_streamBufferPtr;
++ int m_streamParserPtr;
++ bool m_firstPUSIseen;
++
++ bool m_Configured;
++ int m_AudioMuxVersion_A;
++ int m_FrameLengthType;
++ int m_SampleRateIndex;
++ int m_ChannelConfig;
++ int m_FrameDuration;
++ int m_SampleRate;
++
++public:
++ cParserAAC(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID);
++ virtual ~cParserAAC();
++
++ virtual void Parse(unsigned char *data, int size, bool pusi);
++ void ParseLATMAudioMuxElement(uint8_t *data, int len);
++ void ReadStreamMuxConfig(cBitstream *bs);
++ void ReadAudioSpecificConfig(cBitstream *bs);
++ uint32_t LATMGetValue(cBitstream *bs) { return bs->readBits(bs->readBits(2) * 8); }
++};
++
++#endif // VNSI_DEMUXER_AAC_H
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_AC3.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_AC3.c
+new file mode 100644
+index 0000000..147a169
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_AC3.c
+@@ -0,0 +1,371 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <stdlib.h>
++#include <assert.h>
++#include "config.h"
++
++#include "demuxer_AC3.h"
++#include "bitstream.h"
++
++#define AC3_HEADER_SIZE 7
++
++/** Channel mode (audio coding mode) */
++typedef enum
++{
++ AC3_CHMODE_DUALMONO = 0,
++ AC3_CHMODE_MONO,
++ AC3_CHMODE_STEREO,
++ AC3_CHMODE_3F,
++ AC3_CHMODE_2F1R,
++ AC3_CHMODE_3F1R,
++ AC3_CHMODE_2F2R,
++ AC3_CHMODE_3F2R
++} AC3ChannelMode;
++
++/* possible frequencies */
++const uint16_t AC3SampleRateTable[3] = { 48000, 44100, 32000 };
++
++/* possible bitrates */
++const uint16_t AC3BitrateTable[19] = {
++ 32, 40, 48, 56, 64, 80, 96, 112, 128,
++ 160, 192, 224, 256, 320, 384, 448, 512, 576, 640
++};
++
++const uint8_t AC3ChannelsTable[8] = {
++ 2, 1, 2, 3, 3, 4, 4, 5
++};
++
++const uint16_t AC3FrameSizeTable[38][3] = {
++ { 64, 69, 96 },
++ { 64, 70, 96 },
++ { 80, 87, 120 },
++ { 80, 88, 120 },
++ { 96, 104, 144 },
++ { 96, 105, 144 },
++ { 112, 121, 168 },
++ { 112, 122, 168 },
++ { 128, 139, 192 },
++ { 128, 140, 192 },
++ { 160, 174, 240 },
++ { 160, 175, 240 },
++ { 192, 208, 288 },
++ { 192, 209, 288 },
++ { 224, 243, 336 },
++ { 224, 244, 336 },
++ { 256, 278, 384 },
++ { 256, 279, 384 },
++ { 320, 348, 480 },
++ { 320, 349, 480 },
++ { 384, 417, 576 },
++ { 384, 418, 576 },
++ { 448, 487, 672 },
++ { 448, 488, 672 },
++ { 512, 557, 768 },
++ { 512, 558, 768 },
++ { 640, 696, 960 },
++ { 640, 697, 960 },
++ { 768, 835, 1152 },
++ { 768, 836, 1152 },
++ { 896, 975, 1344 },
++ { 896, 976, 1344 },
++ { 1024, 1114, 1536 },
++ { 1024, 1115, 1536 },
++ { 1152, 1253, 1728 },
++ { 1152, 1254, 1728 },
++ { 1280, 1393, 1920 },
++ { 1280, 1394, 1920 },
++};
++
++const uint8_t EAC3Blocks[4] = {
++ 1, 2, 3, 6
++};
++
++typedef enum {
++ EAC3_FRAME_TYPE_INDEPENDENT = 0,
++ EAC3_FRAME_TYPE_DEPENDENT,
++ EAC3_FRAME_TYPE_AC3_CONVERT,
++ EAC3_FRAME_TYPE_RESERVED
++} EAC3FrameType;
++
++cParserAC3::cParserAC3(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID)
++ : cParser(streamer, streamID)
++{
++ m_CurrentFrameStartIndex = 0;
++ m_Offset = 0;
++ m_PTS = 0;
++ m_DTS = 0;
++ m_FrameSize = 0;
++ m_SampleRate = 0;
++ m_Channels = 0;
++ m_BitRate = 0;
++ m_demuxer = demuxer;
++ m_firstPUSIseen = false;
++ m_PESStart = false;
++ m_FetchTimestamp = true;
++ m_NextDTS = 0;
++ m_AC3BufferPtr = 0;
++ m_HeaderFound = false;
++
++ for (int i = 0; i < AV_PARSER_PTS_NB; i++)
++ {
++ m_CurrentFrameDTS[i] = DVD_NOPTS_VALUE;
++ m_CurrentFramePTS[i] = DVD_NOPTS_VALUE;
++ m_CurrentFrameEnd[i] = 0;
++ m_CurrentFrameOffset[i] = 0;
++ }
++}
++
++cParserAC3::~cParserAC3()
++{
++}
++
++void cParserAC3::Parse(unsigned char *data, int size, bool pusi)
++{
++ if (pusi)
++ {
++ /* Payload unit start */
++ m_PESStart = true;
++ m_firstPUSIseen = true;
++ }
++
++ /* Wait for first pusi */
++ if (!m_firstPUSIseen)
++ return;
++
++ if (m_PESStart)
++ {
++ if (!PesIsPS1Packet(data))
++ {
++ ERRORLOG("AC3 PES packet contains no valid private stream 1, ignored this packet");
++ m_firstPUSIseen = false;
++ return;
++ }
++
++ int hlen = ParsePESHeader(data, size);
++ if (hlen <= 0)
++ return;
++
++ data += hlen;
++ size -= hlen;
++
++ m_PESStart = false;
++
++ assert(size >= 0);
++ if(size == 0)
++ return;
++ }
++
++ while (size > 0)
++ {
++ uint8_t *outbuf;
++ int outlen;
++
++ int rlen = FindHeaders(&outbuf, &outlen, data, size, m_curPTS, m_curDTS);
++ if (rlen < 0)
++ break;
++
++ m_curPTS = DVD_NOPTS_VALUE;
++ m_curDTS = DVD_NOPTS_VALUE;
++
++ if (outlen)
++ {
++ sStreamPacket pkt;
++ pkt.id = m_streamID;
++ pkt.data = outbuf;
++ pkt.size = outlen;
++ pkt.duration = 90000 * 1536 / m_SampleRate;
++ pkt.dts = m_DTS;
++ if (pkt.dts == DVD_NOPTS_VALUE)
++ pkt.dts = m_NextDTS;
++ pkt.pts = pkt.dts;
++ m_NextDTS = pkt.dts + pkt.duration;
++
++ m_demuxer->SetAudioInformation(m_Channels, m_SampleRate, m_BitRate, 0, 0);
++
++ SendPacket(&pkt);
++ }
++ data += rlen;
++ size -= rlen;
++ }
++}
++
++int cParserAC3::FindHeaders(uint8_t **poutbuf, int *poutbuf_size,
++ uint8_t *buf, int buf_size,
++ int64_t pts, int64_t dts)
++{
++ *poutbuf = NULL;
++ *poutbuf_size = 0;
++
++ /* add a new packet descriptor */
++ if (pts != DVD_NOPTS_VALUE || dts != DVD_NOPTS_VALUE)
++ {
++ int i = (m_CurrentFrameStartIndex + 1) & (AV_PARSER_PTS_NB - 1);
++ m_CurrentFrameStartIndex = i;
++ m_CurrentFrameOffset[i] = m_CurrentOffset;
++ m_CurrentFrameEnd[i] = m_CurrentOffset + buf_size;
++ m_CurrentFramePTS[i] = pts;
++ m_CurrentFrameDTS[i] = dts;
++ }
++
++ if (m_FetchTimestamp)
++ {
++ m_FetchTimestamp = false;
++ FetchTimestamp(0, false);
++ }
++
++ uint8_t *buf_ptr = buf;
++ while (buf_size > 0)
++ {
++ if (buf_ptr[0] == 0x0b && buf_ptr[1] == 0x77 && !m_HeaderFound)
++ {
++ cBitstream bs(buf_ptr + 2, AC3_HEADER_SIZE * 8);
++
++ /* read ahead to bsid to distinguish between AC-3 and E-AC-3 */
++ int bsid = bs.showBits(29) & 0x1F;
++ if (bsid > 16)
++ return -1;
++
++ if (bsid <= 10)
++ {
++ /* Normal AC-3 */
++ bs.skipBits(16);
++ int fscod = bs.readBits(2);
++ int frmsizecod = bs.readBits(6);
++ bs.skipBits(5); // skip bsid, already got it
++ bs.skipBits(3); // skip bitstream mode
++ int acmod = bs.readBits(3);
++
++ if (fscod == 3 || frmsizecod > 37)
++ return -1;
++
++ if (acmod == AC3_CHMODE_STEREO)
++ {
++ bs.skipBits(2); // skip dsurmod
++ }
++ else
++ {
++ if ((acmod & 1) && acmod != AC3_CHMODE_MONO)
++ bs.skipBits(2);
++ if (acmod & 4)
++ bs.skipBits(2);
++ }
++ int lfeon = bs.readBits(1);
++
++ int srShift = max(bsid, 8) - 8;
++ m_SampleRate = AC3SampleRateTable[fscod] >> srShift;
++ m_BitRate = (AC3BitrateTable[frmsizecod>>1] * 1000) >> srShift;
++ m_Channels = AC3ChannelsTable[acmod] + lfeon;
++ m_FrameSize = AC3FrameSizeTable[frmsizecod][fscod] * 2;
++ }
++ else
++ {
++ /* Enhanced AC-3 */
++ int frametype = bs.readBits(2);
++ if (frametype == EAC3_FRAME_TYPE_RESERVED)
++ return -1;
++
++ /*int substreamid =*/ bs.readBits(3);
++
++ int framesize = (bs.readBits(11) + 1) << 1;
++ if (framesize < AC3_HEADER_SIZE)
++ return -1;
++
++ int numBlocks = 6;
++ int sr_code = bs.readBits(2);
++ if (sr_code == 3)
++ {
++ int sr_code2 = bs.readBits(2);
++ if (sr_code2 == 3)
++ return -1;
++ m_SampleRate = AC3SampleRateTable[sr_code2] / 2;
++ }
++ else
++ {
++ numBlocks = EAC3Blocks[bs.readBits(2)];
++ m_SampleRate = AC3SampleRateTable[sr_code];
++ }
++
++ int channelMode = bs.readBits(3);
++ int lfeon = bs.readBits(1);
++
++ m_BitRate = (uint32_t)(8.0 * framesize * m_SampleRate / (numBlocks * 256.0));
++ m_Channels = AC3ChannelsTable[channelMode] + lfeon;
++ }
++ m_HeaderFound = true;
++ }
++
++ if (m_HeaderFound)
++ m_AC3Buffer[m_AC3BufferPtr++] = buf_ptr[0];
++
++ if (m_FrameSize && m_AC3BufferPtr >= m_FrameSize)
++ {
++ *poutbuf = m_AC3Buffer;
++ *poutbuf_size = m_AC3BufferPtr;
++
++ m_AC3BufferPtr = 0;
++ m_HeaderFound = false;
++ break;
++ }
++
++ buf_ptr++;
++ buf_size--;
++ }
++ int index = buf_ptr - buf;
++
++ /* update the file pointer */
++ if (*poutbuf_size)
++ {
++ /* fill the data for the current frame */
++ m_FrameOffset = m_NextFrameOffset;
++
++ /* offset of the next frame */
++ m_NextFrameOffset = m_CurrentOffset + index;
++ m_FetchTimestamp = true;
++ }
++ if (index < 0)
++ index = 0;
++ m_CurrentOffset += index;
++ return index;
++}
++
++void cParserAC3::FetchTimestamp(int off, bool remove)
++{
++ m_DTS = DVD_NOPTS_VALUE;
++ m_PTS = DVD_NOPTS_VALUE;
++ m_Offset = 0;
++ for (int i = 0; i < AV_PARSER_PTS_NB; i++)
++ {
++ if ( m_NextFrameOffset + off >= m_CurrentFrameOffset[i]
++ &&(m_FrameOffset < m_CurrentFrameOffset[i] || !m_FrameOffset)
++ && m_CurrentFrameEnd[i])
++ {
++ m_DTS = m_CurrentFrameDTS[i];
++ m_PTS = m_CurrentFramePTS[i];
++ m_Offset = m_NextFrameOffset - m_CurrentFrameOffset[i];
++ if (remove)
++ m_CurrentFrameOffset[i] = -1;
++ }
++ }
++}
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_AC3.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_AC3.h
+new file mode 100644
+index 0000000..1144c54
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_AC3.h
+@@ -0,0 +1,79 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#ifndef VNSI_DEMUXER_AC3_H
++#define VNSI_DEMUXER_AC3_H
++
++#include "demuxer.h"
++
++// --- cParserAC3 -------------------------------------------------
++
++class cParserAC3 : public cParser
++{
++private:
++ cTSDemuxer *m_demuxer;
++ bool m_firstPUSIseen;
++ bool m_PESStart;
++ int m_SampleRate;
++ int m_Channels;
++ int m_BitRate;
++
++ bool m_FetchTimestamp;
++ int64_t m_FrameOffset; /* offset of the current frame */
++ int64_t m_CurrentOffset; /* current offset (incremented by each av_parser_parse()) */
++ int64_t m_NextFrameOffset; /* offset of the next frame */
++
++#define AV_PARSER_PTS_NB 4
++ int m_CurrentFrameStartIndex;
++ int64_t m_CurrentFrameOffset[AV_PARSER_PTS_NB];
++ int64_t m_CurrentFramePTS[AV_PARSER_PTS_NB];
++ int64_t m_CurrentFrameDTS[AV_PARSER_PTS_NB];
++ int64_t m_CurrentFrameEnd[AV_PARSER_PTS_NB];
++ int64_t m_Offset; /* byte offset from starting packet start */
++ int64_t m_PTS; /* pts of the current frame */
++ int64_t m_DTS; /* dts of the current frame */
++ int64_t m_NextDTS;
++
++ int m_FrameSize;
++ bool m_HeaderFound;
++
++#define AC3_MAX_CODED_FRAME_SIZE 1920*2
++ uint8_t m_AC3Buffer[AC3_MAX_CODED_FRAME_SIZE]; /* input buffer */
++ int m_AC3BufferSize;
++ int m_AC3BufferPtr;
++
++ int FindHeaders(uint8_t **poutbuf, int *poutbuf_size,
++ uint8_t *buf, int buf_size,
++ int64_t pts, int64_t dts);
++ void FetchTimestamp(int off, bool remove);
++
++public:
++ cParserAC3(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID);
++ virtual ~cParserAC3();
++
++ virtual void Parse(unsigned char *data, int size, bool pusi);
++};
++
++
++#endif // VNSI_DEMUXER_AC3_H
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_DTS.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_DTS.c
+new file mode 100644
+index 0000000..c622f1b
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_DTS.c
+@@ -0,0 +1,45 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <stdlib.h>
++#include <assert.h>
++#include "config.h"
++
++#include "demuxer_DTS.h"
++#include "bitstream.h"
++
++cParserDTS::cParserDTS(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID)
++ : cParser(streamer, streamID)
++{
++ m_demuxer = demuxer;
++}
++
++cParserDTS::~cParserDTS()
++{
++}
++
++void cParserDTS::Parse(unsigned char *data, int size, bool pusi)
++{
++
++}
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_DTS.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_DTS.h
+new file mode 100644
+index 0000000..102ac76
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_DTS.h
+@@ -0,0 +1,45 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#ifndef VNSI_DEMUXER_DTS_H
++#define VNSI_DEMUXER_DTS_H
++
++#include "demuxer.h"
++
++// --- cParserDTS -------------------------------------------------
++
++class cParserDTS : public cParser
++{
++private:
++ cTSDemuxer *m_demuxer;
++
++public:
++ cParserDTS(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID);
++ virtual ~cParserDTS();
++
++ virtual void Parse(unsigned char *data, int size, bool pusi);
++};
++
++
++#endif // VNSI_DEMUXER_DTS_H
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_MPEGAudio.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_MPEGAudio.c
+new file mode 100644
+index 0000000..6405996
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_MPEGAudio.c
+@@ -0,0 +1,396 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <stdlib.h>
++#include <assert.h>
++#include "config.h"
++
++#include "demuxer_MPEGAudio.h"
++
++cParserMPEG2Audio::cParserMPEG2Audio(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID)
++ : cParser(streamer, streamID)
++{
++ m_FrameOffset = 0;
++ m_CurrentOffset = 0;
++ m_NextFrameOffset = 0;
++ m_CurrentFrameStartIndex = 0;
++ m_Offset = 0;
++ m_PTS = 0;
++ m_DTS = 0;
++ m_FrameSize = 0;
++ m_FrameFreeFormatFrameSize = 0;
++ m_FrameFreeFormatNextHeader = 0;
++ m_FrameHeader = 0;
++ m_HeaderCount = 0;
++ m_SampleRate = 0;
++ m_Channels = 0;
++ m_BitRate = 0;
++ m_demuxer = demuxer;
++ m_firstPUSIseen = false;
++ m_PESStart = false;
++ m_FetchTimestamp = true;
++ m_FrameInBufferPtr = m_FrameInBuffer;
++ m_NextDTS = 0;
++
++ for (int i = 0; i < AV_PARSER_PTS_NB; i++)
++ {
++ m_CurrentFrameDTS[i] = DVD_NOPTS_VALUE;
++ m_CurrentFramePTS[i] = DVD_NOPTS_VALUE;
++ m_CurrentFrameEnd[i] = 0;
++ m_CurrentFrameOffset[i] = 0;
++ }
++
++ for (int i = 0; i < MPA_MAX_CODED_FRAME_SIZE; i++)
++ {
++ m_FrameInBuffer[i] = 0;
++ }
++}
++
++cParserMPEG2Audio::~cParserMPEG2Audio()
++{
++}
++
++void cParserMPEG2Audio::Parse(unsigned char *data, int size, bool pusi)
++{
++ if (pusi)
++ {
++ /* Payload unit start */
++ m_PESStart = true;
++ m_firstPUSIseen = true;
++ }
++
++ /* Wait for first pusi */
++ if (!m_firstPUSIseen)
++ return;
++
++ if (m_PESStart)
++ {
++ int hlen = ParsePESHeader(data, size);
++ if (hlen <= 0)
++ return;
++
++ data += hlen;
++ size -= hlen;
++
++ m_PESStart = false;
++
++ assert(size >= 0);
++ if(size == 0)
++ return;
++ }
++
++ while (size > 0)
++ {
++ uint8_t *outbuf;
++ int outlen;
++
++ int rlen = FindHeaders(&outbuf, &outlen, data, size, m_curPTS, m_curDTS);
++ if (rlen < 0)
++ break;
++
++ m_curPTS = DVD_NOPTS_VALUE;
++ m_curDTS = DVD_NOPTS_VALUE;
++
++ if (outlen)
++ {
++ sStreamPacket pkt;
++ pkt.id = m_streamID;
++ pkt.data = outbuf;
++ pkt.size = outlen;
++ pkt.duration = 90000 * 1152 / m_SampleRate;
++ pkt.dts = m_DTS;
++ if (pkt.dts == DVD_NOPTS_VALUE)
++ pkt.dts = m_NextDTS;
++ pkt.pts = pkt.dts;
++ m_NextDTS = pkt.dts + pkt.duration;
++
++ SendPacket(&pkt);
++ }
++ data += rlen;
++ size -= rlen;
++ }
++}
++
++int cParserMPEG2Audio::FindHeaders(uint8_t **poutbuf, int *poutbuf_size,
++ uint8_t *buf, int buf_size,
++ int64_t pts, int64_t dts)
++{
++ *poutbuf = NULL;
++ *poutbuf_size = 0;
++
++ /* add a new packet descriptor */
++ if (pts != DVD_NOPTS_VALUE || dts != DVD_NOPTS_VALUE)
++ {
++ int i = (m_CurrentFrameStartIndex + 1) & (AV_PARSER_PTS_NB - 1);
++ m_CurrentFrameStartIndex = i;
++ m_CurrentFrameOffset[i] = m_CurrentOffset;
++ m_CurrentFrameEnd[i] = m_CurrentOffset + buf_size;
++ m_CurrentFramePTS[i] = pts;
++ m_CurrentFrameDTS[i] = dts;
++ }
++
++ if (m_FetchTimestamp)
++ {
++ m_FetchTimestamp = false;
++ FetchTimestamp(0, false);
++ }
++
++ const uint8_t *buf_ptr = buf;
++ while (buf_size > 0)
++ {
++ int len = m_FrameInBufferPtr - m_FrameInBuffer;
++ if (m_FrameSize == 0)
++ {
++ /* special case for next header for first frame in free
++ format case (XXX: find a simpler method) */
++ if (m_FrameFreeFormatNextHeader != 0)
++ {
++ m_FrameInBuffer[3] = m_FrameFreeFormatNextHeader;
++ m_FrameInBuffer[2] = m_FrameFreeFormatNextHeader>>8;
++ m_FrameInBuffer[1] = m_FrameFreeFormatNextHeader>>16;
++ m_FrameInBuffer[0] = m_FrameFreeFormatNextHeader>>24;
++ m_FrameInBufferPtr = m_FrameInBuffer + 4;
++ m_FrameFreeFormatNextHeader = 0;
++ goto got_header;
++ }
++ /* no header seen : find one. We need at least MPA_HEADER_SIZE
++ bytes to parse it */
++ len = min(MPA_HEADER_SIZE - len, buf_size);
++ if (len > 0)
++ {
++ memcpy(m_FrameInBufferPtr, buf_ptr, len);
++ buf_ptr += len;
++ buf_size -= len;
++ m_FrameInBufferPtr += len;
++ }
++ if ((m_FrameInBufferPtr - m_FrameInBuffer) >= MPA_HEADER_SIZE)
++ {
++ got_header:
++ MPADecodeHeader s;
++ uint32_t header = ((m_FrameInBuffer[0] << 24) | (m_FrameInBuffer[1] << 16) | (m_FrameInBuffer[2] << 8) | m_FrameInBuffer[3]);
++ if (CheckHeader(header) && DecodeHeader(&s, header))
++ {
++ if ((header&SAME_HEADER_MASK) != (m_FrameHeader&SAME_HEADER_MASK) && m_FrameHeader)
++ m_HeaderCount = -3;
++ m_FrameHeader = header;
++ m_FrameSize = s.frame_size;
++ m_SampleRate = s.sample_rate;
++ m_Channels = s.nb_channels;
++ m_BitRate = s.bit_rate;
++ m_HeaderCount++;
++ }
++ else
++ {
++ m_HeaderCount = -2;
++ /* no sync found : move by one byte (inefficient, but simple!) */
++ memmove(m_FrameInBuffer, m_FrameInBuffer + 1, m_FrameInBufferPtr - m_FrameInBuffer - 1);
++ m_FrameInBufferPtr--;
++ //LOGDBG("skip %x", header);
++ /* reset free format frame size to give a chance
++ to get a new bitrate */
++ m_FrameFreeFormatFrameSize = 0;
++ }
++ }
++ }
++ else if (len < m_FrameSize)
++ {
++ if (m_FrameSize > MPA_MAX_CODED_FRAME_SIZE)
++ m_FrameSize = MPA_MAX_CODED_FRAME_SIZE;
++ len = min(m_FrameSize - len, buf_size);
++ memcpy(m_FrameInBufferPtr, buf_ptr, len);
++ buf_ptr += len;
++ m_FrameInBufferPtr += len;
++ buf_size -= len;
++ }
++
++ if(m_FrameSize > 0 && buf_ptr - buf == m_FrameInBufferPtr - m_FrameInBuffer && buf_size + buf_ptr - buf >= m_FrameSize)
++ {
++ if(m_HeaderCount > 0)
++ {
++ *poutbuf = buf;
++ *poutbuf_size = m_FrameSize;
++ }
++ buf_ptr = buf + m_FrameSize;
++ m_FrameInBufferPtr = m_FrameInBuffer;
++ m_FrameSize = 0;
++ break;
++ }
++
++ // next_data:
++ if (m_FrameSize > 0 && (m_FrameInBufferPtr - m_FrameInBuffer) >= m_FrameSize)
++ {
++ if (m_HeaderCount > 0)
++ {
++ *poutbuf = m_FrameInBuffer;
++ *poutbuf_size = m_FrameInBufferPtr - m_FrameInBuffer;
++ }
++ m_FrameInBufferPtr = m_FrameInBuffer;
++ m_FrameSize = 0;
++ break;
++ }
++ }
++ int index = buf_ptr - buf;
++
++ /* update the file pointer */
++ if (*poutbuf_size)
++ {
++ /* fill the data for the current frame */
++ m_FrameOffset = m_NextFrameOffset;
++
++ /* offset of the next frame */
++ m_NextFrameOffset = m_CurrentOffset + index;
++ m_FetchTimestamp = true;
++ }
++ if (index < 0)
++ index = 0;
++ m_CurrentOffset += index;
++ return index;
++}
++
++void cParserMPEG2Audio::FetchTimestamp(int off, bool remove)
++{
++ m_DTS = DVD_NOPTS_VALUE;
++ m_PTS = DVD_NOPTS_VALUE;
++ m_Offset = 0;
++ for (int i = 0; i < AV_PARSER_PTS_NB; i++)
++ {
++ if ( m_NextFrameOffset + off >= m_CurrentFrameOffset[i]
++ &&(m_FrameOffset < m_CurrentFrameOffset[i] || !m_FrameOffset)
++ && m_CurrentFrameEnd[i])
++ {
++ m_DTS = m_CurrentFrameDTS[i];
++ m_PTS = m_CurrentFramePTS[i];
++ m_Offset = m_NextFrameOffset - m_CurrentFrameOffset[i];
++ if (remove)
++ m_CurrentFrameOffset[i] = -1;
++ }
++ }
++}
++
++bool cParserMPEG2Audio::DecodeHeader(MPADecodeHeader *s, uint32_t header)
++{
++ const uint16_t FrequencyTable[3] = { 44100, 48000, 32000 };
++ const uint16_t BitrateTable[2][3][15] =
++ {
++ {
++ {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448 },
++ {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384 },
++ {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 }
++ },
++ {
++ {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256},
++ {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160},
++ {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}
++ }
++ };
++
++ int sample_rate, frame_size, mpeg25, padding;
++ int sample_rate_index, bitrate_index;
++ if (header & (1<<20))
++ {
++ s->lsf = (header & (1<<19)) ? 0 : 1;
++ mpeg25 = 0;
++ }
++ else
++ {
++ s->lsf = 1;
++ mpeg25 = 1;
++ }
++
++ s->layer = 4 - ((header >> 17) & 3);
++ /* extract frequency */
++ sample_rate_index = (header >> 10) & 3;
++ sample_rate = FrequencyTable[sample_rate_index] >> (s->lsf + mpeg25);
++ sample_rate_index += 3 * (s->lsf + mpeg25);
++ s->sample_rate_index = sample_rate_index;
++ s->error_protection = ((header >> 16) & 1) ^ 1;
++ s->sample_rate = sample_rate;
++
++ bitrate_index = (header >> 12) & 0xf;
++ padding = (header >> 9) & 1;
++ //extension = (header >> 8) & 1;
++ s->mode = (header >> 6) & 3;
++ s->mode_ext = (header >> 4) & 3;
++ //copyright = (header >> 3) & 1;
++ //original = (header >> 2) & 1;
++ //emphasis = header & 3;
++
++ if (s->mode == MPA_MONO)
++ s->nb_channels = 1;
++ else
++ s->nb_channels = 2;
++
++ if (bitrate_index != 0)
++ {
++ frame_size = BitrateTable[s->lsf][s->layer - 1][bitrate_index];
++ s->bit_rate = frame_size * 1000;
++ switch(s->layer)
++ {
++ case 1:
++ frame_size = (frame_size * 12000) / sample_rate;
++ frame_size = (frame_size + padding) * 4;
++ break;
++ case 2:
++ frame_size = (frame_size * 144000) / sample_rate;
++ frame_size += padding;
++ break;
++ default:
++ case 3:
++ frame_size = (frame_size * 144000) / (sample_rate << s->lsf);
++ frame_size += padding;
++ break;
++ }
++ s->frame_size = frame_size;
++ }
++ else
++ {
++ /* if no frame size computed, signal it */
++ return false;
++ }
++
++ m_demuxer->SetAudioInformation(s->nb_channels, s->sample_rate, s->bit_rate, 0, 0);
++
++#if defined(DEBUG)
++#define MODE_EXT_MS_STEREO 2
++#define MODE_EXT_I_STEREO 1
++ printf("layer%d, %d Hz, %d kbits/s, ", s->layer, s->sample_rate, s->bit_rate);
++ if (s->nb_channels == 2)
++ {
++ if (s->layer == 3)
++ {
++ if (s->mode_ext & MODE_EXT_MS_STEREO)
++ printf("ms-");
++ if (s->mode_ext & MODE_EXT_I_STEREO)
++ printf("i-");
++ }
++ printf("stereo");
++ }
++ else
++ {
++ printf("mono");
++ }
++ printf("\n");
++#endif
++ return true;
++}
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_MPEGAudio.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_MPEGAudio.h
+new file mode 100644
+index 0000000..da8112c
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_MPEGAudio.h
+@@ -0,0 +1,118 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#ifndef VNSI_DEMUXER_MPEGAUDIO_H
++#define VNSI_DEMUXER_MPEGAUDIO_H
++
++#include "demuxer.h"
++
++// --- cParserMPEG2Audio -------------------------------------------------
++
++class cParserMPEG2Audio : public cParser
++{
++private:
++ typedef struct MPADecodeHeader
++ {
++ int frame_size;
++ int error_protection;
++ int layer;
++ int sample_rate;
++ int sample_rate_index; /* between 0 and 8 */
++ int bit_rate;
++ int nb_channels;
++ int mode;
++ int mode_ext;
++ int lsf;
++ } MPADecodeHeader;
++
++ cTSDemuxer *m_demuxer;
++ bool m_firstPUSIseen;
++ bool m_PESStart;
++
++ bool m_FetchTimestamp;
++ int64_t m_FrameOffset; /* offset of the current frame */
++ int64_t m_CurrentOffset; /* current offset (incremented by each av_parser_parse()) */
++ int64_t m_NextFrameOffset; /* offset of the next frame */
++
++#define AV_PARSER_PTS_NB 4
++ int m_CurrentFrameStartIndex;
++ int64_t m_CurrentFrameOffset[AV_PARSER_PTS_NB];
++ int64_t m_CurrentFramePTS[AV_PARSER_PTS_NB];
++ int64_t m_CurrentFrameDTS[AV_PARSER_PTS_NB];
++ int64_t m_CurrentFrameEnd[AV_PARSER_PTS_NB];
++ int64_t m_Offset; /* byte offset from starting packet start */
++ int64_t m_PTS; /* pts of the current frame */
++ int64_t m_DTS; /* dts of the current frame */
++ int64_t m_NextDTS;
++
++#define SAME_HEADER_MASK (0xffe00000 | (3 << 17) | (3 << 10) | (3 << 19))
++#define MPA_HEADER_SIZE 4
++#define MPA_MAX_CODED_FRAME_SIZE 1792
++#define MPA_STEREO 0
++#define MPA_JSTEREO 1
++#define MPA_DUAL 2
++#define MPA_MONO 3
++ int m_FrameSize;
++ int m_FrameFreeFormatFrameSize;
++ int m_FrameFreeFormatNextHeader;
++ uint32_t m_FrameHeader;
++ int m_HeaderCount;
++ int m_SampleRate;
++ int m_Channels;
++ int m_BitRate;
++ uint8_t m_FrameInBuffer[MPA_MAX_CODED_FRAME_SIZE]; /* input buffer */
++ uint8_t *m_FrameInBufferPtr;
++
++ /* fast header check for resync */
++ void FetchTimestamp(int off, bool remove);
++ int FindHeaders(uint8_t **poutbuf, int *poutbuf_size,
++ uint8_t *buf, int buf_size,
++ int64_t pts, int64_t dts);
++ static inline bool CheckHeader(uint32_t header)
++ {
++ /* header */
++ if ((header & 0xffe00000) != 0xffe00000)
++ return false;
++ /* layer check */
++ if ((header & (3<<17)) == 0)
++ return false;
++ /* bit rate */
++ if ((header & (0xf<<12)) == 0xf<<12)
++ return false;
++ /* frequency */
++ if ((header & (3<<10)) == 3<<10)
++ return false;
++ return true;
++ }
++ bool DecodeHeader(MPADecodeHeader *s, uint32_t header);
++
++public:
++ cParserMPEG2Audio(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID);
++ virtual ~cParserMPEG2Audio();
++
++ virtual void Parse(unsigned char *data, int size, bool pusi);
++};
++
++
++#endif // VNSI_DEMUXER_MPEGAUDIO_H
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_MPEGVideo.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_MPEGVideo.c
+new file mode 100644
+index 0000000..a972b16
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_MPEGVideo.c
+@@ -0,0 +1,414 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <stdlib.h>
++#include <assert.h>
++#include "config.h"
++#include "bitstream.h"
++#include "receiver.h"
++
++#include "demuxer_MPEGVideo.h"
++
++using namespace std;
++
++#define MPEG_PICTURE_START 0x00000100
++#define MPEG_SEQUENCE_START 0x000001b3
++#define MPEG_SEQUENCE_EXTENSION 0x000001b5
++#define MPEG_SLICE_S 0x00000101
++#define MPEG_SLICE_E 0x000001af
++
++/**
++ * MPEG2VIDEO frame duration table (in 90kHz clock domain)
++ */
++const unsigned int mpeg2video_framedurations[16] = {
++ 0,
++ 3753,
++ 3750,
++ 3600,
++ 3003,
++ 3000,
++ 1800,
++ 1501,
++ 1500,
++};
++
++cParserMPEG2Video::cParserMPEG2Video(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID)
++ : cParser(streamer, streamID)
++{
++ m_pictureBuffer = NULL;
++ m_pictureBufferSize = 0;
++ m_pictureBufferPtr = 0;
++ m_StartCond = 0;
++ m_StartCode = 0;
++ m_StartCodeOffset = 0;
++ m_FrameDuration = 0;
++ m_vbvDelay = -1;
++ m_vbvSize = 0;
++ m_Height = 0;
++ m_Width = 0;
++ m_StreamPacket = NULL;
++ m_demuxer = demuxer;
++}
++
++cParserMPEG2Video::~cParserMPEG2Video()
++{
++ while (!m_PTSQueue.empty())
++ {
++ sStreamPacket* pkt = m_PTSQueue.front();
++ m_PTSQueue.pop_front();
++ free(pkt->data);
++ delete pkt;
++ }
++ while (!m_DurationQueue.empty())
++ {
++ sStreamPacket* pkt = m_DurationQueue.front();
++ m_DurationQueue.pop_front();
++ free(pkt->data);
++ delete pkt;
++ }
++ if (m_pictureBuffer)
++ {
++ free(m_pictureBuffer);
++ m_pictureBuffer = NULL;
++ }
++}
++
++void cParserMPEG2Video::Parse(unsigned char *data, int size, bool pusi)
++{
++ uint32_t startcode = m_StartCond;
++
++ /* Parse PES header here for MPEG PS streams like from pvrinput */
++ if (pusi && m_Streamer->IsMPEGPS())
++ {
++ int hlen;
++
++ hlen = ParsePESHeader(data, size);
++ #if 0
++ int i;
++ for(i = 0; i < 16; i++)
++ printf("%02x.", data[i]);
++ printf(" %d\n", hlen);
++ #endif
++ data += hlen;
++ size -= hlen;
++
++ if(size < 1)
++ return;
++ }
++
++ if (m_pictureBuffer == NULL)
++ {
++ m_pictureBufferSize = 4000;
++ m_pictureBuffer = (uint8_t*)malloc(m_pictureBufferSize);
++ }
++
++ if (m_pictureBufferPtr + size + 4 >= m_pictureBufferSize)
++ {
++ m_pictureBufferSize += size * 4;
++ m_pictureBuffer = (uint8_t*)realloc(m_pictureBuffer, m_pictureBufferSize);
++ }
++
++ for (int i = 0; i < size; i++)
++ {
++ if (!m_pictureBuffer)
++ break;
++
++ m_pictureBuffer[m_pictureBufferPtr++] = data[i];
++ startcode = startcode << 8 | data[i];
++
++ if ((startcode & 0xffffff00) != 0x00000100)
++ continue;
++
++ bool reset = true;
++ if (m_pictureBufferPtr - 4 > 0 && m_StartCode != 0)
++ {
++ reset = Parse_MPEG2Video(m_pictureBufferPtr - 4, startcode, m_StartCodeOffset);
++ }
++
++ if (reset)
++ {
++ /* Reset packet parser upon length error or if parser tells us so */
++ m_pictureBufferPtr = 0;
++ m_pictureBuffer[m_pictureBufferPtr++] = startcode >> 24;
++ m_pictureBuffer[m_pictureBufferPtr++] = startcode >> 16;
++ m_pictureBuffer[m_pictureBufferPtr++] = startcode >> 8;
++ m_pictureBuffer[m_pictureBufferPtr++] = startcode >> 0;
++ }
++ m_StartCode = startcode;
++ m_StartCodeOffset = m_pictureBufferPtr - 4;
++ }
++ m_StartCond = startcode;
++}
++
++bool cParserMPEG2Video::Parse_MPEG2Video(size_t len, uint32_t next_startcode, int sc_offset)
++{
++ int frametype;
++ uint8_t *buf = m_pictureBuffer + sc_offset;
++ cBitstream bs(buf + 4, (len - 4) * 8);
++
++ switch (m_StartCode)
++ {
++ case 0x000001e0 ... 0x000001ef:
++ /* System start codes for video */
++ if (len >= 9)
++ ParsePESHeader(buf, len);
++ return true;
++
++ case 0x00000100:
++ /* Picture start code */
++ if (m_FrameDuration == 0 || m_curDTS == DVD_NOPTS_VALUE)
++ return true;
++
++ if (Parse_MPEG2Video_PicStart(&frametype, &bs))
++ return true;
++
++ if (m_StreamPacket != NULL)
++ {
++ ERRORLOG("MPEG2 Video got a new picture start code with already openend steam packed");
++ }
++
++ m_StreamPacket = new sStreamPacket;
++ m_StreamPacket->id = m_streamID;
++ m_StreamPacket->pts = m_curPTS;
++ m_StreamPacket->dts = m_curDTS;
++ m_StreamPacket->frametype = frametype;
++ m_StreamPacket->duration = 0;
++ m_FoundFrame = true;
++ break;
++
++ case 0x000001b3:
++ /* Sequence start code */
++ if (Parse_MPEG2Video_SeqStart(&bs))
++ return true;
++
++ break;
++
++ case 0x000001b5:
++ if(len < 5)
++ return true;
++ switch(buf[4] >> 4) {
++ case 0x1:
++ /* sequence extension */
++ // printf("Sequence extension, len = %d\n", len);
++ if(len < 10)
++ return true;
++ // printf("Profile = %d\n", buf[4] & 0x7);
++ // printf(" Level = %d\n", buf[5] >> 4);
++ break;
++ }
++ break;
++
++ case 0x00000101 ... 0x000001af:
++ /* Slices */
++
++ if (next_startcode == 0x100 || next_startcode > 0x1af)
++ {
++ /* Last picture slice (because next not a slice) */
++ if(m_StreamPacket == NULL)
++ {
++ /* no packet, may've been discarded by sanity checks here */
++ return true;
++ }
++
++ m_StreamPacket->data = m_pictureBuffer;
++ m_StreamPacket->size = m_pictureBufferPtr - 4;
++ m_StreamPacket->duration = m_FrameDuration;
++
++ Parse_ComputePTS(m_StreamPacket);
++ m_StreamPacket = NULL;
++
++ m_pictureBuffer = (uint8_t*)malloc(m_pictureBufferSize);
++
++ /* If we know the frame duration, increase DTS accordingly */
++ m_curDTS += m_FrameDuration;
++
++ /* PTS cannot be extrapolated (it's not linear) */
++ m_curPTS = DVD_NOPTS_VALUE;
++ return true;
++ }
++ break;
++
++ default:
++ break;
++ }
++
++ return false;
++}
++
++bool cParserMPEG2Video::Parse_MPEG2Video_SeqStart(cBitstream *bs)
++{
++ if (bs->length() < 61)
++ return true;
++
++ m_Width = bs->readBits(12);
++ m_Height = bs->readBits(12);
++
++ // figure out Display Aspect Ratio
++ double DAR = 0;
++ uint8_t aspect = bs->readBits(4);
++
++ switch(aspect)
++ {
++ case 0:
++ default:
++ ERRORLOG("invalid / forbidden DAR in sequence header !");
++ break;
++ case 1:
++ DAR = 1.0;
++ break;
++ case 2:
++ DAR = 4.0/3.0;
++ break;
++ case 3:
++ DAR = 16.0/9.0;
++ break;
++ case 4:
++ DAR = 2.21;
++ break;
++ }
++
++ DEBUGLOG("MPEG2 DAR: %.2f", DAR);
++
++ m_FrameDuration = mpeg2video_framedurations[bs->readBits(4)];
++ bs->skipBits(18);
++ bs->skipBits(1);
++
++ if (m_Width > 0)
++ m_Streamer->SetReady();
++
++ m_vbvSize = bs->readBits(10) * 16 * 1024 / 8;
++ m_demuxer->SetVideoInformation(0,0, m_Height, m_Width, DAR);
++
++ return false;
++}
++
++bool cParserMPEG2Video::Parse_MPEG2Video_PicStart(int *frametype, cBitstream *bs)
++{
++ if (bs->length() < 29)
++ return true;
++
++ bs->skipBits(10); /* temporal reference */
++
++ int pct = bs->readBits(3);
++ if (pct < PKT_I_FRAME || pct > PKT_B_FRAME)
++ return true; /* Illegal picture_coding_type */
++
++ *frametype = pct;
++
++ int vbvDelay = bs->readBits(16); /* vbv_delay */
++ if (vbvDelay == 0xffff)
++ m_vbvDelay = -1;
++ else
++ m_vbvDelay = Rescale(vbvDelay);
++
++ return false;
++}
++
++void cParserMPEG2Video::Parse_ComputePTS(sStreamPacket *pkt)
++{
++ bool validpts = pkt->pts != DVD_NOPTS_VALUE && m_PTSQueue.size() == 0;
++
++ /* PTS known and no other packets in queue, deliver at once */
++ if (validpts && pkt->duration)
++ {
++ if (m_Width > 0)
++ SendPacket(pkt);
++ free(pkt->data);
++ delete pkt;
++ return;
++ }
++
++ if (validpts)
++ return Parse_ComputeDuration(pkt);
++
++ m_PTSQueue.push_back(pkt);
++
++ while (!m_PTSQueue.empty())
++ {
++ pkt = m_PTSQueue.front();
++
++ switch (pkt->frametype)
++ {
++ case PKT_B_FRAME:
++ /* B-frames have same PTS as DTS, pass them on */
++ pkt->pts = pkt->dts;
++ break;
++
++ case PKT_I_FRAME:
++ case PKT_P_FRAME:
++ /* Presentation occures at DTS of next I or P frame, try to find it */
++ deque<sStreamPacket*>::iterator it;
++ it = m_PTSQueue.begin()+1;
++ while (1)
++ {
++ if (it >= m_PTSQueue.end())
++ return; /* not arrived yet, wait */
++
++ sStreamPacket* pkt2 = *it++;
++ if (pkt2->frametype <= PKT_P_FRAME)
++ {
++ pkt->pts = pkt2->dts;
++ break;
++ }
++ }
++ break;
++ }
++
++ m_PTSQueue.pop_front();
++
++ if (pkt->duration == 0)
++ {
++ Parse_ComputeDuration(pkt);
++ }
++ else
++ {
++ if (m_Width > 0)
++ SendPacket(pkt);
++ free(pkt->data);
++ delete pkt;
++ }
++ }
++}
++
++void cParserMPEG2Video::Parse_ComputeDuration(sStreamPacket *pkt)
++{
++ m_DurationQueue.push_back(pkt);
++
++ pkt = m_DurationQueue.front();
++ if (m_DurationQueue.size() <= 1)
++ return;
++
++ sStreamPacket *next = m_DurationQueue[1];
++
++ int64_t duration = next->dts - pkt->dts;
++ m_DurationQueue.pop_front();
++
++ if (duration >= 10)
++ {
++ pkt->duration = duration;
++ if (m_Width > 0)
++ SendPacket(pkt);
++ }
++ free(pkt->data);
++}
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_MPEGVideo.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_MPEGVideo.h
+new file mode 100644
+index 0000000..96fe66e
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_MPEGVideo.h
+@@ -0,0 +1,68 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#ifndef VNSI_DEMUXER_MPEGVIDEO_H
++#define VNSI_DEMUXER_MPEGVIDEO_H
++
++#include <deque>
++#include "demuxer.h"
++
++class cBitstream;
++
++// --- cParserMPEG2Video -------------------------------------------------
++
++class cParserMPEG2Video : public cParser
++{
++private:
++ std::deque<sStreamPacket*> m_DurationQueue;
++ std::deque<sStreamPacket*> m_PTSQueue;
++
++ cTSDemuxer *m_demuxer;
++ uint8_t *m_pictureBuffer;
++ int m_pictureBufferSize;
++ int m_pictureBufferPtr;
++ int m_FrameDuration;
++ uint32_t m_StartCond;
++ uint32_t m_StartCode;
++ int m_StartCodeOffset;
++ sStreamPacket *m_StreamPacket;
++ int m_vbvDelay; /* -1 if CBR */
++ int m_vbvSize; /* Video buffer size (in bytes) */
++ int m_Width;
++ int m_Height;
++
++ bool Parse_MPEG2Video(size_t len, uint32_t next_startcode, int sc_offset);
++ bool Parse_MPEG2Video_SeqStart(cBitstream *bs);
++ bool Parse_MPEG2Video_PicStart(int *frametype, cBitstream *bs);
++ void Parse_ComputePTS(sStreamPacket* pkt);
++ void Parse_ComputeDuration(sStreamPacket* pkt);
++
++public:
++ cParserMPEG2Video(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID);
++ virtual ~cParserMPEG2Video();
++
++ virtual void Parse(unsigned char *data, int size, bool pusi);
++};
++
++#endif // VNSI_DEMUXER_MPEGVIDEO_H
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_Subtitle.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_Subtitle.c
+new file mode 100644
+index 0000000..03785c7
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_Subtitle.c
+@@ -0,0 +1,139 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <stdlib.h>
++#include <assert.h>
++#include "config.h"
++
++#include "demuxer_Subtitle.h"
++
++cParserSubtitle::cParserSubtitle(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID)
++ : cParser(streamer, streamID)
++{
++ m_firstPUSIseen = false;
++ m_PESStart = false;
++ m_subtitleBuffer = NULL;
++ m_subtitleBufferSize = 0;
++ m_subtitleBufferPtr = 0;
++ m_lastDTS = DVD_NOPTS_VALUE;
++ m_lastPTS = DVD_NOPTS_VALUE;
++ m_lastLength = 0;
++ m_curLength = 0;
++}
++
++cParserSubtitle::~cParserSubtitle()
++{
++ if (m_subtitleBuffer)
++ free(m_subtitleBuffer);
++}
++
++void cParserSubtitle::Parse(unsigned char *data, int size, bool pusi)
++{
++ if (pusi)
++ {
++ /* Payload unit start */
++ m_subtitleBufferPtr = 0;
++ m_firstPUSIseen = true;
++ }
++
++ /* Wait for first pusi */
++ if (!m_firstPUSIseen)
++ return;
++
++ if (m_subtitleBuffer == NULL)
++ {
++ m_subtitleBufferSize = 4000;
++ m_subtitleBuffer = (uint8_t*)malloc(m_subtitleBufferSize);
++ }
++
++ if (m_subtitleBufferPtr + size + 4 >= m_subtitleBufferSize)
++ {
++ m_subtitleBufferSize += size * 4;
++ m_subtitleBuffer = (uint8_t*)realloc(m_subtitleBuffer, m_subtitleBufferSize);
++ }
++
++ memcpy(m_subtitleBuffer+m_subtitleBufferPtr, data, size);
++ m_subtitleBufferPtr += size;
++
++ if (m_subtitleBufferPtr < 6)
++ return;
++
++ uint32_t startcode =
++ (m_subtitleBuffer[0] << 24) |
++ (m_subtitleBuffer[1] << 16) |
++ (m_subtitleBuffer[2] << 8) |
++ (m_subtitleBuffer[3]);
++
++ if (startcode == 0x1be)
++ {
++ m_firstPUSIseen = false;
++ return;
++ }
++
++ int psize = m_subtitleBuffer[4] << 8 | m_subtitleBuffer[5];
++
++ if (m_subtitleBufferPtr != psize + 6)
++ return;
++
++ m_firstPUSIseen = false;
++
++ int hlen = ParsePESHeader(m_subtitleBuffer, m_subtitleBufferPtr);
++ if (hlen < 0)
++ return;
++
++ if (m_lastDTS == DVD_NOPTS_VALUE)
++ {
++ m_lastDTS = m_curDTS;
++ m_lastPTS = m_curPTS;
++ }
++
++ psize -= hlen - 6;
++ uint8_t *buf = m_subtitleBuffer + hlen;
++
++ if (psize < 2 || buf[0] != 0x20 || buf[1] != 0x00)
++ return;
++
++ psize -= 2;
++ buf += 2;
++
++ if (psize >= 6)
++ {
++ // end_of_PES_data_field_marker
++ if (buf[psize - 1] == 0xff)
++ {
++ sStreamPacket pkt;
++ pkt.id = m_streamID;
++ pkt.data = buf;
++ pkt.size = psize - 1;
++ pkt.duration = m_curDTS-m_lastDTS;
++ pkt.dts = m_curPTS;
++ pkt.pts = m_curPTS;
++ SendPacket(&pkt);
++
++ m_lastDTS = m_curDTS;
++ m_lastPTS = m_curPTS;
++ }
++ }
++
++}
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_Subtitle.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_Subtitle.h
+new file mode 100644
+index 0000000..6bc0ac2
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_Subtitle.h
+@@ -0,0 +1,53 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#ifndef VNSI_DEMUXER_SUBTITLE_H
++#define VNSI_DEMUXER_SUBTITLE_H
++
++#include "demuxer.h"
++
++// --- cParserSubtitle -------------------------------------------------
++
++class cParserSubtitle : public cParser
++{
++private:
++ bool m_firstPUSIseen;
++ bool m_PESStart;
++ uint8_t *m_subtitleBuffer;
++ int m_subtitleBufferSize;
++ int m_subtitleBufferPtr;
++ int64_t m_lastDTS;
++ int64_t m_lastPTS;
++ int m_lastLength;
++ int m_curLength;
++
++public:
++ cParserSubtitle(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID);
++ virtual ~cParserSubtitle();
++
++ virtual void Parse(unsigned char *data, int size, bool pusi);
++};
++
++
++#endif // VNSI_DEMUXER_SUBTITLE_H
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_Teletext.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_Teletext.c
+new file mode 100644
+index 0000000..3ac4a4e
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_Teletext.c
+@@ -0,0 +1,113 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <stdlib.h>
++#include "config.h"
++
++#include "demuxer_Teletext.h"
++
++cParserTeletext::cParserTeletext(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID)
++ : cParser(streamer, streamID)
++{
++ m_firstPUSIseen = false;
++ m_PESStart = false;
++ m_teletextBuffer = NULL;
++ m_teletextBufferSize = 0;
++ m_teletextBufferPtr = 0;
++ m_lastDTS = DVD_NOPTS_VALUE;
++ m_lastPTS = DVD_NOPTS_VALUE;
++}
++
++cParserTeletext::~cParserTeletext()
++{
++ if (m_teletextBuffer)
++ free(m_teletextBuffer);
++}
++
++void cParserTeletext::Parse(unsigned char *data, int size, bool pusi)
++{
++ if (pusi)
++ {
++ /* Payload unit start */
++ m_PESStart = true;
++ m_firstPUSIseen = true;
++ }
++
++ /* Wait for first pusi */
++ if (!m_firstPUSIseen)
++ return;
++
++ if (m_PESStart)
++ {
++ if (!PesIsPS1Packet(data))
++ {
++ ERRORLOG("Teletext PES packet contains no valid private stream 1, ignored this packet");
++ m_firstPUSIseen = false;
++ return;
++ }
++
++ m_lastDTS = m_curDTS;
++ m_lastPTS = m_curPTS;
++ int hlen = ParsePESHeader(data, size);
++
++ m_PESStart = false;
++ data += hlen;
++ size -= hlen;
++
++ if (data[0] < 0x10 || data[0] > 0x1F)
++ {
++ ERRORLOG("Teletext PES packet contains no valid identifier, ignored this packet");
++ m_firstPUSIseen = false;
++ return;
++ }
++
++ if (m_teletextBuffer && m_teletextBufferPtr > 0)
++ {
++ sStreamPacket pkt;
++ pkt.id = m_streamID;
++ pkt.data = m_teletextBuffer;
++ pkt.size = m_teletextBufferPtr;
++ pkt.duration = m_curDTS-m_lastDTS;
++ pkt.dts = m_lastDTS;
++ pkt.pts = m_lastPTS;
++ SendPacket(&pkt);
++ m_teletextBufferPtr = 0;
++ }
++ }
++
++ if (m_teletextBuffer == NULL)
++ {
++ m_teletextBufferSize = 4000;
++ m_teletextBuffer = (uint8_t*)malloc(m_teletextBufferSize);
++ }
++
++ if (m_teletextBufferPtr + size + 4 >= m_teletextBufferSize)
++ {
++ m_teletextBufferSize += size * 4;
++ m_teletextBuffer = (uint8_t*)realloc(m_teletextBuffer, m_teletextBufferSize);
++ }
++
++ memcpy(m_teletextBuffer+m_teletextBufferPtr, data, size);
++ m_teletextBufferPtr += size;
++}
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_Teletext.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_Teletext.h
+new file mode 100644
+index 0000000..d12be8f
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_Teletext.h
+@@ -0,0 +1,50 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#ifndef VNSI_DEMUXER_TELETEXT_H
++#define VNSI_DEMUXER_TELETEXT_H
++
++#include "demuxer.h"
++
++// --- cParserTeletext -------------------------------------------------
++
++class cParserTeletext : public cParser
++{
++private:
++ bool m_firstPUSIseen;
++ bool m_PESStart;
++ uint8_t *m_teletextBuffer;
++ int m_teletextBufferSize;
++ int m_teletextBufferPtr;
++ int64_t m_lastDTS;
++ int64_t m_lastPTS;
++
++public:
++ cParserTeletext(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID);
++ virtual ~cParserTeletext();
++
++ virtual void Parse(unsigned char *data, int size, bool pusi);
++};
++
++#endif // VNSI_DEMUXER_TELETEXT_H
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_h264.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_h264.c
+new file mode 100644
+index 0000000..2fdb151
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_h264.c
+@@ -0,0 +1,455 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <stdlib.h>
++#include <assert.h>
++#include "config.h"
++#include "bitstream.h"
++#include "receiver.h"
++
++#include "demuxer_h264.h"
++
++static const int h264_lev2cpbsize[][2] =
++{
++ {10, 175},
++ {11, 500},
++ {12, 1000},
++ {13, 2000},
++ {20, 2000},
++ {21, 4000},
++ {22, 4000},
++ {30, 10000},
++ {31, 14000},
++ {32, 20000},
++ {40, 25000},
++ {41, 62500},
++ {42, 62500},
++ {50, 135000},
++ {51, 240000},
++ {-1, -1},
++};
++
++cParserH264::cParserH264(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID)
++ : cParser(streamer, streamID)
++{
++ m_pictureBuffer = NULL;
++ m_pictureBufferSize = 0;
++ m_pictureBufferPtr = 0;
++ m_StartCond = 0;
++ m_StartCode = 0;
++ m_StartCodeOffset = 0;
++ m_PrevDTS = DVD_NOPTS_VALUE;
++ m_Height = 0;
++ m_Width = 0;
++ m_FrameDuration = 0;
++ m_demuxer = demuxer;
++ m_vbvDelay = -1;
++ m_vbvSize = 0;
++ m_firstIFrame = false;
++ m_PixelAspect.den = 1;
++ m_PixelAspect.num = 0;
++ memset(&m_streamData, 0, sizeof(m_streamData));
++}
++
++cParserH264::~cParserH264()
++{
++ if (m_pictureBuffer)
++ free(m_pictureBuffer);
++}
++
++void cParserH264::Parse(unsigned char *data, int size, bool pusi)
++{
++ uint32_t startcode = m_StartCond;
++
++ if (m_pictureBuffer == NULL)
++ {
++ m_pictureBufferSize = 80000;
++ m_pictureBuffer = (uint8_t*)malloc(m_pictureBufferSize);
++ }
++
++ if (m_pictureBufferPtr + size + 4 >= m_pictureBufferSize)
++ {
++ m_pictureBufferSize += size * 4;
++ m_pictureBuffer = (uint8_t*)realloc(m_pictureBuffer, m_pictureBufferSize);
++ }
++
++ for (int i = 0; i < size; i++)
++ {
++ m_pictureBuffer[m_pictureBufferPtr++] = data[i];
++ startcode = startcode << 8 | data[i];
++
++ if ((startcode & 0xffffff00) != 0x00000100)
++ continue;
++
++ bool reset = true;
++ if (m_pictureBufferPtr - 4 > 0 && m_StartCode != 0)
++ {
++ reset = Parse_H264(m_pictureBufferPtr - 4, startcode, m_StartCodeOffset);
++ }
++
++ if (reset)
++ {
++ /* Reset packet parser upon length error or if parser tells us so */
++ m_pictureBufferPtr = 0;
++ m_pictureBuffer[m_pictureBufferPtr++] = startcode >> 24;
++ m_pictureBuffer[m_pictureBufferPtr++] = startcode >> 16;
++ m_pictureBuffer[m_pictureBufferPtr++] = startcode >> 8;
++ m_pictureBuffer[m_pictureBufferPtr++] = startcode >> 0;
++ }
++ m_StartCode = startcode;
++ m_StartCodeOffset = m_pictureBufferPtr - 4;
++ }
++ m_StartCond = startcode;
++}
++
++bool cParserH264::Parse_H264(size_t len, uint32_t next_startcode, int sc_offset)
++{
++ uint8_t nal_data[len];
++ int pkttype;
++ uint8_t *buf = m_pictureBuffer + sc_offset;
++ uint32_t startcode = m_StartCode;
++
++ if (startcode == 0x10c)
++ {
++ /* RBSP padding, we don't want this */
++ int length = len - sc_offset;
++ memcpy(buf, buf + length, 4); /* Move down new start code */
++ m_pictureBufferPtr -= length; /* Drop buffer */
++ }
++
++ if (startcode >= 0x000001e0 && startcode <= 0x000001ef)
++ {
++ /* System start codes for video */
++ if (len >= 9)
++ ParsePESHeader(buf, len);
++
++ if (m_PrevDTS != DVD_NOPTS_VALUE)
++ {
++ int64_t duration = (m_curDTS - m_PrevDTS) & 0x1ffffffffLL;
++
++ if (duration < 90000)
++ m_FrameDuration = duration;
++ }
++ m_PrevDTS = m_curDTS;
++ return true;
++ }
++
++ switch(startcode & 0x1f)
++ {
++ case NAL_SPS:
++ {
++ int nal_len = nalUnescape(nal_data, buf + 4, len - 4);
++ if (!Parse_SPS(nal_data, nal_len))
++ return true;
++
++ double PAR = (double)m_PixelAspect.num/(double)m_PixelAspect.den;
++ double DAR = (PAR * m_Width) / m_Height;
++ DEBUGLOG("H.264 SPS: PAR %i:%i", m_PixelAspect.num, m_PixelAspect.den);
++ DEBUGLOG("H.264 SPS: DAR %.2f", DAR);
++
++ m_demuxer->SetVideoInformation(0,0, m_Height, m_Width, DAR);
++ break;
++ }
++
++ case NAL_PPS:
++ {
++ int nal_len = nalUnescape(nal_data, buf + 4, len - 4);
++ if (!Parse_PPS(nal_data, nal_len))
++ return true;
++
++ break;
++ }
++
++ case 5: /* IDR+SLICE */
++ case NAL_SLH:
++ {
++ if (m_FoundFrame || m_FrameDuration == 0 || m_curDTS == DVD_NOPTS_VALUE)
++ break;
++
++ int nal_len = nalUnescape(nal_data, buf + 4, len - 4 > 64 ? 64 : len - 3);
++ if (!Parse_SLH(nal_data, nal_len, &pkttype))
++ return true;
++
++ m_StreamPacket.id = m_streamID;
++ m_StreamPacket.pts = m_curPTS;
++ m_StreamPacket.dts = m_curDTS;
++ m_StreamPacket.frametype = pkttype;
++ m_StreamPacket.duration = m_FrameDuration;
++ m_FoundFrame = true;
++
++ if (pkttype == PKT_I_FRAME)
++ m_firstIFrame = true;
++
++ break;
++ }
++
++ default:
++ break;
++ }
++
++ if (next_startcode >= 0x000001e0 && next_startcode <= 0x000001ef)
++ {
++ /* Complete frame */
++ if (!m_FoundFrame)
++ return true;
++
++ /* Discard Packets until we have the picture size (XBMC can't enable VDPAU without it) */
++ if (!m_firstIFrame || m_Width <= 0)
++ return true;
++
++ // send packet (will be cached if the stream isn't ready yet)
++ m_FoundFrame = false;
++ m_StreamPacket.data = m_pictureBuffer;
++ m_StreamPacket.size = m_pictureBufferPtr;
++ SendPacket(&m_StreamPacket);
++
++ // signal stream is ready
++ m_Streamer->SetReady();
++ return true;
++ }
++
++ return false;
++}
++
++int cParserH264::nalUnescape(uint8_t *dst, const uint8_t *src, int len)
++{
++ int s = 0, d = 0;
++
++ while (s < len)
++ {
++ if (!src[s] && !src[s + 1])
++ {
++ // hit 00 00 xx
++ dst[d] = dst[d + 1] = 0;
++ s += 2;
++ d += 2;
++ if (src[s] == 3)
++ {
++ s++; // 00 00 03 xx --> 00 00 xx
++ if (s >= len)
++ return d;
++ }
++ }
++ dst[d++] = src[s++];
++ }
++
++ return d;
++}
++
++bool cParserH264::Parse_PPS(uint8_t *buf, int len)
++{
++ cBitstream bs(buf, len*8);
++
++ int pps_id = bs.readGolombUE();
++ int sps_id = bs.readGolombUE();
++ m_streamData.pps[pps_id].sps = sps_id;
++ return true;
++}
++
++bool cParserH264::Parse_SLH(uint8_t *buf, int len, int *pkttype)
++{
++ cBitstream bs(buf, len*8);
++
++ bs.readGolombUE(); /* first_mb_in_slice */
++ int slice_type = bs.readGolombUE();
++
++ if (slice_type > 4)
++ slice_type -= 5; /* Fixed slice type per frame */
++
++ switch (slice_type)
++ {
++ case 0:
++ *pkttype = PKT_P_FRAME;
++ break;
++ case 1:
++ *pkttype = PKT_B_FRAME;
++ break;
++ case 2:
++ *pkttype = PKT_I_FRAME;
++ break;
++ default:
++ return false;
++ }
++
++ int pps_id = bs.readGolombUE();
++ int sps_id = m_streamData.pps[pps_id].sps;
++ if (m_streamData.sps[sps_id].cbpsize == 0)
++ return false;
++
++ m_vbvSize = m_streamData.sps[sps_id].cbpsize;
++ m_vbvDelay = -1;
++ return true;
++}
++
++bool cParserH264::Parse_SPS(uint8_t *buf, int len)
++{
++ cBitstream bs(buf, len*8);
++ unsigned int tmp, frame_mbs_only;
++ int cbpsize = -1;
++
++ int profile_idc = bs.readBits(8);
++ /* constraint_set0_flag = bs.readBits1(); */
++ /* constraint_set1_flag = bs.readBits1(); */
++ /* constraint_set2_flag = bs.readBits1(); */
++ /* constraint_set3_flag = bs.readBits1(); */
++ /* reserved = bs.readBits(4); */
++ bs.skipBits(8);
++ int level_idc = bs.readBits(8);
++ unsigned int seq_parameter_set_id = bs.readGolombUE();
++
++ unsigned int i = 0;
++ while (h264_lev2cpbsize[i][0] != -1)
++ {
++ if (h264_lev2cpbsize[i][0] >= level_idc)
++ {
++ cbpsize = h264_lev2cpbsize[i][1];
++ break;
++ }
++ i++;
++ }
++ if (cbpsize < 0)
++ return false;
++
++ m_streamData.sps[seq_parameter_set_id].cbpsize = cbpsize * 125; /* Convert from kbit to bytes */
++
++ if (profile_idc >= 100) /* high profile */
++ {
++ if(bs.readGolombUE() == 3) /* chroma_format_idc */
++ bs.skipBits(1); /* residual_colour_transform_flag */
++ bs.readGolombUE(); /* bit_depth_luma - 8 */
++ bs.readGolombUE(); /* bit_depth_chroma - 8 */
++ bs.skipBits(1); /* transform_bypass */
++ if (bs.readBits1()) /* seq_scaling_matrix_present */
++ {
++ for (int i = 0; i < 8; i++)
++ {
++ if (bs.readBits1()) /* seq_scaling_list_present */
++ {
++ int last = 8, next = 8, size = (i<6) ? 16 : 64;
++ for (int j = 0; j < size; j++)
++ {
++ if (next)
++ next = (last + bs.readGolombSE()) & 0xff;
++ last = next ?: last;
++ }
++ }
++ }
++ }
++ }
++
++ bs.readGolombUE(); /* log2_max_frame_num - 4 */
++ int pic_order_cnt_type = bs.readGolombUE();
++ if (pic_order_cnt_type == 0)
++ bs.readGolombUE(); /* log2_max_poc_lsb - 4 */
++ else if (pic_order_cnt_type == 1)
++ {
++ bs.skipBits(1); /* delta_pic_order_always_zero */
++ bs.readGolombSE(); /* offset_for_non_ref_pic */
++ bs.readGolombSE(); /* offset_for_top_to_bottom_field */
++ tmp = bs.readGolombUE(); /* num_ref_frames_in_pic_order_cnt_cycle */
++ for (unsigned int i = 0; i < tmp; i++)
++ bs.readGolombSE(); /* offset_for_ref_frame[i] */
++ }
++ else if(pic_order_cnt_type != 2)
++ {
++ /* Illegal poc */
++ return false;
++ }
++
++ bs.readGolombUE(); /* ref_frames */
++ bs.skipBits(1); /* gaps_in_frame_num_allowed */
++ m_Width /* mbs */ = bs.readGolombUE() + 1;
++ m_Height /* mbs */ = bs.readGolombUE() + 1;
++ frame_mbs_only = bs.readBits1();
++ DEBUGLOG("H.264 SPS: pic_width: %u mbs", (unsigned) m_Width);
++ DEBUGLOG("H.264 SPS: pic_height: %u mbs", (unsigned) m_Height);
++ DEBUGLOG("H.264 SPS: frame only flag: %d", frame_mbs_only);
++
++ m_Width *= 16;
++ m_Height *= 16 * (2-frame_mbs_only);
++
++ if (!frame_mbs_only)
++ {
++ if (bs.readBits1()) /* mb_adaptive_frame_field_flag */
++ DEBUGLOG("H.264 SPS: MBAFF");
++ }
++ bs.skipBits(1); /* direct_8x8_inference_flag */
++ if (bs.readBits1()) /* frame_cropping_flag */
++ {
++ uint32_t crop_left = bs.readGolombUE();
++ uint32_t crop_right = bs.readGolombUE();
++ uint32_t crop_top = bs.readGolombUE();
++ uint32_t crop_bottom = bs.readGolombUE();
++ DEBUGLOG("H.264 SPS: cropping %d %d %d %d", crop_left, crop_top, crop_right, crop_bottom);
++
++ m_Width -= 2*(crop_left + crop_right);
++ if (frame_mbs_only)
++ m_Height -= 2*(crop_top + crop_bottom);
++ else
++ m_Height -= 4*(crop_top + crop_bottom);
++ }
++
++ /* VUI parameters */
++ m_PixelAspect.num = 0;
++ if (bs.readBits1()) /* vui_parameters_present flag */
++ {
++ if (bs.readBits1()) /* aspect_ratio_info_present */
++ {
++ uint32_t aspect_ratio_idc = bs.readBits(8);
++ DEBUGLOG("H.264 SPS: aspect_ratio_idc %d", aspect_ratio_idc);
++
++ if (aspect_ratio_idc == 255 /* Extended_SAR */)
++ {
++ m_PixelAspect.num = bs.readBits(16); /* sar_width */
++ m_PixelAspect.den = bs.readBits(16); /* sar_height */
++ DEBUGLOG("H.264 SPS: -> sar %dx%d", m_PixelAspect.num, m_PixelAspect.den);
++ }
++ else
++ {
++ static const mpeg_rational_t aspect_ratios[] =
++ { /* page 213: */
++ /* 0: unknown */
++ {0, 1},
++ /* 1...16: */
++ { 1, 1}, {12, 11}, {10, 11}, {16, 11}, { 40, 33}, {24, 11}, {20, 11}, {32, 11},
++ {80, 33}, {18, 11}, {15, 11}, {64, 33}, {160, 99}, { 4, 3}, { 3, 2}, { 2, 1}
++ };
++
++ if (aspect_ratio_idc < sizeof(aspect_ratios)/sizeof(aspect_ratios[0]))
++ {
++ memcpy(&m_PixelAspect, &aspect_ratios[aspect_ratio_idc], sizeof(mpeg_rational_t));
++ DEBUGLOG("H.264 SPS: PAR %d / %d", m_PixelAspect.num, m_PixelAspect.den);
++ }
++ else
++ {
++ DEBUGLOG("H.264 SPS: aspect_ratio_idc out of range !");
++ }
++ }
++ }
++ }
++
++ DEBUGLOG("H.264 SPS: -> video size %dx%d, aspect %d:%d", m_Width, m_Height, m_PixelAspect.num, m_PixelAspect.den);
++ return true;
++}
++
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_h264.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_h264.h
+new file mode 100644
+index 0000000..78dc9b7
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_h264.h
+@@ -0,0 +1,99 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#ifndef VNSI_DEMUXER_H264_H
++#define VNSI_DEMUXER_H264_H
++
++#include "demuxer.h"
++
++class cBitstream;
++
++// --- cParserH264 -------------------------------------------------
++
++class cParserH264 : public cParser
++{
++private:
++ typedef struct h264_private
++ {
++ struct
++ {
++ int frame_duration;
++ int cbpsize;
++ } sps[256];
++
++ struct
++ {
++ int sps;
++ } pps[256];
++
++ } h264_private_t;
++
++ typedef struct mpeg_rational_s {
++ int num;
++ int den;
++ } mpeg_rational_t;
++
++ enum
++ {
++ NAL_SLH = 0x01, // Slice Header
++ NAL_SEI = 0x06, // Supplemental Enhancement Information
++ NAL_SPS = 0x07, // Sequence Parameter Set
++ NAL_PPS = 0x08, // Picture Parameter Set
++ NAL_AUD = 0x09, // Access Unit Delimiter
++ NAL_END_SEQ = 0x0A // End of Sequence
++ };
++
++ cTSDemuxer *m_demuxer;
++ uint8_t *m_pictureBuffer;
++ int m_pictureBufferSize;
++ int m_pictureBufferPtr;
++ uint32_t m_StartCond;
++ uint32_t m_StartCode;
++ int m_StartCodeOffset;
++ int m_Width;
++ int m_Height;
++ mpeg_rational_t m_PixelAspect;
++ int64_t m_PrevDTS;
++ int m_FrameDuration;
++ sStreamPacket m_StreamPacket;
++ h264_private m_streamData;
++ int m_vbvDelay; /* -1 if CBR */
++ int m_vbvSize; /* Video buffer size (in bytes) */
++ bool m_firstIFrame;
++
++ bool Parse_H264(size_t len, uint32_t next_startcode, int sc_offset);
++ bool Parse_PPS(uint8_t *buf, int len);
++ bool Parse_SLH(uint8_t *buf, int len, int *pkttype);
++ bool Parse_SPS(uint8_t *buf, int len);
++ int nalUnescape(uint8_t *dst, const uint8_t *src, int len);
++
++public:
++ cParserH264(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID);
++ virtual ~cParserH264();
++
++ virtual void Parse(unsigned char *data, int size, bool pusi);
++};
++
++
++#endif // VNSI_DEMUXER_H264_H
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/hash.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/hash.c
+new file mode 100644
+index 0000000..888fd52
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/hash.c
+@@ -0,0 +1,111 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 1986 Gary S. Brown (CRC32 code)
++ * Copyright (C) 2011 Alexander Pipelka
++ *
++ * This Program is free software; you can redistribute 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <vdr/tools.h>
++#include <vdr/channels.h>
++
++#include "hash.h"
++
++static uint32_t crc32_tab[] = {
++ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
++ 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
++ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
++ 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
++ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
++ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
++ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
++ 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
++ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
++ 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
++ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
++ 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
++ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
++ 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
++ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
++ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
++ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
++ 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
++ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
++ 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
++ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
++ 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
++ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
++ 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
++ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
++ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
++ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
++ 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
++ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
++ 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
++ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
++ 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
++ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
++ 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
++ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
++ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
++ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
++ 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
++ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
++ 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
++ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
++ 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
++ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
++};
++
++uint32_t crc32(const unsigned char *buf, size_t size)
++{
++ uint32_t crc = 0xFFFFFFFF;
++ const uint8_t *p = (uint8_t*)buf;
++
++ while (size--)
++ crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
++
++ return (crc ^ ~0U) & 0x7FFFFFFF; // channeluid is signed
++}
++
++uint32_t CreateStringHash(const cString& string) {
++ const char* p = string;
++ int len = strlen(p);
++
++ return crc32((const unsigned char*)p, len);
++}
++
++uint32_t CreateChannelUID(const cChannel* channel) {
++ cString channelid = channel->GetChannelID().ToString();
++ return CreateStringHash(channelid);
++}
++
++const cChannel* FindChannelByUID(uint32_t channelUID) {
++ cChannel* result = NULL;
++
++ // maybe we need to use a lookup table
++ for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) {
++ cString channelid = channel->GetChannelID().ToString();
++ if(channelUID == CreateStringHash(channelid)) {
++ result = channel;
++ break;
++ }
++ }
++
++ return result;
++}
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/hash.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/hash.h
+new file mode 100644
+index 0000000..eba8a25
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/hash.h
+@@ -0,0 +1,37 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 1986 Gary S. Brown (CRC32 code)
++ * Copyright (C) 2011 Alexander Pipelka
++ *
++ * This Program is free software; you can redistribute 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#ifndef VNSI_HASH_H
++#define VNSI_HASH_H
++
++#include <stdint.h>
++#include <vdr/channels.h>
++
++class cChannel;
++
++uint32_t CreateChannelUID(const cChannel* channel);
++const cChannel* FindChannelByUID(uint32_t channelUID);
++
++uint32_t CreateStringHash(const cString& string);
++
++#endif // VNSI_HASH_H
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/patches/vdr-wirbelscan-0.0.5-pre11e-AddServiceInterface.diff b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/patches/vdr-wirbelscan-0.0.5-pre11e-AddServiceInterface.diff
+new file mode 100644
+index 0000000..769f25e
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/patches/vdr-wirbelscan-0.0.5-pre11e-AddServiceInterface.diff
+@@ -0,0 +1,426 @@
++diff -NaurwB wirbelscan-0.0.5-pre11e/common.h wirbelscan-patched/common.h
++--- wirbelscan-0.0.5-pre11e/common.h 2010-03-17 11:32:34.000000000 +0100
+++++ wirbelscan-patched/common.h 2010-04-19 00:55:36.000000000 +0200
++@@ -11,16 +11,7 @@
++
++ #include <linux/types.h>
++ #include <sys/ioctl.h>
++-
++-typedef enum scantype {
++- DVB_TERR = 0,
++- DVB_CABLE = 1,
++- DVB_SAT = 2,
++- PVRINPUT = 3,
++- PVRINPUT_FM = 4,
++- DVB_ATSC = 5,
++-} scantype_t;
++-
+++#include "wirbelscanservice.h"
++
++
++ /* generic functions */
++diff -NaurwB wirbelscan-0.0.5-pre11e/dvb_wrapper.c wirbelscan-patched/dvb_wrapper.c
++--- wirbelscan-0.0.5-pre11e/dvb_wrapper.c 2010-03-18 11:06:33.000000000 +0100
+++++ wirbelscan-patched/dvb_wrapper.c 2010-04-23 03:59:28.000000000 +0200
++@@ -1,5 +1,4 @@
++
++-#include <linux/dvb/frontend.h> //either API version 3.2 or 5.0
++ #include <linux/dvb/version.h>
++ #include <vdr/dvbdevice.h>
++ #include <vdr/channels.h>
++diff -NaurwB wirbelscan-0.0.5-pre11e/menusetup.c wirbelscan-patched/menusetup.c
++--- wirbelscan-0.0.5-pre11e/menusetup.c 2010-03-17 13:10:15.000000000 +0100
+++++ wirbelscan-patched/menusetup.c 2010-04-23 03:59:41.000000000 +0200
++@@ -7,7 +7,6 @@
++ */
++
++
++-#include <linux/dvb/frontend.h>
++ #include <vdr/menuitems.h>
++ #include <vdr/device.h>
++ #include <vdr/config.h>
++@@ -39,6 +38,7 @@
++ cOsdItem * ChanNew = NULL;
++ cOsdItem * ChanAll = NULL;
++ cOsdItem * ScanType = NULL;
+++sRemoteMenuScanning * RemoteMenuScanning = NULL;
++
++ #define LOGLEN 8
++ cOsdItem * LogMsg[LOGLEN];
++diff -NaurwB wirbelscan-0.0.5-pre11e/menusetup.h wirbelscan-patched/menusetup.h
++--- wirbelscan-0.0.5-pre11e/menusetup.h 2010-03-17 11:32:34.000000000 +0100
+++++ wirbelscan-patched/menusetup.h 2010-04-23 02:04:08.000000000 +0200
++@@ -49,6 +49,16 @@
++ bool DoScan (int DVB_Type);
++ void DoStop (void);
++
+++struct sRemoteMenuScanning {
+++ void (*SetPercentage)(int percent);
+++ void (*SetSignalStrength)(int strenght, bool locked);
+++ void (*SetDeviceInfo)(const char *Info);
+++ void (*SetTransponder)(const char *Info);
+++ void (*NewChannel)(const char *Name, bool isRadio, bool isEncrypted, bool isHD);
+++ void (*IsFinished)();
+++ void (*SetStatus)(int status);
+++};
+++
++ class cWirbelscan {
++ private:
++ public:
++@@ -91,5 +101,6 @@
++ void AddLogMsg(const char * Msg);
++ };
++ extern cMenuScanning * MenuScanning;
+++extern sRemoteMenuScanning * RemoteMenuScanning;
++
++ #endif
++diff -NaurwB wirbelscan-0.0.5-pre11e/scanfilter.c wirbelscan-patched/scanfilter.c
++--- wirbelscan-0.0.5-pre11e/scanfilter.c 2010-03-17 11:32:34.000000000 +0100
+++++ wirbelscan-patched/scanfilter.c 2010-04-23 01:55:30.000000000 +0200
++@@ -1000,6 +1000,8 @@
++ dlog(4, " SDT: old %s", *PrintChannel(channel));
++ channel->SetName(pn, ps, pp);
++ dlog(2, " Upd: %s", *PrintChannel(channel));
+++ if (RemoteMenuScanning)
+++ RemoteMenuScanning->NewChannel(channel->Name(), (channel->Vpid() == 0 && (channel->Apid(0) != 0 || channel->Dpid(0) != 0)), channel->Ca() != 0, sd->getServiceType() == 0x19);
++ }
++ }
++ else {
++@@ -1009,6 +1011,8 @@
++ transponder->CopyTransponderData(Channel());
++ dlog(3, " SDT: Add: %s", *PrintTransponder(transponder));
++ NewTransponders.Add(transponder);
+++ if (RemoteMenuScanning)
+++ RemoteMenuScanning->NewChannel(channel->Name(), (channel->Vpid() == 0 && (channel->Apid(0) != 0 || channel->Dpid(0) != 0)), channel->Ca() != 0, sd->getServiceType() == 0x19);
++ }
++ dlog(2, " SDT: Add %s", *PrintChannel(channel));
++ }
++diff -NaurwB wirbelscan-0.0.5-pre11e/scanner.c wirbelscan-patched/scanner.c
++--- wirbelscan-0.0.5-pre11e/scanner.c 2010-03-18 10:52:52.000000000 +0100
+++++ wirbelscan-patched/scanner.c 2010-04-23 04:04:27.000000000 +0200
++@@ -289,6 +289,8 @@
++ }
++
++ dlog(1, "%s", *PrintChannel(channel));
+++ if (RemoteMenuScanning)
+++ RemoteMenuScanning->NewChannel(channel->Name(), false, false, false);
++ Channels.IncBeingEdited();
++ Channels.Add(channel);
++ Channels.DecBeingEdited();
++@@ -358,6 +360,7 @@
++ if ((dev = GetPreferredDevice(aChannel)) == NULL) {
++ dlog(0, "No device available - exiting!");
++ if (MenuScanning) MenuScanning->SetStatus((status = 2));
+++ if (RemoteMenuScanning) RemoteMenuScanning->SetStatus((status = 2));
++ DeleteAndNull(aChannel);
++ return;
++ }
++@@ -366,6 +369,7 @@
++ GetTerrCapabilities(dev->CardIndex(), &crAuto, &modAuto, &invAuto, &bwAuto, &hAuto, &tmAuto, &gAuto);
++ dlog(1, "frontend %s supports", *GetFeName(dev->CardIndex()));
++ if (MenuScanning) MenuScanning->SetDeviceInfo(cString::sprintf("%s", *GetFeName(dev->CardIndex())));
+++ if (RemoteMenuScanning) RemoteMenuScanning->SetDeviceInfo(*cString::sprintf("%s", *GetFeName(dev->CardIndex())));
++
++ if (invAuto) {
++ dlog(1, "INVERSION_AUTO");
++@@ -439,6 +443,7 @@
++ if ((dev = GetPreferredDevice(aChannel)) == NULL) {
++ dlog(0, "No device available - exiting!");
++ if (MenuScanning) MenuScanning->SetStatus((status = 2));
+++ if (RemoteMenuScanning) RemoteMenuScanning->SetStatus((status = 2));
++ DeleteAndNull(aChannel);
++ return;
++ }
++@@ -447,6 +452,7 @@
++ GetCableCapabilities(dev->CardIndex(), &crAuto, &modAuto, &invAuto);
++ dlog(1, "frontend %s supports", *GetFeName(dev->CardIndex()));
++ if (MenuScanning) MenuScanning->SetDeviceInfo(cString::sprintf("%s", *GetFeName(dev->CardIndex())));
+++ if (RemoteMenuScanning) RemoteMenuScanning->SetDeviceInfo(*cString::sprintf("%s", *GetFeName(dev->CardIndex())));
++ if (invAuto) {
++ dlog(1, "INVERSION_AUTO");
++ caps_inversion = INVERSION_AUTO;
++@@ -526,6 +532,7 @@
++ #endif
++ dlog(0, "No DVB-S2 device available - trying fallback to DVB-S");
++ if (MenuScanning) MenuScanning->SetStatus(3);
+++ if (RemoteMenuScanning) RemoteMenuScanning->SetStatus(3);
++ // SetSatTransponderDataFromVDR(aChannel, cSource::FromString(sat_list[this_channellist].source_id), 11112, eHorizontal, 27500, eCoderate56, eSatModulationQpsk, eDvbs, eRolloff35);
++ SetSatTransponderDataFromDVB(aChannel,
++ cSource::FromString(sat_list[this_channellist].source_id),
++@@ -536,6 +543,7 @@
++ if ((dev = GetPreferredDevice(aChannel)) == NULL) {
++ dlog(0, "No device available - exiting!");
++ if (MenuScanning) MenuScanning->SetStatus((status = 2));
+++ if (RemoteMenuScanning) RemoteMenuScanning->SetStatus((status = 2));
++ DeleteAndNull(aChannel);
++ return;
++ }
++@@ -545,6 +553,7 @@
++ GetSatCapabilities(dev->CardIndex(), &crAuto, &modAuto, &roAuto, &s2Support);
++ dlog(1, "frontend %s supports", *GetFeName(dev->CardIndex()));
++ if (MenuScanning) MenuScanning->SetDeviceInfo(cString::sprintf("%s", *GetFeName(dev->CardIndex())));
+++ if (RemoteMenuScanning) RemoteMenuScanning->SetDeviceInfo(*cString::sprintf("%s", *GetFeName(dev->CardIndex())));
++ caps_inversion = INVERSION_AUTO;
++ if (crAuto) {
++ dlog(1, "FEC_AUTO");
++@@ -578,6 +587,7 @@
++ if ((dev = GetPreferredDevice(aChannel)) == NULL) {
++ dlog(0, "No device available - exiting!");
++ if (MenuScanning) MenuScanning->SetStatus((status = 2));
+++ if (RemoteMenuScanning) RemoteMenuScanning->SetStatus((status = 2));
++ DeleteAndNull(aChannel);
++ return;
++ }
++@@ -586,6 +596,7 @@
++ GetAtscCapabilities(dev->CardIndex(), &crAuto, &modAuto, &invAuto, &vsbSupport, &qamSupport);
++ dlog(1, "frontend %s supports", *GetFeName(dev->CardIndex()));
++ if (MenuScanning) MenuScanning->SetDeviceInfo(cString::sprintf("%s", *GetFeName(dev->CardIndex())));
+++ if (RemoteMenuScanning) RemoteMenuScanning->SetDeviceInfo(*cString::sprintf("%s", *GetFeName(dev->CardIndex())));
++ if (invAuto) {
++ dlog(1, "INVERSION_AUTO\n");
++ caps_inversion = INVERSION_AUTO;
++@@ -660,6 +671,7 @@
++ #endif
++ dlog(0, "No device available - exiting! (pvrinput not running?)");
++ if (MenuScanning) MenuScanning->SetStatus((status = 2));
+++ if (RemoteMenuScanning) RemoteMenuScanning->SetStatus((status = 2));
++ DeleteAndNull(aChannel);
++ return;
++ }
++@@ -707,6 +719,8 @@
++ }
++ if (MenuScanning)
++ MenuScanning->SetDeviceInfo(cString::sprintf("%s", vcap.card));
+++ if (RemoteMenuScanning)
+++ RemoteMenuScanning->SetDeviceInfo(*cString::sprintf("%s", vcap.card));
++ dev->DetachAllReceivers();
++ break;
++ }
++@@ -716,6 +730,7 @@
++ } // end switch type
++
++ if (MenuScanning) MenuScanning->SetStatus(1);
+++ if (RemoteMenuScanning) RemoteMenuScanning->SetStatus(1);
++
++ //count channels.
++
++@@ -933,6 +948,11 @@
++ type, (lastChannel - thisChannel));
++ MenuScanning->SetTransponder(aChannel);
++ }
+++ if (RemoteMenuScanning)
+++ {
+++ RemoteMenuScanning->SetPercentage((int) (thisChannel * 100) / lastChannel);
+++ RemoteMenuScanning->SetTransponder(*PrintTransponder(aChannel));
+++ }
++ dev->SwitchChannel(aChannel, false);
++ SwReceiver = new cSwReceiver::cSwReceiver(aChannel);
++ dev->AttachReceiver(SwReceiver);
++@@ -952,6 +972,7 @@
++ lock = false;
++
++ if (MenuScanning) MenuScanning->SetStr(GetFrontendStrength(dev->CardIndex()), lock);
+++ if (RemoteMenuScanning) RemoteMenuScanning->SetSignalStrength(GetFrontendStrength(dev->CardIndex()), lock);
++ if (! lock) {
++ continue;
++ }
++@@ -971,6 +992,8 @@
++ cChannel * newChannel = new cChannel;
++ if (MenuScanning)
++ MenuScanning->SetStr(s, true);
+++ if (RemoteMenuScanning)
+++ RemoteMenuScanning->SetSignalStrength(s, true);
++
++ newChannel->Parse(*aChannel->ToText());
++ newChannel->SetName(*channelname, *shortname, (const char *) "analog");
++@@ -988,6 +1011,8 @@
++ else {
++ if (MenuScanning)
++ MenuScanning->SetStr(0, false);
+++ if (RemoteMenuScanning)
+++ RemoteMenuScanning->SetSignalStrength(0, false);
++ }
++ break;
++ }
++@@ -1108,6 +1133,13 @@
++
++ stop:
++ if (MenuScanning) MenuScanning->SetStatus((status = 0));
+++ if (RemoteMenuScanning)
+++ {
+++ RemoteMenuScanning->SetStatus((status = 0));
+++ RemoteMenuScanning->IsFinished();
+++ delete RemoteMenuScanning;
+++ RemoteMenuScanning = NULL;
+++ }
++ dlog(3, "leaving scanner");
++ Cancel(0);
++ }
++diff -NaurwB wirbelscan-0.0.5-pre11e/statemachine.c wirbelscan-patched/statemachine.c
++--- wirbelscan-0.0.5-pre11e/statemachine.c 2010-03-17 11:32:34.000000000 +0100
+++++ wirbelscan-patched/statemachine.c 2010-04-22 17:07:02.000000000 +0200
++@@ -129,6 +129,11 @@
++ MenuScanning->SetTransponder(Transponder);
++ MenuScanning->SetProgress(-1, DVB_TERR, -1);
++ }
+++ if (RemoteMenuScanning)
+++ {
+++ RemoteMenuScanning->SetPercentage(-1);
+++ RemoteMenuScanning->SetTransponder(*PrintTransponder(Transponder));
+++ }
++
++ ScannedTransponder = new cChannel(* Transponder);
++ ScannedTransponders.Add(ScannedTransponder);
++@@ -143,6 +148,8 @@
++ }
++ if (MenuScanning)
++ MenuScanning->SetStr(GetFrontendStrength(dev->CardIndex()), dev->HasLock(1));
+++ if (RemoteMenuScanning)
+++ RemoteMenuScanning->SetSignalStrength(GetFrontendStrength(dev->CardIndex()), dev->HasLock(1));
++ break;
++
++ case eNextTransponder:
++diff -NaurwB wirbelscan-0.0.5-pre11e/wirbelscan.c wirbelscan-patched/wirbelscan.c
++--- wirbelscan-0.0.5-pre11e/wirbelscan.c 2010-03-17 11:32:34.000000000 +0100
+++++ wirbelscan-patched/wirbelscan.c 2010-04-23 03:17:50.000000000 +0200
++@@ -9,6 +9,8 @@
++ #include <vdr/plugin.h>
++ #include <vdr/i18n.h>
++ #include "menusetup.h"
+++#include "countries.h"
+++#include "satellites.h"
++ #if VDRVERSNUM < 10507
++ #include "i18n.h"
++ #endif
++@@ -141,7 +143,69 @@
++
++ bool cPluginWirbelscan::Service(const char *Id, void *Data)
++ {
++- // Handle custom service requests from other plugins
+++ if (strcmp(Id,"WirbelScanService-DoScan-v1.0") == 0)
+++ {
+++ if (Data)
+++ {
+++ WirbelScanService_DoScan_v1_0 *svc = (WirbelScanService_DoScan_v1_0*)Data;
+++
+++ Wirbelscan.scanflags = svc->scan_tv ? SCAN_TV : 0;
+++ Wirbelscan.scanflags |= svc->scan_radio ? SCAN_RADIO : 0;
+++ Wirbelscan.scanflags |= svc->scan_scrambled ? SCAN_SCRAMBLED : 0;
+++ Wirbelscan.scanflags |= svc->scan_fta ? SCAN_FTA : 0;
+++ Wirbelscan.scanflags |= svc->scan_hd ? SCAN_HD : 0;
+++ Wirbelscan.CountryIndex = svc->CountryIndex;
+++ Wirbelscan.DVBC_Inversion = svc->DVBC_Inversion;
+++ Wirbelscan.DVBC_Symbolrate = svc->DVBC_Symbolrate;
+++ Wirbelscan.DVBC_QAM = svc->DVBC_QAM;
+++ Wirbelscan.DVBT_Inversion = svc->DVBT_Inversion;
+++ Wirbelscan.SatIndex = svc->SatIndex;
+++ Wirbelscan.ATSC_type = svc->ATSC_Type;
+++
+++ RemoteMenuScanning = new sRemoteMenuScanning;
+++ RemoteMenuScanning->SetPercentage = svc->SetPercentage;
+++ RemoteMenuScanning->SetSignalStrength = svc->SetSignalStrength;
+++ RemoteMenuScanning->SetDeviceInfo = svc->SetDeviceInfo;
+++ RemoteMenuScanning->SetTransponder = svc->SetTransponder;
+++ RemoteMenuScanning->NewChannel = svc->NewChannel;
+++ RemoteMenuScanning->IsFinished = svc->IsFinished;
+++ RemoteMenuScanning->SetStatus = svc->SetStatus;
+++
+++ return DoScan(svc->type);
+++ }
+++ }
+++ else if (strcmp(Id,"WirbelScanService-StopScan-v1.0") == 0)
+++ {
+++ DoStop();
+++ return true;
+++ }
+++ else if (strcmp(Id,"WirbelScanService-GetCountries-v1.0") == 0)
+++ {
+++ if (Data)
+++ {
+++ WirbelScanService_GetCountries_v1_0 SetCountry = (WirbelScanService_GetCountries_v1_0) Data;
+++ for (int i=0; i < COUNTRY::country_count(); i++)
+++ {
+++ SetCountry(COUNTRY::country_list[i].id, COUNTRY::country_list[i].short_name, COUNTRY::country_list[i].full_name);
+++ }
+++ return true;
+++ }
+++ }
+++ else if (strcmp(Id,"WirbelScanService-GetSatellites-v1.0") == 0)
+++ {
+++ if (Data)
+++ {
+++ WirbelScanService_GetSatellites_v1_0 SetSatellite = (WirbelScanService_GetSatellites_v1_0) Data;
+++ for (int i=0; i < sat_count(); i++)
+++ {
+++ SetSatellite(sat_list[i].id, sat_list[i].short_name, sat_list[i].full_name);
+++ }
+++ return true;
+++ }
+++
+++ return true;
+++ }
+++
++ return false;
++ }
++
++diff -NaurwB wirbelscan-0.0.5-pre11e/wirbelscanservice.h wirbelscan-patched/wirbelscanservice.h
++--- wirbelscan-0.0.5-pre11e/wirbelscanservice.h 1970-01-01 01:00:00.000000000 +0100
+++++ wirbelscan-patched/wirbelscanservice.h 2010-04-23 02:03:56.000000000 +0200
++@@ -0,0 +1,57 @@
+++/*
+++ * wirbelscan.c: A plugin for the Video Disk Recorder
+++ *
+++ * See the README file for copyright information and how to reach the author.
+++ *
+++ * $Id$
+++ */
+++
+++#ifndef __WIRBELSCAN_SERVICE_H
+++#define __WIRBELSCAN_SERVICE_H
+++
+++typedef enum scantype
+++{
+++ DVB_TERR = 0,
+++ DVB_CABLE = 1,
+++ DVB_SAT = 2,
+++ PVRINPUT = 3,
+++ PVRINPUT_FM = 4,
+++ DVB_ATSC = 5,
+++} scantype_t;
+++
+++typedef void (*WirbelScanService_GetCountries_v1_0)(int index, const char *isoName, const char *longName);
+++typedef void (*WirbelScanService_GetSatellites_v1_0)(int index, const char *shortName, const char *longName);
+++
+++struct WirbelScanService_DoScan_v1_0
+++{
+++ scantype_t type;
+++
+++ bool scan_tv;
+++ bool scan_radio;
+++ bool scan_fta;
+++ bool scan_scrambled;
+++ bool scan_hd;
+++
+++ int CountryIndex;
+++
+++ int DVBC_Inversion;
+++ int DVBC_Symbolrate;
+++ int DVBC_QAM;
+++
+++ int DVBT_Inversion;
+++
+++ int SatIndex;
+++
+++ int ATSC_Type;
+++
+++ void (*SetPercentage)(int percent);
+++ void (*SetSignalStrength)(int strenght, bool locked);
+++ void (*SetDeviceInfo)(const char *Info);
+++ void (*SetTransponder)(const char *Info);
+++ void (*NewChannel)(const char *Name, bool isRadio, bool isEncrypted, bool isHD);
+++ void (*IsFinished)();
+++ void (*SetStatus)(int status);
+++};
+++
+++#endif //__WIRBELSCAN_SERVICE_H
+++
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/receiver.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/receiver.c
+new file mode 100644
+index 0000000..f07a1c5
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/receiver.c
+@@ -0,0 +1,1212 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (Team XBMC)
++ * Copyright (C) 2010, 2011 Alexander Pipelka
++ *
++ * 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <stdlib.h>
++#include <sys/ioctl.h>
++#include <time.h>
++
++#include <libsi/section.h>
++#include <libsi/descriptor.h>
++
++#include <vdr/remux.h>
++#include <vdr/channels.h>
++#include <asm/byteorder.h>
++
++#include "config.h"
++#include "receiver.h"
++#include "cxsocket.h"
++#include "vnsicommand.h"
++#include "responsepacket.h"
++
++// --- cLiveReceiver -------------------------------------------------
++
++class cLiveReceiver: public cReceiver
++{
++ friend class cLiveStreamer;
++
++private:
++ cLiveStreamer *m_Streamer;
++
++protected:
++ virtual void Activate(bool On);
++ virtual void Receive(uchar *Data, int Length);
++
++public:
++ cLiveReceiver(cLiveStreamer *Streamer, tChannelID ChannelID, int Priority, const int *Pids);
++ virtual ~cLiveReceiver();
++};
++
++cLiveReceiver::cLiveReceiver(cLiveStreamer *Streamer, tChannelID ChannelID, int Priority, const int *Pids)
++ : cReceiver(ChannelID, Priority, 0, Pids)
++ , m_Streamer(Streamer)
++{
++ DEBUGLOG("Starting live receiver");
++}
++
++cLiveReceiver::~cLiveReceiver()
++{
++ DEBUGLOG("Killing live receiver");
++}
++
++void cLiveReceiver::Receive(uchar *Data, int Length)
++{
++ int p = m_Streamer->Put(Data, Length);
++
++ if (p != Length)
++ m_Streamer->ReportOverflow(Length - p);
++}
++
++inline void cLiveReceiver::Activate(bool On)
++{
++ m_Streamer->Activate(On);
++}
++
++// --- cLivePatFilter ----------------------------------------------------
++
++class cLivePatFilter : public cFilter
++{
++private:
++ int m_pmtPid;
++ int m_pmtSid;
++ int m_pmtVersion;
++ const cChannel *m_Channel;
++ cLiveStreamer *m_Streamer;
++
++ int GetPid(SI::PMT::Stream& stream, eStreamType *type, char *langs, int *subtitlingType, int *compositionPageId, int *ancillaryPageId);
++ void GetLanguage(SI::PMT::Stream& stream, char *langs);
++ virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
++
++public:
++ cLivePatFilter(cLiveStreamer *Streamer, const cChannel *Channel);
++};
++
++cLivePatFilter::cLivePatFilter(cLiveStreamer *Streamer, const cChannel *Channel)
++{
++ DEBUGLOG("cStreamdevPatFilter(\"%s\")", Channel->Name());
++ m_Channel = Channel;
++ m_Streamer = Streamer;
++ m_pmtPid = 0;
++ m_pmtSid = 0;
++ m_pmtVersion = -1;
++ Set(0x00, 0x00); // PAT
++
++}
++
++static const char * const psStreamTypes[] = {
++ "UNKNOWN",
++ "ISO/IEC 11172 Video",
++ "ISO/IEC 13818-2 Video",
++ "ISO/IEC 11172 Audio",
++ "ISO/IEC 13818-3 Audio",
++ "ISO/IEC 13818-1 Privete sections",
++ "ISO/IEC 13818-1 Private PES data",
++ "ISO/IEC 13512 MHEG",
++ "ISO/IEC 13818-1 Annex A DSM CC",
++ "0x09",
++ "ISO/IEC 13818-6 Multiprotocol encapsulation",
++ "ISO/IEC 13818-6 DSM-CC U-N Messages",
++ "ISO/IEC 13818-6 Stream Descriptors",
++ "ISO/IEC 13818-6 Sections (any type, including private data)",
++ "ISO/IEC 13818-1 auxiliary",
++ "ISO/IEC 13818-7 Audio with ADTS transport sytax",
++ "ISO/IEC 14496-2 Visual (MPEG-4)",
++ "ISO/IEC 14496-3 Audio with LATM transport syntax",
++ "0x12", "0x13", "0x14", "0x15", "0x16", "0x17", "0x18", "0x19", "0x1a",
++ "ISO/IEC 14496-10 Video (MPEG-4 part 10/AVC, aka H.264)",
++ "",
++};
++
++void cLivePatFilter::GetLanguage(SI::PMT::Stream& stream, char *langs)
++{
++ SI::Descriptor *d;
++ for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); )
++ {
++ switch (d->getDescriptorTag())
++ {
++ case SI::ISO639LanguageDescriptorTag:
++ {
++ SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d;
++ strn0cpy(langs, I18nNormalizeLanguageCode(ld->languageCode), MAXLANGCODE1);
++ break;
++ }
++ default: ;
++ }
++ delete d;
++ }
++}
++
++int cLivePatFilter::GetPid(SI::PMT::Stream& stream, eStreamType *type, char *langs, int *subtitlingType, int *compositionPageId, int *ancillaryPageId)
++{
++ SI::Descriptor *d;
++ *langs = 0;
++
++ if (!stream.getPid())
++ return 0;
++
++ if(m_Channel->Tpid() == stream.getPid())
++ {
++ DEBUGLOG("cStreamdevPatFilter PMT scanner: adding PID %d %s\n", stream.getPid(), "Teletext");
++ *type = stTELETEXT;
++ return stream.getPid();
++ }
++
++ switch (stream.getStreamType())
++ {
++ case 0x01: // ISO/IEC 11172 Video
++ case 0x02: // ISO/IEC 13818-2 Video
++ case 0x80: // ATSC Video MPEG2 (ATSC DigiCipher QAM)
++ DEBUGLOG("cStreamdevPatFilter PMT scanner adding PID %d (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()]);
++ *type = stMPEG2VIDEO;
++ return stream.getPid();
++ case 0x03: // ISO/IEC 11172 Audio
++ case 0x04: // ISO/IEC 13818-3 Audio
++ *type = stMPEG2AUDIO;
++ GetLanguage(stream, langs);
++ DEBUGLOG("cStreamdevPatFilter PMT scanner adding PID %d (%s) (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()], langs);
++ return stream.getPid();
++ case 0x0f: // ISO/IEC 13818-7 Audio with ADTS transport syntax
++ case 0x11: // ISO/IEC 14496-3 Audio with LATM transport syntax
++ *type = stAAC;
++ GetLanguage(stream, langs);
++ DEBUGLOG("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "AAC", langs);
++ return stream.getPid();
++#if 1
++ case 0x07: // ISO/IEC 13512 MHEG
++ case 0x08: // ISO/IEC 13818-1 Annex A DSM CC
++ case 0x0a: // ISO/IEC 13818-6 Multiprotocol encapsulation
++ case 0x0b: // ISO/IEC 13818-6 DSM-CC U-N Messages
++ case 0x0c: // ISO/IEC 13818-6 Stream Descriptors
++ case 0x0d: // ISO/IEC 13818-6 Sections (any type, including private data)
++ case 0x0e: // ISO/IEC 13818-1 auxiliary
++#endif
++ case 0x10: // ISO/IEC 14496-2 Visual (MPEG-4)
++ DEBUGLOG("cStreamdevPatFilter PMT scanner: Not adding PID %d (%s) (skipped)\n", stream.getPid(), psStreamTypes[stream.getStreamType()]);
++ break;
++ case 0x1b: // ISO/IEC 14496-10 Video (MPEG-4 part 10/AVC, aka H.264)
++ DEBUGLOG("cStreamdevPatFilter PMT scanner adding PID %d (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()]);
++ *type = stH264;
++ return stream.getPid();
++ case 0x05: // ISO/IEC 13818-1 private sections
++ case 0x06: // ISO/IEC 13818-1 PES packets containing private data
++ for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); )
++ {
++ switch (d->getDescriptorTag())
++ {
++ case SI::AC3DescriptorTag:
++ DEBUGLOG("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "AC3", langs);
++ *type = stAC3;
++ GetLanguage(stream, langs);
++ delete d;
++ return stream.getPid();
++ case SI::EnhancedAC3DescriptorTag:
++ DEBUGLOG("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "EAC3", langs);
++ *type = stEAC3;
++ GetLanguage(stream, langs);
++ delete d;
++ return stream.getPid();
++ case SI::DTSDescriptorTag:
++ DEBUGLOG("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "DTS", langs);
++ *type = stDTS;
++ GetLanguage(stream, langs);
++ delete d;
++ return stream.getPid();
++ case SI::AACDescriptorTag:
++ DEBUGLOG("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "AAC", langs);
++ *type = stAAC;
++ GetLanguage(stream, langs);
++ delete d;
++ return stream.getPid();
++ case SI::TeletextDescriptorTag:
++ DEBUGLOG("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "Teletext");
++ *type = stTELETEXT;
++ delete d;
++ return stream.getPid();
++ case SI::SubtitlingDescriptorTag:
++ {
++ *type = stDVBSUB;
++ *langs = 0;
++ *subtitlingType = 0;
++ *compositionPageId = 0;
++ *ancillaryPageId = 0;
++ SI::SubtitlingDescriptor *sd = (SI::SubtitlingDescriptor *)d;
++ SI::SubtitlingDescriptor::Subtitling sub;
++ char *s = langs;
++ int n = 0;
++ for (SI::Loop::Iterator it; sd->subtitlingLoop.getNext(sub, it); )
++ {
++ if (sub.languageCode[0])
++ {
++ *subtitlingType = sub.getSubtitlingType();
++ *compositionPageId = sub.getCompositionPageId();
++ *ancillaryPageId = sub.getAncillaryPageId();
++ if (n > 0)
++ *s++ = '+';
++ strn0cpy(s, I18nNormalizeLanguageCode(sub.languageCode), MAXLANGCODE1);
++ s += strlen(s);
++ if (n++ > 1)
++ break;
++ }
++ }
++ delete d;
++ DEBUGLOG("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "DVBSUB");
++ return stream.getPid();
++ }
++ default:
++ DEBUGLOG("cStreamdevPatFilter PMT scanner: NOT adding PID %d (%s) %s (%i)\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "UNKNOWN", d->getDescriptorTag());
++ break;
++ }
++ delete d;
++ }
++ break;
++ default:
++ /* This following section handles all the cases where the audio track
++ * info is stored in PMT user info with stream id >= 0x81
++ * we check the registration format identifier to see if it
++ * holds "AC-3"
++ */
++ if (stream.getStreamType() >= 0x81)
++ {
++ bool found = false;
++ for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); )
++ {
++ switch (d->getDescriptorTag())
++ {
++ case SI::RegistrationDescriptorTag:
++ /* unfortunately libsi does not implement RegistrationDescriptor */
++ if (d->getLength() >= 4)
++ {
++ found = true;
++ SI::CharArray rawdata = d->getData();
++ if (/*rawdata[0] == 5 && rawdata[1] >= 4 && */
++ rawdata[2] == 'A' && rawdata[3] == 'C' &&
++ rawdata[4] == '-' && rawdata[5] == '3')
++ {
++ DEBUGLOG("cStreamdevPatFilter PMT scanner: Adding pid %d (type 0x%x) RegDesc len %d (%c%c%c%c)\n",
++ stream.getPid(), stream.getStreamType(), d->getLength(), rawdata[2], rawdata[3], rawdata[4], rawdata[5]);
++ *type = stAC3;
++ delete d;
++ return stream.getPid();
++ }
++ }
++ break;
++ default:
++ break;
++ }
++ delete d;
++ }
++ if (!found)
++ {
++ DEBUGLOG("NOT adding PID %d (type 0x%x) RegDesc not found -> UNKNOWN\n", stream.getPid(), stream.getStreamType());
++ }
++ }
++ DEBUGLOG("cStreamdevPatFilter PMT scanner: NOT adding PID %d (%s) %s\n", stream.getPid(), psStreamTypes[stream.getStreamType()<0x1c?stream.getStreamType():0], "UNKNOWN");
++ break;
++ }
++ *type = stNone;
++ return 0;
++}
++
++void cLivePatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
++{
++ if (Pid == 0x00)
++ {
++ if (Tid == 0x00)
++ {
++ SI::PAT pat(Data, false);
++ if (!pat.CheckCRCAndParse())
++ return;
++ SI::PAT::Association assoc;
++ for (SI::Loop::Iterator it; pat.associationLoop.getNext(assoc, it); )
++ {
++ if (!assoc.isNITPid())
++ {
++ const cChannel *Channel = Channels.GetByServiceID(Source(), Transponder(), assoc.getServiceId());
++ if (Channel && (Channel == m_Channel))
++ {
++ int prevPmtPid = m_pmtPid;
++ if (0 != (m_pmtPid = assoc.getPid()))
++ {
++ m_pmtSid = assoc.getServiceId();
++ if (m_pmtPid != prevPmtPid)
++ {
++ Add(m_pmtPid, 0x02);
++ m_pmtVersion = -1;
++ }
++ return;
++ }
++ }
++ }
++ }
++ }
++ }
++ else if (Pid == m_pmtPid && Tid == SI::TableIdPMT && Source() && Transponder())
++ {
++ SI::PMT pmt(Data, false);
++ if (!pmt.CheckCRCAndParse())
++ return;
++ if (pmt.getServiceId() != m_pmtSid)
++ return; // skip broken PMT records
++ if (m_pmtVersion != -1)
++ {
++ if (m_pmtVersion != pmt.getVersionNumber())
++ {
++// printf("cStreamdevPatFilter: PMT version changed, detaching all pids\n");
++ cFilter::Del(m_pmtPid, 0x02);
++ m_pmtPid = 0; // this triggers PAT scan
++ }
++ return;
++ }
++ m_pmtVersion = pmt.getVersionNumber();
++
++ SI::PMT::Stream stream;
++ int pids[MAXRECEIVEPIDS + 1];
++ eStreamType types[MAXRECEIVEPIDS + 1];
++ char langs[MAXRECEIVEPIDS + 1][MAXLANGCODE2];
++ int subtitlingType[MAXRECEIVEPIDS + 1];
++ int compositionPageId[MAXRECEIVEPIDS + 1];
++ int ancillaryPageId[MAXRECEIVEPIDS + 1];
++ int streams = 0;
++ for (SI::Loop::Iterator it; pmt.streamLoop.getNext(stream, it); )
++ {
++ eStreamType type;
++ int pid = GetPid(stream, &type, langs[streams], &subtitlingType[streams], &compositionPageId[streams], &ancillaryPageId[streams]);
++ if (0 != pid && streams < MAXRECEIVEPIDS)
++ {
++ pids[streams] = pid;
++ types[streams] = type;
++ streams++;
++ }
++ }
++ pids[streams] = 0;
++
++ int newstreams = 0;
++ for (int i = 0; i < streams; i++)
++ {
++ if (m_Streamer->HaveStreamDemuxer(pids[i], types[i]) == -1)
++ newstreams++;
++ }
++
++ if (newstreams > 0)
++ {
++ if (m_Streamer->m_Receiver)
++ {
++ DEBUGLOG("Detaching Live Receiver");
++ m_Streamer->m_Device->Detach(m_Streamer->m_Receiver);
++ DELETENULL(m_Streamer->m_Receiver);
++ }
++
++ for (int idx = 0; idx < MAXRECEIVEPIDS; ++idx)
++ {
++ if (m_Streamer->m_Streams[idx])
++ {
++ DELETENULL(m_Streamer->m_Streams[idx]);
++ m_Streamer->m_Pids[idx] = 0;
++ }
++ }
++ m_Streamer->m_NumStreams = 0;
++ m_Streamer->m_streamReady = false;
++ m_Streamer->m_IFrameSeen = false;
++
++ for (int i = 0; i < streams; i++)
++ {
++ switch (types[i])
++ {
++ case stMPEG2AUDIO:
++ {
++ m_Streamer->m_Streams[m_Streamer->m_NumStreams] = new cTSDemuxer(m_Streamer, m_Streamer->m_NumStreams, stMPEG2AUDIO, pids[i]);
++ m_Streamer->m_Pids[m_Streamer->m_NumStreams] = pids[i];
++ m_Streamer->m_NumStreams++;
++ break;
++ }
++ case stMPEG2VIDEO:
++ {
++ m_Streamer->m_Streams[m_Streamer->m_NumStreams] = new cTSDemuxer(m_Streamer, m_Streamer->m_NumStreams, stMPEG2VIDEO, pids[i]);
++ m_Streamer->m_Pids[m_Streamer->m_NumStreams] = pids[i];
++ m_Streamer->m_NumStreams++;
++ break;
++ }
++ case stH264:
++ {
++ m_Streamer->m_Streams[m_Streamer->m_NumStreams] = new cTSDemuxer(m_Streamer, m_Streamer->m_NumStreams, stH264, pids[i]);
++ m_Streamer->m_Pids[m_Streamer->m_NumStreams] = pids[i];
++ m_Streamer->m_NumStreams++;
++ break;
++ }
++ case stAC3:
++ {
++ m_Streamer->m_Streams[m_Streamer->m_NumStreams] = new cTSDemuxer(m_Streamer, m_Streamer->m_NumStreams, stAC3, pids[i]);
++ m_Streamer->m_Pids[m_Streamer->m_NumStreams] = pids[i];
++ m_Streamer->m_NumStreams++;
++ break;
++ }
++ case stEAC3:
++ {
++ m_Streamer->m_Streams[m_Streamer->m_NumStreams] = new cTSDemuxer(m_Streamer, m_Streamer->m_NumStreams, stEAC3, pids[i]);
++ m_Streamer->m_Pids[m_Streamer->m_NumStreams] = pids[i];
++ m_Streamer->m_NumStreams++;
++ break;
++ }
++ case stDTS:
++ {
++ m_Streamer->m_Streams[m_Streamer->m_NumStreams] = new cTSDemuxer(m_Streamer, m_Streamer->m_NumStreams, stDTS, pids[i]);
++ m_Streamer->m_Pids[m_Streamer->m_NumStreams] = pids[i];
++ m_Streamer->m_NumStreams++;
++ break;
++ }
++ case stAAC:
++ {
++ m_Streamer->m_Streams[m_Streamer->m_NumStreams] = new cTSDemuxer(m_Streamer, m_Streamer->m_NumStreams, stAAC, pids[i]);
++ m_Streamer->m_Pids[m_Streamer->m_NumStreams] = pids[i];
++ m_Streamer->m_NumStreams++;
++ break;
++ }
++ case stDVBSUB:
++ {
++ m_Streamer->m_Streams[m_Streamer->m_NumStreams] = new cTSDemuxer(m_Streamer, m_Streamer->m_NumStreams, stDVBSUB, pids[i]);
++ m_Streamer->m_Streams[m_Streamer->m_NumStreams]->SetLanguage(langs[i]);
++ m_Streamer->m_Streams[m_Streamer->m_NumStreams]->SetSubtitlingDescriptor(subtitlingType[i], compositionPageId[i], ancillaryPageId[i]);
++ m_Streamer->m_Pids[m_Streamer->m_NumStreams] = pids[i];
++ m_Streamer->m_NumStreams++;
++ break;
++ }
++ case stTELETEXT:
++ {
++ m_Streamer->m_Streams[m_Streamer->m_NumStreams] = new cTSDemuxer(m_Streamer, m_Streamer->m_NumStreams, stTELETEXT, pids[i]);
++ m_Streamer->m_Pids[m_Streamer->m_NumStreams] = pids[i];
++ m_Streamer->m_NumStreams++;
++ break;
++ }
++ default:
++ break;
++ }
++ }
++
++ m_Streamer->m_Receiver = new cLiveReceiver(m_Streamer, m_Channel->GetChannelID(), m_Streamer->m_Priority, m_Streamer->m_Pids);
++ m_Streamer->m_Device->AttachReceiver(m_Streamer->m_Receiver);
++ INFOLOG("Currently unknown new streams found, receiver and demuxers reinited\n");
++ m_Streamer->RequestStreamChange();
++ }
++ }
++}
++
++// --- cLiveStreamer -------------------------------------------------
++
++cLiveStreamer::cLiveStreamer(uint32_t timeout)
++ : cThread("cLiveStreamer stream processor")
++ , cRingBufferLinear(MEGABYTE(3), TS_SIZE, true)
++ , m_scanTimeout(timeout)
++{
++ m_Channel = NULL;
++ m_Priority = 0;
++ m_Socket = NULL;
++ m_Device = NULL;
++ m_Receiver = NULL;
++ m_PatFilter = NULL;
++ m_Frontend = -1;
++ m_NumStreams = 0;
++ m_streamReady = false;
++ m_IsAudioOnly = false;
++ m_IsMPEGPS = false;
++ m_startup = true;
++ m_SignalLost = false;
++ m_IFrameSeen = false;
++
++ m_requestStreamChange = false;
++
++ m_packetEmpty = new cResponsePacket;
++ m_packetEmpty->initStream(VNSI_STREAM_MUXPKT, 0, 0, 0, 0);
++
++ memset(&m_FrontendInfo, 0, sizeof(m_FrontendInfo));
++ for (int idx = 0; idx < MAXRECEIVEPIDS; ++idx)
++ {
++ m_Streams[idx] = NULL;
++ m_Pids[idx] = 0;
++ }
++
++ if(m_scanTimeout == 0)
++ m_scanTimeout = VNSIServerConfig.stream_timeout;
++
++ SetTimeouts(0, 100);
++}
++
++cLiveStreamer::~cLiveStreamer()
++{
++ DEBUGLOG("Started to delete live streamer");
++
++ Cancel(-1);
++
++ if (m_Device)
++ {
++ if (m_Receiver)
++ {
++ DEBUGLOG("Detaching Live Receiver");
++ m_Device->Detach(m_Receiver);
++ }
++ else
++ {
++ DEBUGLOG("No live receiver present");
++ }
++
++ if (m_PatFilter)
++ {
++ DEBUGLOG("Detaching Live Filter");
++ m_Device->Detach(m_PatFilter);
++ }
++ else
++ {
++ DEBUGLOG("No live filter present");
++ }
++
++ for (int idx = 0; idx < MAXRECEIVEPIDS; ++idx)
++ {
++ if (m_Streams[idx])
++ {
++ DEBUGLOG("Deleting stream demuxer %i for pid=%i and type=%i", m_Streams[idx]->GetStreamID(), m_Streams[idx]->GetPID(), m_Streams[idx]->Type());
++ DELETENULL(m_Streams[idx]);
++ m_Pids[idx] = 0;
++ }
++ }
++
++ if (m_Receiver)
++ {
++ DEBUGLOG("Deleting Live Receiver");
++ DELETENULL(m_Receiver);
++ }
++
++ if (m_PatFilter)
++ {
++ DEBUGLOG("Deleting Live Filter");
++ DELETENULL(m_PatFilter);
++ }
++ }
++ if (m_Frontend >= 0)
++ {
++ close(m_Frontend);
++ m_Frontend = -1;
++ }
++
++ delete m_packetEmpty;
++
++ DEBUGLOG("Finished to delete live streamer");
++}
++
++void cLiveStreamer::RequestStreamChange()
++{
++ m_requestStreamChange = true;
++}
++
++void cLiveStreamer::Action(void)
++{
++ int size = 0;
++ int used = 0;
++ unsigned char *buf = NULL;
++ m_startup = true;
++
++ cTimeMs last_tick;
++ cTimeMs last_info;
++ cTimeMs starttime;
++
++ while (Running())
++ {
++ size = 0;
++ used = 0;
++ buf = Get(size);
++
++ if (!m_Receiver->IsAttached())
++ {
++ INFOLOG("returning from streamer thread, receiver is no more attached");
++ break;
++ }
++
++ // prevent inifinite loop on encrypted channels
++ if(!IsReady() && (starttime.Elapsed() >= (uint64_t)(m_scanTimeout*1000))) {
++ INFOLOG("returning from streamer thread, timeout on starting streaming");
++ break;
++ }
++
++ // no data
++ if (buf == NULL || size <= TS_SIZE)
++ {
++ // keep client going
++ if(last_tick.Elapsed() >= 1000 && !IsReady()) {
++ m_Socket->write(m_packetEmpty->getPtr(), m_packetEmpty->getLen());
++ last_tick.Set(0);
++ }
++ continue;
++ }
++
++ /* Make sure we are looking at a TS packet */
++ while (size > TS_SIZE)
++ {
++ if (buf[0] == TS_SYNC_BYTE && buf[TS_SIZE] == TS_SYNC_BYTE)
++ break;
++ used++;
++ buf++;
++ size--;
++ }
++
++ // Send stream information as the first packet on startup
++ if (IsStarting() && IsReady())
++ {
++ INFOLOG("streaming of channel started");
++ last_info.Set(0);
++ m_last_tick.Set(0);
++ m_requestStreamChange = true;
++ m_startup = false;
++ }
++
++ while (size >= TS_SIZE)
++ {
++ if(!Running())
++ {
++ break;
++ }
++
++ unsigned int ts_pid = TsPid(buf);
++ cTSDemuxer *demuxer = FindStreamDemuxer(ts_pid);
++ if (demuxer)
++ {
++ demuxer->ProcessTSPacket(buf);
++ }
++
++ buf += TS_SIZE;
++ size -= TS_SIZE;
++ used += TS_SIZE;
++ }
++ Del(used);
++
++ if(last_info.Elapsed() >= 10*1000 && IsReady())
++ {
++ last_info.Set(0);
++ sendStreamInfo();
++ sendSignalInfo();
++ }
++ }
++}
++
++bool cLiveStreamer::StreamChannel(const cChannel *channel, int priority, cxSocket *Socket, cResponsePacket *resp)
++{
++ if (channel == NULL)
++ {
++ ERRORLOG("Starting streaming of channel without valid channel");
++ return false;
++ }
++
++ m_Channel = channel;
++ m_Priority = priority;
++ m_Socket = Socket;
++ m_Device = cDevice::GetDevice(channel, m_Priority, true);
++
++ if (m_Device != NULL)
++ {
++ DEBUGLOG("Successfully found following device: %p (%d) for receiving", m_Device, m_Device ? m_Device->CardIndex() + 1 : 0);
++
++ if (m_Device->SwitchChannel(m_Channel, false))
++ {
++ if (m_Channel->Vpid())
++ {
++#if APIVERSNUM >= 10701
++ if (m_Channel->Vtype() == 0x1B)
++ m_Streams[m_NumStreams] = new cTSDemuxer(this, m_NumStreams, stH264, m_Channel->Vpid());
++ else
++#endif
++ m_Streams[m_NumStreams] = new cTSDemuxer(this, m_NumStreams, stMPEG2VIDEO, m_Channel->Vpid());
++
++ m_Pids[m_NumStreams] = m_Channel->Vpid();
++ m_NumStreams++;
++ }
++ else
++ {
++ /* m_streamReady is set by the Video demuxers, to have always valid stream informations
++ * like height and width. But if no Video PID is present like for radio channels
++ * VNSI will deadlock
++ */
++ m_streamReady = true;
++ m_IsAudioOnly = true;
++ }
++
++ const int *APids = m_Channel->Apids();
++ for ( ; *APids && m_NumStreams < MAXRECEIVEPIDS; APids++)
++ {
++ int index = 0;
++ if (!FindStreamDemuxer(*APids))
++ {
++ m_Pids[m_NumStreams] = *APids;
++ m_Streams[m_NumStreams] = new cTSDemuxer(this, m_NumStreams, stMPEG2AUDIO, *APids);
++ m_Streams[m_NumStreams]->SetLanguage(m_Channel->Alang(index));
++ m_NumStreams++;
++ }
++ index++;
++ }
++
++ const int *DPids = m_Channel->Dpids();
++ for ( ; *DPids && m_NumStreams < MAXRECEIVEPIDS; DPids++)
++ {
++ int index = 0;
++ if (!FindStreamDemuxer(*DPids))
++ {
++ m_Pids[m_NumStreams] = *DPids;
++ m_Streams[m_NumStreams] = new cTSDemuxer(this, m_NumStreams, stAC3, *DPids);
++ m_Streams[m_NumStreams]->SetLanguage(m_Channel->Dlang(index));
++ m_NumStreams++;
++ }
++ index++;
++ }
++
++ const int *SPids = m_Channel->Spids();
++ if (SPids)
++ {
++ int index = 0;
++ for ( ; *SPids && m_NumStreams < MAXRECEIVEPIDS; SPids++)
++ {
++ if (!FindStreamDemuxer(*SPids))
++ {
++ m_Pids[m_NumStreams] = *SPids;
++ m_Streams[m_NumStreams] = new cTSDemuxer(this, m_NumStreams, stDVBSUB, *SPids);
++ m_Streams[m_NumStreams]->SetLanguage(m_Channel->Slang(index));
++#if APIVERSNUM >= 10709
++ m_Streams[m_NumStreams]->SetSubtitlingDescriptor(m_Channel->SubtitlingType(index),
++ m_Channel->CompositionPageId(index),
++ m_Channel->AncillaryPageId(index));
++#endif
++ m_NumStreams++;
++ }
++ index++;
++ }
++ }
++
++ if (m_Channel->Tpid())
++ {
++ m_Streams[m_NumStreams] = new cTSDemuxer(this, m_NumStreams, stTELETEXT, m_Channel->Tpid());
++ m_Pids[m_NumStreams] = m_Channel->Tpid();
++ cCamSlot* cam = m_Device->CamSlot();
++ if(cam != NULL)
++ {
++ cam->AddPid(m_Channel->Sid(), m_Channel->Tpid(), 0x06);
++ }
++ m_NumStreams++;
++ }
++
++ m_Streams[m_NumStreams] = NULL;
++ m_Pids[m_NumStreams] = 0;
++
++ /* Send the OK response here, that it is before the Stream end message */
++ resp->add_U32(VNSI_RET_OK);
++ resp->finalise();
++ m_Socket->write(resp->getPtr(), resp->getLen());
++
++ if (m_Channel && ((m_Channel->Source() >> 24) == 'V')) m_IsMPEGPS = true;
++
++ if (m_NumStreams > 0 && m_Socket)
++ {
++ dsyslog("VNSI: Creating new live Receiver");
++ m_Receiver = new cLiveReceiver(this, m_Channel->GetChannelID(), m_Priority, m_Pids);
++ m_PatFilter = new cLivePatFilter(this, m_Channel);
++ m_Device->AttachReceiver(m_Receiver);
++ m_Device->AttachFilter(m_PatFilter);
++ }
++
++ INFOLOG("Successfully switched to channel %i - %s", m_Channel->Number(), m_Channel->Name());
++ return true;
++ }
++ else
++ {
++ ERRORLOG("Can't switch to channel %i - %s", m_Channel->Number(), m_Channel->Name());
++ }
++ }
++ else
++ {
++ ERRORLOG("Can't get device for channel %i - %s", m_Channel->Number(), m_Channel->Name());
++ }
++ return false;
++}
++
++cTSDemuxer *cLiveStreamer::FindStreamDemuxer(int Pid)
++{
++ int idx;
++ for (idx = 0; idx < m_NumStreams; ++idx)
++ if (m_Streams[idx] && m_Streams[idx]->GetPID() == Pid)
++ return m_Streams[idx];
++ return NULL;
++}
++
++int cLiveStreamer::HaveStreamDemuxer(int Pid, eStreamType streamType)
++{
++ int idx;
++ for (idx = 0; idx < m_NumStreams; ++idx)
++ if (m_Streams[idx] && (Pid == 0 || m_Streams[idx]->GetPID() == Pid) && m_Streams[idx]->Type() == streamType)
++ return idx;
++ return -1;
++}
++
++inline void cLiveStreamer::Activate(bool On)
++{
++ if (On)
++ {
++ DEBUGLOG("VDR active, sending stream start message");
++ Start();
++ }
++ else
++ {
++ DEBUGLOG("VDR inactive, sending stream end message");
++ Cancel(5);
++ }
++}
++
++void cLiveStreamer::Attach(void)
++{
++ DEBUGLOG("%s", __FUNCTION__);
++ if (m_Device)
++ {
++ if (m_Receiver)
++ {
++ m_Device->Detach(m_Receiver);
++ m_Device->AttachReceiver(m_Receiver);
++ }
++ }
++}
++
++void cLiveStreamer::Detach(void)
++{
++ DEBUGLOG("%s", __FUNCTION__);
++ if (m_Device)
++ {
++ if (m_Receiver)
++ m_Device->Detach(m_Receiver);
++ }
++}
++
++void cLiveStreamer::sendStreamPacket(sStreamPacket *pkt)
++{
++ if(pkt == NULL)
++ return;
++
++ if(pkt->size == 0)
++ return;
++
++ if(!m_IsAudioOnly && !m_IFrameSeen && (pkt->frametype != PKT_I_FRAME))
++ return;
++
++ if(m_requestStreamChange)
++ sendStreamChange();
++
++ m_IFrameSeen = true;
++
++ m_streamHeader.channel = htonl(VNSI_CHANNEL_STREAM); // stream channel
++ m_streamHeader.opcode = htonl(VNSI_STREAM_MUXPKT); // Stream packet operation code
++
++ m_streamHeader.id = htonl(pkt->id); // Stream ID
++ m_streamHeader.duration = htonl(pkt->duration); // Duration
++
++ *(int64_t*)&m_streamHeader.dts = __cpu_to_be64(pkt->dts); // DTS
++ *(int64_t*)&m_streamHeader.pts = __cpu_to_be64(pkt->pts); // PTS
++
++ m_streamHeader.length = htonl(pkt->size); // Data length
++ m_Socket->write(&m_streamHeader, sizeof(m_streamHeader), -1, true);
++
++ m_Socket->write(pkt->data, pkt->size);
++
++}
++
++void cLiveStreamer::sendStreamChange()
++{
++ cResponsePacket *resp = new cResponsePacket();
++ if (!resp->initStream(VNSI_STREAM_CHANGE, 0, 0, 0, 0))
++ {
++ ERRORLOG("stream response packet init fail");
++ delete resp;
++ return;
++ }
++
++ for (int idx = 0; idx < m_NumStreams; ++idx)
++ {
++ if (m_Streams[idx])
++ {
++ resp->add_U32(m_Streams[idx]->GetStreamID());
++ if (m_Streams[idx]->Type() == stMPEG2AUDIO)
++ {
++ resp->add_String("MPEG2AUDIO");
++ resp->add_String(m_Streams[idx]->GetLanguage());
++ }
++ else if (m_Streams[idx]->Type() == stMPEG2VIDEO)
++ {
++ resp->add_String("MPEG2VIDEO");
++ resp->add_U32(m_Streams[idx]->GetFpsScale());
++ resp->add_U32(m_Streams[idx]->GetFpsRate());
++ resp->add_U32(m_Streams[idx]->GetHeight());
++ resp->add_U32(m_Streams[idx]->GetWidth());
++ resp->add_double(m_Streams[idx]->GetAspect());
++ }
++ else if (m_Streams[idx]->Type() == stAC3)
++ {
++ resp->add_String("AC3");
++ resp->add_String(m_Streams[idx]->GetLanguage());
++ }
++ else if (m_Streams[idx]->Type() == stH264)
++ {
++ resp->add_String("H264");
++ resp->add_U32(m_Streams[idx]->GetFpsScale());
++ resp->add_U32(m_Streams[idx]->GetFpsRate());
++ resp->add_U32(m_Streams[idx]->GetHeight());
++ resp->add_U32(m_Streams[idx]->GetWidth());
++ resp->add_double(m_Streams[idx]->GetAspect());
++ }
++ else if (m_Streams[idx]->Type() == stDVBSUB)
++ {
++ resp->add_String("DVBSUB");
++ resp->add_String(m_Streams[idx]->GetLanguage());
++ resp->add_U32(m_Streams[idx]->CompositionPageId());
++ resp->add_U32(m_Streams[idx]->AncillaryPageId());
++ }
++ else if (m_Streams[idx]->Type() == stTELETEXT)
++ resp->add_String("TELETEXT");
++ else if (m_Streams[idx]->Type() == stAAC)
++ {
++ resp->add_String("AAC");
++ resp->add_String(m_Streams[idx]->GetLanguage());
++ }
++ else if (m_Streams[idx]->Type() == stEAC3)
++ {
++ resp->add_String("EAC3");
++ resp->add_String(m_Streams[idx]->GetLanguage());
++ }
++ else if (m_Streams[idx]->Type() == stDTS)
++ {
++ resp->add_String("DTS");
++ resp->add_String(m_Streams[idx]->GetLanguage());
++ }
++ }
++ }
++
++ resp->finaliseStream();
++ m_Socket->write(resp->getPtr(), resp->getLen(), -1, true);
++ delete resp;
++
++ m_requestStreamChange = false;
++
++ sendStreamInfo();
++}
++
++void cLiveStreamer::sendSignalInfo()
++{
++ /* If no frontend is found m_Frontend is set to -2, in this case
++ return a empty signalinfo package */
++ if (m_Frontend == -2)
++ {
++ cResponsePacket *resp = new cResponsePacket();
++ if (!resp->initStream(VNSI_STREAM_SIGNALINFO, 0, 0, 0, 0))
++ {
++ ERRORLOG("stream response packet init fail");
++ delete resp;
++ return;
++ }
++
++ resp->add_String(*cString::sprintf("Unknown"));
++ resp->add_String(*cString::sprintf("Unknown"));
++ resp->add_U32(0);
++ resp->add_U32(0);
++ resp->add_U32(0);
++ resp->add_U32(0);
++
++ resp->finaliseStream();
++ m_Socket->write(resp->getPtr(), resp->getLen(), -1, true);
++ delete resp;
++ return;
++ }
++
++ if (m_Channel && ((m_Channel->Source() >> 24) == 'V'))
++ {
++ if (m_Frontend < 0)
++ {
++ for (int i = 0; i < 8; i++)
++ {
++ m_DeviceString = cString::sprintf("/dev/video%d", i);
++ m_Frontend = open(m_DeviceString, O_RDONLY | O_NONBLOCK);
++ if (m_Frontend >= 0)
++ {
++ if (ioctl(m_Frontend, VIDIOC_QUERYCAP, &m_vcap) < 0)
++ {
++ ERRORLOG("cannot read analog frontend info.");
++ close(m_Frontend);
++ m_Frontend = -1;
++ memset(&m_vcap, 0, sizeof(m_vcap));
++ continue;
++ }
++ break;
++ }
++ }
++ if (m_Frontend < 0)
++ m_Frontend = -2;
++ }
++
++ if (m_Frontend >= 0)
++ {
++ cResponsePacket *resp = new cResponsePacket();
++ if (!resp->initStream(VNSI_STREAM_SIGNALINFO, 0, 0, 0, 0))
++ {
++ ERRORLOG("stream response packet init fail");
++ delete resp;
++ return;
++ }
++ resp->add_String(*cString::sprintf("Analog #%s - %s (%s)", *m_DeviceString, (char *) m_vcap.card, m_vcap.driver));
++ resp->add_String("");
++ resp->add_U32(0);
++ resp->add_U32(0);
++ resp->add_U32(0);
++ resp->add_U32(0);
++
++ resp->finaliseStream();
++ m_Socket->write(resp->getPtr(), resp->getLen(), -1, true);
++ delete resp;
++ }
++ }
++ else
++ {
++ if (m_Frontend < 0)
++ {
++ m_DeviceString = cString::sprintf(FRONTEND_DEVICE, m_Device->CardIndex(), 0);
++ m_Frontend = open(m_DeviceString, O_RDONLY | O_NONBLOCK);
++ if (m_Frontend >= 0)
++ {
++ if (ioctl(m_Frontend, FE_GET_INFO, &m_FrontendInfo) < 0)
++ {
++ ERRORLOG("cannot read frontend info.");
++ close(m_Frontend);
++ m_Frontend = -2;
++ memset(&m_FrontendInfo, 0, sizeof(m_FrontendInfo));
++ return;
++ }
++ }
++ }
++
++ if (m_Frontend >= 0)
++ {
++ cResponsePacket *resp = new cResponsePacket();
++ if (!resp->initStream(VNSI_STREAM_SIGNALINFO, 0, 0, 0, 0))
++ {
++ ERRORLOG("stream response packet init fail");
++ delete resp;
++ return;
++ }
++
++ fe_status_t status;
++ uint16_t fe_snr;
++ uint16_t fe_signal;
++ uint32_t fe_ber;
++ uint32_t fe_unc;
++
++ memset(&status, 0, sizeof(status));
++ ioctl(m_Frontend, FE_READ_STATUS, &status);
++
++ if (ioctl(m_Frontend, FE_READ_SIGNAL_STRENGTH, &fe_signal) == -1)
++ fe_signal = -2;
++ if (ioctl(m_Frontend, FE_READ_SNR, &fe_snr) == -1)
++ fe_snr = -2;
++ if (ioctl(m_Frontend, FE_READ_BER, &fe_ber) == -1)
++ fe_ber = -2;
++ if (ioctl(m_Frontend, FE_READ_UNCORRECTED_BLOCKS, &fe_unc) == -1)
++ fe_unc = -2;
++
++ switch (m_Channel->Source() & cSource::st_Mask)
++ {
++ case cSource::stSat:
++ resp->add_String(*cString::sprintf("DVB-S%s #%d - %s", (m_FrontendInfo.caps & 0x10000000) ? "2" : "", cDevice::ActualDevice()->CardIndex(), m_FrontendInfo.name));
++ break;
++ case cSource::stCable:
++ resp->add_String(*cString::sprintf("DVB-C #%d - %s", cDevice::ActualDevice()->CardIndex(), m_FrontendInfo.name));
++ break;
++ case cSource::stTerr:
++ resp->add_String(*cString::sprintf("DVB-T #%d - %s", cDevice::ActualDevice()->CardIndex(), m_FrontendInfo.name));
++ break;
++ }
++ resp->add_String(*cString::sprintf("%s:%s:%s:%s:%s", (status & FE_HAS_LOCK) ? "LOCKED" : "-", (status & FE_HAS_SIGNAL) ? "SIGNAL" : "-", (status & FE_HAS_CARRIER) ? "CARRIER" : "-", (status & FE_HAS_VITERBI) ? "VITERBI" : "-", (status & FE_HAS_SYNC) ? "SYNC" : "-"));
++ resp->add_U32(fe_snr);
++ resp->add_U32(fe_signal);
++ resp->add_U32(fe_ber);
++ resp->add_U32(fe_unc);
++
++ resp->finaliseStream();
++ m_Socket->write(resp->getPtr(), resp->getLen(), -1, true);
++ delete resp;
++ }
++ }
++}
++
++void cLiveStreamer::sendStreamInfo()
++{
++ if(m_NumStreams == 0)
++ {
++ return;
++ }
++
++ cResponsePacket *resp = new cResponsePacket();
++ if (!resp->initStream(VNSI_STREAM_CONTENTINFO, 0, 0, 0, 0))
++ {
++ ERRORLOG("stream response packet init fail");
++ delete resp;
++ return;
++ }
++
++ for (int idx = 0; idx < m_NumStreams; ++idx)
++ {
++ if (m_Streams[idx])
++ {
++ if (m_Streams[idx]->Type() == stMPEG2AUDIO ||
++ m_Streams[idx]->Type() == stAC3 ||
++ m_Streams[idx]->Type() == stEAC3 ||
++ m_Streams[idx]->Type() == stDTS ||
++ m_Streams[idx]->Type() == stAAC)
++ {
++ resp->add_U32(m_Streams[idx]->GetStreamID());
++ resp->add_String(m_Streams[idx]->GetLanguage());
++ resp->add_U32(m_Streams[idx]->GetChannels());
++ resp->add_U32(m_Streams[idx]->GetSampleRate());
++ resp->add_U32(m_Streams[idx]->GetBlockAlign());
++ resp->add_U32(m_Streams[idx]->GetBitRate());
++ resp->add_U32(m_Streams[idx]->GetBitsPerSample());
++ }
++ else if (m_Streams[idx]->Type() == stMPEG2VIDEO || m_Streams[idx]->Type() == stH264)
++ {
++ resp->add_U32(m_Streams[idx]->GetStreamID());
++ resp->add_U32(m_Streams[idx]->GetFpsScale());
++ resp->add_U32(m_Streams[idx]->GetFpsRate());
++ resp->add_U32(m_Streams[idx]->GetHeight());
++ resp->add_U32(m_Streams[idx]->GetWidth());
++ resp->add_double(m_Streams[idx]->GetAspect());
++ }
++ else if (m_Streams[idx]->Type() == stDVBSUB)
++ {
++ resp->add_U32(m_Streams[idx]->GetStreamID());
++ resp->add_String(m_Streams[idx]->GetLanguage());
++ resp->add_U32(m_Streams[idx]->CompositionPageId());
++ resp->add_U32(m_Streams[idx]->AncillaryPageId());
++ }
++ }
++ }
++
++ resp->finaliseStream();
++ m_Socket->write(resp->getPtr(), resp->getLen());
++ delete resp;
++}
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/receiver.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/receiver.h
+new file mode 100644
+index 0000000..dd523f1
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/receiver.h
+@@ -0,0 +1,116 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (Team XBMC)
++ * Copyright (C) 2010, 2011 Alexander Pipelka
++ *
++ * 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#ifndef VNSI_RECEIVER_H
++#define VNSI_RECEIVER_H
++
++#include <linux/dvb/frontend.h>
++#include <linux/videodev2.h>
++#include <vdr/channels.h>
++#include <vdr/device.h>
++#include <vdr/receiver.h>
++#include <vdr/thread.h>
++#include <vdr/ringbuffer.h>
++
++#include "demuxer.h"
++
++class cxSocket;
++class cChannel;
++class cLiveReceiver;
++class cTSDemuxer;
++class cResponsePacket;
++class cLivePatFilter;
++
++class cLiveStreamer : public cThread
++ , public cRingBufferLinear
++{
++private:
++ friend class cParser;
++ friend class cLivePatFilter;
++
++ void Detach(void);
++ void Attach(void);
++ cTSDemuxer *FindStreamDemuxer(int Pid);
++
++ void sendStreamPacket(sStreamPacket *pkt);
++ void sendStreamChange();
++ void sendSignalInfo();
++ void sendStreamInfo();
++
++ const cChannel *m_Channel; /*!> Channel to stream */
++ cDevice *m_Device; /*!> The receiving device the channel depents to */
++ cLiveReceiver *m_Receiver; /*!> Our stream transceiver */
++ cLivePatFilter *m_PatFilter; /*!> Filter processor to get changed pid's */
++ int m_Priority; /*!> The priority over other streamers */
++ int m_Pids[MAXRECEIVEPIDS + 1]; /*!> PID for cReceiver also as extra array */
++ cTSDemuxer *m_Streams[MAXRECEIVEPIDS + 1]; /*!> Stream information data (partly filled, rest is done by cLiveReceiver */
++ int m_NumStreams; /*!> Number of streams selected */
++ cxSocket *m_Socket; /*!> The socket class to communicate with client */
++ int m_Frontend; /*!> File descriptor to access used receiving device */
++ dvb_frontend_info m_FrontendInfo; /*!> DVB Information about the receiving device (DVB only) */
++ v4l2_capability m_vcap; /*!> PVR Information about the receiving device (pvrinput only) */
++ cString m_DeviceString; /*!> The name of the receiving device */
++ bool m_streamReady; /*!> Set by the video demuxer after we got video information */
++ bool m_startup;
++ bool m_IsAudioOnly; /*!> Set to true if streams contains only audio */
++ bool m_IsMPEGPS; /*!> TS Stream contains MPEG PS data like from pvrinput */
++ cResponsePacket* m_packetEmpty; /*!> Empty stream packet */
++ bool m_requestStreamChange;
++ uint32_t m_scanTimeout; /*!> Channel scanning timeout (in seconds) */
++ cTimeMs m_last_tick;
++ bool m_SignalLost;
++ bool m_IFrameSeen;
++
++ struct {
++ uint32_t channel;
++ uint32_t opcode;
++ uint32_t id;
++ uint32_t duration;
++ uint8_t pts[sizeof(int64_t)];
++ uint8_t dts[sizeof(int64_t)];
++ uint32_t length;
++ } m_streamHeader;
++
++protected:
++ virtual void Action(void);
++ void RequestStreamChange();
++
++public:
++ cLiveStreamer(uint32_t timeout = 0);
++ virtual ~cLiveStreamer();
++
++ void Activate(bool On);
++
++ bool StreamChannel(const cChannel *channel, int priority, cxSocket *Socket, cResponsePacket* resp);
++ void SetReady() { m_streamReady = true; }
++ bool IsReady() { return m_streamReady && (m_NumStreams > 0); }
++ bool IsStarting() { return m_startup; }
++ bool IsAudioOnly() { return m_IsAudioOnly; }
++ bool IsMPEGPS() { return m_IsMPEGPS; }
++ int HaveStreamDemuxer(int Pid, eStreamType streamType);
++
++};
++
++#endif // VNSI_RECEIVER_H
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/recordingscache.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/recordingscache.c
+new file mode 100644
+index 0000000..b435ce4
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/recordingscache.c
+@@ -0,0 +1,70 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2011 Alexander Pipelka
++ *
++ * This Program is free software; you can redistribute 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "config.h"
++#include "recordingscache.h"
++#include "hash.h"
++
++cRecordingsCache::cRecordingsCache() {
++}
++
++cRecordingsCache::~cRecordingsCache() {
++}
++
++cRecordingsCache& cRecordingsCache::GetInstance() {
++ static cRecordingsCache singleton;
++ return singleton;
++}
++
++uint32_t cRecordingsCache::Register(cRecording* recording) {
++ cString filename = recording->FileName();
++ uint32_t uid = CreateStringHash(filename);
++
++ m_mutex.Lock();
++ if(m_recordings.find(uid) == m_recordings.end())
++ {
++ DEBUGLOG("%s - uid: %08x '%s'", __FUNCTION__, uid, (const char*)filename);
++ m_recordings[uid] = filename;
++ }
++ m_mutex.Unlock();
++
++ return uid;
++}
++
++cRecording* cRecordingsCache::Lookup(uint32_t uid) {
++ DEBUGLOG("%s - lookup uid: %08x", __FUNCTION__, uid);
++
++ if(m_recordings.find(uid) == m_recordings.end()) {
++ DEBUGLOG("%s - not found !", __FUNCTION__);
++ return NULL;
++ }
++
++ m_mutex.Lock();
++ cString filename = m_recordings[uid];
++ DEBUGLOG("%s - filename: %s", __FUNCTION__, (const char*)filename);
++
++ cRecording* r = Recordings.GetByName(filename);
++ DEBUGLOG("%s - recording %s", __FUNCTION__, (r == NULL) ? "not found !" : "found");
++ m_mutex.Unlock();
++
++ return r;
++}
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/recordingscache.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/recordingscache.h
+new file mode 100644
+index 0000000..964e948
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/recordingscache.h
+@@ -0,0 +1,56 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2011 Alexander Pipelka
++ *
++ * This Program is free software; you can redistribute 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#ifndef VNSI_RECORDINGSCACHE_H
++#define VNSI_RECORDINGSCACHE_H
++
++#include <stdint.h>
++#include <map>
++#include <vdr/thread.h>
++#include <vdr/tools.h>
++#include <vdr/recording.h>
++
++class cRecordingsCache
++{
++protected:
++
++ cRecordingsCache();
++
++ virtual ~cRecordingsCache();
++
++public:
++
++ static cRecordingsCache& GetInstance();
++
++ uint32_t Register(cRecording* recording);
++
++ cRecording* Lookup(uint32_t uid);
++
++private:
++
++ std::map<uint32_t, cString> m_recordings;
++
++ cMutex m_mutex;
++};
++
++
++#endif // VNSI_RECORDINGSCACHE_H
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/recplayer.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/recplayer.c
+new file mode 100644
+index 0000000..07b653e
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/recplayer.c
+@@ -0,0 +1,295 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2004-2005 Chris Tallon
++ * Copyright (C) 2010 Alwin Esch (Team XBMC)
++ * Copyright (C) 2010, 2011 Alexander Pipelka
++ *
++ * 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++/*
++ * This code is taken from VOMP for VDR plugin.
++ */
++
++#include "recplayer.h"
++#include <fcntl.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <unistd.h>
++
++cRecPlayer::cRecPlayer(cRecording* rec)
++{
++ m_file = -1;
++ m_fileOpen = -1;
++ m_recordingFilename = strdup(rec->FileName());
++
++ // FIXME find out max file path / name lengths
++#if VDRVERSNUM < 10703
++ m_pesrecording = true;
++ m_indexFile = new cIndexFile(m_recordingFilename, false);
++#else
++ m_pesrecording = rec->IsPesRecording();
++ if(m_pesrecording) INFOLOG("recording '%s' is a PES recording", m_recordingFilename);
++ m_indexFile = new cIndexFile(m_recordingFilename, false, m_pesrecording);
++#endif
++
++ scan();
++}
++
++void cRecPlayer::cleanup() {
++ for(int i = 0; i != m_segments.Size(); i++) {
++ delete m_segments[i];
++ }
++ m_segments.Clear();
++}
++
++void cRecPlayer::scan()
++{
++ struct stat s;
++
++ closeFile();
++
++ m_totalLength = 0;
++ m_fileOpen = -1;
++ m_totalFrames = 0;
++
++ cleanup();
++
++ for(int i = 0; ; i++) // i think we only need one possible loop
++ {
++ fileNameFromIndex(i);
++
++ if(stat(m_fileName, &s) == -1) {
++ break;
++ }
++
++ cSegment* segment = new cSegment();
++ segment->start = m_totalLength;
++ segment->end = segment->start + s.st_size;
++
++ m_segments.Append(segment);
++
++ m_totalLength += s.st_size;
++ INFOLOG("File %i found, size: %llu, totalLength now %llu", i, s.st_size, m_totalLength);
++ }
++
++ m_totalFrames = m_indexFile->Last();
++ INFOLOG("total frames: %u", m_totalFrames);
++}
++
++cRecPlayer::~cRecPlayer()
++{
++ cleanup();
++ closeFile();
++ free(m_recordingFilename);
++}
++
++char* cRecPlayer::fileNameFromIndex(int index) {
++ if (m_pesrecording)
++ snprintf(m_fileName, sizeof(m_fileName), "%s/%03i.vdr", m_recordingFilename, index+1);
++ else
++ snprintf(m_fileName, sizeof(m_fileName), "%s/%05i.ts", m_recordingFilename, index+1);
++
++ return m_fileName;
++}
++
++bool cRecPlayer::openFile(int index)
++{
++ if (index == m_fileOpen) return true;
++ closeFile();
++
++ fileNameFromIndex(index);
++ INFOLOG("openFile called for index %i string:%s", index, m_fileName);
++
++ m_file = open(m_fileName, O_RDONLY | O_NOATIME);
++ if (m_file == -1)
++ {
++ INFOLOG("file failed to open");
++ m_fileOpen = -1;
++ return false;
++ }
++ m_fileOpen = index;
++ return true;
++}
++
++void cRecPlayer::closeFile()
++{
++ if(m_file == -1) {
++ return;
++ }
++
++ INFOLOG("file closed");
++ close(m_file);
++
++ m_file = -1;
++ m_fileOpen = -1;
++}
++
++uint64_t cRecPlayer::getLengthBytes()
++{
++ return m_totalLength;
++}
++
++uint32_t cRecPlayer::getLengthFrames()
++{
++ return m_totalFrames;
++}
++
++int cRecPlayer::getBlock(unsigned char* buffer, uint64_t position, int amount)
++{
++ // dont let the block be larger than 256 kb
++ if (amount > 256*1024)
++ amount = 256*1024;
++
++ if ((uint64_t)amount > m_totalLength)
++ amount = m_totalLength;
++
++ if (position >= m_totalLength)
++ return 0;
++
++ if ((position + amount) > m_totalLength)
++ amount = m_totalLength - position;
++
++ // work out what block "position" is in
++ int segmentNumber = -1;
++ for(int i = 0; i < m_segments.Size(); i++)
++ {
++ if ((position >= m_segments[i]->start) && (position < m_segments[i]->end)) {
++ segmentNumber = i;
++ break;
++ }
++ }
++
++ // segment not found / invalid position
++ if (segmentNumber == -1) return 0;
++
++ // open file (if not already open)
++ if (!openFile(segmentNumber)) return 0;
++
++ // work out position in current file
++ uint64_t filePosition = position - m_segments[segmentNumber]->start;
++
++ // seek to position
++ if(lseek(m_file, filePosition, SEEK_SET) == -1) {
++ ERRORLOG("unable to seek to position: %llu", filePosition);
++ return 0;
++ }
++
++ // try to read the block
++ int bytes_read = read(m_file, buffer, amount);
++ INFOLOG("read %i bytes from file %i at position %llu", bytes_read, segmentNumber, filePosition);
++
++ if(bytes_read <= 0) {
++ return 0;
++ }
++
++ // Tell linux not to bother keeping the data in the FS cache
++ posix_fadvise(m_file, filePosition, bytes_read, POSIX_FADV_DONTNEED);
++
++ // divide and conquer
++ if(bytes_read < amount) {
++ bytes_read += getBlock(&buffer[bytes_read], position + bytes_read, amount - bytes_read);
++ }
++
++ return bytes_read;
++}
++
++uint64_t cRecPlayer::positionFromFrameNumber(uint32_t frameNumber)
++{
++ if (!m_indexFile) return 0;
++#if VDRVERSNUM < 10703
++ unsigned char retFileNumber;
++ int retFileOffset;
++ unsigned char retPicType;
++#else
++ uint16_t retFileNumber;
++ off_t retFileOffset;
++ bool retPicType;
++#endif
++ int retLength;
++
++
++ if (!m_indexFile->Get((int)frameNumber, &retFileNumber, &retFileOffset, &retPicType, &retLength))
++ return 0;
++
++ if (retFileNumber >= m_segments.Size())
++ return 0;
++
++ uint64_t position = m_segments[retFileNumber]->start + retFileOffset;
++ return position;
++}
++
++uint32_t cRecPlayer::frameNumberFromPosition(uint64_t position)
++{
++ if (!m_indexFile) return 0;
++
++ if (position >= m_totalLength)
++ {
++ DEBUGLOG("Client asked for data starting past end of recording!");
++ return m_totalFrames;
++ }
++
++ int segmentNumber = -1;
++ for(int i = 0; i < m_segments.Size(); i++)
++ {
++ if ((position >= m_segments[i]->start) && (position < m_segments[i]->end)) {
++ segmentNumber = i;
++ break;
++ }
++ }
++
++ if(segmentNumber == -1) {
++ return m_totalFrames;
++ }
++
++ uint32_t askposition = position - m_segments[segmentNumber]->start;
++ return m_indexFile->Get((int)segmentNumber, askposition);
++}
++
++
++bool cRecPlayer::getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength)
++{
++ // 0 = backwards
++ // 1 = forwards
++
++ if (!m_indexFile) return false;
++
++#if VDRVERSNUM < 10703
++ unsigned char waste1;
++ int waste2;
++#else
++ uint16_t waste1;
++ off_t waste2;
++#endif
++
++ int iframeLength;
++ int indexReturnFrameNumber;
++
++ indexReturnFrameNumber = (uint32_t)m_indexFile->GetNextIFrame(frameNumber, (direction==1 ? true : false), &waste1, &waste2, &iframeLength);
++ DEBUGLOG("GNIF input framenumber:%u, direction=%u, output:framenumber=%i, framelength=%i", frameNumber, direction, indexReturnFrameNumber, iframeLength);
++
++ if (indexReturnFrameNumber == -1) return false;
++
++ *rfilePosition = positionFromFrameNumber(indexReturnFrameNumber);
++ *rframeNumber = (uint32_t)indexReturnFrameNumber;
++ *rframeLength = (uint32_t)iframeLength;
++
++ return true;
++}
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/recplayer.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/recplayer.h
+new file mode 100644
+index 0000000..a94e0d7
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/recplayer.h
+@@ -0,0 +1,80 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2004-2005 Chris Tallon
++ * Copyright (C) 2010 Alwin Esch (Team XBMC)
++ * Copyright (C) 2010, 2011 Alexander Pipelka
++ *
++ * 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++/*
++ * This code is taken from VOMP for VDR plugin.
++ */
++
++#ifndef VNSI_RECPLAYER_H
++#define VNSI_RECPLAYER_H
++
++#include <stdio.h>
++#include <vdr/recording.h>
++#include <vdr/tools.h>
++
++#include "config.h"
++
++class cSegment
++{
++ public:
++ uint64_t start;
++ uint64_t end;
++};
++
++class cRecPlayer
++{
++public:
++ cRecPlayer(cRecording* rec);
++ ~cRecPlayer();
++ uint64_t getLengthBytes();
++ uint32_t getLengthFrames();
++ int getBlock(unsigned char* buffer, uint64_t position, int amount);
++
++ bool openFile(int index);
++ void closeFile();
++
++ void scan();
++ uint64_t positionFromFrameNumber(uint32_t frameNumber);
++ uint32_t frameNumberFromPosition(uint64_t position);
++ bool getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength);
++
++private:
++ void cleanup();
++ char* fileNameFromIndex(int index);
++ void checkBufferSize(int s);
++
++ char m_fileName[512];
++ cIndexFile *m_indexFile;
++ int m_file;
++ int m_fileOpen;
++ cVector<cSegment*> m_segments;
++ uint64_t m_totalLength;
++ uint32_t m_totalFrames;
++ char *m_recordingFilename;
++ bool m_pesrecording;
++};
++
++#endif // VNSI_RECPLAYER_H
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/requestpacket.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/requestpacket.c
+new file mode 100644
+index 0000000..2e62c3b
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/requestpacket.c
+@@ -0,0 +1,123 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2007 Chris Tallon
++ * Copyright (C) 2010 Alwin Esch (Team XBMC)
++ * Copyright (C) 2010, 2011 Alexander Pipelka
++ *
++ * 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <stdlib.h>
++#include <stdint.h>
++#include <string.h>
++
++#include <asm/byteorder.h>
++
++#include "config.h"
++#include "requestpacket.h"
++#include "vnsicommand.h"
++
++cRequestPacket::cRequestPacket(uint32_t requestID, uint32_t opcode, uint8_t* data, uint32_t dataLength)
++ : userData(data), userDataLength(dataLength), opCode(opcode), requestID(requestID)
++{
++ packetPos = 0;
++ ownBlock = true;
++ channelID = 0;
++ streamID = 0;
++ flag = 0;
++}
++
++cRequestPacket::~cRequestPacket()
++{
++ if (!ownBlock) return; // don't free if it's a getblock
++
++ if (userData) free(userData);
++}
++
++bool cRequestPacket::end()
++{
++ return (packetPos >= userDataLength);
++}
++
++int cRequestPacket::serverError()
++{
++ if ((packetPos == 0) && (userDataLength == 4) && !ntohl(*(uint32_t*)userData)) return 1;
++ else return 0;
++}
++
++char* cRequestPacket::extract_String()
++{
++ if (serverError()) return NULL;
++
++ int length = strlen((char*)&userData[packetPos]);
++ if ((packetPos + length) > userDataLength) return NULL;
++ char* str = new char[length + 1];
++ strcpy(str, (char*)&userData[packetPos]);
++ packetPos += length + 1;
++ return str;
++}
++
++uint8_t cRequestPacket::extract_U8()
++{
++ if ((packetPos + sizeof(uint8_t)) > userDataLength) return 0;
++ uint8_t uc = userData[packetPos];
++ packetPos += sizeof(uint8_t);
++ return uc;
++}
++
++uint32_t cRequestPacket::extract_U32()
++{
++ if ((packetPos + sizeof(uint32_t)) > userDataLength) return 0;
++ uint32_t ul = ntohl(*(uint32_t*)&userData[packetPos]);
++ packetPos += sizeof(uint32_t);
++ return ul;
++}
++
++uint64_t cRequestPacket::extract_U64()
++{
++ if ((packetPos + sizeof(uint64_t)) > userDataLength) return 0;
++ uint64_t ull = __be64_to_cpu(*(uint64_t*)&userData[packetPos]);
++ packetPos += sizeof(uint64_t);
++ return ull;
++}
++
++double cRequestPacket::extract_Double()
++{
++ if ((packetPos + sizeof(uint64_t)) > userDataLength) return 0;
++ uint64_t ull = __be64_to_cpu(*(uint64_t*)&userData[packetPos]);
++ double d;
++ memcpy(&d,&ull,sizeof(double));
++ packetPos += sizeof(uint64_t);
++ return d;
++}
++
++int32_t cRequestPacket::extract_S32()
++{
++ if ((packetPos + sizeof(int32_t)) > userDataLength) return 0;
++ int32_t l = ntohl(*(int32_t*)&userData[packetPos]);
++ packetPos += sizeof(int32_t);
++ return l;
++}
++
++uint8_t* cRequestPacket::getData()
++{
++ ownBlock = false;
++ return userData;
++}
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/requestpacket.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/requestpacket.h
+new file mode 100644
+index 0000000..4e3704d
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/requestpacket.h
+@@ -0,0 +1,73 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2007 Chris Tallon
++ * Copyright (C) 2010 Alwin Esch (Team XBMC)
++ * Copyright (C) 2010, 2011 Alexander Pipelka
++ *
++ * 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#ifndef VNSI_REQUESTPACKET_H
++#define VNSI_REQUESTPACKET_H
++
++class cRequestPacket
++{
++public:
++ cRequestPacket(uint32_t requestID, uint32_t opcode, uint8_t* data, uint32_t dataLength);
++ ~cRequestPacket();
++
++ int serverError();
++
++ uint32_t getDataLength() { return userDataLength; }
++ uint32_t getChannelID() { return channelID; }
++ uint32_t getRequestID() { return requestID; }
++ uint32_t getStreamID() { return streamID; }
++ uint32_t getFlag() { return flag; }
++ uint32_t getOpCode() { return opCode; }
++
++ char* extract_String();
++ uint8_t extract_U8();
++ uint32_t extract_U32();
++ uint64_t extract_U64();
++ int32_t extract_S32();
++ double extract_Double();
++
++ bool end();
++
++ // If you call this, the memory becomes yours. Free with free()
++ uint8_t* getData();
++
++private:
++ uint8_t* userData;
++ uint32_t userDataLength;
++ uint32_t packetPos;
++ uint32_t opCode;
++
++ uint32_t channelID;
++
++ uint32_t requestID;
++ uint32_t streamID;
++
++ uint32_t flag; // stream only
++
++ bool ownBlock;
++};
++
++#endif // VNSI_REQUESTPACKET_H
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/responsepacket.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/responsepacket.c
+new file mode 100644
+index 0000000..09f6d55
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/responsepacket.c
+@@ -0,0 +1,214 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2007 Chris Tallon
++ * Copyright (C) 2010 Alwin Esch (Team XBMC)
++ * Copyright (C) 2010, 2011 Alexander Pipelka
++ *
++ * 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++/*
++ * This code is taken from VOMP for VDR plugin.
++ */
++
++#include <arpa/inet.h>
++#include <stdlib.h>
++#include <string.h>
++
++#include <asm/byteorder.h>
++
++#include "responsepacket.h"
++#include "vnsicommand.h"
++#include "config.h"
++
++/* Packet format for an RR channel response:
++
++4 bytes = channel ID = 1 (request/response channel)
++4 bytes = request ID (from serialNumber)
++4 bytes = length of the rest of the packet
++? bytes = rest of packet. depends on packet
++*/
++
++cResponsePacket::cResponsePacket()
++{
++ buffer = NULL;
++ bufSize = 0;
++ bufUsed = 0;
++}
++
++cResponsePacket::~cResponsePacket()
++{
++ if (buffer) free(buffer);
++}
++
++void cResponsePacket::initBuffers()
++{
++ if (buffer == NULL) {
++ bufSize = 512;
++ buffer = (uint8_t*)malloc(bufSize);
++ }
++}
++
++bool cResponsePacket::init(uint32_t requestID)
++{
++ initBuffers();
++
++ *(uint32_t*)&buffer[0] = htonl(VNSI_CHANNEL_REQUEST_RESPONSE); // RR channel
++ *(uint32_t*)&buffer[4] = htonl(requestID);
++ *(uint32_t*)&buffer[userDataLenPos] = 0;
++ bufUsed = headerLength;
++
++ return true;
++}
++
++bool cResponsePacket::initScan(uint32_t opCode)
++{
++ initBuffers();
++
++ *(uint32_t*)&buffer[0] = htonl(VNSI_CHANNEL_SCAN); // RR channel
++ *(uint32_t*)&buffer[4] = htonl(opCode);
++ *(uint32_t*)&buffer[userDataLenPos] = 0;
++ bufUsed = headerLength;
++
++ return true;
++}
++
++bool cResponsePacket::initStatus(uint32_t opCode)
++{
++ initBuffers();
++
++ *(uint32_t*)&buffer[0] = htonl(VNSI_CHANNEL_STATUS); // RR channel
++ *(uint32_t*)&buffer[4] = htonl(opCode);
++ *(uint32_t*)&buffer[userDataLenPos] = 0;
++ bufUsed = headerLength;
++
++ return true;
++}
++
++bool cResponsePacket::initStream(uint32_t opCode, uint32_t streamID, uint32_t duration, int64_t dts, int64_t pts)
++{
++ initBuffers();
++
++ *(uint32_t*)&buffer[0] = htonl(VNSI_CHANNEL_STREAM); // stream channel
++ *(uint32_t*)&buffer[4] = htonl(opCode); // Stream packet operation code
++ *(uint32_t*)&buffer[8] = htonl(streamID); // Stream ID
++ *(uint32_t*)&buffer[12] = htonl(duration); // Duration
++ *(int64_t*) &buffer[16] = __cpu_to_be64(dts); // DTS
++ *(int64_t*) &buffer[24] = __cpu_to_be64(pts); // PTS
++ *(uint32_t*)&buffer[userDataLenPosStream] = 0;
++ bufUsed = headerLengthStream;
++
++ return true;
++}
++
++void cResponsePacket::finalise()
++{
++ *(uint32_t*)&buffer[userDataLenPos] = htonl(bufUsed - headerLength);
++}
++
++void cResponsePacket::finaliseStream()
++{
++ *(uint32_t*)&buffer[userDataLenPosStream] = htonl(bufUsed - headerLengthStream);
++}
++
++
++bool cResponsePacket::copyin(const uint8_t* src, uint32_t len)
++{
++ if (!checkExtend(len)) return false;
++ memcpy(buffer + bufUsed, src, len);
++ bufUsed += len;
++ return true;
++}
++
++uint8_t* cResponsePacket::reserve(uint32_t len) {
++ if (!checkExtend(len)) return false;
++ uint8_t* result = buffer + bufUsed;
++ bufUsed += len;
++ return result;
++}
++
++bool cResponsePacket::unreserve(uint32_t len) {
++ if(bufUsed < len) return false;
++ bufUsed -= len;
++ return true;
++}
++
++bool cResponsePacket::add_String(const char* string)
++{
++ uint32_t len = strlen(string) + 1;
++ if (!checkExtend(len)) return false;
++ memcpy(buffer + bufUsed, string, len);
++ bufUsed += len;
++ return true;
++}
++
++bool cResponsePacket::add_U32(uint32_t ul)
++{
++ if (!checkExtend(sizeof(uint32_t))) return false;
++ *(uint32_t*)&buffer[bufUsed] = htonl(ul);
++ bufUsed += sizeof(uint32_t);
++ return true;
++}
++
++bool cResponsePacket::add_U8(uint8_t c)
++{
++ if (!checkExtend(sizeof(uint8_t))) return false;
++ buffer[bufUsed] = c;
++ bufUsed += sizeof(uint8_t);
++ return true;
++}
++
++bool cResponsePacket::add_S32(int32_t l)
++{
++ if (!checkExtend(sizeof(int32_t))) return false;
++ *(int32_t*)&buffer[bufUsed] = htonl(l);
++ bufUsed += sizeof(int32_t);
++ return true;
++}
++
++bool cResponsePacket::add_U64(uint64_t ull)
++{
++ if (!checkExtend(sizeof(uint64_t))) return false;
++ *(uint64_t*)&buffer[bufUsed] = __cpu_to_be64(ull);
++ bufUsed += sizeof(uint64_t);
++ return true;
++}
++
++bool cResponsePacket::add_double(double d)
++{
++ if (!checkExtend(sizeof(double))) return false;
++ uint64_t ull;
++ memcpy(&ull,&d,sizeof(double));
++ *(uint64_t*)&buffer[bufUsed] = __cpu_to_be64(ull);
++ bufUsed += sizeof(uint64_t);
++ return true;
++}
++
++
++bool cResponsePacket::checkExtend(uint32_t by)
++{
++ if ((bufUsed + by) < bufSize) return true;
++ if (512 > by) by = 512;
++ uint8_t* newBuf = (uint8_t*)realloc(buffer, bufSize + by);
++ if (!newBuf) return false;
++ buffer = newBuf;
++ bufSize += by;
++ return true;
++}
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/responsepacket.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/responsepacket.h
+new file mode 100644
+index 0000000..ec90639
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/responsepacket.h
+@@ -0,0 +1,75 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2007 Chris Tallon
++ * Copyright (C) 2010 Alwin Esch (Team XBMC)
++ * Copyright (C) 2010, 2011 Alexander Pipelka
++ *
++ * 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++/*
++ * This code is taken from VOMP for VDR plugin.
++ */
++
++#ifndef VNSI_RESPONSEPACKET_H
++#define VNSI_RESPONSEPACKET_H
++
++class cResponsePacket
++{
++public:
++ cResponsePacket();
++ ~cResponsePacket();
++
++ bool init(uint32_t requestID);
++ bool initScan(uint32_t opCode);
++ bool initStatus(uint32_t opCode);
++ bool initStream(uint32_t opCode, uint32_t streamID, uint32_t duration, int64_t dts, int64_t pts);
++ void finalise();
++ void finaliseStream();
++ bool copyin(const uint8_t* src, uint32_t len);
++ uint8_t* reserve(uint32_t len);
++ bool unreserve(uint32_t len);
++
++ bool add_String(const char* string);
++ bool add_U32(uint32_t ul);
++ bool add_S32(int32_t l);
++ bool add_U8(uint8_t c);
++ bool add_U64(uint64_t ull);
++ bool add_double(double d);
++
++ uint8_t* getPtr() { return buffer; }
++ uint32_t getLen() { return bufUsed; }
++
++private:
++ uint8_t* buffer;
++ uint32_t bufSize;
++ uint32_t bufUsed;
++
++ void initBuffers();
++ bool checkExtend(uint32_t by);
++
++ const static uint32_t headerLength = 12;
++ const static uint32_t userDataLenPos = 8;
++ const static uint32_t headerLengthStream = 36;
++ const static uint32_t userDataLenPosStream = 32;
++};
++
++#endif // VNSI_RESPONSEPACKET_H
++
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsi.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsi.c
+new file mode 100644
+index 0000000..7b25130
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsi.c
+@@ -0,0 +1,138 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (Team XBMC)
++ * Copyright (C) 2010, 2011 Alexander Pipelka
++ *
++ * 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <getopt.h>
++#include <vdr/plugin.h>
++#include "vnsi.h"
++
++cPluginVNSIServer::cPluginVNSIServer(void)
++{
++ Server = NULL;
++}
++
++cPluginVNSIServer::~cPluginVNSIServer()
++{
++ // Clean up after yourself!
++}
++
++const char *cPluginVNSIServer::CommandLineHelp(void)
++{
++ return " -t n, --timeout=n stream data timeout in seconds (default: 10)\n";
++}
++
++bool cPluginVNSIServer::ProcessArgs(int argc, char *argv[])
++{
++ // Implement command line argument processing here if applicable.
++ static struct option long_options[] = {
++ { "timeout", required_argument, NULL, 't' },
++ { NULL, no_argument, NULL, 0 }
++ };
++
++ int c;
++
++ while ((c = getopt_long(argc, argv, "t:", long_options, NULL)) != -1) {
++ switch (c) {
++ case 't': if(optarg != NULL) VNSIServerConfig.stream_timeout = atoi(optarg);
++ break;
++ default: return false;
++ }
++ }
++ return true;
++}
++
++bool cPluginVNSIServer::Initialize(void)
++{
++ // Initialize any background activities the plugin shall perform.
++ VNSIServerConfig.ConfigDirectory = ConfigDirectory(PLUGIN_NAME_I18N);
++ return true;
++}
++
++bool cPluginVNSIServer::Start(void)
++{
++ Server = new cVNSIServer(VNSIServerConfig.listen_port);
++
++ return true;
++}
++
++void cPluginVNSIServer::Stop(void)
++{
++ delete Server;
++ Server = NULL;
++}
++
++void cPluginVNSIServer::Housekeeping(void)
++{
++ // Perform any cleanup or other regular tasks.
++}
++
++void cPluginVNSIServer::MainThreadHook(void)
++{
++ // Perform actions in the context of the main program thread.
++ // WARNING: Use with great care - see PLUGINS.html!
++}
++
++cString cPluginVNSIServer::Active(void)
++{
++ // Return a message string if shutdown should be postponed
++ return NULL;
++}
++
++time_t cPluginVNSIServer::WakeupTime(void)
++{
++ // Return custom wakeup time for shutdown script
++ return 0;
++}
++
++cMenuSetupPage *cPluginVNSIServer::SetupMenu(void)
++{
++ // Return a setup menu in case the plugin supports one.
++ return NULL;
++}
++
++bool cPluginVNSIServer::SetupParse(const char *Name, const char *Value)
++{
++ // Parse your own setup parameters and store their values.
++ return false;
++}
++
++bool cPluginVNSIServer::Service(const char *Id, void *Data)
++{
++ // Handle custom service requests from other plugins
++ return false;
++}
++
++const char **cPluginVNSIServer::SVDRPHelpPages(void)
++{
++ // Return help text for SVDRP commands this plugin implements
++ return NULL;
++}
++
++cString cPluginVNSIServer::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
++{
++ // Process SVDRP commands this plugin implements
++ return NULL;
++}
++
++VDRPLUGINCREATOR(cPluginVNSIServer); // Don't touch this!
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsi.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsi.h
+new file mode 100644
+index 0000000..5adcf59
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsi.h
+@@ -0,0 +1,59 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (Team XBMC)
++ * Copyright (C) 2010, 2011 Alexander Pipelka
++ *
++ * 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <getopt.h>
++#include <vdr/plugin.h>
++#include "vnsiserver.h"
++
++static const char *VERSION = "0.9.0";
++static const char *DESCRIPTION = "VDR-Network-Streaming-Interface (VNSI) Server";
++
++class cPluginVNSIServer : public cPlugin {
++private:
++ cVNSIServer *Server;
++
++public:
++ cPluginVNSIServer(void);
++ virtual ~cPluginVNSIServer();
++ virtual const char *Version(void) { return VERSION; }
++ virtual const char *Description(void) { return DESCRIPTION; }
++ virtual const char *CommandLineHelp(void);
++ virtual bool ProcessArgs(int argc, char *argv[]);
++ virtual bool Initialize(void);
++ virtual bool Start(void);
++ virtual void Stop(void);
++ virtual void Housekeeping(void);
++ virtual void MainThreadHook(void);
++ virtual cString Active(void);
++ virtual time_t WakeupTime(void);
++ virtual const char *MainMenuEntry(void) { return NULL; }
++ virtual cOsdObject *MainMenuAction(void) { return NULL; }
++ virtual cMenuSetupPage *SetupMenu(void);
++ virtual bool SetupParse(const char *Name, const char *Value);
++ virtual bool Service(const char *Id, void *Data = NULL);
++ virtual const char **SVDRPHelpPages(void);
++ virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode);
++};
++
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsiclient.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsiclient.c
+new file mode 100644
+index 0000000..c4a4c89
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsiclient.c
+@@ -0,0 +1,1926 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (Team XBMC)
++ * Copyright (C) 2010, 2011 Alexander Pipelka
++ *
++ * 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <stdlib.h>
++#include <stdio.h>
++
++#include <vdr/recording.h>
++#include <vdr/channels.h>
++#include <vdr/videodir.h>
++#include <vdr/plugin.h>
++#include <vdr/timers.h>
++#include <vdr/menu.h>
++#include <vdr/device.h>
++
++#include "config.h"
++#include "vnsicommand.h"
++#include "recordingscache.h"
++#include "vnsiclient.h"
++#include "receiver.h"
++#include "vnsiserver.h"
++#include "recplayer.h"
++#include "requestpacket.h"
++#include "responsepacket.h"
++#include "hash.h"
++#include "wirbelscanservice.h" /// copied from modified wirbelscan plugin
++ /// must be hold up to date with wirbelscan
++
++
++static bool IsRadio(const cChannel* channel)
++{
++ bool isRadio = false;
++
++ // assume channels without VPID & APID are video channels
++ if (channel->Vpid() == 0 && channel->Apid(0) == 0)
++ isRadio = false;
++ // channels without VPID are radio channels (channels with VPID 1 are encrypted radio channels)
++ else if (channel->Vpid() == 0 || channel->Vpid() == 1)
++ isRadio = true;
++
++ return isRadio;
++}
++
++cMutex cVNSIClient::m_timerLock;
++
++cVNSIClient::cVNSIClient(int fd, unsigned int id, const char *ClientAdr)
++{
++ m_Id = id;
++ m_Streamer = NULL;
++ m_isStreaming = false;
++ m_ClientAddress = ClientAdr;
++ m_StatusInterfaceEnabled = false;
++ m_RecPlayer = NULL;
++ m_req = NULL;
++ m_resp = NULL;
++ m_processSCAN_Response = NULL;
++ m_processSCAN_Socket = NULL;
++
++
++ m_socket.set_handle(fd);
++
++ Start();
++}
++
++cVNSIClient::~cVNSIClient()
++{
++ DEBUGLOG("%s", __FUNCTION__);
++ StopChannelStreaming();
++ m_socket.close(); // force closing connection
++ Cancel(10);
++ DEBUGLOG("done");
++}
++
++void cVNSIClient::Action(void)
++{
++ uint32_t kaTimeStamp;
++ uint32_t channelID;
++ uint32_t requestID;
++ uint32_t opcode;
++ uint32_t dataLength;
++ uint8_t* data;
++
++ while (Running())
++ {
++ if (!m_socket.read((uint8_t*)&channelID, sizeof(uint32_t))) break;
++ channelID = ntohl(channelID);
++
++ if (channelID == 1)
++ {
++ if (!m_socket.read((uint8_t*)&requestID, sizeof(uint32_t), 10000)) break;
++ requestID = ntohl(requestID);
++
++ if (!m_socket.read((uint8_t*)&opcode, sizeof(uint32_t), 10000)) break;
++ opcode = ntohl(opcode);
++
++ if (!m_socket.read((uint8_t*)&dataLength, sizeof(uint32_t), 10000)) break;
++ dataLength = ntohl(dataLength);
++ if (dataLength > 200000) // a random sanity limit
++ {
++ ERRORLOG("dataLength > 200000!");
++ break;
++ }
++
++ if (dataLength)
++ {
++ data = (uint8_t*)malloc(dataLength);
++ if (!data)
++ {
++ ERRORLOG("Extra data buffer malloc error");
++ break;
++ }
++
++ if (!m_socket.read(data, dataLength, 10000))
++ {
++ ERRORLOG("Could not read data");
++ free(data);
++ break;
++ }
++ }
++ else
++ {
++ data = NULL;
++ }
++
++ DEBUGLOG("Received chan=%u, ser=%u, op=%u, edl=%u", channelID, requestID, opcode, dataLength);
++
++ if (!m_loggedIn && (opcode != VNSI_LOGIN))
++ {
++ ERRORLOG("Clients must be logged in before sending commands! Aborting.");
++ if (data) free(data);
++ break;
++ }
++
++ cRequestPacket* req = new cRequestPacket(requestID, opcode, data, dataLength);
++
++ processRequest(req);
++ }
++ else if (channelID == 3)
++ {
++ if (!m_socket.read((uint8_t*)&kaTimeStamp, sizeof(uint32_t), 1000)) break;
++ kaTimeStamp = ntohl(kaTimeStamp);
++
++ uint8_t buffer[8];
++ *(uint32_t*)&buffer[0] = htonl(3); // KA CHANNEL
++ *(uint32_t*)&buffer[4] = htonl(kaTimeStamp);
++ if (!m_socket.write(buffer, 8))
++ {
++ ERRORLOG("Could not send back KA reply");
++ break;
++ }
++ }
++ else
++ {
++ ERRORLOG("Incoming channel number unknown");
++ break;
++ }
++ }
++
++ /* If thread is ended due to closed connection delete a
++ possible running stream here */
++ StopChannelStreaming();
++}
++
++bool cVNSIClient::StartChannelStreaming(const cChannel *channel, uint32_t timeout)
++{
++ m_Streamer = new cLiveStreamer(timeout);
++ m_isStreaming = m_Streamer->StreamChannel(channel, 50, &m_socket, m_resp);
++ return m_isStreaming;
++}
++
++void cVNSIClient::StopChannelStreaming()
++{
++ m_isStreaming = false;
++ if (m_Streamer)
++ {
++ delete m_Streamer;
++ m_Streamer = NULL;
++ }
++}
++
++void cVNSIClient::TimerChange(const cTimer *Timer, eTimerChange Change)
++{
++ TimerChange();
++}
++
++void cVNSIClient::TimerChange()
++{
++ cMutexLock lock(&m_msgLock);
++
++ if (m_StatusInterfaceEnabled)
++ {
++ cResponsePacket *resp = new cResponsePacket();
++ if (!resp->initStatus(VNSI_STATUS_TIMERCHANGE))
++ {
++ delete resp;
++ return;
++ }
++
++ resp->finalise();
++ m_socket.write(resp->getPtr(), resp->getLen());
++ delete resp;
++ }
++}
++
++void cVNSIClient::ChannelChange()
++{
++ cMutexLock lock(&m_msgLock);
++
++ if (!m_StatusInterfaceEnabled)
++ return;
++
++ cResponsePacket *resp = new cResponsePacket();
++ if (!resp->initStatus(VNSI_STATUS_CHANNELCHANGE))
++ {
++ delete resp;
++ return;
++ }
++
++ resp->finalise();
++ m_socket.write(resp->getPtr(), resp->getLen());
++ delete resp;
++}
++
++void cVNSIClient::RecordingsChange()
++{
++ cMutexLock lock(&m_msgLock);
++
++ if (!m_StatusInterfaceEnabled)
++ return;
++
++ cResponsePacket *resp = new cResponsePacket();
++ if (!resp->initStatus(VNSI_STATUS_RECORDINGSCHANGE))
++ {
++ delete resp;
++ return;
++ }
++
++ resp->finalise();
++ m_socket.write(resp->getPtr(), resp->getLen());
++ delete resp;
++}
++
++void cVNSIClient::Recording(const cDevice *Device, const char *Name, const char *FileName, bool On)
++{
++ cMutexLock lock(&m_msgLock);
++
++ if (m_StatusInterfaceEnabled)
++ {
++ cResponsePacket *resp = new cResponsePacket();
++ if (!resp->initStatus(VNSI_STATUS_RECORDING))
++ {
++ delete resp;
++ return;
++ }
++
++ resp->add_U32(Device->CardIndex());
++ resp->add_U32(On);
++ if (Name)
++ resp->add_String(Name);
++ else
++ resp->add_String("");
++
++ if (FileName)
++ resp->add_String(FileName);
++ else
++ resp->add_String("");
++
++ resp->finalise();
++ m_socket.write(resp->getPtr(), resp->getLen());
++ delete resp;
++ }
++}
++
++void cVNSIClient::OsdStatusMessage(const char *Message)
++{
++ cMutexLock lock(&m_msgLock);
++
++ if (m_StatusInterfaceEnabled && Message)
++ {
++ /* Ignore this messages */
++ if (strcasecmp(Message, trVDR("Channel not available!")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Delete timer?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Delete recording?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Press any key to cancel shutdown")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Press any key to cancel restart")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Editing - shut down anyway?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Recording - shut down anyway?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("shut down anyway?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Recording - restart anyway?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Editing - restart anyway?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Delete channel?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Timer still recording - really delete?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Delete marks information?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Delete resume information?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("CAM is in use - really reset?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Really restart?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Stop recording?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Cancel editing?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Cutter already running - Add to cutting queue?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("No index-file found. Creating may take minutes. Create one?")) == 0) return;
++
++ cResponsePacket *resp = new cResponsePacket();
++ if (!resp->initStatus(VNSI_STATUS_MESSAGE))
++ {
++ delete resp;
++ return;
++ }
++
++ resp->add_U32(0);
++ resp->add_String(Message);
++ resp->finalise();
++ m_socket.write(resp->getPtr(), resp->getLen());
++ delete resp;
++ }
++}
++
++bool cVNSIClient::processRequest(cRequestPacket* req)
++{
++ cMutexLock lock(&m_msgLock);
++
++ m_req = req;
++ m_resp = new cResponsePacket();
++ if (!m_resp->init(m_req->getRequestID()))
++ {
++ ERRORLOG("Response packet init fail");
++ delete m_resp;
++ delete m_req;
++ m_resp = NULL;
++ m_req = NULL;
++ return false;
++ }
++
++ bool result = false;
++ switch(m_req->getOpCode())
++ {
++ /** OPCODE 1 - 19: VNSI network functions for general purpose */
++ case VNSI_LOGIN:
++ result = process_Login();
++ break;
++
++ case VNSI_GETTIME:
++ result = process_GetTime();
++ break;
++
++ case VNSI_ENABLESTATUSINTERFACE:
++ result = process_EnableStatusInterface();
++ break;
++
++ case VNSI_PING:
++ result = process_Ping();
++ break;
++
++
++ /** OPCODE 20 - 39: VNSI network functions for live streaming */
++ case VNSI_CHANNELSTREAM_OPEN:
++ result = processChannelStream_Open();
++ break;
++
++ case VNSI_CHANNELSTREAM_CLOSE:
++ result = processChannelStream_Close();
++ break;
++
++
++ /** OPCODE 40 - 59: VNSI network functions for recording streaming */
++ case VNSI_RECSTREAM_OPEN:
++ result = processRecStream_Open();
++ break;
++
++ case VNSI_RECSTREAM_CLOSE:
++ result = processRecStream_Close();
++ break;
++
++ case VNSI_RECSTREAM_GETBLOCK:
++ result = processRecStream_GetBlock();
++ break;
++
++ case VNSI_RECSTREAM_POSTOFRAME:
++ result = processRecStream_PositionFromFrameNumber();
++ break;
++
++ case VNSI_RECSTREAM_FRAMETOPOS:
++ result = processRecStream_FrameNumberFromPosition();
++ break;
++
++ case VNSI_RECSTREAM_GETIFRAME:
++ result = processRecStream_GetIFrame();
++ break;
++
++
++ /** OPCODE 60 - 79: VNSI network functions for channel access */
++ case VNSI_CHANNELS_GETCOUNT:
++ result = processCHANNELS_ChannelsCount();
++ break;
++
++ case VNSI_CHANNELS_GETCHANNELS:
++ result = processCHANNELS_GetChannels();
++ break;
++
++ case VNSI_CHANNELGROUP_GETCOUNT:
++ result = processCHANNELS_GroupsCount();
++ break;
++
++ case VNSI_CHANNELGROUP_LIST:
++ result = processCHANNELS_GroupList();
++ break;
++
++ case VNSI_CHANNELGROUP_MEMBERS:
++ result = processCHANNELS_GetGroupMembers();
++ break;
++
++ /** OPCODE 80 - 99: VNSI network functions for timer access */
++ case VNSI_TIMER_GETCOUNT:
++ result = processTIMER_GetCount();
++ break;
++
++ case VNSI_TIMER_GET:
++ result = processTIMER_Get();
++ break;
++
++ case VNSI_TIMER_GETLIST:
++ result = processTIMER_GetList();
++ break;
++
++ case VNSI_TIMER_ADD:
++ result = processTIMER_Add();
++ break;
++
++ case VNSI_TIMER_DELETE:
++ result = processTIMER_Delete();
++ break;
++
++ case VNSI_TIMER_UPDATE:
++ result = processTIMER_Update();
++ break;
++
++
++ /** OPCODE 100 - 119: VNSI network functions for recording access */
++ case VNSI_RECORDINGS_DISKSIZE:
++ result = processRECORDINGS_GetDiskSpace();
++ break;
++
++ case VNSI_RECORDINGS_GETCOUNT:
++ result = processRECORDINGS_GetCount();
++ break;
++
++ case VNSI_RECORDINGS_GETLIST:
++ result = processRECORDINGS_GetList();
++ break;
++
++ case VNSI_RECORDINGS_RENAME:
++ result = processRECORDINGS_Rename();
++ break;
++
++ case VNSI_RECORDINGS_DELETE:
++ result = processRECORDINGS_Delete();
++ break;
++
++
++ /** OPCODE 120 - 139: VNSI network functions for epg access and manipulating */
++ case VNSI_EPG_GETFORCHANNEL:
++ result = processEPG_GetForChannel();
++ break;
++
++
++ /** OPCODE 140 - 159: VNSI network functions for channel scanning */
++ case VNSI_SCAN_SUPPORTED:
++ result = processSCAN_ScanSupported();
++ break;
++
++ case VNSI_SCAN_GETCOUNTRIES:
++ result = processSCAN_GetCountries();
++ break;
++
++ case VNSI_SCAN_GETSATELLITES:
++ result = processSCAN_GetSatellites();
++ break;
++
++ case VNSI_SCAN_START:
++ result = processSCAN_Start();
++ break;
++
++ case VNSI_SCAN_STOP:
++ result = processSCAN_Stop();
++ break;
++ }
++
++ delete m_resp;
++ m_resp = NULL;
++
++ delete m_req;
++ m_req = NULL;
++
++ return result;
++}
++
++
++/** OPCODE 1 - 19: VNSI network functions for general purpose */
++
++bool cVNSIClient::process_Login() /* OPCODE 1 */
++{
++ if (m_req->getDataLength() <= 4) return false;
++
++ m_protocolVersion = m_req->extract_U32();
++ m_req->extract_U8();
++ const char *clientName = m_req->extract_String();
++
++ if (m_protocolVersion > VNSI_PROTOCOLVERSION)
++ {
++ ERRORLOG("Client '%s' have a not allowed protocol version '%u', terminating client", clientName, m_protocolVersion);
++ delete[] clientName;
++ return false;
++ }
++
++ INFOLOG("Welcome client '%s' with protocol version '%u'", clientName, m_protocolVersion);
++
++ // Send the login reply
++ time_t timeNow = time(NULL);
++ struct tm* timeStruct = localtime(&timeNow);
++ int timeOffset = timeStruct->tm_gmtoff;
++
++ m_resp->add_U32(VNSI_PROTOCOLVERSION);
++ m_resp->add_U32(timeNow);
++ m_resp->add_S32(timeOffset);
++ m_resp->add_String("VDR-Network-Streaming-Interface (VNSI) Server");
++ m_resp->add_String(VNSI_SERVER_VERSION);
++ m_resp->finalise();
++ SetLoggedIn(true);
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++
++ delete[] clientName;
++
++ return true;
++}
++
++bool cVNSIClient::process_GetTime() /* OPCODE 2 */
++{
++ time_t timeNow = time(NULL);
++ struct tm* timeStruct = localtime(&timeNow);
++ int timeOffset = timeStruct->tm_gmtoff;
++
++ m_resp->add_U32(timeNow);
++ m_resp->add_S32(timeOffset);
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ return true;
++}
++
++bool cVNSIClient::process_EnableStatusInterface()
++{
++ bool enabled = m_req->extract_U8();
++
++ SetStatusInterface(enabled);
++
++ m_resp->add_U32(VNSI_RET_OK);
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ return true;
++}
++
++bool cVNSIClient::process_Ping() /* OPCODE 7 */
++{
++ m_resp->add_U32(1);
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ return true;
++}
++
++
++/** OPCODE 20 - 39: VNSI network functions for live streaming */
++
++bool cVNSIClient::processChannelStream_Open() /* OPCODE 20 */
++{
++ uint32_t uid = m_req->extract_U32();
++ uint32_t timeout = m_req->extract_U32();
++
++ if(timeout == 0)
++ timeout = VNSIServerConfig.stream_timeout;
++
++ if (m_isStreaming)
++ StopChannelStreaming();
++
++ Channels.Lock(false);
++ const cChannel *channel = NULL;
++
++ // try to find channel by uid first
++ channel = FindChannelByUID(uid);
++ Channels.Unlock();
++
++ // try channelnumber
++ if (channel == NULL)
++ channel = Channels.GetByNumber(uid);
++
++ if (channel == NULL) {
++ ERRORLOG("Can't find channel %08x", uid);
++ m_resp->add_U32(VNSI_RET_DATAINVALID);
++ }
++ else
++ {
++ if (StartChannelStreaming(channel, timeout))
++ {
++ INFOLOG("Started streaming of channel %s (timeout %i seconds)", channel->Name(), timeout);
++ // return here without sending the response
++ // (was already done in cLiveStreamer::StreamChannel)
++ return true;
++ }
++
++ DEBUGLOG("Can't stream channel %s", channel->Name());
++ m_resp->add_U32(VNSI_RET_DATALOCKED);
++ }
++
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++
++ return false;
++}
++
++bool cVNSIClient::processChannelStream_Close() /* OPCODE 21 */
++{
++ if (m_isStreaming)
++ StopChannelStreaming();
++
++ return true;
++}
++
++
++/** OPCODE 40 - 59: VNSI network functions for recording streaming */
++
++bool cVNSIClient::processRecStream_Open() /* OPCODE 40 */
++{
++ cRecording *recording = NULL;
++
++ if(m_protocolVersion >= 2) {
++ uint32_t uid = m_req->extract_U32();
++ recording = cRecordingsCache::GetInstance().Lookup(uid);
++ }
++ else {
++ const char *fileName = m_req->extract_String();
++ recording = Recordings.GetByName(fileName);
++ delete[] fileName;
++ }
++
++ if (recording && m_RecPlayer == NULL)
++ {
++ m_RecPlayer = new cRecPlayer(recording);
++
++ m_resp->add_U32(VNSI_RET_OK);
++ m_resp->add_U32(m_RecPlayer->getLengthFrames());
++ m_resp->add_U64(m_RecPlayer->getLengthBytes());
++
++#if VDRVERSNUM < 10703
++ m_resp->add_U8(true);//added for TS
++#else
++ m_resp->add_U8(recording->IsPesRecording());//added for TS
++#endif
++ }
++ else
++ {
++ m_resp->add_U32(VNSI_RET_DATAUNKNOWN);
++ ERRORLOG("%s - unable to start recording !", __FUNCTION__);
++ }
++
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++
++ return true;
++}
++
++bool cVNSIClient::processRecStream_Close() /* OPCODE 41 */
++{
++ if (m_RecPlayer)
++ {
++ delete m_RecPlayer;
++ m_RecPlayer = NULL;
++ }
++
++ m_resp->add_U32(VNSI_RET_OK);
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ return true;
++}
++
++bool cVNSIClient::processRecStream_GetBlock() /* OPCODE 42 */
++{
++ if (m_isStreaming)
++ {
++ ERRORLOG("Get block called during live streaming");
++ return false;
++ }
++
++ if (!m_RecPlayer)
++ {
++ ERRORLOG("Get block called when no recording open");
++ return false;
++ }
++
++ uint64_t position = m_req->extract_U64();
++ uint32_t amount = m_req->extract_U32();
++
++ uint8_t* p = m_resp->reserve(amount);
++ uint32_t amountReceived = m_RecPlayer->getBlock(p, position, amount);
++
++ if(amount > amountReceived) m_resp->unreserve(amount - amountReceived);
++
++ if (!amountReceived)
++ {
++ m_resp->add_U32(0);
++ DEBUGLOG("written 4(0) as getblock got 0");
++ }
++
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ return true;
++}
++
++bool cVNSIClient::processRecStream_PositionFromFrameNumber() /* OPCODE 43 */
++{
++ uint64_t retval = 0;
++ uint32_t frameNumber = m_req->extract_U32();
++
++ if (m_RecPlayer)
++ retval = m_RecPlayer->positionFromFrameNumber(frameNumber);
++
++ m_resp->add_U64(retval);
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++
++ DEBUGLOG("Wrote posFromFrameNum reply to client");
++ return true;
++}
++
++bool cVNSIClient::processRecStream_FrameNumberFromPosition() /* OPCODE 44 */
++{
++ uint32_t retval = 0;
++ uint64_t position = m_req->extract_U64();
++
++ if (m_RecPlayer)
++ retval = m_RecPlayer->frameNumberFromPosition(position);
++
++ m_resp->add_U32(retval);
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++
++ DEBUGLOG("Wrote frameNumFromPos reply to client");
++ return true;
++}
++
++bool cVNSIClient::processRecStream_GetIFrame() /* OPCODE 45 */
++{
++ bool success = false;
++ uint32_t frameNumber = m_req->extract_U32();
++ uint32_t direction = m_req->extract_U32();
++ uint64_t rfilePosition = 0;
++ uint32_t rframeNumber = 0;
++ uint32_t rframeLength = 0;
++
++ if (m_RecPlayer)
++ success = m_RecPlayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
++
++ // returns file position, frame number, length
++ if (success)
++ {
++ m_resp->add_U64(rfilePosition);
++ m_resp->add_U32(rframeNumber);
++ m_resp->add_U32(rframeLength);
++ }
++ else
++ {
++ m_resp->add_U32(0);
++ }
++
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++
++ DEBUGLOG("Wrote GNIF reply to client %llu %u %u", rfilePosition, rframeNumber, rframeLength);
++ return true;
++}
++
++
++/** OPCODE 60 - 79: VNSI network functions for channel access */
++
++bool cVNSIClient::processCHANNELS_ChannelsCount() /* OPCODE 61 */
++{
++ Channels.Lock(false);
++ int count = Channels.MaxNumber();
++ Channels.Unlock();
++
++ m_resp->add_U32(count);
++
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ return true;
++}
++
++bool cVNSIClient::processCHANNELS_GetChannels() /* OPCODE 63 */
++{
++ if (m_req->getDataLength() != 4) return false;
++
++ bool radio = m_req->extract_U32();
++
++ Channels.Lock(false);
++
++ for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
++ {
++ if (radio != IsRadio(channel))
++ continue;
++
++ // skip invalid channels
++ if (channel->Sid() == 0)
++ continue;
++
++ m_resp->add_U32(channel->Number());
++ m_resp->add_String(m_toUTF8.Convert(channel->Name()));
++ if(m_protocolVersion >= 2) {
++ m_resp->add_U32(CreateChannelUID(channel));
++ }
++ else {
++ m_resp->add_U32(channel->Sid());
++ }
++ m_resp->add_U32(0); // groupindex unused
++ m_resp->add_U32(channel->Ca());
++#if APIVERSNUM >= 10701
++ m_resp->add_U32(channel->Vtype());
++#else
++ m_resp->add_U32(2);
++#endif
++ }
++
++ Channels.Unlock();
++
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++
++ return true;
++}
++
++bool cVNSIClient::processCHANNELS_GroupsCount()
++{
++ uint32_t type = m_req->extract_U32();
++
++ Channels.Lock(false);
++
++ m_channelgroups[0].clear();
++ m_channelgroups[1].clear();
++
++ switch(type)
++ {
++ // get groups defined in channels.conf
++ default:
++ case 0:
++ CreateChannelGroups(false);
++ break;
++ // automatically create groups
++ case 1:
++ CreateChannelGroups(true);
++ break;
++ }
++
++ Channels.Unlock();
++
++ uint32_t count = m_channelgroups[0].size() + m_channelgroups[1].size();
++
++ m_resp->add_U32(count);
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ return true;
++}
++
++bool cVNSIClient::processCHANNELS_GroupList()
++{
++ uint32_t radio = m_req->extract_U8();
++ std::map<std::string, ChannelGroup>::iterator i;
++
++ for(i = m_channelgroups[radio].begin(); i != m_channelgroups[radio].end(); i++)
++ {
++ m_resp->add_String(i->second.name.c_str());
++ m_resp->add_U8(i->second.radio);
++ }
++
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ return true;
++}
++
++bool cVNSIClient::processCHANNELS_GetGroupMembers()
++{
++ char* groupname = m_req->extract_String();
++ uint32_t radio = m_req->extract_U8();
++ int index = 0;
++
++ // unknown group
++ if(m_channelgroups[radio].find(groupname) == m_channelgroups[radio].end())
++ {
++ delete[] groupname;
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ return true;
++ }
++
++ bool automatic = m_channelgroups[radio][groupname].automatic;
++ std::string name;
++
++ Channels.Lock(false);
++
++ for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
++ {
++
++ if(automatic && !channel->GroupSep())
++ name = channel->Provider();
++ else
++ {
++ if(channel->GroupSep())
++ {
++ name = channel->Name();
++ continue;
++ }
++ }
++
++ if(name.empty())
++ continue;
++
++ if(IsRadio(channel) != radio)
++ continue;
++
++ if(name == groupname)
++ {
++ m_resp->add_U32(CreateChannelUID(channel));
++ m_resp->add_U32(++index);
++ }
++ }
++
++ Channels.Unlock();
++
++ delete[] groupname;
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ return true;
++}
++
++void cVNSIClient::CreateChannelGroups(bool automatic)
++{
++ std::string groupname;
++
++ for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
++ {
++ bool isRadio = IsRadio(channel);
++
++ if(automatic && !channel->GroupSep())
++ groupname = channel->Provider();
++ else if(!automatic && channel->GroupSep())
++ groupname = channel->Name();
++
++ if(groupname.empty())
++ continue;
++
++ if(m_channelgroups[isRadio].find(groupname) == m_channelgroups[isRadio].end())
++ {
++ ChannelGroup group;
++ group.name = groupname;
++ group.radio = isRadio;
++ group.automatic = automatic;
++ m_channelgroups[isRadio][groupname] = group;
++ }
++ }
++}
++
++/** OPCODE 80 - 99: VNSI network functions for timer access */
++
++bool cVNSIClient::processTIMER_GetCount() /* OPCODE 80 */
++{
++ cMutexLock lock(&m_timerLock);
++
++ int count = Timers.Count();
++
++ m_resp->add_U32(count);
++
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ return true;
++}
++
++bool cVNSIClient::processTIMER_Get() /* OPCODE 81 */
++{
++ cMutexLock lock(&m_timerLock);
++
++ uint32_t number = m_req->extract_U32();
++
++ int numTimers = Timers.Count();
++ if (numTimers > 0)
++ {
++ cTimer *timer = Timers.Get(number-1);
++ if (timer)
++ {
++ m_resp->add_U32(VNSI_RET_OK);
++
++ m_resp->add_U32(timer->Index()+1);
++ m_resp->add_U32(timer->HasFlags(tfActive));
++ m_resp->add_U32(timer->Recording());
++ m_resp->add_U32(timer->Pending());
++ m_resp->add_U32(timer->Priority());
++ m_resp->add_U32(timer->Lifetime());
++ m_resp->add_U32(timer->Channel()->Number());
++ if(m_protocolVersion >= 2) {
++ m_resp->add_U32(CreateChannelUID(timer->Channel()));
++ }
++ m_resp->add_U32(timer->StartTime());
++ m_resp->add_U32(timer->StopTime());
++ m_resp->add_U32(timer->Day());
++ m_resp->add_U32(timer->WeekDays());
++ m_resp->add_String(m_toUTF8.Convert(timer->File()));
++ }
++ else
++ m_resp->add_U32(VNSI_RET_DATAUNKNOWN);
++ }
++ else
++ m_resp->add_U32(VNSI_RET_DATAUNKNOWN);
++
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ return true;
++}
++
++bool cVNSIClient::processTIMER_GetList() /* OPCODE 82 */
++{
++ cMutexLock lock(&m_timerLock);
++
++ cTimer *timer;
++ int numTimers = Timers.Count();
++
++ m_resp->add_U32(numTimers);
++
++ for (int i = 0; i < numTimers; i++)
++ {
++ timer = Timers.Get(i);
++ if (!timer)
++ continue;
++
++ m_resp->add_U32(timer->Index()+1);
++ m_resp->add_U32(timer->HasFlags(tfActive));
++ m_resp->add_U32(timer->Recording());
++ m_resp->add_U32(timer->Pending());
++ m_resp->add_U32(timer->Priority());
++ m_resp->add_U32(timer->Lifetime());
++ m_resp->add_U32(timer->Channel()->Number());
++ if(m_protocolVersion >= 2) {
++ m_resp->add_U32(CreateChannelUID(timer->Channel()));
++ }
++ m_resp->add_U32(timer->StartTime());
++ m_resp->add_U32(timer->StopTime());
++ m_resp->add_U32(timer->Day());
++ m_resp->add_U32(timer->WeekDays());
++ m_resp->add_String(m_toUTF8.Convert(timer->File()));
++ }
++
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ return true;
++}
++
++bool cVNSIClient::processTIMER_Add() /* OPCODE 83 */
++{
++ cMutexLock lock(&m_timerLock);
++
++ uint32_t flags = m_req->extract_U32() > 0 ? tfActive : tfNone;
++ uint32_t priority = m_req->extract_U32();
++ uint32_t lifetime = m_req->extract_U32();
++ uint32_t channelid = m_req->extract_U32();
++ time_t startTime = m_req->extract_U32();
++ time_t stopTime = m_req->extract_U32();
++ time_t day = m_req->extract_U32();
++ uint32_t weekdays = m_req->extract_U32();
++ const char *file = m_req->extract_String();
++ const char *aux = m_req->extract_String();
++
++ // handle instant timers
++ if(startTime == -1 || startTime == 0)
++ {
++ startTime = time(NULL);
++ }
++
++ struct tm tm_r;
++ struct tm *time = localtime_r(&startTime, &tm_r);
++ if (day <= 0)
++ day = cTimer::SetTime(startTime, 0);
++ int start = time->tm_hour * 100 + time->tm_min;
++ time = localtime_r(&stopTime, &tm_r);
++ int stop = time->tm_hour * 100 + time->tm_min;
++
++ cString buffer;
++ if(m_protocolVersion == 1) {
++ buffer = cString::sprintf("%u:%i:%s:%04d:%04d:%d:%d:%s:%s\n", flags, channelid, *cTimer::PrintDay(day, weekdays, true), start, stop, priority, lifetime, file, aux);
++ }
++ else {
++ const cChannel* channel = FindChannelByUID(channelid);
++ if(channel != NULL) {
++ buffer = cString::sprintf("%u:%s:%s:%04d:%04d:%d:%d:%s:%s\n", flags, (const char*)channel->GetChannelID().ToString(), *cTimer::PrintDay(day, weekdays, true), start, stop, priority, lifetime, file, aux);
++ }
++ }
++
++ delete[] file;
++ delete[] aux;
++
++ cTimer *timer = new cTimer;
++ if (timer->Parse(buffer))
++ {
++ cTimer *t = Timers.GetTimer(timer);
++ if (!t)
++ {
++ Timers.Add(timer);
++ Timers.SetModified();
++ INFOLOG("Timer %s added", *timer->ToDescr());
++ m_resp->add_U32(VNSI_RET_OK);
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ return true;
++ }
++ else
++ {
++ ERRORLOG("Timer already defined: %d %s", t->Index() + 1, *t->ToText());
++ m_resp->add_U32(VNSI_RET_DATALOCKED);
++ }
++ }
++ else
++ {
++ ERRORLOG("Error in timer settings");
++ m_resp->add_U32(VNSI_RET_DATAINVALID);
++ }
++
++ delete timer;
++
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ return true;
++}
++
++bool cVNSIClient::processTIMER_Delete() /* OPCODE 84 */
++{
++ cMutexLock lock(&m_timerLock);
++
++ uint32_t number = m_req->extract_U32();
++ bool force = m_req->extract_U32();
++
++ if (number <= 0 || number > (uint32_t)Timers.Count())
++ {
++ ERRORLOG("Unable to delete timer - invalid timer identifier");
++ m_resp->add_U32(VNSI_RET_DATAINVALID);
++ }
++ else
++ {
++ cTimer *timer = Timers.Get(number-1);
++ if (timer)
++ {
++ if (!Timers.BeingEdited())
++ {
++ if (timer->Recording())
++ {
++ if (force)
++ {
++ timer->Skip();
++ cRecordControls::Process(time(NULL));
++ }
++ else
++ {
++ ERRORLOG("Timer \"%i\" is recording and can be deleted (use force=1 to stop it)", number);
++ m_resp->add_U32(VNSI_RET_RECRUNNING);
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ return true;
++ }
++ }
++ INFOLOG("Deleting timer %s", *timer->ToDescr());
++ Timers.Del(timer);
++ Timers.SetModified();
++ m_resp->add_U32(VNSI_RET_OK);
++ }
++ else
++ {
++ ERRORLOG("Unable to delete timer - timers being edited at VDR");
++ m_resp->add_U32(VNSI_RET_DATALOCKED);
++ }
++ }
++ else
++ {
++ ERRORLOG("Unable to delete timer - invalid timer identifier");
++ m_resp->add_U32(VNSI_RET_DATAINVALID);
++ }
++ }
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ return true;
++}
++
++bool cVNSIClient::processTIMER_Update() /* OPCODE 85 */
++{
++ cMutexLock lock(&m_timerLock);
++
++ int length = m_req->getDataLength();
++ uint32_t index = m_req->extract_U32();
++ bool active = m_req->extract_U32();
++
++ cTimer *timer = Timers.Get(index - 1);
++ if (!timer)
++ {
++ ERRORLOG("Timer \"%u\" not defined", index);
++ m_resp->add_U32(VNSI_RET_DATAUNKNOWN);
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ return true;
++ }
++
++ cTimer t = *timer;
++
++ if (length == 8)
++ {
++ if (active)
++ t.SetFlags(tfActive);
++ else
++ t.ClrFlags(tfActive);
++ }
++ else
++ {
++ uint32_t flags = active ? tfActive : tfNone;
++ uint32_t priority = m_req->extract_U32();
++ uint32_t lifetime = m_req->extract_U32();
++ uint32_t channelid = m_req->extract_U32();
++ time_t startTime = m_req->extract_U32();
++ time_t stopTime = m_req->extract_U32();
++ time_t day = m_req->extract_U32();
++ uint32_t weekdays = m_req->extract_U32();
++ const char *file = m_req->extract_String();
++ const char *aux = m_req->extract_String();
++
++ struct tm tm_r;
++ struct tm *time = localtime_r(&startTime, &tm_r);
++ if (day <= 0)
++ day = cTimer::SetTime(startTime, 0);
++ int start = time->tm_hour * 100 + time->tm_min;
++ time = localtime_r(&stopTime, &tm_r);
++ int stop = time->tm_hour * 100 + time->tm_min;
++
++ cString buffer;
++ if(m_protocolVersion == 1) {
++ buffer = cString::sprintf("%u:%i:%s:%04d:%04d:%d:%d:%s:%s\n", flags, channelid, *cTimer::PrintDay(day, weekdays, true), start, stop, priority, lifetime, file, aux);
++ }
++ else {
++ const cChannel* channel = FindChannelByUID(channelid);
++ if(channel != NULL) {
++ buffer = cString::sprintf("%u:%s:%s:%04d:%04d:%d:%d:%s:%s\n", flags, (const char*)channel->GetChannelID().ToString(), *cTimer::PrintDay(day, weekdays, true), start, stop, priority, lifetime, file, aux);
++ }
++ }
++
++ delete[] file;
++ delete[] aux;
++
++ if (!t.Parse(buffer))
++ {
++ ERRORLOG("Error in timer settings");
++ m_resp->add_U32(VNSI_RET_DATAINVALID);
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ return true;
++ }
++ }
++
++ *timer = t;
++ Timers.SetModified();
++
++ m_resp->add_U32(VNSI_RET_OK);
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ return true;
++}
++
++
++/** OPCODE 100 - 119: VNSI network functions for recording access */
++
++bool cVNSIClient::processRECORDINGS_GetDiskSpace() /* OPCODE 100 */
++{
++ int FreeMB;
++ int Percent = VideoDiskSpace(&FreeMB);
++ int Total = (FreeMB / (100 - Percent)) * 100;
++
++ m_resp->add_U32(Total);
++ m_resp->add_U32(FreeMB);
++ m_resp->add_U32(Percent);
++
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ return true;
++}
++
++bool cVNSIClient::processRECORDINGS_GetCount() /* OPCODE 101 */
++{
++ Recordings.Load();
++ m_resp->add_U32(Recordings.Count());
++
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ return true;
++}
++
++bool cVNSIClient::processRECORDINGS_GetList() /* OPCODE 102 */
++{
++ cMutexLock lock(&m_timerLock);
++
++ if(m_protocolVersion == 1) {
++ m_resp->add_String(VideoDirectory);
++ }
++
++ for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
++ {
++#if APIVERSNUM >= 10705
++ const cEvent *event = recording->Info()->GetEvent();
++#else
++ const cEvent *event = NULL;
++#endif
++
++ time_t recordingStart = 0;
++ int recordingDuration = 0;
++ if (event)
++ {
++ recordingStart = event->StartTime();
++ recordingDuration = event->Duration();
++ }
++ else
++ {
++ cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
++ if (rc)
++ {
++ recordingStart = rc->Timer()->StartTime();
++ recordingDuration = rc->Timer()->StopTime() - recordingStart;
++ }
++ else
++ {
++ recordingStart = recording->start;
++ }
++ }
++ DEBUGLOG("GRI: RC: recordingStart=%lu recordingDuration=%i", recordingStart, recordingDuration);
++
++ // recording_time
++ m_resp->add_U32(recordingStart);
++
++ // duration
++ m_resp->add_U32(recordingDuration);
++
++ // priority
++ m_resp->add_U32(recording->priority);
++
++ // lifetime
++ m_resp->add_U32(recording->lifetime);
++
++ // channel_name
++ m_resp->add_String(recording->Info()->ChannelName() ? m_toUTF8.Convert(recording->Info()->ChannelName()) : "");
++
++ char* fullname = strdup(recording->Name());
++ char* recname = strrchr(fullname, FOLDERDELIMCHAR);
++ char* directory = NULL;
++
++ if(recname == NULL) {
++ recname = fullname;
++ }
++ else {
++ *recname = 0;
++ recname++;
++ directory = fullname;
++ }
++
++ // title
++ m_resp->add_String(m_toUTF8.Convert(recname));
++
++ // subtitle
++ if (!isempty(recording->Info()->ShortText()))
++ m_resp->add_String(m_toUTF8.Convert(recording->Info()->ShortText()));
++ else
++ m_resp->add_String("");
++
++ // description
++ if (!isempty(recording->Info()->Description()))
++ m_resp->add_String(m_toUTF8.Convert(recording->Info()->Description()));
++ else
++ m_resp->add_String("");
++
++ // directory
++ if(m_protocolVersion >= 2) {
++ if(directory != NULL) {
++ char* p = directory;
++ while(*p != 0) {
++ if(*p == FOLDERDELIMCHAR) *p = '/';
++ if(*p == '_') *p = ' ';
++ p++;
++ }
++ while(*directory == '/') directory++;
++ }
++
++ m_resp->add_String((isempty(directory)) ? "" : m_toUTF8.Convert(directory));
++ }
++
++ // filename / uid of recording
++ if(m_protocolVersion >= 2) {
++ uint32_t uid = cRecordingsCache::GetInstance().Register(recording);
++ m_resp->add_U32(uid);
++ }
++ else {
++ cString filename = recording->FileName();
++ m_resp->add_String(filename);
++ }
++
++ free(fullname);
++ }
++
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ return true;
++}
++
++bool cVNSIClient::processRECORDINGS_Rename() /* OPCODE 103 */
++{
++ uint32_t uid = m_req->extract_U32();
++ char* newtitle = m_req->extract_String();
++ cRecording* recording = cRecordingsCache::GetInstance().Lookup(uid);
++ int r = VNSI_RET_DATAINVALID;
++
++ if(recording != NULL) {
++ // get filename and remove last part (recording time)
++ char* filename_old = strdup((const char*)recording->FileName());
++ char* sep = strrchr(filename_old, '/');
++ if(sep != NULL) {
++ *sep = 0;
++ }
++
++ // replace spaces in newtitle
++ strreplace(newtitle, ' ', '_');
++ char* filename_new = new char[512];
++ strncpy(filename_new, filename_old, 512);
++ sep = strrchr(filename_new, '/');
++ if(sep != NULL) {
++ sep++;
++ *sep = 0;
++ }
++ strncat(filename_new, newtitle, 512);
++
++ INFOLOG("renaming recording '%s' to '%s'", filename_old, filename_new);
++ r = rename(filename_old, filename_new);
++ Recordings.Update();
++
++ free(filename_old);
++ delete[] filename_new;
++ }
++
++ m_resp->add_U32(r);
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++
++ return true;
++}
++
++bool cVNSIClient::processRECORDINGS_Delete() /* OPCODE 104 */
++{
++ cString recName;
++ cRecording* recording = NULL;
++
++ if(m_protocolVersion >= 2) {
++ uint32_t uid = m_req->extract_U32();
++ recording = cRecordingsCache::GetInstance().Lookup(uid);
++ }
++ else {
++ const char* temp = m_req->extract_String();
++ recName = temp;
++ recording = Recordings.GetByName(recName);
++ delete[] temp;
++ }
++
++
++ if (recording)
++ {
++ DEBUGLOG("deleting recording: %s", recording->Name());
++
++ cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
++ if (!rc)
++ {
++ if (recording->Delete())
++ {
++ // Copy svdrdeveldevelp's way of doing this, see if it works
++ Recordings.DelByName(recording->FileName());
++ INFOLOG("Recording \"%s\" deleted", recording->FileName());
++ m_resp->add_U32(VNSI_RET_OK);
++ }
++ else
++ {
++ ERRORLOG("Error while deleting recording!");
++ m_resp->add_U32(VNSI_RET_ERROR);
++ }
++ }
++ else
++ {
++ ERRORLOG("Recording \"%s\" is in use by timer %d", recording->Name(), rc->Timer()->Index() + 1);
++ m_resp->add_U32(VNSI_RET_DATALOCKED);
++ }
++ }
++ else
++ {
++ ERRORLOG("Error in recording name \"%s\"", (const char*)recName);
++ m_resp->add_U32(VNSI_RET_DATAUNKNOWN);
++ }
++
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++
++ return true;
++}
++
++
++/** OPCODE 120 - 139: VNSI network functions for epg access and manipulating */
++
++bool cVNSIClient::processEPG_GetForChannel() /* OPCODE 120 */
++{
++ uint32_t channelNumber = 0;
++ uint32_t channelUID = 0;
++
++ if(m_protocolVersion == 1) {
++ channelNumber = m_req->extract_U32();
++ }
++ else {
++ channelUID = m_req->extract_U32();
++ }
++
++ uint32_t startTime = m_req->extract_U32();
++ uint32_t duration = m_req->extract_U32();
++
++ Channels.Lock(false);
++
++ const cChannel* channel = NULL;
++
++ if(m_protocolVersion == 1) {
++ channel = Channels.GetByNumber(channelNumber);
++ DEBUGLOG("get schedule called for channel %u", channelNumber);
++ }
++ else {
++ channel = FindChannelByUID(channelUID);
++ if(channel != NULL) {
++ DEBUGLOG("get schedule called for channel '%s'", (const char*)channel->GetChannelID().ToString());
++ }
++ }
++
++ if (!channel)
++ {
++ m_resp->add_U32(0);
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ Channels.Unlock();
++
++ ERRORLOG("written 0 because channel = NULL");
++ return true;
++ }
++
++ cSchedulesLock MutexLock;
++ const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
++ if (!Schedules)
++ {
++ m_resp->add_U32(0);
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ Channels.Unlock();
++
++ DEBUGLOG("written 0 because Schedule!s! = NULL");
++ return true;
++ }
++
++ const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
++ if (!Schedule)
++ {
++ m_resp->add_U32(0);
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ Channels.Unlock();
++
++ DEBUGLOG("written 0 because Schedule = NULL");
++ return true;
++ }
++
++ bool atLeastOneEvent = false;
++
++ uint32_t thisEventID;
++ uint32_t thisEventTime;
++ uint32_t thisEventDuration;
++ uint32_t thisEventContent;
++ uint32_t thisEventRating;
++ const char* thisEventTitle;
++ const char* thisEventSubTitle;
++ const char* thisEventDescription;
++
++ for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
++ {
++ thisEventID = event->EventID();
++ thisEventTitle = event->Title();
++ thisEventSubTitle = event->ShortText();
++ thisEventDescription = event->Description();
++ thisEventTime = event->StartTime();
++ thisEventDuration = event->Duration();
++#if defined(USE_PARENTALRATING) || defined(PARENTALRATINGCONTENTVERSNUM)
++ thisEventContent = event->Contents();
++ thisEventRating = 0;
++#elif APIVERSNUM >= 10711
++ thisEventContent = event->Contents();
++ thisEventRating = event->ParentalRating();
++#else
++ thisEventContent = 0;
++ thisEventRating = 0;
++#endif
++
++ //in the past filter
++ if ((thisEventTime + thisEventDuration) < (uint32_t)time(NULL)) continue;
++
++ //start time filter
++ if ((thisEventTime + thisEventDuration) <= startTime) continue;
++
++ //duration filter
++ if (duration != 0 && thisEventTime >= (startTime + duration)) continue;
++
++ if (!thisEventTitle) thisEventTitle = "";
++ if (!thisEventSubTitle) thisEventSubTitle = "";
++ if (!thisEventDescription) thisEventDescription = "";
++
++ m_resp->add_U32(thisEventID);
++ m_resp->add_U32(thisEventTime);
++ m_resp->add_U32(thisEventDuration);
++ m_resp->add_U32(thisEventContent);
++ m_resp->add_U32(thisEventRating);
++
++ m_resp->add_String(m_toUTF8.Convert(thisEventTitle));
++ m_resp->add_String(m_toUTF8.Convert(thisEventSubTitle));
++ m_resp->add_String(m_toUTF8.Convert(thisEventDescription));
++
++ atLeastOneEvent = true;
++ }
++
++ Channels.Unlock();
++ DEBUGLOG("Got all event data");
++
++ if (!atLeastOneEvent)
++ {
++ m_resp->add_U32(0);
++ DEBUGLOG("Written 0 because no data");
++ }
++
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++
++ DEBUGLOG("written schedules packet");
++
++ return true;
++}
++
++
++/** OPCODE 140 - 169: VNSI network functions for channel scanning */
++
++bool cVNSIClient::processSCAN_ScanSupported() /* OPCODE 140 */
++{
++ /** Note: Using "WirbelScanService-StopScan-v1.0" to detect
++ a present service interface in wirbelscan plugin,
++ it returns true if supported */
++ cPlugin *p = cPluginManager::GetPlugin("wirbelscan");
++ if (p && p->Service("WirbelScanService-StopScan-v1.0", NULL))
++ m_resp->add_U32(VNSI_RET_OK);
++ else
++ m_resp->add_U32(VNSI_RET_NOTSUPPORTED);
++
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ return true;
++}
++
++bool cVNSIClient::processSCAN_GetCountries() /* OPCODE 141 */
++{
++ if (!m_processSCAN_Response)
++ {
++ m_processSCAN_Response = m_resp;
++ cPlugin *p = cPluginManager::GetPlugin("wirbelscan");
++ if (p)
++ {
++ m_resp->add_U32(VNSI_RET_OK);
++ p->Service("WirbelScanService-GetCountries-v1.0", (void*) processSCAN_AddCountry);
++ }
++ else
++ {
++ m_resp->add_U32(VNSI_RET_NOTSUPPORTED);
++ }
++ m_processSCAN_Response = NULL;
++ }
++ else
++ {
++ m_resp->add_U32(VNSI_RET_DATALOCKED);
++ }
++
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ return true;
++}
++
++bool cVNSIClient::processSCAN_GetSatellites() /* OPCODE 142 */
++{
++ if (!m_processSCAN_Response)
++ {
++ m_processSCAN_Response = m_resp;
++ cPlugin *p = cPluginManager::GetPlugin("wirbelscan");
++ if (p)
++ {
++ m_resp->add_U32(VNSI_RET_OK);
++ p->Service("WirbelScanService-GetSatellites-v1.0", (void*) processSCAN_AddSatellite);
++ }
++ else
++ {
++ m_resp->add_U32(VNSI_RET_NOTSUPPORTED);
++ }
++ m_processSCAN_Response = NULL;
++ }
++ else
++ {
++ m_resp->add_U32(VNSI_RET_DATALOCKED);
++ }
++
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ return true;
++}
++
++bool cVNSIClient::processSCAN_Start() /* OPCODE 143 */
++{
++ WirbelScanService_DoScan_v1_0 svc;
++ svc.type = (scantype_t)m_req->extract_U32();
++ svc.scan_tv = (bool)m_req->extract_U8();
++ svc.scan_radio = (bool)m_req->extract_U8();
++ svc.scan_fta = (bool)m_req->extract_U8();
++ svc.scan_scrambled = (bool)m_req->extract_U8();
++ svc.scan_hd = (bool)m_req->extract_U8();
++ svc.CountryIndex = (int)m_req->extract_U32();
++ svc.DVBC_Inversion = (int)m_req->extract_U32();
++ svc.DVBC_Symbolrate = (int)m_req->extract_U32();
++ svc.DVBC_QAM = (int)m_req->extract_U32();
++ svc.DVBT_Inversion = (int)m_req->extract_U32();
++ svc.SatIndex = (int)m_req->extract_U32();
++ svc.ATSC_Type = (int)m_req->extract_U32();
++ svc.SetPercentage = processSCAN_SetPercentage;
++ svc.SetSignalStrength = processSCAN_SetSignalStrength;
++ svc.SetDeviceInfo = processSCAN_SetDeviceInfo;
++ svc.SetTransponder = processSCAN_SetTransponder;
++ svc.NewChannel = processSCAN_NewChannel;
++ svc.IsFinished = processSCAN_IsFinished;
++ svc.SetStatus = processSCAN_SetStatus;
++ m_processSCAN_Socket = &m_socket;
++
++ cPlugin *p = cPluginManager::GetPlugin("wirbelscan");
++ if (p)
++ {
++ if (p->Service("WirbelScanService-DoScan-v1.0", (void*) &svc))
++ m_resp->add_U32(VNSI_RET_OK);
++ else
++ m_resp->add_U32(VNSI_RET_ERROR);
++ }
++ else
++ {
++ m_resp->add_U32(VNSI_RET_NOTSUPPORTED);
++ }
++
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ return true;
++}
++
++bool cVNSIClient::processSCAN_Stop() /* OPCODE 144 */
++{
++ cPlugin *p = cPluginManager::GetPlugin("wirbelscan");
++ if (p)
++ {
++ p->Service("WirbelScanService-StopScan-v1.0", NULL);
++ m_resp->add_U32(VNSI_RET_OK);
++ }
++ else
++ {
++ m_resp->add_U32(VNSI_RET_NOTSUPPORTED);
++ }
++ m_resp->finalise();
++ m_socket.write(m_resp->getPtr(), m_resp->getLen());
++ return true;
++}
++
++cResponsePacket *cVNSIClient::m_processSCAN_Response = NULL;
++cxSocket *cVNSIClient::m_processSCAN_Socket = NULL;
++
++void cVNSIClient::processSCAN_AddCountry(int index, const char *isoName, const char *longName)
++{
++ m_processSCAN_Response->add_U32(index);
++ m_processSCAN_Response->add_String(isoName);
++ m_processSCAN_Response->add_String(longName);
++}
++
++void cVNSIClient::processSCAN_AddSatellite(int index, const char *shortName, const char *longName)
++{
++ m_processSCAN_Response->add_U32(index);
++ m_processSCAN_Response->add_String(shortName);
++ m_processSCAN_Response->add_String(longName);
++}
++
++void cVNSIClient::processSCAN_SetPercentage(int percent)
++{
++ cResponsePacket *resp = new cResponsePacket();
++ if (!resp->initScan(VNSI_SCANNER_PERCENTAGE))
++ {
++ delete resp;
++ return;
++ }
++ resp->add_U32(percent);
++ resp->finalise();
++ m_processSCAN_Socket->write(resp->getPtr(), resp->getLen());
++ delete resp;
++}
++
++void cVNSIClient::processSCAN_SetSignalStrength(int strength, bool locked)
++{
++ cResponsePacket *resp = new cResponsePacket();
++ if (!resp->initScan(VNSI_SCANNER_SIGNAL))
++ {
++ delete resp;
++ return;
++ }
++ strength *= 100;
++ strength /= 0xFFFF;
++ resp->add_U32(strength);
++ resp->add_U32(locked);
++ resp->finalise();
++ m_processSCAN_Socket->write(resp->getPtr(), resp->getLen());
++ delete resp;
++}
++
++void cVNSIClient::processSCAN_SetDeviceInfo(const char *Info)
++{
++ cResponsePacket *resp = new cResponsePacket();
++ if (!resp->initScan(VNSI_SCANNER_DEVICE))
++ {
++ delete resp;
++ return;
++ }
++ resp->add_String(Info);
++ resp->finalise();
++ m_processSCAN_Socket->write(resp->getPtr(), resp->getLen());
++ delete resp;
++}
++
++void cVNSIClient::processSCAN_SetTransponder(const char *Info)
++{
++ cResponsePacket *resp = new cResponsePacket();
++ if (!resp->initScan(VNSI_SCANNER_TRANSPONDER))
++ {
++ delete resp;
++ return;
++ }
++ resp->add_String(Info);
++ resp->finalise();
++ m_processSCAN_Socket->write(resp->getPtr(), resp->getLen());
++ delete resp;
++}
++
++void cVNSIClient::processSCAN_NewChannel(const char *Name, bool isRadio, bool isEncrypted, bool isHD)
++{
++ cResponsePacket *resp = new cResponsePacket();
++ if (!resp->initScan(VNSI_SCANNER_NEWCHANNEL))
++ {
++ delete resp;
++ return;
++ }
++ resp->add_U32(isRadio);
++ resp->add_U32(isEncrypted);
++ resp->add_U32(isHD);
++ resp->add_String(Name);
++ resp->finalise();
++ m_processSCAN_Socket->write(resp->getPtr(), resp->getLen());
++ delete resp;
++}
++
++void cVNSIClient::processSCAN_IsFinished()
++{
++ cResponsePacket *resp = new cResponsePacket();
++ if (!resp->initScan(VNSI_SCANNER_FINISHED))
++ {
++ delete resp;
++ return;
++ }
++ resp->finalise();
++ m_processSCAN_Socket->write(resp->getPtr(), resp->getLen());
++ m_processSCAN_Socket = NULL;
++ delete resp;
++}
++
++void cVNSIClient::processSCAN_SetStatus(int status)
++{
++ cResponsePacket *resp = new cResponsePacket();
++ if (!resp->initScan(VNSI_SCANNER_STATUS))
++ {
++ delete resp;
++ return;
++ }
++ resp->add_U32(status);
++ resp->finalise();
++ m_processSCAN_Socket->write(resp->getPtr(), resp->getLen());
++ delete resp;
++}
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsiclient.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsiclient.h
+new file mode 100644
+index 0000000..632ea3f
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsiclient.h
+@@ -0,0 +1,168 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (Team XBMC)
++ * Copyright (C) 2010, 2011 Alexander Pipelka
++ *
++ * 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#ifndef VNSI_CLIENT_H
++#define VNSI_CLIENT_H
++
++#include <vdr/thread.h>
++#include <vdr/tools.h>
++#include <vdr/receiver.h>
++#include <vdr/status.h>
++
++#include "config.h"
++#include "cxsocket.h"
++
++#include <map>
++#include <string>
++
++class cChannel;
++class cDevice;
++class cLiveStreamer;
++class cRequestPacket;
++class cResponsePacket;
++class cRecPlayer;
++class cCmdControl;
++
++class cVNSIClient : public cThread
++ , public cStatus
++{
++private:
++
++ unsigned int m_Id;
++ cxSocket m_socket;
++ bool m_loggedIn;
++ bool m_StatusInterfaceEnabled;
++ cLiveStreamer *m_Streamer;
++ bool m_isStreaming;
++ cString m_ClientAddress;
++ cRecPlayer *m_RecPlayer;
++ cRequestPacket *m_req;
++ cResponsePacket *m_resp;
++ cCharSetConv m_toUTF8;
++ uint32_t m_protocolVersion;
++ cMutex m_msgLock;
++ static cMutex m_timerLock;
++
++protected:
++
++ bool processRequest(cRequestPacket* req);
++
++ virtual void Action(void);
++
++ virtual void TimerChange(const cTimer *Timer, eTimerChange Change);
++ virtual void Recording(const cDevice *Device, const char *Name, const char *FileName, bool On);
++ virtual void OsdStatusMessage(const char *Message);
++
++public:
++
++ cVNSIClient(int fd, unsigned int id, const char *ClientAdr);
++ virtual ~cVNSIClient();
++
++ void ChannelChange();
++ void RecordingsChange();
++ void TimerChange();
++
++ unsigned int GetID() { return m_Id; }
++
++protected:
++
++ void SetLoggedIn(bool yesNo) { m_loggedIn = yesNo; }
++ void SetStatusInterface(bool yesNo) { m_StatusInterfaceEnabled = yesNo; }
++ bool StartChannelStreaming(const cChannel *channel, uint32_t timeout);
++ void StopChannelStreaming();
++
++private:
++
++ typedef struct {
++ bool automatic;
++ bool radio;
++ std::string name;
++ } ChannelGroup;
++
++ std::map<std::string, ChannelGroup> m_channelgroups[2];
++
++ bool process_Login();
++ bool process_GetTime();
++ bool process_EnableStatusInterface();
++ bool process_Ping();
++
++ bool processChannelStream_Open();
++ bool processChannelStream_Close();
++
++ bool processRecStream_Open();
++ bool processRecStream_Close();
++ bool processRecStream_GetBlock();
++ bool processRecStream_PositionFromFrameNumber();
++ bool processRecStream_FrameNumberFromPosition();
++ bool processRecStream_GetIFrame();
++
++ bool processCHANNELS_GroupsCount();
++ bool processCHANNELS_ChannelsCount();
++ bool processCHANNELS_GroupList();
++ bool processCHANNELS_GetChannels();
++ bool processCHANNELS_GetGroupMembers();
++
++ void CreateChannelGroups(bool automatic);
++
++ bool processTIMER_GetCount();
++ bool processTIMER_Get();
++ bool processTIMER_GetList();
++ bool processTIMER_Add();
++ bool processTIMER_Delete();
++ bool processTIMER_Update();
++
++ bool processRECORDINGS_GetDiskSpace();
++ bool processRECORDINGS_GetCount();
++ bool processRECORDINGS_GetList();
++ bool processRECORDINGS_GetInfo();
++ bool processRECORDINGS_Rename();
++ bool processRECORDINGS_Delete();
++ bool processRECORDINGS_Move();
++
++ bool processEPG_GetForChannel();
++
++ bool processSCAN_ScanSupported();
++ bool processSCAN_GetCountries();
++ bool processSCAN_GetSatellites();
++ bool processSCAN_Start();
++ bool processSCAN_Stop();
++
++ /** Static callback functions to interact with wirbelscan plugin over
++ the plugin service interface */
++ static void processSCAN_AddCountry(int index, const char *isoName, const char *longName);
++ static void processSCAN_AddSatellite(int index, const char *shortName, const char *longName);
++ static void processSCAN_SetPercentage(int percent);
++ static void processSCAN_SetSignalStrength(int strength, bool locked);
++ static void processSCAN_SetDeviceInfo(const char *Info);
++ static void processSCAN_SetTransponder(const char *Info);
++ static void processSCAN_NewChannel(const char *Name, bool isRadio, bool isEncrypted, bool isHD);
++ static void processSCAN_IsFinished();
++ static void processSCAN_SetStatus(int status);
++ static cResponsePacket *m_processSCAN_Response;
++ static cxSocket *m_processSCAN_Socket;
++
++};
++
++#endif // VNSI_CLIENT_H
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsicommand.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsicommand.h
+new file mode 100644
+index 0000000..fd7f031
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsicommand.h
+@@ -0,0 +1,127 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (Team XBMC)
++ * Copyright (C) 2010, 2011 Alexander Pipelka
++ *
++ * 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#ifndef VNSI_COMMAND_H
++#define VNSI_COMMAND_H
++
++/** Current VNSI Protocol Version number */
++#define VNSI_PROTOCOLVERSION 2
++
++/** Packet types */
++#define VNSI_CHANNEL_REQUEST_RESPONSE 1
++#define VNSI_CHANNEL_STREAM 2
++#define VNSI_CHANNEL_KEEPALIVE 3
++#define VNSI_CHANNEL_NETLOG 4
++#define VNSI_CHANNEL_STATUS 5
++#define VNSI_CHANNEL_SCAN 6
++
++
++/** Response packets operation codes */
++
++/* OPCODE 1 - 19: VNSI network functions for general purpose */
++#define VNSI_LOGIN 1
++#define VNSI_GETTIME 2
++#define VNSI_ENABLESTATUSINTERFACE 3
++#define VNSI_PING 7
++
++/* OPCODE 20 - 39: VNSI network functions for live streaming */
++#define VNSI_CHANNELSTREAM_OPEN 20
++#define VNSI_CHANNELSTREAM_CLOSE 21
++
++/* OPCODE 40 - 59: VNSI network functions for recording streaming */
++#define VNSI_RECSTREAM_OPEN 40
++#define VNSI_RECSTREAM_CLOSE 41
++#define VNSI_RECSTREAM_GETBLOCK 42
++#define VNSI_RECSTREAM_POSTOFRAME 43
++#define VNSI_RECSTREAM_FRAMETOPOS 44
++#define VNSI_RECSTREAM_GETIFRAME 45
++
++/* OPCODE 60 - 79: VNSI network functions for channel access */
++#define VNSI_CHANNELS_GETCOUNT 61
++#define VNSI_CHANNELS_GETCHANNELS 63
++#define VNSI_CHANNELGROUP_GETCOUNT 65
++#define VNSI_CHANNELGROUP_LIST 66
++#define VNSI_CHANNELGROUP_MEMBERS 67
++
++/* OPCODE 80 - 99: VNSI network functions for timer access */
++#define VNSI_TIMER_GETCOUNT 80
++#define VNSI_TIMER_GET 81
++#define VNSI_TIMER_GETLIST 82
++#define VNSI_TIMER_ADD 83
++#define VNSI_TIMER_DELETE 84
++#define VNSI_TIMER_UPDATE 85
++
++/* OPCODE 100 - 119: VNSI network functions for recording access */
++#define VNSI_RECORDINGS_DISKSIZE 100
++#define VNSI_RECORDINGS_GETCOUNT 101
++#define VNSI_RECORDINGS_GETLIST 102
++#define VNSI_RECORDINGS_RENAME 103
++#define VNSI_RECORDINGS_DELETE 104
++
++/* OPCODE 120 - 139: VNSI network functions for epg access and manipulating */
++#define VNSI_EPG_GETFORCHANNEL 120
++
++/* OPCODE 140 - 159: VNSI network functions for channel scanning */
++#define VNSI_SCAN_SUPPORTED 140
++#define VNSI_SCAN_GETCOUNTRIES 141
++#define VNSI_SCAN_GETSATELLITES 142
++#define VNSI_SCAN_START 143
++#define VNSI_SCAN_STOP 144
++
++
++/** Stream packet types (server -> client) */
++#define VNSI_STREAM_CHANGE 1
++#define VNSI_STREAM_STATUS 2
++#define VNSI_STREAM_QUEUESTATUS 3
++#define VNSI_STREAM_MUXPKT 4
++#define VNSI_STREAM_SIGNALINFO 5
++#define VNSI_STREAM_CONTENTINFO 6
++
++/** Scan packet types (server -> client) */
++#define VNSI_SCANNER_PERCENTAGE 1
++#define VNSI_SCANNER_SIGNAL 2
++#define VNSI_SCANNER_DEVICE 3
++#define VNSI_SCANNER_TRANSPONDER 4
++#define VNSI_SCANNER_NEWCHANNEL 5
++#define VNSI_SCANNER_FINISHED 6
++#define VNSI_SCANNER_STATUS 7
++
++/** Status packet types (server -> client) */
++#define VNSI_STATUS_TIMERCHANGE 1
++#define VNSI_STATUS_RECORDING 2
++#define VNSI_STATUS_MESSAGE 3
++#define VNSI_STATUS_CHANNELCHANGE 4
++#define VNSI_STATUS_RECORDINGSCHANGE 5
++
++/** Packet return codes */
++#define VNSI_RET_OK 0
++#define VNSI_RET_RECRUNNING 1
++#define VNSI_RET_NOTSUPPORTED 995
++#define VNSI_RET_DATAUNKNOWN 996
++#define VNSI_RET_DATALOCKED 997
++#define VNSI_RET_DATAINVALID 998
++#define VNSI_RET_ERROR 999
++
++#endif // VNSI_COMMAND_H
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsiserver.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsiserver.c
+new file mode 100644
+index 0000000..5dfcfea
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsiserver.c
+@@ -0,0 +1,271 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (Team XBMC)
++ * Copyright (C) 2010, 2011 Alexander Pipelka
++ *
++ * 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include <netdb.h>
++#include <poll.h>
++#include <assert.h>
++#include <stdio.h>
++#include <unistd.h>
++#include <stdlib.h>
++#include <string.h>
++#include <stdarg.h>
++#include <fcntl.h>
++#include <errno.h>
++#include <netinet/in.h>
++#include <netinet/tcp.h>
++#include <arpa/inet.h>
++
++#include <vdr/plugin.h>
++#include <vdr/shutdown.h>
++
++#include "vnsiserver.h"
++#include "vnsiclient.h"
++
++unsigned int cVNSIServer::m_IdCnt = 0;
++
++class cAllowedHosts : public cSVDRPhosts
++{
++public:
++ cAllowedHosts(const cString& AllowedHostsFile)
++ {
++ if (!Load(AllowedHostsFile, true, true))
++ {
++ ERRORLOG("Invalid or missing '%s'. falling back to 'svdrphosts.conf'.", *AllowedHostsFile);
++ cString Base = cString::sprintf("%s/../svdrphosts.conf", *VNSIServerConfig.ConfigDirectory);
++ if (!Load(Base, true, true))
++ {
++ ERRORLOG("Invalid or missing %s. Adding 127.0.0.1 to list of allowed hosts.", *Base);
++ cSVDRPhost *localhost = new cSVDRPhost;
++ if (localhost->Parse("127.0.0.1"))
++ Add(localhost);
++ else
++ delete localhost;
++ }
++ }
++ }
++};
++
++cVNSIServer::cVNSIServer(int listenPort) : cThread("VDR VNSI Server")
++{
++ m_ServerPort = listenPort;
++
++ if(*VNSIServerConfig.ConfigDirectory)
++ {
++ m_AllowedHostsFile = cString::sprintf("%s/" ALLOWED_HOSTS_FILE, *VNSIServerConfig.ConfigDirectory);
++ }
++ else
++ {
++ ERRORLOG("cVNSIServer: missing ConfigDirectory!");
++ m_AllowedHostsFile = cString::sprintf("/video/" ALLOWED_HOSTS_FILE);
++ }
++
++ m_ServerFD = socket(AF_INET, SOCK_STREAM, 0);
++ if(m_ServerFD == -1)
++ return;
++
++ fcntl(m_ServerFD, F_SETFD, fcntl(m_ServerFD, F_GETFD) | FD_CLOEXEC);
++
++ int one = 1;
++ setsockopt(m_ServerFD, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
++
++ struct sockaddr_in s;
++ memset(&s, 0, sizeof(s));
++ s.sin_family = AF_INET;
++ s.sin_port = htons(m_ServerPort);
++
++ int x = bind(m_ServerFD, (struct sockaddr *)&s, sizeof(s));
++ if (x < 0)
++ {
++ close(m_ServerFD);
++ INFOLOG("Unable to start VNSI Server, port already in use ?");
++ m_ServerFD = -1;
++ return;
++ }
++
++ listen(m_ServerFD, 10);
++
++ Start();
++
++ INFOLOG("VNSI Server started");
++ INFOLOG("Channel streaming timeout: %i seconds", VNSIServerConfig.stream_timeout);
++ return;
++}
++
++cVNSIServer::~cVNSIServer()
++{
++ Cancel(-1);
++ for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
++ {
++ delete (*i);
++ }
++ m_clients.erase(m_clients.begin(), m_clients.end());
++ Cancel();
++ INFOLOG("VNSI Server stopped");
++}
++
++void cVNSIServer::NewClientConnected(int fd)
++{
++ char buf[64];
++ struct sockaddr_in sin;
++ socklen_t len = sizeof(sin);
++
++ if (getpeername(fd, (struct sockaddr *)&sin, &len))
++ {
++ ERRORLOG("getpeername() failed, dropping new incoming connection %d", m_IdCnt);
++ close(fd);
++ return;
++ }
++
++ cAllowedHosts AllowedHosts(m_AllowedHostsFile);
++ if (!AllowedHosts.Acceptable(sin.sin_addr.s_addr))
++ {
++ ERRORLOG("Address not allowed to connect (%s)", *m_AllowedHostsFile);
++ close(fd);
++ return;
++ }
++
++ if (fcntl(fd, F_SETFL, fcntl (fd, F_GETFL) | O_NONBLOCK) == -1)
++ {
++ ERRORLOG("Error setting control socket to nonblocking mode");
++ close(fd);
++ return;
++ }
++
++ int val = 1;
++ setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val));
++
++ val = 30;
++ setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &val, sizeof(val));
++
++ val = 15;
++ setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &val, sizeof(val));
++
++ val = 5;
++ setsockopt(fd, SOL_TCP, TCP_KEEPCNT, &val, sizeof(val));
++
++ val = 1;
++ setsockopt(fd, SOL_TCP, TCP_NODELAY, &val, sizeof(val));
++
++ INFOLOG("Client with ID %d connected: %s", m_IdCnt, cxSocket::ip2txt(sin.sin_addr.s_addr, sin.sin_port, buf));
++ cVNSIClient *connection = new cVNSIClient(fd, m_IdCnt, cxSocket::ip2txt(sin.sin_addr.s_addr, sin.sin_port, buf));
++ m_clients.push_back(connection);
++ m_IdCnt++;
++}
++
++void cVNSIServer::Action(void)
++{
++ fd_set fds;
++ struct timeval tv;
++
++ // get initial state of the recordings
++ int recState = -1;
++ Recordings.StateChanged(recState);
++
++ // get initial state of the timers
++ int timerState = -1;
++ Timers.Modified(timerState);
++
++ while (Running())
++ {
++ FD_ZERO(&fds);
++ FD_SET(m_ServerFD, &fds);
++
++ tv.tv_sec = 0;
++ tv.tv_usec = 250*1000;
++
++ int r = select(m_ServerFD + 1, &fds, NULL, NULL, &tv);
++ if (r == -1)
++ {
++ ERRORLOG("failed during select");
++ continue;
++ }
++ if (r == 0)
++ {
++ // remove disconnected clients
++ for (ClientList::iterator i = m_clients.begin(); i != m_clients.end();)
++ {
++ if (!(*i)->Active())
++ {
++ INFOLOG("Client with ID %u seems to be disconnected, removing from client list", (*i)->GetID());
++ delete (*i);
++ i = m_clients.erase(i);
++ }
++ else {
++ i++;
++ }
++ }
++
++ // trigger clients to reload the modified channel list
++ if(m_clients.size() > 0)
++ {
++ Channels.Lock(false);
++ if(Channels.Modified() != 0)
++ {
++ INFOLOG("Requesting clients to reload channel list");
++ for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
++ (*i)->ChannelChange();
++ }
++ Channels.Unlock();
++ }
++
++ // reset inactivity timeout as long as there are clients connected
++ if(m_clients.size() > 0) {
++ ShutdownHandler.SetUserInactiveTimeout();
++ }
++
++ // update recordings
++ if(Recordings.StateChanged(recState))
++ {
++ INFOLOG("Recordings state changed (%i)", recState);
++ INFOLOG("Requesting clients to reload recordings list");
++ for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
++ (*i)->RecordingsChange();
++ }
++
++ // update timers
++ if(Timers.Modified(timerState))
++ {
++ INFOLOG("Timers state changed (%i)", timerState);
++ INFOLOG("Requesting clients to reload timers");
++ for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
++ {
++ (*i)->TimerChange();
++ }
++ }
++ continue;
++ }
++
++ int fd = accept(m_ServerFD, 0, 0);
++ if (fd >= 0)
++ {
++ NewClientConnected(fd);
++ }
++ else
++ {
++ ERRORLOG("accept failed");
++ }
++ }
++ return;
++}
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsiserver.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsiserver.h
+new file mode 100644
+index 0000000..bf52b41
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsiserver.h
+@@ -0,0 +1,57 @@
++/*
++ * vdr-plugin-vnsi - XBMC server plugin for VDR
++ *
++ * Copyright (C) 2010 Alwin Esch (Team XBMC)
++ * Copyright (C) 2010, 2011 Alexander Pipelka
++ *
++ * 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#ifndef VNSI_SERVER_H
++#define VNSI_SERVER_H
++
++#include <list>
++#include <vdr/thread.h>
++
++#include "config.h"
++
++class cVNSIClient;
++
++class cVNSIServer : public cThread
++{
++protected:
++
++ typedef std::list<cVNSIClient*> ClientList;
++
++ virtual void Action(void);
++ void NewClientConnected(int fd);
++
++ int m_ServerPort;
++ int m_ServerFD;
++ cString m_AllowedHostsFile;
++ ClientList m_clients;
++
++ static unsigned int m_IdCnt;
++
++public:
++ cVNSIServer(int listenPort);
++ virtual ~cVNSIServer();
++};
++
++#endif // VNSI_SERVER_H
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsiserver/allowed_hosts.conf b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsiserver/allowed_hosts.conf
+new file mode 100644
+index 0000000..07a7c49
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsiserver/allowed_hosts.conf
+@@ -0,0 +1,13 @@
++#
++# allowed_hosts.conf This file describes a number of host addresses that
++# are allowed to connect to the streamdev server running
++# with the Video Disk Recorder (VDR) on this system.
++# Syntax:
++#
++# IP-Address[/Netmask]
++#
++
++127.0.0.1 # always accept localhost
++192.168.0.0/24 # any host on the local net
++#204.152.189.113 # a specific host
++#0.0.0.0/0 # any host on any net (USE THIS WITH CARE!)
+diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/wirbelscanservice.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/wirbelscanservice.h
+new file mode 100644
+index 0000000..625e0d3
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/wirbelscanservice.h
+@@ -0,0 +1,57 @@
++/*
++ * wirbelscan.c: A plugin for the Video Disk Recorder
++ *
++ * See the README file for copyright information and how to reach the author.
++ *
++ * $Id$
++ */
++
++#ifndef __WIRBELSCAN_SERVICE_H
++#define __WIRBELSCAN_SERVICE_H
++
++typedef enum scantype
++{
++ DVB_TERR = 0,
++ DVB_CABLE = 1,
++ DVB_SAT = 2,
++ PVRINPUT = 3,
++ PVRINPUT_FM = 4,
++ DVB_ATSC = 5,
++} scantype_t;
++
++typedef void (*WirbelScanService_GetCountries_v1_0)(int index, const char *isoName, const char *longName);
++typedef void (*WirbelScanService_GetSatellites_v1_0)(int index, const char *shortName, const char *longName);
++
++struct WirbelScanService_DoScan_v1_0
++{
++ scantype_t type;
++
++ bool scan_tv;
++ bool scan_radio;
++ bool scan_fta;
++ bool scan_scrambled;
++ bool scan_hd;
++
++ int CountryIndex;
++
++ int DVBC_Inversion;
++ int DVBC_Symbolrate;
++ int DVBC_QAM;
++
++ int DVBT_Inversion;
++
++ int SatIndex;
++
++ int ATSC_Type;
++
++ void (*SetPercentage)(int percent);
++ void (*SetSignalStrength)(int strenght, bool locked);
++ void (*SetDeviceInfo)(const char *Info);
++ void (*SetTransponder)(const char *Info);
++ void (*NewChannel)(const char *Name, bool isRadio, bool isEncrypted, bool isHD);
++ void (*IsFinished)();
++ void (*SetStatus)(int status);
++};
++
++#endif //__WIRBELSCAN_SERVICE_H
++
+diff --git a/xbmc/pvrclients/vdr-vnsi/vnsicommand.h b/xbmc/pvrclients/vdr-vnsi/vnsicommand.h
+new file mode 100644
+index 0000000..b41dc0a
+--- /dev/null
++++ b/xbmc/pvrclients/vdr-vnsi/vnsicommand.h
+@@ -0,0 +1,122 @@
++/*
++ * Copyright (C) 2010 Alwin Esch (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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#ifndef VNSI_COMMAND_H
++#define VNSI_COMMAND_H
++
++/** Current VNSI Protocol Version number */
++#define VNSIPROTOCOLVERSION 2
++
++
++/** Packet types */
++#define VNSI_CHANNEL_REQUEST_RESPONSE 1
++#define VNSI_CHANNEL_STREAM 2
++#define VNSI_CHANNEL_STATUS 5
++#define VNSI_CHANNEL_SCAN 6
++
++
++/** Response packets operation codes */
++
++/* OPCODE 1 - 19: VNSI network functions for general purpose */
++#define VNSI_LOGIN 1
++#define VNSI_GETTIME 2
++#define VNSI_ENABLESTATUSINTERFACE 3
++#define VNSI_PING 7
++
++/* OPCODE 20 - 39: VNSI network functions for live streaming */
++#define VNSI_CHANNELSTREAM_OPEN 20
++#define VNSI_CHANNELSTREAM_CLOSE 21
++
++/* OPCODE 40 - 59: VNSI network functions for recording streaming */
++#define VNSI_RECSTREAM_OPEN 40
++#define VNSI_RECSTREAM_CLOSE 41
++#define VNSI_RECSTREAM_GETBLOCK 42
++#define VNSI_RECSTREAM_POSTOFRAME 43
++#define VNSI_RECSTREAM_FRAMETOPOS 44
++#define VNSI_RECSTREAM_GETIFRAME 45
++
++/* OPCODE 60 - 79: VNSI network functions for channel access */
++#define VNSI_CHANNELS_GETCOUNT 61
++#define VNSI_CHANNELS_GETCHANNELS 63
++#define VNSI_CHANNELGROUP_GETCOUNT 65
++#define VNSI_CHANNELGROUP_LIST 66
++#define VNSI_CHANNELGROUP_MEMBERS 67
++
++/* OPCODE 80 - 99: VNSI network functions for timer access */
++#define VNSI_TIMER_GETCOUNT 80
++#define VNSI_TIMER_GET 81
++#define VNSI_TIMER_GETLIST 82
++#define VNSI_TIMER_ADD 83
++#define VNSI_TIMER_DELETE 84
++#define VNSI_TIMER_UPDATE 85
++
++/* OPCODE 100 - 119: VNSI network functions for recording access */
++#define VNSI_RECORDINGS_DISKSIZE 100
++#define VNSI_RECORDINGS_GETCOUNT 101
++#define VNSI_RECORDINGS_GETLIST 102
++#define VNSI_RECORDINGS_RENAME 103
++#define VNSI_RECORDINGS_DELETE 104
++
++/* OPCODE 120 - 139: VNSI network functions for epg access and manipulating */
++#define VNSI_EPG_GETFORCHANNEL 120
++
++/* OPCODE 140 - 159: VNSI network functions for channel scanning */
++#define VNSI_SCAN_SUPPORTED 140
++#define VNSI_SCAN_GETCOUNTRIES 141
++#define VNSI_SCAN_GETSATELLITES 142
++#define VNSI_SCAN_START 143
++#define VNSI_SCAN_STOP 144
++
++
++/** Stream packet types (server -> client) */
++#define VNSI_STREAM_CHANGE 1
++#define VNSI_STREAM_STATUS 2
++#define VNSI_STREAM_QUEUESTATUS 3
++#define VNSI_STREAM_MUXPKT 4
++#define VNSI_STREAM_SIGNALINFO 5
++#define VNSI_STREAM_CONTENTINFO 6
++
++/** Scan packet types (server -> client) */
++#define VNSI_SCANNER_PERCENTAGE 1
++#define VNSI_SCANNER_SIGNAL 2
++#define VNSI_SCANNER_DEVICE 3
++#define VNSI_SCANNER_TRANSPONDER 4
++#define VNSI_SCANNER_NEWCHANNEL 5
++#define VNSI_SCANNER_FINISHED 6
++#define VNSI_SCANNER_STATUS 7
++
++/** Status packet types (server -> client) */
++#define VNSI_STATUS_TIMERCHANGE 1
++#define VNSI_STATUS_RECORDING 2
++#define VNSI_STATUS_MESSAGE 3
++#define VNSI_STATUS_CHANNELCHANGE 4
++#define VNSI_STATUS_RECORDINGSCHANGE 5
++
++/** Packet return codes */
++#define VNSI_RET_OK 0
++#define VNSI_RET_RECRUNNING 1
++#define VNSI_RET_NOTSUPPORTED 995
++#define VNSI_RET_DATAUNKNOWN 996
++#define VNSI_RET_DATALOCKED 997
++#define VNSI_RET_DATAINVALID 998
++#define VNSI_RET_ERROR 999
++
++#endif // VNSI_COMMAND_H
+diff --git a/xbmc/settings/AdvancedSettings.cpp b/xbmc/settings/AdvancedSettings.cpp
+index 17dc021..7860067 100644
+--- a/xbmc/settings/AdvancedSettings.cpp
++++ b/xbmc/settings/AdvancedSettings.cpp
+@@ -226,6 +226,14 @@ void CAdvancedSettings::Initialize()
+
+ m_iMythMovieLength = 0; // 0 == Off
+
++ m_iEpgLingerTime = 60; /* keep 1 hour by default */
++ m_iEpgUpdateCheckInterval = 300; /* check if tables need to be updated every 5 minutes */
++ m_iEpgCleanupInterval = 900; /* remove old entries from the EPG every 15 minutes */
++ m_iEpgActiveTagCheckInterval = 60; /* check for updated active tags every minute */
++ m_iEpgRetryInterruptedUpdateInterval = 30; /* retry an interrupted epg update after 30 seconds */
++ m_bEpgDisplayUpdatePopup = true; /* display a progress popup while updating EPG data from clients */
++ m_bEpgDisplayIncrementalUpdatePopup = false; /* also display a progress popup while doing incremental EPG updates */
++
+ m_bEdlMergeShortCommBreaks = false; // Off by default
+ m_iEdlMaxCommBreakLength = 8 * 30 + 10; // Just over 8 * 30 second commercial break.
+ m_iEdlMinCommBreakLength = 3 * 30; // 3 * 30 second commercial breaks.
+@@ -274,6 +282,13 @@ void CAdvancedSettings::Initialize()
+
+ m_bgInfoLoaderMaxThreads = 5;
+
++ m_iPVRTimeCorrection = 0;
++ m_iPVRInfoToggleInterval = 3000;
++ m_bPVRShowEpgInfoOnEpgItemSelect = true;
++ m_iPVRMinVideoCacheLevel = 5;
++ m_iPVRMinAudioCacheLevel = 5;
++ m_bPVRCacheInDvdPlayer = true;
++
+ m_measureRefreshrate = false;
+
+ m_cacheMemBufferSize = 1024 * 1024 * 20;
+@@ -746,6 +761,19 @@ void CAdvancedSettings::ParseSettingsFile(const CStdString &file)
+ XMLUtils::GetInt(pElement, "movielength", m_iMythMovieLength);
+ }
+
++ // EPG
++ pElement = pRootElement->FirstChildElement("epg");
++ if (pElement)
++ {
++ XMLUtils::GetInt(pElement, "lingertime", m_iEpgLingerTime);
++ XMLUtils::GetInt(pElement, "updatecheckinterval", m_iEpgUpdateCheckInterval);
++ XMLUtils::GetInt(pElement, "cleanupinterval", m_iEpgCleanupInterval);
++ XMLUtils::GetInt(pElement, "activetagcheckinterval", m_iEpgActiveTagCheckInterval);
++ XMLUtils::GetInt(pElement, "retryinterruptedupdateinterval", m_iEpgRetryInterruptedUpdateInterval);
++ XMLUtils::GetBoolean(pElement, "displayupdatepopup", m_bEpgDisplayUpdatePopup);
++ XMLUtils::GetBoolean(pElement, "displayincrementalupdatepopup", m_bEpgDisplayIncrementalUpdatePopup);
++ }
++
+ // EDL commercial break handling
+ pElement = pRootElement->FirstChildElement("edl");
+ if (pElement)
+@@ -922,6 +950,17 @@ void CAdvancedSettings::ParseSettingsFile(const CStdString &file)
+ XMLUtils::GetInt(pRootElement, "bginfoloadermaxthreads", m_bgInfoLoaderMaxThreads);
+ m_bgInfoLoaderMaxThreads = std::max(1, m_bgInfoLoaderMaxThreads);
+
++ TiXmlElement *pPVR = pRootElement->FirstChildElement("pvr");
++ if (pPVR)
++ {
++ XMLUtils::GetInt(pPVR, "timecorrection", m_iPVRTimeCorrection, 0, 1440);
++ XMLUtils::GetInt(pPVR, "infotoggleinterval", m_iPVRInfoToggleInterval, 0, 30000);
++ XMLUtils::GetBoolean(pPVR, "showepginfoonselect", m_bPVRShowEpgInfoOnEpgItemSelect);
++ XMLUtils::GetInt(pPVR, "minvideocachelevel", m_iPVRMinVideoCacheLevel, 0, 100);
++ XMLUtils::GetInt(pPVR, "minaudiocachelevel", m_iPVRMinAudioCacheLevel, 0, 100);
++ XMLUtils::GetBoolean(pPVR, "cacheindvdplayer", m_bPVRCacheInDvdPlayer);
++ }
++
+ XMLUtils::GetBoolean(pRootElement, "measurerefreshrate", m_measureRefreshrate);
+
+ TiXmlElement* pDatabase = pRootElement->FirstChildElement("videodatabase");
+@@ -947,6 +986,28 @@ void CAdvancedSettings::ParseSettingsFile(const CStdString &file)
+ XMLUtils::GetString(pDatabase, "name", m_databaseMusic.name);
+ }
+
++ pDatabase = pRootElement->FirstChildElement("tvdatabase");
++ if (pDatabase)
++ {
++ XMLUtils::GetString(pDatabase, "type", m_databaseTV.type);
++ XMLUtils::GetString(pDatabase, "host", m_databaseTV.host);
++ XMLUtils::GetString(pDatabase, "port", m_databaseTV.port);
++ XMLUtils::GetString(pDatabase, "user", m_databaseTV.user);
++ XMLUtils::GetString(pDatabase, "pass", m_databaseTV.pass);
++ XMLUtils::GetString(pDatabase, "name", m_databaseTV.name);
++ }
++
++ pDatabase = pRootElement->FirstChildElement("epgdatabase");
++ if (pDatabase)
++ {
++ XMLUtils::GetString(pDatabase, "type", m_databaseEpg.type);
++ XMLUtils::GetString(pDatabase, "host", m_databaseEpg.host);
++ XMLUtils::GetString(pDatabase, "port", m_databaseEpg.port);
++ XMLUtils::GetString(pDatabase, "user", m_databaseEpg.user);
++ XMLUtils::GetString(pDatabase, "pass", m_databaseEpg.pass);
++ XMLUtils::GetString(pDatabase, "name", m_databaseEpg.name);
++ }
++
+ pElement = pRootElement->FirstChildElement("enablemultimediakeys");
+ if (pElement)
+ {
+diff --git a/xbmc/settings/AdvancedSettings.h b/xbmc/settings/AdvancedSettings.h
+index 6c38dd4..e3309f0 100644
+--- a/xbmc/settings/AdvancedSettings.h
++++ b/xbmc/settings/AdvancedSettings.h
+@@ -249,6 +249,14 @@ class CAdvancedSettings
+
+ int m_iMythMovieLength; // minutes
+
++ int m_iEpgLingerTime; // minutes
++ int m_iEpgUpdateCheckInterval; // seconds
++ int m_iEpgCleanupInterval; // seconds
++ int m_iEpgActiveTagCheckInterval; // seconds
++ int m_iEpgRetryInterruptedUpdateInterval; // seconds
++ bool m_bEpgDisplayUpdatePopup;
++ bool m_bEpgDisplayIncrementalUpdatePopup;
++
+ // EDL Commercial Break
+ bool m_bEdlMergeShortCommBreaks;
+ int m_iEdlMaxCommBreakLength; // seconds
+@@ -297,11 +305,21 @@ class CAdvancedSettings
+ CStdString m_gpuTempCmd;
+ int m_bgInfoLoaderMaxThreads;
+
++ /* PVR/TV related advanced settings */
++ int m_iPVRTimeCorrection; /*!< @brief correct all times (epg tags, timer tags, recording tags) by this amount of minutes. defaults to 0. */
++ int m_iPVRInfoToggleInterval; /*!< @brief if there are more than 1 pvr gui info item available (e.g. multiple recordings active at the same time), use this toggle delay in milliseconds. defaults to 3000. */
++ bool m_bPVRShowEpgInfoOnEpgItemSelect; /*!< @brief when selecting an EPG fileitem, show the EPG info dialog if this setting is true. start playback on the selected channel if false */
++ int m_iPVRMinVideoCacheLevel; /*!< @brief cache up to this level in the video buffer buffer before resuming playback if the buffers run dry */
++ int m_iPVRMinAudioCacheLevel; /*!< @brief cache up to this level in the audio buffer before resuming playback if the buffers run dry */
++ bool m_bPVRCacheInDvdPlayer; /*!< @brief true to use "CACHESTATE_PVR" in CDVDPlayer (default) */
++
+ bool m_measureRefreshrate; //when true the videoreferenceclock will measure the refreshrate when direct3d is used
+ //otherwise it will use the windows refreshrate
+
+ DatabaseSettings m_databaseMusic; // advanced music database setup
+ DatabaseSettings m_databaseVideo; // advanced video database setup
++ DatabaseSettings m_databaseTV; // advanced tv database setup
++ DatabaseSettings m_databaseEpg; /*!< advanced EPG database setup */
+
+ bool m_guiVisualizeDirtyRegions;
+ int m_guiAlgorithmDirtyRegions;
+diff --git a/xbmc/settings/GUIDialogSettings.cpp b/xbmc/settings/GUIDialogSettings.cpp
+index 609a299..9353542 100644
+--- a/xbmc/settings/GUIDialogSettings.cpp
++++ b/xbmc/settings/GUIDialogSettings.cpp
+@@ -20,6 +20,7 @@
+ */
+
+ #include "GUIDialogSettings.h"
++#include "guilib/GUIEditControl.h"
+ #include "guilib/GUISpinControlEx.h"
+ #include "guilib/GUIRadioButtonControl.h"
+ #include "guilib/GUISettingsSliderControl.h"
+@@ -38,6 +39,8 @@
+ #define CONTROL_DEFAULT_SPIN 9
+ #define CONTROL_DEFAULT_SLIDER 10
+ #define CONTROL_DEFAULT_SEPARATOR 11
++#define CONTROL_DEFAULT_EDIT 12
++#define CONTROL_DEFAULT_EDIT_NUM 13
+ #define CONTROL_OKAY_BUTTON 28
+ #define CONTROL_CANCEL_BUTTON 29
+ #define CONTROL_START 30
+@@ -48,6 +51,8 @@ using namespace std;
+ CGUIDialogSettings::CGUIDialogSettings(int id, const char *xmlFile)
+ : CGUIDialog(id, xmlFile)
+ {
++ m_pOriginalEdit = NULL;
++ m_pOriginalEditNum = NULL;
+ m_pOriginalSpin = NULL;
+ m_pOriginalRadioButton = NULL;
+ m_pOriginalSettingsButton = NULL;
+@@ -88,11 +93,15 @@ void CGUIDialogSettings::SetupPage()
+ {
+ // cleanup first, if necessary
+ FreeControls();
++ m_pOriginalEdit = (CGUIEditControl*)GetControl(CONTROL_DEFAULT_EDIT);
++ m_pOriginalEditNum = (CGUIEditControl*)GetControl(CONTROL_DEFAULT_EDIT_NUM);
+ m_pOriginalSpin = (CGUISpinControlEx*)GetControl(CONTROL_DEFAULT_SPIN);
+ m_pOriginalRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_DEFAULT_RADIOBUTTON);
+ m_pOriginalSettingsButton = (CGUIButtonControl *)GetControl(CONTROL_DEFAULT_BUTTON);
+ m_pOriginalSlider = (CGUISettingsSliderControl *)GetControl(CONTROL_DEFAULT_SLIDER);
+ m_pOriginalSeparator = (CGUIImage *)GetControl(CONTROL_DEFAULT_SEPARATOR);
++ if (m_pOriginalEdit) m_pOriginalEdit->SetVisible(false);
++ if (m_pOriginalEditNum) m_pOriginalEditNum->SetVisible(false);
+ if (m_pOriginalSpin) m_pOriginalSpin->SetVisible(false);
+ if (m_pOriginalRadioButton) m_pOriginalRadioButton->SetVisible(false);
+ if (m_pOriginalSettingsButton) m_pOriginalSettingsButton->SetVisible(false);
+@@ -100,7 +109,14 @@ void CGUIDialogSettings::SetupPage()
+ if (m_pOriginalSeparator) m_pOriginalSeparator->SetVisible(false);
+
+ // update our settings label
++ if (GetID() == WINDOW_DIALOG_PVR_TIMER_SETTING)
++ {
++ SET_CONTROL_LABEL(CONTROL_SETTINGS_LABEL, g_localizeStrings.Get(19057));
++ }
++ else
++ {
+ SET_CONTROL_LABEL(CONTROL_SETTINGS_LABEL, g_localizeStrings.Get(13395 + GetID() - WINDOW_DIALOG_VIDEO_OSD_SETTINGS));
++ }
+
+ CGUIControlGroupList *group = (CGUIControlGroupList *)GetControl(CONTROL_GROUP_LIST);
+ if (!group)
+@@ -185,11 +201,25 @@ void CGUIDialogSettings::UpdateSetting(unsigned int id)
+ if (setting.formatFunction) pControl->SetTextValue(setting.formatFunction(value, setting.interval));
+ }
+ }
+- else if (setting.type == SettingInfo::BUTTON)
++ else if (setting.type == SettingInfo::BUTTON_DIALOG)
+ {
+ SET_CONTROL_LABEL(controlID,setting.name);
+- if (m_usePopupSliders && setting.data && setting.formatFunction)
+- SET_CONTROL_LABEL2(controlID,setting.formatFunction(*(float *)setting.data, setting.interval));
++ CGUIButtonControl *pControl = (CGUIButtonControl *)GetControl(controlID);
++ if (pControl && setting.data) pControl->SetLabel2(*(CStdString *)setting.data);
++ }
++ else if (setting.type == SettingInfo::EDIT)
++ {
++ CGUIEditControl *pControl = (CGUIEditControl *)GetControl(controlID);
++ if (pControl && setting.data) pControl->SetLabel2(*(CStdString *)setting.data);
++ }
++ else if (setting.type == SettingInfo::EDIT_NUM)
++ {
++ CGUIEditControl *pControl = (CGUIEditControl *)GetControl(controlID);
++ if (pControl && setting.data) {
++ CStdString strIndex;
++ strIndex.Format("%i", *(int *)setting.data);
++ pControl->SetLabel2(strIndex);
++ }
+ }
+ else if (setting.type == SettingInfo::STRING)
+ {
+@@ -238,6 +268,24 @@ void CGUIDialogSettings::OnClick(int iID)
+ CGUISpinControlEx *pControl = (CGUISpinControlEx *)GetControl(iID);
+ if (setting.data) *(int *)setting.data = pControl->GetValue();
+ }
++ else if (setting.type == SettingInfo::BUTTON_DIALOG)
++ {
++ CGUIButtonControl *pControl = (CGUIButtonControl *)GetControl(iID);
++ if (setting.data) *(CStdString *)setting.data = pControl->GetLabel2();
++ }
++ else if (setting.type == SettingInfo::EDIT)
++ {
++ CGUIEditControl *pControl = (CGUIEditControl *)GetControl(iID);
++ if (setting.data) *(CStdString *)setting.data = pControl->GetLabel2();
++ }
++ else if (setting.type == SettingInfo::EDIT_NUM)
++ {
++ CGUIEditControl *pControl = (CGUIEditControl *)GetControl(iID);
++ if (setting.data) {
++ CStdString strIndex = pControl->GetLabel2();
++ *(int *)setting.data = atol(strIndex.c_str());
++ }
++ }
+ else if (setting.type == SettingInfo::CHECK)
+ {
+ CGUIRadioButtonControl *pControl = (CGUIRadioButtonControl *)GetControl(iID);
+@@ -285,7 +333,15 @@ void CGUIDialogSettings::FreeControls()
+ void CGUIDialogSettings::AddSetting(SettingInfo &setting, float width, int iControlID)
+ {
+ CGUIControl *pControl = NULL;
+- if (setting.type == SettingInfo::BUTTON && m_pOriginalSettingsButton)
++ if (setting.type == SettingInfo::BUTTON_DIALOG && m_pOriginalSettingsButton)
++ {
++ pControl = new CGUIButtonControl(*m_pOriginalSettingsButton);
++ if (!pControl) return ;
++ ((CGUIButtonControl *)pControl)->SetLabel(setting.name);
++ pControl->SetWidth(width);
++ if (setting.data) ((CGUIButtonControl *)pControl)->SetLabel2(*(CStdString *)setting.data);
++ }
++ else if (setting.type == SettingInfo::BUTTON && m_pOriginalSettingsButton)
+ {
+ pControl = new CGUIButtonControl(*m_pOriginalSettingsButton);
+ if (!pControl) return ;
+@@ -294,6 +350,27 @@ void CGUIDialogSettings::AddSetting(SettingInfo &setting, float width, int iCont
+ ((CGUIButtonControl *)pControl)->SetLabel2(setting.formatFunction(*(float *)setting.data, setting.interval));
+ pControl->SetWidth(width);
+ }
++ else if (setting.type == SettingInfo::EDIT && m_pOriginalEdit)
++ {
++ pControl = new CGUIEditControl(*m_pOriginalEdit);
++ if (!pControl) return ;
++ ((CGUIEditControl *)pControl)->SetLabel(setting.name);
++ pControl->SetWidth(width);
++ if (setting.data) ((CGUIEditControl *)pControl)->SetLabel2(*(CStdString *)setting.data);
++ }
++ else if (setting.type == SettingInfo::EDIT_NUM && m_pOriginalEditNum)
++ {
++ pControl = new CGUIEditControl(*m_pOriginalEditNum);
++ if (!pControl) return ;
++ ((CGUIEditControl *)pControl)->SetLabel(setting.name);
++ pControl->SetWidth(width);
++ ((CGUIEditControl *)pControl)->SetInputType(CGUIEditControl::INPUT_TYPE_NUMBER, 0);
++ if (setting.data) {
++ CStdString strIndex;
++ strIndex.Format("%i", *(int *)setting.data);
++ ((CGUIEditControl *)pControl)->SetLabel2(strIndex);
++ }
++ }
+ else if (setting.type == SettingInfo::SEPARATOR && m_pOriginalSeparator)
+ {
+ pControl = new CGUIImage(*m_pOriginalSeparator);
+@@ -360,6 +437,28 @@ void CGUIDialogSettings::AddSetting(SettingInfo &setting, float width, int iCont
+ delete pControl;
+ }
+
++void CGUIDialogSettings::AddEdit(unsigned int id, int label, CStdString *str, bool enabled)
++{
++ SettingInfo setting;
++ setting.id = id;
++ setting.name = g_localizeStrings.Get(label);
++ setting.type = SettingInfo::EDIT;
++ setting.enabled = enabled;
++ setting.data = str;
++ m_settings.push_back(setting);
++}
++
++void CGUIDialogSettings::AddNumEdit(unsigned int id, int label, int *current, bool enabled)
++{
++ SettingInfo setting;
++ setting.id = id;
++ setting.name = g_localizeStrings.Get(label);
++ setting.type = SettingInfo::EDIT_NUM;
++ setting.enabled = enabled;
++ setting.data = current;
++ m_settings.push_back(setting);
++}
++
+ void CGUIDialogSettings::AddButton(unsigned int id, int label, float *current, float min, float interval, float max, FORMATFUNCTION function)
+ {
+ SettingInfo setting;
+@@ -374,6 +473,17 @@ void CGUIDialogSettings::AddButton(unsigned int id, int label, float *current, f
+ m_settings.push_back(setting);
+ }
+
++void CGUIDialogSettings::AddButton(unsigned int id, int label, CStdString *str, bool bOn)
++{
++ SettingInfo setting;
++ setting.id = id;
++ setting.name = g_localizeStrings.Get(label);
++ setting.type = SettingInfo::BUTTON_DIALOG;
++ setting.enabled = bOn;
++ setting.data = str;
++ m_settings.push_back(setting);
++}
++
+ void CGUIDialogSettings::AddString(unsigned int id, int label, CStdString *current)
+ {
+ SettingInfo setting;
+@@ -396,6 +506,18 @@ void CGUIDialogSettings::AddBool(unsigned int id, int label, bool *on, bool enab
+ m_settings.push_back(setting);
+ }
+
++void CGUIDialogSettings::AddSpin(unsigned int id, int label, int *current, unsigned int max, const SETTINGSTRINGS &entries)
++{
++ SettingInfo setting;
++ setting.id = id;
++ setting.name = g_localizeStrings.Get(label);
++ setting.type = SettingInfo::SPIN;
++ setting.data = current;
++ for (unsigned int i = 0; i < max; i++)
++ setting.entry.push_back(make_pair(i, entries[i]));
++ m_settings.push_back(setting);
++}
++
+ void CGUIDialogSettings::AddSpin(unsigned int id, int label, int *current, unsigned int max, const int *entries)
+ {
+ SettingInfo setting;
+diff --git a/xbmc/settings/GUIDialogSettings.h b/xbmc/settings/GUIDialogSettings.h
+index 9768bf3..e186638 100644
+--- a/xbmc/settings/GUIDialogSettings.h
++++ b/xbmc/settings/GUIDialogSettings.h
+@@ -28,14 +28,16 @@ class CGUISpinControlEx;
+ class CGUIButtonControl;
+ class CGUIRadioButtonControl;
+ class CGUISettingsSliderControl;
++class CGUIEditControl;
+ class CGUIImage;
+
++typedef std::vector<CStdString> SETTINGSTRINGS;
+ typedef CStdString (*FORMATFUNCTION) (float value, float min);
+
+ class SettingInfo
+ {
+ public:
+- enum SETTING_TYPE { NONE=0, BUTTON, CHECK, CHECK_UCHAR, SPIN, SLIDER, SEPARATOR, STRING };
++ enum SETTING_TYPE { NONE=0, EDIT, EDIT_NUM, BUTTON, BUTTON_DIALOG, CHECK, CHECK_UCHAR, SPIN, SLIDER, SEPARATOR, STRING };
+ SettingInfo()
+ {
+ id = 0;
+@@ -83,8 +85,12 @@ protected:
+
+ void AddSetting(SettingInfo &setting, float width, int iControlID);
+
++ void AddEdit(unsigned int id, int label, CStdString *str, bool enabled = true);
++ void AddNumEdit(unsigned int id, int label, int *current, bool enabled = true);
+ void AddButton(unsigned int id, int label, float *current = NULL, float min = 0, float interval = 0, float max = 0, FORMATFUNCTION function = NULL);
++ void AddButton(unsigned int it, int label, CStdString *str, bool bOn=true);
+ void AddBool(unsigned int id, int label, bool *on, bool enabled = true);
++ void AddSpin(unsigned int id, int label, int *current, unsigned int max, const SETTINGSTRINGS &entries);
+ void AddString(unsigned int id, int label, CStdString *current);
+ void AddSpin(unsigned int id, int label, int *current, unsigned int max, const int *entries);
+ void AddSpin(unsigned int id, int label, int *current, unsigned int min, unsigned int max, const char* minLabel = NULL);
+@@ -93,6 +99,8 @@ protected:
+ void AddSlider(unsigned int id, int label, float *current, float min, float interval, float max, FORMATFUNCTION formatFunction, bool allowPopup = true);
+ void AddSeparator(unsigned int id);
+
++ CGUIEditControl *m_pOriginalEdit;
++ CGUIEditControl *m_pOriginalEditNum;
+ CGUISpinControlEx *m_pOriginalSpin;
+ CGUIRadioButtonControl *m_pOriginalRadioButton;
+ CGUIButtonControl *m_pOriginalSettingsButton;
+diff --git a/xbmc/settings/GUISettings.cpp b/xbmc/settings/GUISettings.cpp
+index 6cce6ac..af9af2e 100644
+--- a/xbmc/settings/GUISettings.cpp
++++ b/xbmc/settings/GUISettings.cpp
+@@ -43,6 +43,7 @@
+ #include "guilib/GUIFontManager.h"
+ #include "utils/Weather.h"
+ #include "LangInfo.h"
++#include "pvr/PVRManager.h"
+ #include "utils/XMLUtils.h"
+ #if defined(__APPLE__)
+ #include "osx/DarwinUtils.h"
+@@ -50,13 +51,17 @@
+
+ using namespace std;
+ using namespace ADDON;
++using namespace PVR;
+
+ // String id's of the masks
++#define MASK_DAYS 17999
++#define MASK_HOURS 17998
+ #define MASK_MINS 14044
+ #define MASK_SECS 14045
+ #define MASK_MS 14046
+ #define MASK_PERCENT 14047
+ #define MASK_KBPS 14048
++#define MASK_MB 17997
+ #define MASK_KB 14049
+ #define MASK_DB 14050
+
+@@ -862,8 +867,65 @@ void CGUISettings::Initialize()
+
+ AddPath(NULL,"system.playlistspath",20006,"set default",BUTTON_CONTROL_PATH_INPUT,false);
+
+- // PVR-related setting typically used by skins that are aimed at PVR and non-PVR builds
+- AddBool(NULL, "pvrmanager.enabled", 449, false);
++ // tv settings (access over TV menu from home window)
++ AddGroup(8, 19180);
++ CSettingsCategory* pvr = AddCategory(8, "pvrmanager", 128);
++ AddBool(pvr, "pvrmanager.enabled", 449, false);
++ AddSeparator(pvr, "pvrmanager.sep1");
++ AddBool(pvr, "pvrmanager.syncchannelgroups", 19221, true);
++ AddBool(pvr, "pvrmanager.backendchannelorder", 19231, false);
++ AddBool(pvr, "pvrmanager.usebackendchannelnumbers", 19234, false);
++ AddSeparator(pvr, "pvrmanager.sep2");
++ AddString(pvr, "pvrmanager.channelmanager", 19199, "", BUTTON_CONTROL_STANDARD);
++ AddString(pvr, "pvrmanager.channelscan", 19117, "", BUTTON_CONTROL_STANDARD);
++ AddString(pvr, "pvrmanager.resetdb", 19185, "", BUTTON_CONTROL_STANDARD);
++
++ CSettingsCategory* pvrm = AddCategory(8, "pvrmenu", 19181);
++ AddBool(pvrm, "pvrmenu.infoswitch", 19178, true);
++ AddBool(pvrm, "pvrmenu.infotimeout", 19179, true);
++ AddBool(pvrm, "pvrmenu.closechannelosdonswitch", 19229, false);
++ AddInt(pvrm, "pvrmenu.infotime", 19184, 5, 1, 1, 10, SPIN_CONTROL_INT_PLUS, MASK_SECS);
++ AddBool(pvrm, "pvrmenu.hidevideolength", 19169, true);
++ AddSeparator(pvrm, "pvrmenu.sep1");
++ AddString(pvrm, "pvrmenu.iconpath", 19018, "", BUTTON_CONTROL_PATH_INPUT, false, 657);
++ AddString(pvrm, "pvrmenu.searchicons", 19167, "", BUTTON_CONTROL_STANDARD);
++
++ CSettingsCategory* pvre = AddCategory(8, "epg", 19069);
++ AddInt(pvre, "epg.defaultguideview", 19065, GUIDE_VIEW_NOW, GUIDE_VIEW_CHANNEL, 1, GUIDE_VIEW_TIMELINE, SPIN_CONTROL_TEXT);
++ AddInt(pvre, "epg.daystodisplay", 19182, 2, 1, 1, 14, SPIN_CONTROL_INT_PLUS, MASK_DAYS);
++ AddSeparator(pvre, "epg.sep1");
++ AddInt(pvre, "epg.epgupdate", 19071, 120, 15, 15, 480, SPIN_CONTROL_INT_PLUS, MASK_MINS);
++ AddBool(pvre, "epg.preventupdateswhileplayingtv", 19230, false);
++ AddBool(pvre, "epg.ignoredbforclient", 19072, false);
++ AddString(pvre, "epg.resetepg", 19187, "", BUTTON_CONTROL_STANDARD);
++
++ CSettingsCategory* pvrp = AddCategory(8, "pvrplayback", 19177);
++ AddBool(pvrp, "pvrplayback.playminimized", 19171, true);
++ AddInt(pvrp, "pvrplayback.startlast", 19189, START_LAST_CHANNEL_OFF, START_LAST_CHANNEL_OFF, 1, START_LAST_CHANNEL_ON, SPIN_CONTROL_TEXT);
++ AddBool(pvrp, "pvrplayback.switchautoclose", 19168, true);
++ AddBool(pvrp, "pvrplayback.signalquality", 19037, true);
++ AddSeparator(pvrp, "pvrplayback.sep1");
++ AddInt(pvrp, "pvrplayback.scantime", 19170, 15, 1, 1, 60, SPIN_CONTROL_INT_PLUS, MASK_SECS);
++ AddInt(pvrp, "pvrplayback.channelentrytimeout", 19073, 0, 0, 250, 2000, SPIN_CONTROL_INT_PLUS, MASK_MS);
++
++ CSettingsCategory* pvrr = AddCategory(8, "pvrrecord", 19043);
++ AddInt(pvrr, "pvrrecord.instantrecordtime", 19172, 180, 1, 1, 720, SPIN_CONTROL_INT_PLUS, MASK_MINS);
++ AddInt(pvrr, "pvrrecord.defaultpriority", 19173, 50, 1, 1, 100, SPIN_CONTROL_INT_PLUS);
++ AddInt(pvrr, "pvrrecord.defaultlifetime", 19174, 99, 1, 1, 365, SPIN_CONTROL_INT_PLUS, MASK_DAYS);
++ AddInt(pvrr, "pvrrecord.marginstart", 19175, 2, 1, 1, 60, SPIN_CONTROL_INT_PLUS, MASK_MINS);
++ AddInt(pvrr, "pvrrecord.marginend", 19176, 10, 1, 1, 60, SPIN_CONTROL_INT_PLUS, MASK_MINS);
++ AddSeparator(pvrr, "pvrrecord.sep1");
++ AddBool(pvr, "pvrrecord.timernotifications", 19233, true);
++
++ CSettingsCategory* pvrpwr = AddCategory(8, "pvrpowermanagement", 14095);
++ AddBool(pvrpwr, "pvrpowermanagement.enabled", 305, false);
++ AddSeparator(pvrpwr, "pvrpowermanagement.sep1");
++ AddInt(pvrpwr, "pvrpowermanagement.backendidletime", 19244, 15, 0, 5, 360, SPIN_CONTROL_INT_PLUS, MASK_MINS, TEXT_OFF);
++ AddString(pvrpwr, "pvrpowermanagement.setwakeupcmd", 19245, "/usr/bin/setwakeup.sh", EDIT_CONTROL_INPUT, true);
++ AddInt(pvrpwr, "pvrpowermanagement.prewakeup", 19246, 15, 0, 1, 60, SPIN_CONTROL_INT_PLUS, MASK_MINS, TEXT_OFF);
++ AddSeparator(pvrpwr, "pvrpowermanagement.sep2");
++ AddBool(pvrpwr, "pvrpowermanagement.dailywakeup", 19247, false);
++ AddString(pvrpwr, "pvrpowermanagement.dailywakeuptime", 19248, "00:00:00", EDIT_CONTROL_INPUT);
+ }
+
+ CGUISettings::~CGUISettings(void)
+@@ -941,6 +1003,9 @@ void CGUISettings::SetBool(const char *strSetting, bool bSetting)
+ if (it != settingsMap.end())
+ { // old category
+ ((CSettingBool*)(*it).second)->SetData(bSetting);
++
++ SetChanged();
++
+ return ;
+ }
+ // Assert here and write debug output
+@@ -954,6 +1019,9 @@ void CGUISettings::ToggleBool(const char *strSetting)
+ if (it != settingsMap.end())
+ { // old category
+ ((CSettingBool*)(*it).second)->SetData(!((CSettingBool *)(*it).second)->GetData());
++
++ SetChanged();
++
+ return ;
+ }
+ // Assert here and write debug output
+@@ -989,6 +1057,9 @@ void CGUISettings::SetFloat(const char *strSetting, float fSetting)
+ if (it != settingsMap.end())
+ {
+ ((CSettingFloat *)(*it).second)->SetData(fSetting);
++
++ SetChanged();
++
+ return ;
+ }
+ // Assert here and write debug output
+@@ -1063,6 +1134,9 @@ void CGUISettings::SetInt(const char *strSetting, int iSetting)
+ if (it != settingsMap.end())
+ {
+ ((CSettingInt *)(*it).second)->SetData(iSetting);
++
++ SetChanged();
++
+ return ;
+ }
+ // Assert here and write debug output
+@@ -1134,6 +1208,9 @@ void CGUISettings::SetString(const char *strSetting, const char *strData)
+ if (it != settingsMap.end())
+ {
+ ((CSettingString *)(*it).second)->SetData(strData);
++
++ SetChanged();
++
+ return ;
+ }
+ // Assert here and write debug output
+@@ -1268,6 +1345,8 @@ void CGUISettings::LoadFromXML(TiXmlElement *pRootElement, mapIter &it, bool adv
+ }
+ }
+ }
++
++ SetChanged();
+ }
+
+ void CGUISettings::SaveXML(TiXmlNode *pRootNode)
+@@ -1304,6 +1383,8 @@ void CGUISettings::SaveXML(TiXmlNode *pRootNode)
+ }
+ }
+ }
++
++ SetChanged();
+ }
+
+ void CGUISettings::Clear()
+@@ -1314,6 +1395,8 @@ void CGUISettings::Clear()
+ for (unsigned int i = 0; i < settingsGroups.size(); i++)
+ delete settingsGroups[i];
+ settingsGroups.clear();
++
++ SetChanged();
+ }
+
+ float square_error(float x, float y)
+@@ -1380,6 +1463,8 @@ void CGUISettings::SetResolution(RESOLUTION res)
+ }
+ SetString("videoscreen.screenmode", mode);
+ m_LookAndFeelResolution = res;
++
++ SetChanged();
+ }
+
+ bool CGUISettings::SetLanguage(const CStdString &strLanguage)
+@@ -1413,6 +1498,7 @@ bool CGUISettings::SetLanguage(const CStdString &strLanguage)
+
+ // also tell our weather and skin to reload as these are localized
+ g_weatherManager.Refresh();
++ g_PVRManager.LocalizationChanged();
+ g_application.ReloadSkin();
+ }
+
+diff --git a/xbmc/settings/GUISettings.h b/xbmc/settings/GUISettings.h
+index 5501064..e4ada7c 100644
+--- a/xbmc/settings/GUISettings.h
++++ b/xbmc/settings/GUISettings.h
+@@ -25,6 +25,7 @@
+ #include <map>
+ #include "guilib/Resolution.h"
+ #include "addons/IAddon.h"
++#include "utils/Observer.h"
+
+ class TiXmlNode;
+ class TiXmlElement;
+@@ -125,6 +126,15 @@ class TiXmlElement;
+ #define APM_HIPOWER_STANDBY 2
+ #define APM_LOPOWER_STANDBY 3
+
++#define GUIDE_VIEW_CHANNEL 0
++#define GUIDE_VIEW_NOW 1
++#define GUIDE_VIEW_NEXT 2
++#define GUIDE_VIEW_TIMELINE 3
++
++#define START_LAST_CHANNEL_OFF 0
++#define START_LAST_CHANNEL_MIN 1
++#define START_LAST_CHANNEL_ON 2
++
+ #define SETTINGS_TYPE_BOOL 1
+ #define SETTINGS_TYPE_FLOAT 2
+ #define SETTINGS_TYPE_INT 3
+@@ -438,7 +448,7 @@ private:
+
+ typedef std::vector<CSetting *> vecSettings;
+
+-class CGUISettings
++class CGUISettings : public Observable
+ {
+ public:
+ CGUISettings();
+diff --git a/xbmc/settings/GUIWindowSettingsCategory.cpp b/xbmc/settings/GUIWindowSettingsCategory.cpp
+index c6492f2..dc26188 100644
+--- a/xbmc/settings/GUIWindowSettingsCategory.cpp
++++ b/xbmc/settings/GUIWindowSettingsCategory.cpp
+@@ -45,6 +45,7 @@
+ #include "network/libscrobbler/lastfmscrobbler.h"
+ #include "network/libscrobbler/librefmscrobbler.h"
+ #include "GUIPassword.h"
++#include "GUIInfoManager.h"
+ #include "dialogs/GUIDialogGamepad.h"
+ #include "dialogs/GUIDialogNumeric.h"
+ #include "dialogs/GUIDialogFileBrowser.h"
+@@ -83,6 +84,8 @@
+ #include "XBMCHelper.h"
+ #endif
+ #endif
++#include "pvr/dialogs/GUIDialogPVRChannelManager.h"
++#include "pvr/PVRManager.h"
+ #include "network/GUIDialogAccessPoints.h"
+ #include "filesystem/Directory.h"
+
+@@ -123,6 +126,7 @@
+ using namespace std;
+ using namespace XFILE;
+ using namespace ADDON;
++using namespace PVR;
+ using namespace PERIPHERALS;
+
+ #define CONTROL_GROUP_BUTTONS 0
+@@ -149,7 +153,7 @@ CGUIWindowSettingsCategory::CGUIWindowSettingsCategory(void)
+ m_pOriginalImage = NULL;
+ m_pOriginalEdit = NULL;
+ // set the correct ID range...
+- m_idRange = 8;
++ m_idRange = 9;
+ m_iScreen = 0;
+ m_strOldTrackFormat = "";
+ m_strOldTrackFormatRight = "";
+@@ -382,6 +386,10 @@ void CGUIWindowSettingsCategory::CreateSettings()
+ FillInScreens(strSetting, g_guiSettings.GetResolution());
+ else if (strSetting.Equals("videoscreen.resolution"))
+ FillInResolutions(strSetting, g_guiSettings.GetInt("videoscreen.screen"), g_guiSettings.GetResolution(), false);
++ else if (strSetting.Equals("epg.defaultguideview"))
++ FillInEpgGuideView(pSetting);
++ else if (strSetting.Equals("pvrplayback.startlast"))
++ FillInPvrStartLastChannel(pSetting);
+ continue;
+ }
+ #ifdef HAS_WEB_SERVER
+@@ -668,6 +676,13 @@ void CGUIWindowSettingsCategory::UpdateSettings()
+ CGUIControl *pControl = (CGUIControl *)GetControl(pSettingControl->GetID());
+ if (pControl) pControl->SetEnabled(g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE);
+ }
++ else if (!strSetting.Equals("pvrmanager.enabled") &&
++ (strSetting.Equals("pvrmanager.channelscan") || strSetting.Equals("pvrmanager.channelmanager") ||
++ strSetting.Equals("pvrmenu.searchicons")))
++ {
++ CGUIControl *pControl = (CGUIControl *)GetControl(pSettingControl->GetID());
++ if (pControl) pControl->SetEnabled(g_guiSettings.GetBool("pvrmanager.enabled"));
++ }
+ else if (!strSetting.Equals("services.esenabled")
+ && strSetting.Left(11).Equals("services.es"))
+ {
+@@ -957,6 +972,9 @@ void CGUIWindowSettingsCategory::UpdateSettings()
+ }
+ #endif
+ }
++
++ g_guiSettings.SetChanged();
++ g_guiSettings.NotifyObservers("settings", true);
+ }
+
+ void CGUIWindowSettingsCategory::OnClick(CBaseSettingControl *pSettingControl)
+@@ -1518,7 +1536,7 @@ void CGUIWindowSettingsCategory::OnSettingChanged(CBaseSettingControl *pSettingC
+ if (CAddonMgr::Get().GetAddon(g_guiSettings.GetString("screensaver.mode"), addon, ADDON_SCREENSAVER))
+ CGUIDialogAddonSettings::ShowAndGetInput(addon);
+ }
+- else if (strSetting.Equals("debug.screenshotpath") || strSetting.Equals("audiocds.recordingpath") || strSetting.Equals("subtitles.custompath"))
++ else if (strSetting.Equals("debug.screenshotpath") || strSetting.Equals("audiocds.recordingpath") || strSetting.Equals("subtitles.custompath") || strSetting.Equals("pvrmenu.iconpath"))
+ {
+ CSettingString *pSettingString = (CSettingString *)pSettingControl->GetSetting();
+ CStdString path = g_guiSettings.GetString(strSetting,false);
+@@ -1526,7 +1544,11 @@ void CGUIWindowSettingsCategory::OnSettingChanged(CBaseSettingControl *pSettingC
+
+ bool bWriteOnly = true;
+
+- if (strSetting.Equals("subtitles.custompath"))
++ if (strSetting.Equals("pvrmenu.iconpath"))
++ {
++ bWriteOnly = false;
++ }
++ else if (strSetting.Equals("subtitles.custompath"))
+ {
+ bWriteOnly = false;
+ shares = g_settings.m_videoSources;
+@@ -1693,6 +1715,13 @@ void CGUIWindowSettingsCategory::OnSettingChanged(CBaseSettingControl *pSettingC
+ }
+ #endif
+ }
++ else if (strSetting.Equals("pvrmanager.enabled"))
++ {
++ if (g_guiSettings.GetBool("pvrmanager.enabled"))
++ g_application.StartPVRManager();
++ else
++ g_application.StopPVRManager();
++ }
+ else if (strSetting.Equals("masterlock.lockcode"))
+ {
+ // Now Prompt User to enter the old and then the new MasterCode!
+@@ -1820,6 +1849,33 @@ void CGUIWindowSettingsCategory::OnSettingChanged(CBaseSettingControl *pSettingC
+ {
+ CUtil::DeleteVideoDatabaseDirectoryCache();
+ }
++ else if (strSetting.Equals("pvrmenu.searchicons") && g_PVRManager.IsStarted())
++ {
++ g_PVRManager.SearchMissingChannelIcons();
++ }
++ else if (strSetting.Equals("pvrmanager.resetdb"))
++ {
++ if (CGUIDialogYesNo::ShowAndGetInput(19098, 19186, 750, 0))
++ g_PVRManager.ResetDatabase();
++ }
++ else if (strSetting.Equals("epg.resetepg"))
++ {
++ if (CGUIDialogYesNo::ShowAndGetInput(19098, 19188, 750, 0))
++ g_PVRManager.ResetEPG();
++ }
++ else if (strSetting.Equals("pvrmanager.channelscan") && g_PVRManager.IsStarted())
++ {
++ if (CGUIDialogYesNo::ShowAndGetInput(19098, 19118, 19194, 0))
++ g_PVRManager.StartChannelScan();
++ }
++ else if (strSetting.Equals("pvrmanager.channelmanager") && g_PVRManager.IsStarted())
++ {
++ CGUIDialogPVRChannelManager *dialog = (CGUIDialogPVRChannelManager *)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_CHANNEL_MANAGER);
++ if (dialog)
++ {
++ dialog->DoModal();
++ }
++ }
+
+ UpdateSettings();
+ }
+@@ -2646,6 +2702,33 @@ void CGUIWindowSettingsCategory::FillInNetworkInterfaces(CSetting *pSetting, flo
+ pControl->AddLabel(vecInterfaces[i], iInterface++);
+ }
+
++void CGUIWindowSettingsCategory::FillInEpgGuideView(CSetting *pSetting)
++{
++ CSettingInt *pSettingInt = (CSettingInt*)pSetting;
++ CGUISpinControlEx *pControl = (CGUISpinControlEx *)GetControl(GetSetting(pSetting->GetSetting())->GetID());
++ pControl->Clear();
++
++ pControl->AddLabel(g_localizeStrings.Get(19029), GUIDE_VIEW_CHANNEL);
++ pControl->AddLabel(g_localizeStrings.Get(19030), GUIDE_VIEW_NOW);
++ pControl->AddLabel(g_localizeStrings.Get(19031), GUIDE_VIEW_NEXT);
++ pControl->AddLabel(g_localizeStrings.Get(19032), GUIDE_VIEW_TIMELINE);
++
++ pControl->SetValue(pSettingInt->GetData());
++}
++
++void CGUIWindowSettingsCategory::FillInPvrStartLastChannel(CSetting *pSetting)
++{
++ CSettingInt *pSettingInt = (CSettingInt*)pSetting;
++ CGUISpinControlEx *pControl = (CGUISpinControlEx *)GetControl(GetSetting(pSetting->GetSetting())->GetID());
++ pControl->Clear();
++
++ pControl->AddLabel(g_localizeStrings.Get(106), START_LAST_CHANNEL_OFF);
++ pControl->AddLabel(g_localizeStrings.Get(19190), START_LAST_CHANNEL_MIN);
++ pControl->AddLabel(g_localizeStrings.Get(107), START_LAST_CHANNEL_ON);
++
++ pControl->SetValue(pSettingInt->GetData());
++}
++
+ void CGUIWindowSettingsCategory::FillInAudioDevices(CSetting* pSetting, bool Passthrough)
+ {
+ #if defined(__APPLE__)
+diff --git a/xbmc/settings/GUIWindowSettingsCategory.h b/xbmc/settings/GUIWindowSettingsCategory.h
+index 7ce31ab..ab1cc59 100644
+--- a/xbmc/settings/GUIWindowSettingsCategory.h
++++ b/xbmc/settings/GUIWindowSettingsCategory.h
+@@ -57,6 +57,8 @@ protected:
+ void FillInStartupWindow(CSetting *pSetting);
+ void FillInViewModes(CSetting *pSetting, int windowID);
+ void FillInSortMethods(CSetting *pSetting, int windowID);
++ void FillInEpgGuideView(CSetting *pSetting);
++ void FillInPvrStartLastChannel(CSetting *pSetting);
+
+ void FillInSkinThemes(CSetting *pSetting);
+ void FillInSkinColors(CSetting *pSetting);
+diff --git a/xbmc/settings/Settings.cpp b/xbmc/settings/Settings.cpp
+index 3557853..a5827bd 100644
+--- a/xbmc/settings/Settings.cpp
++++ b/xbmc/settings/Settings.cpp
+@@ -108,6 +108,8 @@ void CSettings::Initialize()
+ m_discStubExtensions = ".disc";
+ // internal music extensions
+ m_musicExtensions += "|.sidstream|.oggstream|.nsfstream|.asapstream|.cdda";
++ // internal video extensions
++ m_videoExtensions += "|.pvr";
+
+ #ifdef __APPLE__
+ CStdString logDir = getenv("HOME");
+diff --git a/xbmc/settings/VideoSettings.h b/xbmc/settings/VideoSettings.h
+index 6641145..4b462bf 100644
+--- a/xbmc/settings/VideoSettings.h
++++ b/xbmc/settings/VideoSettings.h
+@@ -58,14 +58,13 @@ enum EINTERLACEMETHOD
+ VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF=13,
+ VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL=14,
+ VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF=15,
+-
+ VS_INTERLACEMETHOD_DEINTERLACE_HALF=16,
+-
+ VS_INTERLACEMETHOD_DXVA_BOB = 17,
+ VS_INTERLACEMETHOD_DXVA_BEST = 18,
+ // VS_INTERLACEMETHOD_DXVA_ANY = 19, Legacy
+
+ VS_INTERLACEMETHOD_SW_BLEND = 20,
++ VS_INTERLACEMETHOD_AUTO_ION = 21,
+
+ VS_INTERLACEMETHOD_MAX // do not use and keep as last enum value.
+ };
+diff --git a/xbmc/system.h b/xbmc/system.h
+index b10ebae..47d1660 100644
+--- a/xbmc/system.h
++++ b/xbmc/system.h
+@@ -39,6 +39,7 @@
+ #define HAS_UPNP
+ #define HAS_VIDEO_PLAYBACK
+ #define HAS_VISUALISATION
++#define HAS_PVRCLIENTS
+
+ #ifdef HAVE_LIBMICROHTTPD
+ #define HAS_WEB_SERVER
+diff --git a/xbmc/utils/Makefile b/xbmc/utils/Makefile
+index a420c5b..7551b09 100644
+--- a/xbmc/utils/Makefile
++++ b/xbmc/utils/Makefile
+@@ -32,6 +32,7 @@ SRCS=AlarmClock.cpp \
+ LCDFactory.cpp \
+ log.cpp \
+ md5.cpp \
++ Observer.cpp \
+ PCMAmplifier.cpp \
+ PCMRemap.cpp \
+ PerformanceSample.cpp \
+@@ -49,6 +50,7 @@ SRCS=AlarmClock.cpp \
+ StreamUtils.cpp \
+ StringUtils.cpp \
+ SystemInfo.cpp \
++ TextSearch.cpp \
+ TimeSmoother.cpp \
+ TimeUtils.cpp \
+ TuxBoxUtil.cpp \
+diff --git a/xbmc/utils/MathUtils.h b/xbmc/utils/MathUtils.h
+index 47517b5..cc8e75b 100644
+--- a/xbmc/utils/MathUtils.h
++++ b/xbmc/utils/MathUtils.h
+@@ -84,7 +84,7 @@ namespace MathUtils
+ */
+
+ __asm__ __volatile__ (
+- "vmov.F64 d1,%[rnd_val] \n\t" // Copy round_to_nearest into a working register (d1 = 0.5)
++ "fconstd d1,#%G[rnd_val] \n\t" // Copy round_to_nearest into a working register (d1 = 0.5)
+ "fcmpezd %P[value] \n\t" // Check value against zero (value == 0?)
+ "fmstat \n\t" // Copy the floating-point status flags into the general-purpose status flags
+ "it mi \n\t"
+@@ -94,11 +94,7 @@ namespace MathUtils
+ "vmov %[result],s3 \n\t" // Store the integer result in a general-purpose register (result = s3)
+ "vcvt.F64.S32 d1,s3 \n\t" // Convert back to floating-point (d1 = (double)s3)
+ "vsub.F64 d1,%P[value],d1 \n\t" // Calculate the error (d1 = value - d1)
+- "vmov.F64 d2,%[rnd_val] \n\t" // d2 = 0.5;
+- "fcmped d1, d2 \n\t" // (d1 == 0.5?)
+- "fmstat \n\t" // Copy the floating-point status flags into the general-purpose status flags
+- "it eq \n\t"
+- "addeq %[result],#1 \n\t" // (if (d1 == d2) result++;)
++ "fconstd d2,#%G[rnd_val] \n\t" // d2 = 0.5;
+ : [result] "=r"(i) // Outputs
+ : [rnd_val] "Dv" (round_to_nearest), [value] "w"(x) // Inputs
+ : "d1", "d2", "s3" // Clobbers
+diff --git a/xbmc/utils/Observer.cpp b/xbmc/utils/Observer.cpp
+new file mode 100644
+index 0000000..e402290
+--- /dev/null
++++ b/xbmc/utils/Observer.cpp
+@@ -0,0 +1,189 @@
++/*
++ * Copyright (C) 2005-2012 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "Application.h"
++#include "Observer.h"
++#include "threads/SingleLock.h"
++#include "utils/JobManager.h"
++
++using namespace std;
++using namespace ANNOUNCEMENT;
++
++class ObservableMessageJob : public CJob
++{
++private:
++ Observable m_observable;
++ std::vector<Observer *> m_observers;
++ CStdString m_strMessage;
++public:
++ ObservableMessageJob(const Observable &obs, const CStdString &strMessage);
++ virtual ~ObservableMessageJob() {}
++ virtual const char *GetType() const { return "observable-message-job"; }
++
++ virtual bool DoWork();
++};
++
++Observer::~Observer(void)
++{
++ StopObserving();
++}
++
++void Observer::StopObserving(void)
++{
++ CSingleLock lock(m_obsCritSection);
++ for (unsigned int iObsPtr = 0; iObsPtr < m_observables.size(); iObsPtr++)
++ m_observables.at(iObsPtr)->UnregisterObserver(this);
++ m_observables.clear();
++}
++
++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),
++ m_bAsyncAllowed(true)
++{
++ CAnnouncementManager::AddAnnouncer(this);
++}
++
++Observable::~Observable()
++{
++ CAnnouncementManager::RemoveAnnouncer(this);
++ 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);
++ for (unsigned int iObsPtr = 0; iObsPtr < m_observers.size(); iObsPtr++)
++ m_observers.at(iObsPtr)->UnregisterObservable(this);
++ m_observers.clear();
++}
++
++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 CStdString& strMessage /* = "" */, bool bAsync /* = false */)
++{
++ CSingleLock lock(m_obsCritSection);
++ if (m_bObservableChanged && !g_application.m_bStop)
++ {
++ if (bAsync && m_bAsyncAllowed)
++ CJobManager::GetInstance().AddJob(new ObservableMessageJob(*this, strMessage), NULL);
++ else
++ SendMessage(this, &m_observers, strMessage);
++
++ m_bObservableChanged = false;
++ }
++}
++
++void Observable::SetChanged(bool SetTo)
++{
++ CSingleLock lock(m_obsCritSection);
++ m_bObservableChanged = SetTo;
++}
++
++void Observable::Announce(EAnnouncementFlag flag, const char *sender, const char *message, const CVariant &data)
++{
++ if (flag == System && !strcmp(sender, "xbmc") && !strcmp(message, "ApplicationStop"))
++ {
++ CSingleLock lock(m_obsCritSection);
++ m_bAsyncAllowed = false;
++ }
++}
++
++void Observable::SendMessage(Observable *obs, const vector<Observer *> *observers, const CStdString &strMessage)
++{
++ for(unsigned int ptr = 0; ptr < observers->size(); ptr++)
++ {
++ Observer *observer = observers->at(ptr);
++ if (observer)
++ observer->Notify(*obs, strMessage);
++ }
++}
++
++ObservableMessageJob::ObservableMessageJob(const Observable &obs, const CStdString &strMessage)
++{
++ m_strMessage = strMessage;
++ m_observable = obs;
++ m_observers = obs.m_observers;
++}
++
++bool ObservableMessageJob::DoWork()
++{
++ Observable::SendMessage(&m_observable, &m_observers, m_strMessage);
++
++ return true;
++}
+diff --git a/xbmc/utils/Observer.h b/xbmc/utils/Observer.h
+new file mode 100644
+index 0000000..b49fa0c
+--- /dev/null
++++ b/xbmc/utils/Observer.h
+@@ -0,0 +1,136 @@
++#pragma once
++
++/*
++ * Copyright (C) 2005-2012 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#include "StdString.h"
++#include "threads/CriticalSection.h"
++#include "interfaces/AnnouncementManager.h"
++
++class Observable;
++class ObservableMessageJob;
++
++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 CStdString& 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 : public ANNOUNCEMENT::IAnnouncer
++{
++ 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 strMessage The message to send.
++ * @param bAsync True to send the message async, using the jobmanager.
++ */
++ virtual void NotifyObservers(const CStdString& strMessage = "", bool bAsync = false);
++
++ /*!
++ * @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;
++
++ virtual void Announce(ANNOUNCEMENT::EAnnouncementFlag flag, const char *sender, const char *message, const CVariant &data);
++
++protected:
++ /*!
++ * @brief Send a message to all observer when m_bObservableChanged is true.
++ * @param obs The observer that sends the message.
++ * @param observers The observers to send the message to.
++ * @param strMessage The message to send.
++ */
++ static void SendMessage(Observable *obs, const std::vector<Observer *> *observers, const CStdString &strMessage);
++
++ bool m_bObservableChanged; /*!< true when the observable is marked as changed, false otherwise */
++ std::vector<Observer *> m_observers; /*!< all observers */
++ CCriticalSection m_obsCritSection; /*!< mutex */
++ bool m_bAsyncAllowed; /*!< true when async messages are allowed, false otherwise */
++};
+diff --git a/xbmc/utils/TextSearch.cpp b/xbmc/utils/TextSearch.cpp
+new file mode 100644
+index 0000000..6477ab5
+--- /dev/null
++++ b/xbmc/utils/TextSearch.cpp
+@@ -0,0 +1,166 @@
++/*
++ * Copyright (C) 2005-2012 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#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.IsEmpty() || !IsValid())
++ return false;
++
++ CStdString strSearch(strHaystack);
++ if (!m_bCaseSensitive)
++ strSearch = strSearch.ToLower();
++
++ /* 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)) != -1)
++ 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)) != -1)
++ {
++ 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]) == -1)
++ return false;
++ }
++
++ /* all ok, return true */
++ return true;
++}
++
++void CTextSearch::GetAndCutNextTerm(CStdString &strSearchTerm, CStdString &strNextTerm)
++{
++ CStdString strFindNext(" ");
++
++ if (strSearchTerm.Left(1).Equals("\""))
++ {
++ strSearchTerm.erase(0, 1);
++ strFindNext = "\"";
++ }
++
++ int iNextPos = strSearchTerm.Find(strFindNext);
++ if (iNextPos != -1)
++ {
++ strNextTerm = strSearchTerm.Left(iNextPos);
++ strSearchTerm.erase(0, iNextPos + 1);
++ }
++ else
++ {
++ strNextTerm = strSearchTerm;
++ strSearchTerm.clear();
++ }
++}
++
++void CTextSearch::ExtractSearchTerms(const CStdString &strSearchTerm, TextSearchDefault defaultSearchMode)
++{
++ CStdString strParsedSearchTerm(strSearchTerm);
++ strParsedSearchTerm = strParsedSearchTerm.Trim();
++
++ if (!m_bCaseSensitive)
++ strParsedSearchTerm = strParsedSearchTerm.ToLower();
++
++ bool bNextAND(defaultSearchMode == SEARCH_DEFAULT_AND);
++ bool bNextOR(defaultSearchMode == SEARCH_DEFAULT_OR);
++ bool bNextNOT(defaultSearchMode == SEARCH_DEFAULT_NOT);
++
++ while (strParsedSearchTerm.length() > 0)
++ {
++ strParsedSearchTerm = strParsedSearchTerm.TrimLeft();
++
++ if (strParsedSearchTerm.Left(1).Equals("!") || strParsedSearchTerm.Left(3).Equals("NOT"))
++ {
++ CStdString strDummy;
++ GetAndCutNextTerm(strParsedSearchTerm, strDummy);
++ bNextNOT = true;
++ }
++ else if (strParsedSearchTerm.Left(1).Equals("+") || strParsedSearchTerm.Left(3).Equals("AND"))
++ {
++ CStdString strDummy;
++ GetAndCutNextTerm(strParsedSearchTerm, strDummy);
++ bNextAND = true;
++ }
++ else if (strParsedSearchTerm.Left(1).Equals("|") || strParsedSearchTerm.Left(2).Equals("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
++ m_NOT.push_back(strTerm);
++ }
++ else
++ {
++ break;
++ }
++
++ bNextAND = (defaultSearchMode == SEARCH_DEFAULT_AND);
++ bNextOR = (defaultSearchMode == SEARCH_DEFAULT_OR);
++ bNextNOT = (defaultSearchMode == SEARCH_DEFAULT_NOT);
++ }
++
++ strParsedSearchTerm = strParsedSearchTerm.TrimLeft();
++ }
++}
+diff --git a/xbmc/utils/TextSearch.h b/xbmc/utils/TextSearch.h
+new file mode 100644
+index 0000000..5299178
+--- /dev/null
++++ b/xbmc/utils/TextSearch.h
+@@ -0,0 +1,50 @@
++#pragma once
++/*
++ * Copyright (C) 2005-2012 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, write to
++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
++ * http://www.gnu.org/copyleft/gpl.html
++ *
++ */
++
++#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:
++ 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/xbmc/utils/URIUtils.cpp b/xbmc/utils/URIUtils.cpp
+index 1f2f180..bb45c60 100644
+--- a/xbmc/utils/URIUtils.cpp
++++ b/xbmc/utils/URIUtils.cpp
+@@ -727,12 +727,16 @@ bool URIUtils::IsHTSP(const CStdString& strFile)
+
+ bool URIUtils::IsLiveTV(const CStdString& strFile)
+ {
++ CStdString strFileWithoutSlash(strFile);
++ RemoveSlashAtEnd(strFileWithoutSlash);
++
+ if(IsTuxBox(strFile)
+ || IsVTP(strFile)
+ || IsHDHomeRun(strFile)
+ || IsSlingbox(strFile)
+ || IsHTSP(strFile)
+- || strFile.Left(4).Equals("sap:"))
++ || strFile.Left(4).Equals("sap:")
++ ||(strFileWithoutSlash.Right(4).Equals(".pvr") && !strFileWithoutSlash.Left(16).Equals("pvr://recordings")))
+ return true;
+
+ if (IsMythTV(strFile) && CMythDirectory::IsLiveTV(strFile))
+@@ -741,6 +745,15 @@ bool URIUtils::IsLiveTV(const CStdString& strFile)
+ return false;
+ }
+
++bool URIUtils::IsPVRRecording(const CStdString& strFile)
++{
++ CStdString strFileWithoutSlash(strFile);
++ RemoveSlashAtEnd(strFileWithoutSlash);
++
++ return strFileWithoutSlash.Right(4).Equals(".pvr") &&
++ strFile.Left(16).Equals("pvr://recordings");
++}
++
+ bool URIUtils::IsMusicDb(const CStdString& strFile)
+ {
+ return strFile.Left(8).Equals("musicdb:");
+diff --git a/xbmc/utils/URIUtils.h b/xbmc/utils/URIUtils.h
+index 751f1f0..2ab5e92 100644
+--- a/xbmc/utils/URIUtils.h
++++ b/xbmc/utils/URIUtils.h
+@@ -68,6 +68,7 @@ public:
+ static bool IsISO9660(const CStdString& strFile);
+ static bool IsLastFM(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);
+diff --git a/xbmc/utils/XMLUtils.cpp b/xbmc/utils/XMLUtils.cpp
+index c4085b2..e1e5f7b 100644
+--- a/xbmc/utils/XMLUtils.cpp
++++ b/xbmc/utils/XMLUtils.cpp
+@@ -131,6 +131,14 @@ bool XMLUtils::GetString(const TiXmlNode* pRootNode, const char* strTag, CStdStr
+ return false;
+ }
+
++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 CStdString& strSeparator, CStdString& strStringValue,
+ bool clear)
+diff --git a/xbmc/utils/XMLUtils.h b/xbmc/utils/XMLUtils.h
+index fe02b99..ef0abf3 100644
+--- a/xbmc/utils/XMLUtils.h
++++ b/xbmc/utils/XMLUtils.h
+@@ -29,6 +29,7 @@ class XMLUtils
+ {
+ public:
+ static bool HasUTF8Declaration(const CStdString &strXML);
++ 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);
+diff --git a/xbmc/utils/log.cpp b/xbmc/utils/log.cpp
+index 6f660db..87cf89a 100644
+--- a/xbmc/utils/log.cpp
++++ b/xbmc/utils/log.cpp
+@@ -56,6 +56,7 @@ void CLog::Close()
+ m_repeatLine.clear();
+ }
+
++
+ void CLog::Log(int loglevel, const char *format, ... )
+ {
+ static const char* prefixFormat = "%02.2d:%02.2d:%02.2d T:%"PRIu64" %7s: ";
+@@ -67,18 +68,18 @@ void CLog::Log(int loglevel, const char *format, ... )
+ {
+ if (!m_file)
+ return;
+-
++
+ SYSTEMTIME time;
+ GetLocalTime(&time);
+
+ CStdString strPrefix, strData;
+-
++
+ strData.reserve(16384);
+ va_list va;
+ va_start(va, format);
+ strData.FormatV(format,va);
+ va_end(va);
+-
++
+ if (m_repeatLogLevel == loglevel && m_repeatLine == strData)
+ {
+ m_repeatCount++;
+@@ -111,14 +112,14 @@ void CLog::Log(int loglevel, const char *format, ... )
+ if (!length)
+ return;
+
+- OutputDebugString(strData);
++ strPrefix.Format(prefixFormat, time.wHour, time.wMinute, time.wSecond, (uint64_t)CThread::GetCurrentThreadId(), levelNames[loglevel]);
++
++ OutputDebugString(strPrefix+strData);
+
+ /* fixup newline alignment, number of spaces should equal prefix length */
+ strData.Replace("\n", LINE_ENDING" ");
+ strData += LINE_ENDING;
+-
+- strPrefix.Format(prefixFormat, time.wHour, time.wMinute, time.wSecond, (uint64_t)CThread::GetCurrentThreadId(), levelNames[loglevel]);
+-
++
+ fputs(strPrefix.c_str(), m_file);
+ fputs(strData.c_str(), m_file);
+ fflush(m_file);
+diff --git a/xbmc/video/GUIViewStateVideo.cpp b/xbmc/video/GUIViewStateVideo.cpp
+index 09ed034..b541ce0 100644
+--- a/xbmc/video/GUIViewStateVideo.cpp
++++ b/xbmc/video/GUIViewStateVideo.cpp
+@@ -21,6 +21,8 @@
+
+ #include "GUIViewStateVideo.h"
+ #include "PlayListPlayer.h"
++#include "filesystem/PluginDirectory.h"
++#include "filesystem/PVRDirectory.h"
+ #include "filesystem/VideoDatabaseDirectory.h"
+ #include "filesystem/Directory.h"
+ #include "guilib/GUIBaseContainer.h"
+diff --git a/xbmc/video/dialogs/GUIDialogAudioSubtitleSettings.cpp b/xbmc/video/dialogs/GUIDialogAudioSubtitleSettings.cpp
+index 66eee83..9c5a7b5 100644
+--- a/xbmc/video/dialogs/GUIDialogAudioSubtitleSettings.cpp
++++ b/xbmc/video/dialogs/GUIDialogAudioSubtitleSettings.cpp
+@@ -37,9 +37,11 @@
+ #include "settings/Settings.h"
+ #include "settings/GUISettings.h"
+ #include "guilib/LocalizeStrings.h"
++#include "pvr/PVRManager.h"
+
+ using namespace std;
+ using namespace XFILE;
++using namespace PVR;
+
+ #ifdef HAS_VIDEO_PLAYBACK
+ extern void xbox_audio_switch_channel(int iAudioStream, bool bAudioOnAllSpeakers); //lowlevel audio
+@@ -348,6 +350,9 @@ void CGUIDialogAudioSubtitleSettings::OnSettingChanged(SettingInfo &setting)
+ g_settings.Save();
+ }
+ }
++
++ if (g_PVRManager.IsPlayingRadio() || g_PVRManager.IsPlayingTV())
++ g_PVRManager.TriggerSaveChannelSettings();
+ }
+
+ void CGUIDialogAudioSubtitleSettings::FrameMove()
+diff --git a/xbmc/video/dialogs/GUIDialogVideoOSD.cpp b/xbmc/video/dialogs/GUIDialogVideoOSD.cpp
+index 402fbb9..ffc070c 100644
+--- a/xbmc/video/dialogs/GUIDialogVideoOSD.cpp
++++ b/xbmc/video/dialogs/GUIDialogVideoOSD.cpp
+@@ -42,6 +42,10 @@ void CGUIDialogVideoOSD::FrameMove()
+ if (g_Mouse.IsActive() || g_windowManager.IsWindowActive(WINDOW_DIALOG_AUDIO_OSD_SETTINGS)
+ || g_windowManager.IsWindowActive(WINDOW_DIALOG_VIDEO_OSD_SETTINGS)
+ || g_windowManager.IsWindowActive(WINDOW_DIALOG_VIDEO_BOOKMARKS)
++ || g_windowManager.IsWindowActive(WINDOW_DIALOG_PVR_OSD_CHANNELS)
++ || g_windowManager.IsWindowActive(WINDOW_DIALOG_PVR_OSD_GUIDE)
++ || g_windowManager.IsWindowActive(WINDOW_DIALOG_PVR_OSD_DIRECTOR)
++ || g_windowManager.IsWindowActive(WINDOW_DIALOG_PVR_OSD_CUTTER)
+ || g_windowManager.IsWindowActive(WINDOW_DIALOG_OSD_TELETEXT))
+ SetAutoClose(100); // enough for 10fps
+ }
+@@ -99,6 +103,14 @@ bool CGUIDialogVideoOSD::OnMessage(CGUIMessage& message)
+ if (pDialog && pDialog->IsDialogRunning()) pDialog->Close(true);
+ pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_VIDEO_BOOKMARKS);
+ if (pDialog && pDialog->IsDialogRunning()) pDialog->Close(true);
++ pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_OSD_CHANNELS);
++ if (pDialog && pDialog->IsDialogRunning()) pDialog->Close(true);
++ pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_OSD_GUIDE);
++ if (pDialog && pDialog->IsDialogRunning()) pDialog->Close(true);
++ pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_OSD_DIRECTOR);
++ if (pDialog && pDialog->IsDialogRunning()) pDialog->Close(true);
++ pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_OSD_CUTTER);
++ if (pDialog && pDialog->IsDialogRunning()) pDialog->Close(true);
+ pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_OSD_TELETEXT);
+ if (pDialog && pDialog->IsDialogRunning()) pDialog->Close(true);
+ }
+diff --git a/xbmc/video/dialogs/GUIDialogVideoSettings.cpp b/xbmc/video/dialogs/GUIDialogVideoSettings.cpp
+index 3c35e66..aa082a3 100644
+--- a/xbmc/video/dialogs/GUIDialogVideoSettings.cpp
++++ b/xbmc/video/dialogs/GUIDialogVideoSettings.cpp
+@@ -33,8 +33,10 @@
+ #include "dialogs/GUIDialogYesNo.h"
+ #include "settings/Settings.h"
+ #include "addons/Skin.h"
++#include "pvr/PVRManager.h"
+
+ using namespace std;
++using namespace PVR;
+
+ CGUIDialogVideoSettings::CGUIDialogVideoSettings(void)
+ : CGUIDialogSettings(WINDOW_DIALOG_VIDEO_OSD_SETTINGS, "VideoOSDSettings.xml")
+@@ -111,6 +113,7 @@ void CGUIDialogVideoSettings::CreateSettings()
+ entries.push_back(make_pair(VS_INTERLACEMETHOD_VDPAU_INVERSE_TELECINE , 16314));
+ entries.push_back(make_pair(VS_INTERLACEMETHOD_DXVA_BOB , 16320));
+ entries.push_back(make_pair(VS_INTERLACEMETHOD_DXVA_BEST , 16321));
++ entries.push_back(make_pair(VS_INTERLACEMETHOD_AUTO_ION , 16325));
+
+ /* remove unsupported methods */
+ for(vector<pair<int, int> >::iterator it = entries.begin(); it != entries.end();)
+@@ -241,6 +244,9 @@ void CGUIDialogVideoSettings::OnSettingChanged(SettingInfo &setting)
+ {
+ EnableSettings(VIDEO_SETTINGS_INTERLACEMETHOD, g_settings.m_currentVideoSettings.m_DeinterlaceMode != VS_DEINTERLACEMODE_OFF);
+ }
++
++ if (g_PVRManager.IsPlayingRadio() || g_PVRManager.IsPlayingTV())
++ g_PVRManager.TriggerSaveChannelSettings();
+ }
+
+ CStdString CGUIDialogVideoSettings::FormatInteger(float value, float minimum)
+diff --git a/xbmc/video/windows/GUIWindowFullScreen.cpp b/xbmc/video/windows/GUIWindowFullScreen.cpp
+index 4d77a03..f34ea6a 100644
+--- a/xbmc/video/windows/GUIWindowFullScreen.cpp
++++ b/xbmc/video/windows/GUIWindowFullScreen.cpp
+@@ -41,6 +41,7 @@
+ #include "dialogs/GUIDialogKaiToast.h"
+ #include "guilib/GUISliderControl.h"
+ #include "settings/Settings.h"
++#include "guilib/GUISelectButtonControl.h"
+ #include "FileItem.h"
+ #include "video/VideoReferenceClock.h"
+ #include "settings/AdvancedSettings.h"
+@@ -52,17 +53,23 @@
+ #include "utils/TimeUtils.h"
+ #include "XBDateTime.h"
+ #include "input/ButtonTranslator.h"
++#include "pvr/PVRManager.h"
++#include "pvr/channels/PVRChannelGroupsContainer.h"
+ #include "windowing/WindowingFactory.h"
+
+ #include <stdio.h>
++
+ #ifdef __APPLE__
+ #include "linux/LinuxResourceCounter.h"
+ #endif
+
++using namespace PVR;
++
+ #define BLUE_BAR 0
+ #define LABEL_ROW1 10
+ #define LABEL_ROW2 11
+ #define LABEL_ROW3 12
++#define CONTROL_GROUP_CHOOSER 503
+
+ #define BTN_OSD_VIDEO 13
+ #define BTN_OSD_AUDIO 14
+@@ -124,6 +131,7 @@ CGUIWindowFullScreen::CGUIWindowFullScreen(void)
+ m_dwShowViewModeTimeout = 0;
+ m_bShowCurrentTime = false;
+ m_subsLayout = NULL;
++ m_bGroupSelectShow = false;
+ m_sliderAction = 0;
+ // audio
+ // - language
+@@ -205,36 +213,52 @@ bool CGUIWindowFullScreen::OnAction(const CAction &action)
+ break;
+
+ case ACTION_STEP_BACK:
+- if (m_timeCodePosition > 0)
+- SeekToTimeCodeStamp(SEEK_RELATIVE, SEEK_BACKWARD);
++ if (!g_application.CurrentFileItem().HasPVRChannelInfoTag())
++ {
++ if (m_timeCodePosition > 0)
++ SeekToTimeCodeStamp(SEEK_RELATIVE, SEEK_BACKWARD);
++ else
++ g_application.m_pPlayer->Seek(false, false);
++ }
+ else
+- g_application.m_pPlayer->Seek(false, false);
++ SeekTV(false, false);
+ return true;
+- break;
+
+ case ACTION_STEP_FORWARD:
+- if (m_timeCodePosition > 0)
+- SeekToTimeCodeStamp(SEEK_RELATIVE, SEEK_FORWARD);
++ if (!g_application.CurrentFileItem().HasPVRChannelInfoTag())
++ {
++ if (m_timeCodePosition > 0)
++ SeekToTimeCodeStamp(SEEK_RELATIVE, SEEK_FORWARD);
++ else
++ g_application.m_pPlayer->Seek(true, false);
++ }
+ else
+- g_application.m_pPlayer->Seek(true, false);
++ SeekTV(true, false);
+ return true;
+- break;
+
+ case ACTION_BIG_STEP_BACK:
+- if (m_timeCodePosition > 0)
+- SeekToTimeCodeStamp(SEEK_RELATIVE, SEEK_BACKWARD);
++ if (!g_application.CurrentFileItem().HasPVRChannelInfoTag())
++ {
++ if (m_timeCodePosition > 0)
++ SeekToTimeCodeStamp(SEEK_RELATIVE, SEEK_BACKWARD);
++ else
++ g_application.m_pPlayer->Seek(false, true);
++ }
+ else
+- g_application.m_pPlayer->Seek(false, true);
++ SeekTV(false, true);
+ return true;
+- break;
+
+ case ACTION_BIG_STEP_FORWARD:
+- if (m_timeCodePosition > 0)
+- SeekToTimeCodeStamp(SEEK_RELATIVE, SEEK_FORWARD);
++ if (!g_application.CurrentFileItem().HasPVRChannelInfoTag())
++ {
++ if (m_timeCodePosition > 0)
++ SeekToTimeCodeStamp(SEEK_RELATIVE, SEEK_FORWARD);
++ else
++ g_application.m_pPlayer->Seek(true, true);
++ }
+ else
+- g_application.m_pPlayer->Seek(true, true);
++ SeekTV(true, true);
+ return true;
+- break;
+
+ case ACTION_NEXT_SCENE:
+ if (g_application.m_pPlayer->SeekScene(true))
+@@ -284,6 +308,7 @@ bool CGUIWindowFullScreen::OnAction(const CAction &action)
+ CGUIDialogFullScreenInfo* pDialog = (CGUIDialogFullScreenInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_FULLSCREEN_INFO);
+ if (pDialog)
+ {
++ CFileItem item(g_application.CurrentFileItem());
+ pDialog->DoModal();
+ return true;
+ }
+@@ -421,15 +446,25 @@ bool CGUIWindowFullScreen::OnAction(const CAction &action)
+ {
+ if (g_application.CurrentFileItem().IsLiveTV())
+ {
+- int channelNr = -1;
++ CPVRChannel channel;
++ int iChannelNumber = -1;
++ g_PVRManager.GetCurrentChannel(channel);
+
+- CStdString strChannel;
+- strChannel.Format("%i", action.GetID() - REMOTE_0);
+- if (CGUIDialogNumeric::ShowAndGetNumber(strChannel, g_localizeStrings.Get(19000)))
+- channelNr = atoi(strChannel.c_str());
++ if (action.GetID() == REMOTE_0)
++ {
++ iChannelNumber = g_PVRManager.GetPreviousChannel();
++ }
++ else
++ {
++ int autoCloseTime = g_guiSettings.GetBool("pvrplayback.switchautoclose") ? 1500 : 0;
++ CStdString strChannel;
++ strChannel.Format("%i", action.GetID() - REMOTE_0);
++ if (CGUIDialogNumeric::ShowAndGetNumber(strChannel, g_localizeStrings.Get(19000), autoCloseTime) || autoCloseTime)
++ iChannelNumber = atoi(strChannel.c_str());
++ }
+
+- if (channelNr > 0)
+- OnAction(CAction(ACTION_CHANNEL_SWITCH, (float)channelNr));
++ if (iChannelNumber > 0 && iChannelNumber != channel.ChannelNumber())
++ OnAction(CAction(ACTION_CHANNEL_SWITCH, (float)iChannelNumber));
+ }
+ else
+ {
+@@ -464,6 +499,18 @@ bool CGUIWindowFullScreen::OnAction(const CAction &action)
+ }
+ return true;
+ break;
++ case ACTION_SHOW_PLAYLIST:
++ {
++ CFileItem item(g_application.CurrentFileItem());
++ if (item.HasPVRChannelInfoTag())
++ g_windowManager.ActivateWindow(WINDOW_DIALOG_PVR_OSD_CHANNELS);
++ else if (item.HasVideoInfoTag())
++ g_windowManager.ActivateWindow(WINDOW_VIDEO_PLAYLIST);
++ else if (item.HasMusicInfoTag())
++ g_windowManager.ActivateWindow(WINDOW_MUSIC_PLAYLIST);
++ }
++ return true;
++ break;
+ case ACTION_ZOOM_IN:
+ {
+ g_settings.m_currentVideoSettings.m_CustomZoomAmount += 0.01f;
+@@ -604,6 +651,7 @@ bool CGUIWindowFullScreen::OnAction(const CAction &action)
+ default:
+ break;
+ }
++
+ return CGUIWindow::OnAction(action);
+ }
+
+@@ -638,7 +686,10 @@ void CGUIWindowFullScreen::OnWindowLoaded()
+ pLabel->SetVisible(true);
+ pLabel->SetLabel("$INFO(VIDEOPLAYER.TIME) / $INFO(VIDEOPLAYER.DURATION)");
+ }
++
+ m_showCodec.Parse("player.showcodec", GetID());
++
++ FillInTVGroups();
+ }
+
+ bool CGUIWindowFullScreen::OnMessage(CGUIMessage& message)
+@@ -657,6 +708,7 @@ bool CGUIWindowFullScreen::OnMessage(CGUIMessage& message)
+ g_infoManager.SetShowInfo(false);
+ g_infoManager.SetShowCodec(false);
+ m_bShowCurrentTime = false;
++ m_bGroupSelectShow = false;
+ g_infoManager.SetDisplayAfterSeek(0); // Make sure display after seek is off.
+
+ // switch resolution
+@@ -704,6 +756,14 @@ bool CGUIWindowFullScreen::OnMessage(CGUIMessage& message)
+ if (pDialog) pDialog->Close(true);
+ pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_FULLSCREEN_INFO);
+ if (pDialog) pDialog->Close(true);
++ pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_OSD_CHANNELS);
++ if (pDialog) pDialog->Close(true);
++ pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_OSD_GUIDE);
++ if (pDialog) pDialog->Close(true);
++ pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_OSD_DIRECTOR);
++ if (pDialog) pDialog->Close(true);
++ pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_OSD_CUTTER);
++ if (pDialog) pDialog->Close(true);
+
+ FreeResources(true);
+
+@@ -727,6 +787,59 @@ bool CGUIWindowFullScreen::OnMessage(CGUIMessage& message)
+
+ return true;
+ }
++ case GUI_MSG_CLICKED:
++ {
++ unsigned int iControl = message.GetSenderId();
++ if (iControl == CONTROL_GROUP_CHOOSER && g_PVRManager.IsStarted())
++ {
++ // Get the currently selected label of the Select button
++ CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), iControl);
++ OnMessage(msg);
++ CStdString strLabel = msg.GetLabel();
++
++ CPVRChannel playingChannel;
++ if (g_PVRManager.GetCurrentChannel(playingChannel))
++ {
++ CPVRChannelGroup *selectedGroup = (CPVRChannelGroup *) g_PVRChannelGroups->Get(playingChannel.IsRadio())->GetByName(strLabel);
++ if (selectedGroup)
++ {
++ g_PVRManager.SetPlayingGroup(selectedGroup);
++ CLog::Log(LOGDEBUG, "%s - switched to group '%s'", __FUNCTION__, selectedGroup->GroupName().c_str());
++
++ if (!selectedGroup->IsGroupMember(playingChannel))
++ {
++ CLog::Log(LOGDEBUG, "%s - channel '%s' is not a member of '%s', switching to channel 1 of the new group", __FUNCTION__, playingChannel.ChannelName().c_str(), selectedGroup->GroupName().c_str());
++ const CPVRChannel *switchChannel = selectedGroup->GetByChannelNumber(1);
++ if (switchChannel)
++ OnAction(CAction(ACTION_CHANNEL_SWITCH, (float) switchChannel->ChannelNumber()));
++ else
++ {
++ CLog::Log(LOGERROR, "%s - cannot find channel '1' in group %s", __FUNCTION__, selectedGroup->GroupName().c_str());
++ g_application.getApplicationMessenger().MediaStop(false);
++ }
++ }
++ }
++ else
++ {
++ CLog::Log(LOGERROR, "%s - could not switch to group '%s'", __FUNCTION__, selectedGroup->GroupName().c_str());
++ g_application.getApplicationMessenger().MediaStop(false);
++ }
++ }
++ else
++ {
++ CLog::Log(LOGERROR, "%s - cannot find the current channel", __FUNCTION__);
++ g_application.getApplicationMessenger().MediaStop(false);
++ }
++
++ // hide the control and reset focus
++ m_bGroupSelectShow = false;
++ SET_CONTROL_HIDDEN(CONTROL_GROUP_CHOOSER);
++// SET_CONTROL_FOCUS(0, 0);
++
++ return true;
++ }
++ break;
++ }
+ case GUI_MSG_SETFOCUS:
+ case GUI_MSG_LOSTFOCUS:
+ if (message.GetSenderId() != WINDOW_FULLSCREEN_VIDEO) return true;
+@@ -918,6 +1031,7 @@ void CGUIWindowFullScreen::FrameMove()
+ SET_CONTROL_VISIBLE(LABEL_ROW2);
+ SET_CONTROL_VISIBLE(LABEL_ROW3);
+ SET_CONTROL_VISIBLE(BLUE_BAR);
++ SET_CONTROL_HIDDEN(CONTROL_GROUP_CHOOSER);
+ }
+ else if (m_timeCodeShow)
+ {
+@@ -925,6 +1039,15 @@ void CGUIWindowFullScreen::FrameMove()
+ SET_CONTROL_HIDDEN(LABEL_ROW2);
+ SET_CONTROL_HIDDEN(LABEL_ROW3);
+ SET_CONTROL_VISIBLE(BLUE_BAR);
++ SET_CONTROL_HIDDEN(CONTROL_GROUP_CHOOSER);
++ }
++ else if (m_bGroupSelectShow)
++ {
++ SET_CONTROL_HIDDEN(LABEL_ROW1);
++ SET_CONTROL_HIDDEN(LABEL_ROW2);
++ SET_CONTROL_HIDDEN(LABEL_ROW3);
++ SET_CONTROL_HIDDEN(BLUE_BAR);
++ SET_CONTROL_VISIBLE(CONTROL_GROUP_CHOOSER);
+ }
+ else
+ {
+@@ -932,6 +1055,7 @@ void CGUIWindowFullScreen::FrameMove()
+ SET_CONTROL_HIDDEN(LABEL_ROW2);
+ SET_CONTROL_HIDDEN(LABEL_ROW3);
+ SET_CONTROL_HIDDEN(BLUE_BAR);
++ SET_CONTROL_HIDDEN(CONTROL_GROUP_CHOOSER);
+ }
+ }
+
+@@ -1058,6 +1182,23 @@ void CGUIWindowFullScreen::SeekToTimeCodeStamp(SEEK_TYPE type, SEEK_DIRECTION di
+ m_timeCodeShow = false;
+ }
+
++void CGUIWindowFullScreen::SeekTV(bool bPlus, bool bLargeStep)
++{
++ if (bLargeStep)
++ {
++ if (bPlus)
++ OnAction(CAction(ACTION_NEXT_ITEM));
++ else
++ OnAction(CAction(ACTION_PREV_ITEM));
++ return;
++ }
++ else if (!bLargeStep)
++ {
++ ChangetheTVGroup(bPlus);
++ return;
++ }
++}
++
+ double CGUIWindowFullScreen::GetTimeCodeStamp()
+ {
+ // Convert the timestamp into an integer
+@@ -1121,6 +1262,64 @@ void CGUIWindowFullScreen::OnSliderChange(void *data, CGUISliderControl *slider)
+ }
+ }
+
++void CGUIWindowFullScreen::FillInTVGroups()
++{
++ if (!g_PVRManager.IsStarted())
++ return;
++
++ CGUIMessage msgReset(GUI_MSG_LABEL_RESET, GetID(), CONTROL_GROUP_CHOOSER);
++ g_windowManager.SendMessage(msgReset);
++
++ const CPVRChannelGroups *groups = g_PVRChannelGroups->Get(g_PVRManager.IsPlayingRadio());
++ const CPVRChannelGroup *currentGroup = g_PVRManager.GetPlayingGroup(false);
++
++ int iListGroupPtr = 0;
++ int iCurrentGroupPtr = 0;
++ for (int iGroupPtr = 0; iGroupPtr < (int) groups->size(); iGroupPtr++)
++ {
++ /* skip empty groups */
++ if (groups->at(iGroupPtr)->Size() == 0)
++ continue;
++
++ if (*groups->at(iGroupPtr) == *currentGroup)
++ iCurrentGroupPtr = iListGroupPtr;
++
++ CGUIMessage msg(GUI_MSG_LABEL_ADD, GetID(), CONTROL_GROUP_CHOOSER, iListGroupPtr++);
++ msg.SetLabel(groups->at(iGroupPtr)->GroupName());
++ g_windowManager.SendMessage(msg);
++ }
++ CGUIMessage msgSel(GUI_MSG_ITEM_SELECT, GetID(), CONTROL_GROUP_CHOOSER, iCurrentGroupPtr);
++ g_windowManager.SendMessage(msgSel);
++}
++
++void CGUIWindowFullScreen::ChangetheTVGroup(bool next)
++{
++ if (!g_PVRManager.IsStarted())
++ return;
++
++ CGUISelectButtonControl* pButton = (CGUISelectButtonControl*)GetControl(CONTROL_GROUP_CHOOSER);
++ if (!pButton)
++ return;
++
++ if (!m_bGroupSelectShow)
++ {
++ SET_CONTROL_VISIBLE(CONTROL_GROUP_CHOOSER);
++ SET_CONTROL_FOCUS(CONTROL_GROUP_CHOOSER, 0);
++
++ // fire off an event that we've pressed this button...
++ OnAction(CAction(ACTION_SELECT_ITEM));
++
++ m_bGroupSelectShow = true;
++ }
++ else
++ {
++ if (next)
++ pButton->OnRight();
++ else
++ pButton->OnLeft();
++ }
++}
++
+ void CGUIWindowFullScreen::ToggleOSD()
+ {
+ CGUIDialogVideoOSD *pOSD = (CGUIDialogVideoOSD *)g_windowManager.GetWindow(WINDOW_DIALOG_VIDEO_OSD);
+diff --git a/xbmc/video/windows/GUIWindowFullScreen.h b/xbmc/video/windows/GUIWindowFullScreen.h
+index 1f44df3..222a7de 100644
+--- a/xbmc/video/windows/GUIWindowFullScreen.h
++++ b/xbmc/video/windows/GUIWindowFullScreen.h
+@@ -42,6 +42,7 @@ public:
+ virtual void Render();
+ virtual void OnWindowLoaded();
+ void ChangetheTimeCode(int remote);
++ void ChangetheTVGroup(bool next);
+
+ virtual void OnSliderChange(void *data, CGUISliderControl *slider);
+ protected:
+@@ -49,7 +50,9 @@ protected:
+
+ private:
+ void RenderTTFSubtitles();
++ void SeekTV(bool bPlus, bool bLargeStep);
+ void SeekChapter(int iChapter);
++ void FillInTVGroups();
+ void ToggleOSD();
+
+ enum SEEK_TYPE { SEEK_ABSOLUTE, SEEK_RELATIVE };
+@@ -82,6 +85,7 @@ private:
+
+ bool m_bShowCurrentTime;
+
++ bool m_bGroupSelectShow;
+ bool m_timeCodeShow;
+ unsigned int m_timeCodeTimeout;
+ int m_timeCodeStamp[6];
+diff --git a/xbmc/video/windows/GUIWindowVideoBase.cpp b/xbmc/video/windows/GUIWindowVideoBase.cpp
+index 07c0702..7f8d9dd 100644
+--- a/xbmc/video/windows/GUIWindowVideoBase.cpp
++++ b/xbmc/video/windows/GUIWindowVideoBase.cpp
+@@ -61,6 +61,9 @@
+ #include "utils/StringUtils.h"
+ #include "utils/log.h"
+ #include "utils/FileUtils.h"
++#include "interfaces/AnnouncementManager.h"
++#include "pvr/PVRManager.h"
++#include "pvr/recordings/PVRRecordings.h"
+ #include "utils/URIUtils.h"
+ #include "GUIUserMessages.h"
+ #include "addons/Skin.h"
+@@ -73,6 +76,7 @@ using namespace PLAYLIST;
+ using namespace VIDEODATABASEDIRECTORY;
+ using namespace VIDEO;
+ using namespace ADDON;
++using namespace PVR;
+
+ #define CONTROL_BTNVIEWASICONS 2
+ #define CONTROL_BTNSORTBY 3
+@@ -1350,6 +1354,65 @@ bool CGUIWindowVideoBase::OnPlayMedia(int iItem)
+ }
+ CLog::Log(LOGDEBUG, "%s %s", __FUNCTION__, item.GetPath().c_str());
+
++ if (item.GetPath().Left(17) == "pvr://recordings/")
++ {
++ if (!g_PVRManager.IsStarted())
++ return false;
++
++ /* For recordings we check here for a available stream URL */
++ CPVRRecording *tag = g_PVRRecordings->GetByPath(item.GetPath());
++ if (tag && !tag->m_strStreamURL.IsEmpty())
++ {
++ CStdString stream = tag->m_strStreamURL;
++
++ /* Isolate the folder from the filename */
++ size_t found = stream.find_last_of("/");
++ if (found == CStdString::npos)
++ found = stream.find_last_of("\\");
++
++ if (found != CStdString::npos)
++ {
++ /* Check here for asterix at the begin of the filename */
++ if (stream[found+1] == '*')
++ {
++ /* Create a "stack://" url with all files matching the extension */
++ CStdString ext = URIUtils::GetExtension(stream);
++ CStdString dir = stream.substr(0, found).c_str();
++
++ CFileItemList items;
++ CDirectory::GetDirectory(dir, items);
++ items.Sort(SORT_METHOD_FILE ,SORT_ORDER_ASC);
++
++ vector<int> stack;
++ for (int i = 0; i < items.Size(); ++i)
++ {
++ if (URIUtils::GetExtension(items[i]->GetPath()) == ext)
++ stack.push_back(i);
++ }
++
++ if (stack.size() > 0)
++ {
++ /* If we have a stack change the path of the item to it */
++ CStackDirectory dir;
++ CStdString stackPath = dir.ConstructStackPath(items, stack);
++ item.SetPath(stackPath);
++ }
++ }
++ else
++ {
++ /* If no asterix is present play only the given stream URL */
++ item.SetPath(stream);
++ }
++ }
++ else
++ {
++ CLog::Log(LOGERROR, "CGUIWindowTV: Can't open recording, no valid filename!");
++ CGUIDialogOK::ShowAndGetInput(19033,0,19036,0);
++ return false;
++ }
++ }
++ }
++
+ PlayMovie(&item);
+
+ return true;
+diff --git a/xbmc/win32/stdbool.h b/xbmc/win32/stdbool.h
+new file mode 100644
+index 0000000..87a5575
+--- /dev/null
++++ b/xbmc/win32/stdbool.h
+@@ -0,0 +1,53 @@
++/* Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
++
++This file is part of GCC.
++
++GCC is free software; you can redistribute 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.
++
++GCC is distributed in the hope that it will be useful,
++but WITHOUT ANY WARRANTY; without even the implied warranty of
++MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++GNU General Public License for more details.
++
++You should have received a copy of the GNU General Public License
++along with GCC; see the file COPYING. If not, write to
++the Free Software Foundation, 51 Franklin Street, Fifth Floor,
++Boston, MA 02110-1301, USA. */
++
++/* As a special exception, if you include this header file into source
++ files compiled by GCC, this header file does not by itself cause
++ the resulting executable to be covered by the GNU General Public
++ License. This exception does not however invalidate any other
++ reasons why the executable file might be covered by the GNU General
++ Public License. */
++
++/*
++* ISO C Standard: 7.16 Boolean type and values <stdbool.h>
++*/
++
++#ifndef _STDBOOL_H
++#define _STDBOOL_H
++
++#ifndef __cplusplus
++
++#define bool _Bool
++#define true 1
++#define false 0
++
++#else /* __cplusplus */
++
++/* Supporting <stdbool.h> in C++ is a GCC extension. */
++#define _Bool bool
++#define bool bool
++#define false false
++#define true true
++
++#endif /* __cplusplus */
++
++/* Signal that all the definitions are present. */
++#define __bool_true_false_are_defined 1
++
++#endif /* stdbool.h */
+diff --git a/xbmc/windowing/X11/WinSystemX11GLES.cpp b/xbmc/windowing/X11/WinSystemX11GLES.cpp
+index a94906d..12fd010 100644
+--- a/xbmc/windowing/X11/WinSystemX11GLES.cpp
++++ b/xbmc/windowing/X11/WinSystemX11GLES.cpp
+@@ -135,20 +135,6 @@ bool CWinSystemX11GLES::DestroyWindowSystem()
+ m_eglSurface = NULL;
+ }
+
+- // Needed???
+- if (m_eglWindow)
+- {
+- XDestroyWindow(m_dpy, m_eglWindow);
+- m_eglWindow = 0;
+- }
+-
+- // Needed???
+- if (m_wmWindow)
+- {
+- XDestroyWindow(m_dpy, m_wmWindow);
+- m_wmWindow = 0;
+- }
+-
+ if (m_eglDisplay)
+ {
+ eglTerminate(m_eglDisplay);
+diff --git a/xbmc/windows/GUIMediaWindow.cpp b/xbmc/windows/GUIMediaWindow.cpp
+index 0af0748..3da19cd 100644
+--- a/xbmc/windows/GUIMediaWindow.cpp
++++ b/xbmc/windows/GUIMediaWindow.cpp
+@@ -825,7 +825,8 @@ bool CGUIMediaWindow::Update(const CStdString &strDirectory)
+ if (!bSelectedFound)
+ m_viewControl.SetSelectedItem(0);
+
+- m_history.AddPath(m_vecItems->GetPath());
++ if (iWindow != WINDOW_PVR || (iWindow == WINDOW_PVR && m_vecItems->GetPath().Left(17) == "pvr://recordings/"))
++ m_history.AddPath(m_vecItems->GetPath());
+
+ //m_history.DumpPathHistory();
+
+diff --git a/xbmc/windows/GUIWindowSystemInfo.cpp b/xbmc/windows/GUIWindowSystemInfo.cpp
+index dcd1fef..b5d91f7 100644
+--- a/xbmc/windows/GUIWindowSystemInfo.cpp
++++ b/xbmc/windows/GUIWindowSystemInfo.cpp
+@@ -35,9 +35,10 @@
+ #define CONTROL_BT_NETWORK 96
+ #define CONTROL_BT_VIDEO 97
+ #define CONTROL_BT_HARDWARE 98
++#define CONTROL_BT_PVR 99
+
+ #define CONTROL_START CONTROL_BT_STORAGE
+-#define CONTROL_END CONTROL_BT_HARDWARE
++#define CONTROL_END CONTROL_BT_PVR
+
+ CGUIWindowSystemInfo::CGUIWindowSystemInfo(void)
+ :CGUIWindow(WINDOW_SYSTEM_INFORMATION, "SettingsSystemInfo.xml")
+@@ -151,6 +152,22 @@ void CGUIWindowSystemInfo::FrameMove()
+ SetControlLabel(i++, "%s: %s", 22012, SYSTEM_TOTAL_MEMORY);
+ SetControlLabel(i++, "%s: %s", 158, SYSTEM_FREE_MEMORY);
+ }
++ else if(m_section == CONTROL_BT_PVR)
++ {
++ SET_CONTROL_LABEL(40,g_localizeStrings.Get(19166));
++ int i = 2;
++
++ SetControlLabel(i++, "%s: %s", 19120, PVR_BACKEND_NUMBER);
++ i++; // empty line
++ SetControlLabel(i++, "%s: %s", 19012, PVR_BACKEND_NAME);
++ SetControlLabel(i++, "%s: %s", 19114, PVR_BACKEND_VERSION);
++ SetControlLabel(i++, "%s: %s", 19115, PVR_BACKEND_HOST);
++ SetControlLabel(i++, "%s: %s", 19116, PVR_BACKEND_DISKSPACE);
++ SetControlLabel(i++, "%s: %s", 19019, PVR_BACKEND_CHANNELS);
++ SetControlLabel(i++, "%s: %s", 19163, PVR_BACKEND_RECORDINGS);
++ SetControlLabel(i++, "%s: %s", 19025, PVR_BACKEND_TIMERS);
++ }
++
+ CGUIWindow::FrameMove();
+ }
+
diff --git a/xbmc-Dharma-10.1-gcc-4.6-fixes-0.1.patch b/xbmc-Dharma-10.1-gcc-4.6-fixes-0.1.patch
deleted file mode 100644
index c81829f..0000000
--- a/xbmc-Dharma-10.1-gcc-4.6-fixes-0.1.patch
+++ /dev/null
@@ -1,92 +0,0 @@
-From c66099c4d8e6b2d748ca3ddc31ee90b731d0f620 Mon Sep 17 00:00:00 2001
-From: Stephan Raue <stephan@openelec.tv>
-Date: Wed, 30 Mar 2011 14:57:28 +0200
-Subject: [PATCH] dvdplayer: fix build with gcc-4.6. Flags to the Linker must be passed via -Wl,. This fixes ticket #11383
-
-Signed-off-by: Stephan Raue <stephan@openelec.tv>
----
- xbmc/cores/dvdplayer/Codecs/Makefile.in | 18 +++++++++---------
- xbmc/cores/dvdplayer/Codecs/libdvd/Makefile.in | 4 ++--
- 2 files changed, 11 insertions(+), 11 deletions(-)
-
-diff --git a/xbmc/cores/dvdplayer/Codecs/Makefile.in b/xbmc/cores/dvdplayer/Codecs/Makefile.in
-index a7ef1a0..308664a 100644
---- a/xbmc/cores/dvdplayer/Codecs/Makefile.in
-+++ b/xbmc/cores/dvdplayer/Codecs/Makefile.in
-@@ -148,32 +148,32 @@ liba52:
- else
-
- $(SYSDIR)/avutil-50-$(ARCH).so: ffmpeg/libavutil/libavutil.so
-- $(CC) -o $@ $(LDFLAGS) --soname,$@ -Wl,-Bsymbolic \
-+ $(CC) -o $@ $(LDFLAGS) -Wl,--soname,$@ -Wl,-Bsymbolic \
- ffmpeg/libavutil/*.o `cat $(WRAPPER:.o=.def)` $(WRAPPER)
-
- $(SYSDIR)/avcodec-52-$(ARCH).so: $(WRAPPER) ffmpeg/libavcodec/libavcodec.so
-- $(CC) -o $@ $(LDFLAGS) --soname,$@ -Wl,-Bsymbolic \
-+ $(CC) -o $@ $(LDFLAGS) -Wl,--soname,$@ -Wl,-Bsymbolic \
- ffmpeg/libavcodec/*.o ffmpeg/libavcodec/$(ARCH_DIR)/*.o \
- `cat $(WRAPPER:.o=.def)` $(WRAPPER)
-
- $(SYSDIR)/avformat-52-$(ARCH).so: $(WRAPPER) ffmpeg/libavformat/libavformat.so
-- $(CC) -o $@ $(LDFLAGS) --soname,$@ -Wl,-Bsymbolic \
-+ $(CC) -o $@ $(LDFLAGS) -Wl,--soname,$@ -Wl,-Bsymbolic \
- ffmpeg/libavformat/*.o `cat $(WRAPPER:.o=.def)` $(WRAPPER)
-
- ifneq ($(ARCH), arm)
- $(SYSDIR)/swscale-0.6.1-$(ARCH).so: $(WRAPPER) ffmpeg/libswscale/libswscale.so
-- $(CC) -o $@ $(LDFLAGS) --soname,$@ -Wl,-Bsymbolic \
-+ $(CC) -o $@ $(LDFLAGS) -Wl,--soname,$@ -Wl,-Bsymbolic \
- ffmpeg/libswscale/*.o ffmpeg/libswscale/$(ARCH_DIR)/*.o \
- `cat $(WRAPPER:.o=.def)` $(WRAPPER)
- else # No ARM version of swscale available yet.
- $(SYSDIR)/swscale-0.6.1-$(ARCH).so: $(WRAPPER) ffmpeg/libswscale/libswscale.so
-- $(CC) -o $@ $(LDFLAGS) --soname,$@ -Wl,-Bsymbolic \
-+ $(CC) -o $@ $(LDFLAGS) -Wl,--soname,$@ -Wl,-Bsymbolic \
- ffmpeg/libswscale/*.o \
- `cat $(WRAPPER:.o=.def)` $(WRAPPER)
- endif
-
- $(SYSDIR)/postproc-51-$(ARCH).so: $(WRAPPER) ffmpeg/libpostproc/libpostproc.so
-- $(CC) -o $@ $(LDFLAGS) --soname,$@ -Wl,-Bsymbolic \
-+ $(CC) -o $@ $(LDFLAGS) -Wl,--soname,$@ -Wl,-Bsymbolic \
- ffmpeg/libpostproc/*.o `cat $(WRAPPER:.o=.def)` $(WRAPPER)
-
- ffmpeg/libavutil/libavutil.so : ffmpeg;
-@@ -185,17 +185,17 @@ ffmpeg:
- $(MAKE) -C $@
-
- $(SYSDIR)/libdts-$(ARCH).so: $(WRAPPER) libdts/libdts/libdts.a
-- $(CC) -o $@ $(LDFLAGS) --soname,$@ \
-+ $(CC) -o $@ $(LDFLAGS) -Wl,--soname,$@ \
- libdts/libdts/bitstream.o \
- libdts/libdts/downmix.o libdts/libdts/parse.o \
- `cat $(WRAPPER:.o=.def)` $(WRAPPER)
-
- $(SYSDIR)/liba52-$(ARCH).so: $(WRAPPER) liba52/liba52/liba52.la
-- $(CC) -o $@ $(LDFLAGS) --soname,$@ liba52/liba52/.libs/*.o \
-+ $(CC) -o $@ $(LDFLAGS) -Wl,--soname,$@ liba52/liba52/.libs/*.o \
- -Wl`cat $(WRAPPER:.o=.def)` $(WRAPPER)
-
- $(SYSDIR)/libao-$(ARCH).so: $(WRAPPER) liba52/libao/libao.a
-- $(CC) -o $@ $(LDFLAGS) --soname,$@ liba52/libao/libao.a \
-+ $(CC) -o $@ $(LDFLAGS) -Wl,--soname,$@ liba52/libao/libao.a \
- -Wl`cat $(WRAPPER:.o=.def)` $(WRAPPER)
-
- libdts/libdts/libdts.a : libdts;
---- xbmc-10.1/xbmc/cores/dvdplayer/Codecs/libdvd/Makefile.in.orig 2011-03-30 23:13:00.901672912 -0400
-+++ xbmc-10.1/xbmc/cores/dvdplayer/Codecs/libdvd/Makefile.in 2011-03-30 23:13:23.837829699 -0400
-@@ -48,12 +48,12 @@
- else
-
- $(SYSDIR)/libdvdcss-$(ARCH).so: $(WRAPPER) libdvdcss/src/.libs/libdvdcss.a
-- $(CC) -o $@ $(LDFLAGS) --soname,$@ \
-+ $(CC) -o $@ $(LDFLAGS) -Wl,--soname,$@ \
- libdvdcss/src/*.o \
- `cat $(WRAPPER:.o=.def)` $(WRAPPER)
-
- $(SYSDIR)/libdvdnav-$(ARCH).so: $(WRAPPER) $(DVDCSS_A) libdvdnav/obj/libdvdnav.a
-- $(CC) -o $@ $(LDFLAGS) --soname,$@ $(DVDCSS_O) libdvdnav/obj/*.o \
-+ $(CC) -o $@ $(LDFLAGS) -Wl,--soname,$@ $(DVDCSS_O) libdvdnav/obj/*.o \
- `cat $(WRAPPER:.o=.def)` $(WRAPPER)
-
- endif
diff --git a/xbmc.spec b/xbmc.spec
index 7bfce96..38c832b 100644
--- a/xbmc.spec
+++ b/xbmc.spec
@@ -1,11 +1,11 @@
-%global PRERELEASE Eden_rc2
+#global PRERELEASE Eden_rc2
%global DIRVERSION %{version}
# use below for pre-release
#global DIRVERSION %{version}-%{PRERELEASE}
Name: xbmc
Version: 11.0
-Release: 1%{?dist}
+Release: 2%{?dist}
URL: http://www.xbmc.org/
Source0: %{name}-%{DIRVERSION}-patched.tar.xz
@@ -36,14 +36,28 @@ Patch3: xbmc-10-disable-zlib-in-cximage.patch
# functionality, needs to be able fallback internal version
Patch4: xbmc-11.0-hdhomerun.patch
-
-# libpng 1.5 patch from gentoo
-# http://sources.gentoo.org/cgi-bin/viewvc.cgi/gentoo-x86/media-tv/xbmc/files/xbmc-10.1-libpng-1.5.patch
-# following two patches both submitted upstream:
-# http://trac.xbmc.org/ticket/12001
-Patch5: xbmc-10.1-libpng-1.5.patch
-# second libpng 1.5 patch avoids use of uninitialised values
-Patch6: xbmc-11.0-libpng-1.5-fix-plt-trn-get.patch
+# patch pristine Eden source (git tag: 11.0-Eden-r2) against
+# tsp42's back-port of PVR support (including MythTV support)
+# to stable Eden branch, patch created using the following:
+#
+# git clone git://github.com/tsp/xbmc.git xbmc-Eden-pvr
+# cd xbmc-Eden-pvr/
+# git checkout Eden-pvr
+# git remote add main git://github.com/xbmc/xbmc.git
+# git fetch main
+# git diff 11.0-Eden-r2 Eden-pvr > xbmc-11.0-tsp-Eden-pvr.patch
+#
+# (note that hunks within patch that patch ffmpeg needed to be
+# removed, since ffmpeg is removed from original tarball, and other
+# minor tweaks may be needed)
+Patch5: xbmc-11.0-tsp-Eden-pvr.patch
+
+# backport myth-0.25 patch to Eden-PVR from dteirney
+#
+# git clone --branch myth-0.25 https://github.com/dteirney/xbmc.git xbmc-myth-0.25
+# cd xbmc-myth-0.25
+# git diff 00e6c1c > xbmc-11.0-dteirney-myth-0.25.patch
+Patch6: xbmc-11.0-dteirney-myth-0.25.patch
ExcludeArch: ppc64
Buildroot: %{_tmppath}/%{name}-%{version}
@@ -129,8 +143,7 @@ BuildRequires: cwiid-devel
# nfs-utils-lib-devel package currently broken
#BuildRequires: nfs-utils-lib-devel
-# afp build currently broken
-#BuildRequires: afpfs-ng-devel
+BuildRequires: afpfs-ng-devel
# VAAPI currently not working, comment-out
#BuildRequires: libva-freeworld-devel
@@ -192,8 +205,8 @@ forecast functions, together third-party plugins.
%patch2 -p0
#patch3 -p0
%patch4 -p0
-#patch5 -p2
-#patch6 -p1
+%patch5 -p1
+%patch6 -p1
%build
@@ -211,8 +224,8 @@ chmod +x bootstrap
--disable-dvdcss \
--disable-optimizations --disable-debug \
CPPFLAGS="-I/usr/include/ffmpeg" \
-CFLAGS="$RPM_OPT_FLAGS -fPIC -I/usr/include/ffmpeg -D__STDC_CONSTANT_MACROS" \
-CXXFLAGS="$RPM_OPT_FLAGS -fPIC -I/usr/include/ffmpeg -D__STDC_CONSTANT_MACROS" \
+CFLAGS="$RPM_OPT_FLAGS -fPIC -I/usr/include/afpfs-ng/ -I/usr/include/ffmpeg -D__STDC_CONSTANT_MACROS" \
+CXXFLAGS="$RPM_OPT_FLAGS -fPIC -I/usr/include/afpfs-ng/ -I/usr/include/ffmpeg -D__STDC_CONSTANT_MACROS" \
LDFLAGS="-fPIC" \
LIBS="-L%{_libdir}/mysql -lhdhomerun $LIBS" \
ASFLAGS=-fPIC
@@ -269,6 +282,14 @@ rm -rf $RPM_BUILD_ROOT
#%%{_includedir}/xbmc/xbmcclient.h
%changelog
+* Wed May 16 2012 Alex Lancaster <alexlan[AT]fedoraproject org> - 11.0-2
+- Add support for PVR add-ons (backported from tsp's PVR branch to
+ Eden), including MythTV
+- Workaround bug in compiling against afpfs-ng-devel, will hopefully
+ get AirPlay support working (needs testing)
+- Drop references to obsolete patches now in Eden
+- Add patch from dteirney's branch for MythTV 0.25 support
+
* Sun Mar 25 2012 Alex Lancaster <alexlan[AT]fedoraproject org> - 11.0-1
- Update to Eden final 11.0
- Drop libpng 1.5 patches, applied upstream