source: tools/grab/main.c @ 26965

Last change on this file since 26965 was 4935, checked in by nit, 13 years ago

[grab] segfault fix

File size: 18.0 KB
Line 
1/*
2AiO Screengrabber v0.81
3
4written 2006 - 2008 by Seddi
5Contact: seddi@ihad.tv / http://www.ihad.tv
6
7This standalone binary will grab the video-picture convert it from
8yuv to rgb and resize it, if neccesary, to the same size as the framebuffer or
9vice versa. For the DM7025 (Xilleon) and DM800/DM8000 (Broadcom) the video will be
10grabbed directly from the decoder memory.
11It also grabs the framebuffer picture in 32Bit, 16Bit or in 8Bit mode with the
12correct colortable in 8Bit mode from the main graphics memory, because the
13FBIOGETCMAP is buggy on Vulcan/Pallas boxes and didnt give you the correct color
14map.
15Finally it will combine the pixmaps to one final picture by using the framebuffer
16alphamap and save it as bmp, jpeg or png file. So you will get the same picture
17as you can see on your TV Screen.
18
19There are a few command line switches, use "grab -h" to get them listed.
20
21A special Thanx to tmbinc and ghost for the needed decoder memory information and
22the great support.
23
24Feel free to use the code for your own projects. See LICENSE file for details.
25*/
26
27#define GRAB_VERSION "v0.81"
28
29#include <unistd.h>
30#include <fcntl.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <ctype.h>
35#include <sys/types.h>
36#include <sys/stat.h>
37#include <sys/ioctl.h>
38#include <sys/mman.h>
39#include <linux/types.h>
40#include <linux/videodev.h>
41#include <linux/fb.h>
42#include "png.h"
43#include "jpeglib.h"
44
45#define SWAP(x,y)       { x ^= y; y ^= x; x ^= y; }
46
47#define RED565(x)    ((((x) >> (11 )) & 0x1f) << 3)
48#define GREEN565(x)  ((((x) >> (5 )) & 0x3f) << 2)
49#define BLUE565(x)   ((((x) >> (0)) & 0x1f) << 3)
50
51void getvideo(unsigned char *video, int *xres, int *yres);
52void getosd(unsigned char *osd, unsigned char *osd_alpha, int *xres, int *yres);
53void smooth_resize(unsigned char *source, unsigned char *dest, int xsource, int ysource, int xdest, int ydest, int colors);
54void fast_resize(unsigned char *source, unsigned char *dest, int xsource, int ysource, int xdest, int ydest, int colors);
55void (*resize)(unsigned char *source, unsigned char *dest, int xsource, int ysource, int xdest, int ydest, int colors);
56void combine(unsigned char *output, unsigned char *video, unsigned char *osd, unsigned char *osd_alpha, int xres, int yres);
57char* upcase(char* mixedstr);
58
59char pngfile[256]="";
60unsigned char *image_data, bg_red=0, bg_green=0, bg_blue=0;
61static unsigned long image_width, image_height, image_rowbytes;
62static int image_channels;
63
64void convertpng(unsigned char *video)
65{
66        unsigned char *src, r, g, b;
67        unsigned long color, i, row;
68
69        for (row = 0; row < image_height; ++row) {
70                src = image_data + row*image_rowbytes;
71                for (i = 0; i<image_width; i++) {
72                        r = *src++;
73                        g = *src++;
74                        b = *src++;
75                        color = (r << 16) | (g << 8) | b;
76                        memcpy(video + (i*3)+(row*image_rowbytes), &color, 3);
77                }
78        }
79}
80
81int openpng(void)
82{
83        FILE *infile;
84        int rc;
85
86        if (!(infile = fopen(pngfile, "rb"))) {
87                printf("[grab] can't open PNG file [%s]\n", pngfile);
88                return(1);
89        }
90        if ((rc = readpng_init(infile, &image_width, &image_height)) != 0) {
91                switch (rc) {
92                        case 1:
93                                printf("[grab] %s is not a PNG file:\n", pngfile);
94                                fclose(infile);
95                                return(1);
96                        case 2:
97                                printf("[grab] %s has bad IHDR (libpng longjmp)\n", pngfile);
98                                fclose(infile);
99                                return(1);
100                        case 4:
101                                printf("[grab] insufficient memory\n");
102                                fclose(infile);
103                                return(1);
104                        default:
105                                printf("[grab] unknown readpng_init() error\n");
106                                fclose(infile);
107                                return(1);
108                        }
109                }
110                image_data = readpng_get_image(2.2 , &image_channels, &image_rowbytes);
111                readpng_cleanup(0);
112                fclose(infile);
113
114                if (!image_data) {
115                        printf("[grab] unable to decode PNG image\n");
116                        return(1);
117                }
118
119                printf("[grab] image hight = %ld\n", image_height);
120                printf("[grab] image width = %ld\n", image_width);
121                printf("[grab] image channels = %d\n", image_channels);
122                printf("[grab] image rowbytes = %ld\n", image_rowbytes);
123                return(0);
124}
125
126int main(int argc, char **argv) {
127        FILE *pipe;
128        char buf[256];
129
130        printf("[grab] AiO Screengrabber "GRAB_VERSION"\n\n");
131
132        int xres_v,yres_v,xres_o,yres_o,xres,yres,aspect;
133        int c,osd_only,video_only,use_osd_res,width,own_hight,use_png,use_jpg,jpg_quality,no_aspect,use_letterbox;
134
135        // we use fast resize as standard now
136        resize = &fast_resize;
137       
138        osd_only=video_only=use_osd_res=width=own_hight=use_png=use_jpg=no_aspect=use_letterbox=0;
139        jpg_quality=50;
140        aspect=1;
141       
142        unsigned char *video, *osd, *osd_alpha, *output;
143
144        char filename[256];
145        sprintf(filename,"/tmp/screenshot.bmp");
146       
147        // process command line
148        while ((c = getopt (argc, argv, "dhj:lbnopi:r:vf:")) != -1)
149        {
150                switch (c)
151                {
152                        case 'h':
153                        case '?':
154                                printf("Usage: grab [commands] [filename]\n\n");
155                                printf("command:\n");
156                                printf("-f (videofile) png video screenshot\n");
157                                printf("-o only grab osd (framebuffer)\n");
158                                printf("-v only grab video\n");
159                                printf("-d always use osd resolution (good for skinshots)\n");
160                                printf("-n dont correct 16:9 aspect ratio\n");
161                                printf("-r (size) resize to a fixed width, maximum: 1920\n");
162                                printf("-i (size) resize to a fixed hight (only with -r), maximum: 1920\n");
163                                printf("-l always 4:3, create letterbox if 16:9\n");
164                                printf("-b use bicubic picture resize (slow but smooth)\n");
165                                printf("-j (quality) produce jpg files instead of bmp (quality 0-100)\n");     
166                                printf("-p produce png files instead of bmp\n");
167                                printf("-h this help screen\n\n");
168
169                                printf("If no command is given the complete picture will be grabbed.\n");
170                                printf("If no filename is given /tmp/screenshot.[bmp/jpg/png] will be used.\n");
171                                return 0;
172                                break;
173                        case 'o': // OSD only
174                                osd_only=1;
175                                video_only=0;   
176                                break;
177                        case 'v': // Video only
178                                video_only=1;
179                                osd_only=0;
180                                break;
181                        case 'd': // always use OSD resolution
182                                use_osd_res=1;
183                                no_aspect=1;
184                                break;
185                        case 'r': // use given resolution
186                                width=atoi(optarg);
187                                if (width > 1920)
188                                {
189                                        printf("[grab] Error: -r (size) ist limited to 1920 pixel !\n");
190                                        return 1;
191                                }
192                                break;
193                        case 'i': // use given resolution
194                                own_hight=atoi(optarg);
195                                if (own_hight > 1920)
196                                {
197                                        printf("[grab] Error: -i (size) ist limited to 1920 pixel !\n");
198                                        return 1;
199                                }
200                                break;
201                        case 'l': // create letterbox
202                                use_letterbox=1;
203                                break;
204                        case 'b': // use bicubic resizing
205                                resize = &smooth_resize;
206                                break;                 
207                        case 'p': // use png file format
208                                use_png=1;
209                                use_jpg=0;     
210                                sprintf(filename,"/tmp/screenshot.png");
211                                break;
212                        case 'f':
213                                sprintf(pngfile, "%s", optarg);
214                                break;
215                        case 'j': // use jpg file format
216                                use_jpg=1;
217                                use_png=0;
218                                jpg_quality=atoi(optarg);
219                                sprintf(filename,"/tmp/screenshot.jpg");
220                                break;
221                        case 'n':
222                                no_aspect=1;
223                                break;
224                }
225        }
226        if (optind < argc) // filename
227                sprintf(filename,"%s",argv[optind]);
228
229        int mallocsize=1920*1080;
230       
231        video = (unsigned char *)malloc(mallocsize*3);
232        osd = (unsigned char *)malloc(mallocsize*3);   
233        osd_alpha = (unsigned char *)malloc(mallocsize);       
234
235        output = (unsigned char *)malloc(mallocsize*3);
236
237        // get osd
238        if (!video_only)
239                getosd(osd,osd_alpha,&xres_o,&yres_o);
240       
241        // get video
242        if (!osd_only)
243                getvideo(video,&xres_v,&yres_v);
244       
245        pipe=popen("cat /proc/stb/vmpeg/0/aspect","r");
246        while (fgets(buf,sizeof(buf),pipe))
247                sscanf(buf,"%x",&aspect);
248        pclose(pipe);
249       
250        // resizing
251        if (video_only)
252        {
253                xres=xres_v;
254                yres=yres_v;
255        } else if (osd_only)
256        {
257                xres=xres_o;
258                yres=yres_o;
259        } else if (xres_o == xres_v && yres_o == yres_v)
260        {
261                xres=xres_v;
262                yres=yres_v;
263        } else
264        {
265                if (xres_v > xres_o && !use_osd_res && (width == 0 || width > xres_o))
266                {
267                        // resize osd to video size
268                        printf("[grab] Resizing OSD to %d x %d ...\n",xres_v,yres_v);   
269                        resize(osd,output,xres_o,yres_o,xres_v,yres_v,3);
270                        memcpy(osd,output,xres_v*yres_v*3);
271                        resize(osd_alpha,output,xres_o,yres_o,xres_v,yres_v,1);
272                        memcpy(osd_alpha,output,xres_v*yres_v);
273                        xres=xres_v;
274                        yres=yres_v;
275                } else
276                {
277                        // resize video to osd size
278                        printf("[grab] Resizing Video to %d x %d ...\n",xres_o,yres_o);
279                        resize(video,output,xres_v,yres_v,xres_o,yres_o,3);
280                        memcpy(video,output,xres_o*yres_o*3);
281                        xres=xres_o;
282                        yres=yres_o;
283                }       
284        }
285       
286
287        // merge video and osd if neccessary
288        if (osd_only)
289                memcpy(output,osd,xres*yres*3);
290        else if (video_only)
291                memcpy(output,video,xres*yres*3);
292        else
293        {
294                printf("[grab] Merge Video with Framebuffer ...\n");
295                combine(output,video,osd,osd_alpha,xres,yres);
296        }
297
298       
299        // resize to specific width ?
300        if (width)
301        {
302                if (own_hight == 0)
303                        own_hight = yres*width/xres;
304                printf("[grab] Resizing Screenshot to %d x %d ...\n",width,own_hight);
305                resize(output,video,xres,yres,width,(own_hight),3);
306                yres=own_hight;
307                xres=width;
308                memcpy(output,video,xres*yres*3);
309        }
310       
311
312        // correct aspect ratio
313        if (!no_aspect && aspect == 3 && ((float)xres/(float)yres)<1.5)
314        {
315                printf("[grab] Correct aspect ratio to 16:9 ...\n");
316                resize(output,video,xres,yres,xres,yres/1.42,3);
317                yres/=1.42;
318                memcpy(output,video,xres*yres*3);
319        }
320       
321       
322        // use letterbox ?
323        if (use_letterbox && xres*0.8 != yres && xres*0.8 <= 1080)
324        {
325                int yres_neu;
326                yres_neu=xres*0.8;
327                printf("[grab] Create letterbox %d x %d ...\n",xres,yres_neu);         
328                if (yres_neu > yres)
329                {
330                        int ofs;
331                        ofs=(yres_neu-yres)>>1;
332                        memmove(output+ofs*xres*3,output,xres*yres*3);
333                        memset(output,0,ofs*xres*3);
334                        memset(output+ofs*xres*3+xres*yres*3,0,ofs*xres*3);
335                }
336                yres=yres_neu;
337        }
338       
339       
340        // saving picture
341        printf("[grab] Saving %s ...\n",filename);
342        FILE *fd2 = fopen(filename, "wr");
343       
344       
345        if (!use_png && !use_jpg)
346        {
347                // write bmp
348                unsigned char hdr[14 + 40];
349                int i = 0;
350#define PUT32(x) hdr[i++] = ((x)&0xFF); hdr[i++] = (((x)>>8)&0xFF); hdr[i++] = (((x)>>16)&0xFF); hdr[i++] = (((x)>>24)&0xFF);
351#define PUT16(x) hdr[i++] = ((x)&0xFF); hdr[i++] = (((x)>>8)&0xFF);
352#define PUT8(x) hdr[i++] = ((x)&0xFF);
353                PUT8('B'); PUT8('M');
354                PUT32((((xres * yres) * 3 + 3) &~ 3) + 14 + 40);
355                PUT16(0); PUT16(0); PUT32(14 + 40);
356                PUT32(40); PUT32(xres); PUT32(yres);
357                PUT16(1);
358                PUT16(24);
359                PUT32(0); PUT32(0); PUT32(0); PUT32(0); PUT32(0); PUT32(0);
360#undef PUT32
361#undef PUT16
362#undef PUT8
363                fwrite(hdr, 1, i, fd2);
364               
365                int y;
366                for (y=yres-1; y>=0 ; y-=1) {
367                        fwrite(output+(y*xres*3),xres*3,1,fd2);
368                }
369        } else if (use_png)
370        {       
371                // write png
372                png_bytep *row_pointers;
373                png_structp png_ptr;
374                png_infop info_ptr;
375         
376                png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, (png_error_ptr)NULL, (png_error_ptr)NULL);
377                info_ptr = png_create_info_struct(png_ptr);
378                png_init_io(png_ptr, fd2);
379
380                row_pointers=(png_bytep*)malloc(sizeof(png_bytep)*yres);
381
382                int y;
383                for (y=0; y<yres; y++)
384                        row_pointers[y]=output+(y*xres*3);
385               
386                png_set_bgr(png_ptr);
387                png_set_IHDR(png_ptr, info_ptr, xres, yres, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
388                png_write_info(png_ptr, info_ptr);
389                png_write_image(png_ptr, row_pointers);
390                png_write_end(png_ptr, info_ptr);
391                png_destroy_write_struct(&png_ptr, &info_ptr);
392               
393                free(row_pointers);
394        } else
395        {
396                // write jpg
397                int x,y;
398                for (y=0; y<yres; y++)
399                        for (x=0; x<xres; x++)
400                                SWAP(output[x*3+y*xres*3],output[x*3+y*xres*3+2]);
401
402                struct jpeg_compress_struct cinfo;
403                struct jpeg_error_mgr jerr;
404                JSAMPROW row_pointer[1];       
405                int row_stride;         
406                cinfo.err = jpeg_std_error(&jerr);
407
408                jpeg_create_compress(&cinfo);
409                jpeg_stdio_dest(&cinfo, fd2);
410                cinfo.image_width = xres;       
411                cinfo.image_height = yres;
412                cinfo.input_components = 3;     
413                cinfo.in_color_space = JCS_RGB;
414                jpeg_set_defaults(&cinfo);
415                jpeg_set_quality(&cinfo,jpg_quality, TRUE);
416                jpeg_start_compress(&cinfo, TRUE);
417                row_stride = xres * 3;
418                while (cinfo.next_scanline < cinfo.image_height)
419                {
420                        row_pointer[0] = & output[cinfo.next_scanline * row_stride];
421                        (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
422                }
423                jpeg_finish_compress(&cinfo);
424                jpeg_destroy_compress(&cinfo);
425        }
426       
427        fclose(fd2);   
428       
429        // Thats all folks
430        printf("[grab] ... Done !\n");
431       
432        // clean up
433        free(video);
434        free(osd);
435        free(osd_alpha);
436        free(output);
437
438        return 0;
439}
440
441// grabing the video picture
442
443void getvideo(unsigned char *video, int *xres, int *yres)
444{
445        printf("[grab] Grabbing Video ...\n");
446
447        if(openpng() != 0)
448                exit(1);       
449        convertpng(video);
450        *yres=image_height;
451        *xres=image_width;
452}
453
454// grabing the osd picture
455
456void getosd(unsigned char *osd, unsigned char *osd_alpha, int *xres, int *yres)
457{
458        int fb,x,y,pos,pos1,pos2,ofs;
459        unsigned char *lfb;
460        struct fb_fix_screeninfo fix_screeninfo;
461        struct fb_var_screeninfo var_screeninfo;
462       
463        fb=open("/dev/fb/0", O_RDWR);
464        if (fb == -1)
465        {
466                fb=open("/dev/fb0", O_RDWR);
467                if (fb == -1)
468                {
469                        printf("[grab] Framebuffer failed\n");
470                        return;
471                }
472        }
473       
474        if(ioctl(fb, FBIOGET_FSCREENINFO, &fix_screeninfo) == -1)
475        {
476                printf("[grab] Framebuffer: <FBIOGET_FSCREENINFO failed>\n");
477                return;
478        }
479
480        if(ioctl(fb, FBIOGET_VSCREENINFO, &var_screeninfo) == -1)
481        {
482                printf("[grab] Framebuffer: <FBIOGET_VSCREENINFO failed>\n");
483                return;
484        }
485       
486        if(!(lfb = (unsigned char*)mmap(0, fix_screeninfo.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fb, 0)))
487        {
488                printf("[grab] Framebuffer: <Memmapping failed>\n");
489                return;
490        }
491       
492        if ( var_screeninfo.bits_per_pixel == 32 )
493        {
494                printf("[grab] Grabbing 32bit Framebuffer ...\n");
495       
496                // get 32bit framebuffer
497                pos=pos1=pos2=0;
498                ofs=fix_screeninfo.line_length-(var_screeninfo.xres*4);
499               
500                unsigned char *memory; // use additional buffer to speed up especially when using hd skins
501                memory = (unsigned char *)malloc(fix_screeninfo.line_length*var_screeninfo.yres);
502                memcpy(memory,lfb,fix_screeninfo.line_length*var_screeninfo.yres);
503               
504                for (y=0; y < var_screeninfo.yres; y+=1)
505                {
506                        for (x=0; x < var_screeninfo.xres; x+=1)
507                        {
508                                memcpy(osd+pos1,memory+pos2,3);// bgr
509                                pos1+=3;
510                                pos2+=3;
511                                osd_alpha[pos++]=memory[pos2++];// tr
512                        }
513                        pos2+=ofs;
514                }
515               
516                free(memory);
517        } else if ( var_screeninfo.bits_per_pixel == 16 )
518        {
519                printf("[grab] Grabbing 16bit Framebuffer ...\n");
520                unsigned short color;
521               
522                // get 16bit framebuffer
523                pos=pos1=pos2=0;
524                ofs=fix_screeninfo.line_length-(var_screeninfo.xres*2);         
525                for (y=0; y < var_screeninfo.yres; y+=1)
526                {
527                        for (x=0; x < var_screeninfo.xres; x+=1)
528                        {
529                                color = lfb[pos2] << 8 | lfb[pos2+1];
530                                pos2+=2;
531                               
532                                osd[pos1++] = BLUE565(color); // b
533                                osd[pos1++] = GREEN565(color); // g
534                                osd[pos1++] = RED565(color); // r
535                                osd_alpha[pos++]=0x00; // tr - there is no transparency in 16bit mode
536                        }
537                        pos2+=ofs;
538                }
539        }
540        close(fb);
541
542        *xres=var_screeninfo.xres;
543        *yres=var_screeninfo.yres;
544        printf("[grab] ... Framebuffer-Size: %d x %d\n",*xres,*yres);
545}
546
547// bicubic pixmap resizing
548
549void smooth_resize(unsigned char *source, unsigned char *dest, int xsource, int ysource, int xdest, int ydest, int colors)
550{
551        unsigned int xs,ys,xd,yd,dpixel,fx,fy;
552        unsigned int c,tmp_i;
553        int x,y,t,t1;
554        xs=xsource; // x-resolution source
555        ys=ysource; // y-resolution source
556        xd=xdest; // x-resolution destination
557        yd=ydest; // y-resolution destination
558       
559        // get x scale factor, use bitshifting to get rid of floats
560        fx=((xs-1)<<16)/xd;
561
562        // get y scale factor, use bitshifting to get rid of floats
563        fy=((ys-1)<<16)/yd;
564
565        unsigned int sx1[xd],sx2[xd],sy1,sy2;
566       
567        // pre calculating sx1/sx2 for faster resizing
568        for (x=0; x<xd; x++)
569        {
570                // first x source pixel for calculating destination pixel
571                sx1[x]=(fx*x)>>16; //floor()
572
573                // last x source pixel for calculating destination pixel
574                sx2[x]=sx1[x]+(fx>>16);
575                if (fx & 0x7FFF) //ceil()
576                        sx2[x]++;               
577        }
578       
579        // Scale
580        for (y=0; y<yd; y++)
581        {
582
583                // first y source pixel for calculating destination pixel
584                sy1=(fy*y)>>16; //floor()
585
586                // last y source pixel for calculating destination pixel
587                sy2=sy1+(fy>>16);
588                if (fy & 0x7FFF) //ceil()
589                        sy2++;
590
591                for (x=0; x<xd; x++)
592                {
593                        // we do this for every color
594                        for (c=0; c<colors; c++)
595                        {
596                                // calculationg destination pixel
597                                tmp_i=0;
598                                dpixel=0;
599               
600                                for (t1=sy1; t1<sy2; t1++)
601                                {
602                                        for (t=sx1[x]; t<=sx2[x]; t++)
603                                        {
604                                                tmp_i+=(int)source[(t*colors)+c+(t1*xs*colors)];
605                                                dpixel++;
606                                        }
607                                }
608                                // writing calculated pixel into destination pixmap
609                                dest[(x*colors)+c+(y*xd*colors)]=tmp_i/dpixel;
610                        }
611                }
612        }
613}
614
615// "nearest neighbor" pixmap resizing
616
617void fast_resize(unsigned char *source, unsigned char *dest, int xsource, int ysource, int xdest, int ydest, int colors)
618{
619    int x_ratio = (int)((xsource<<16)/xdest) ;
620    int y_ratio = (int)((ysource<<16)/ydest) ;
621
622    int x2, y2, c, i ,j;
623    for (i=0;i<ydest;i++) {
624        for (j=0;j<xdest;j++) {
625            x2 = ((j*x_ratio)>>16) ;
626            y2 = ((i*y_ratio)>>16) ;
627            for (c=0; c<colors; c++)
628                                dest[((i*xdest)+j)*colors + c] = source[((y2*xsource)+x2)*colors + c] ;
629        }               
630    }         
631}
632
633// combining pixmaps by using an alphamap
634
635void combine(unsigned char *output, unsigned char *video, unsigned char *osd, unsigned char *osd_alpha, int xres, int yres)
636{
637        int x,y,pos,pos1;
638       
639        pos=pos1=0;
640        for (y=0; y < yres; y+=1)
641        {
642                for (x=0; x < xres; x+=1)
643                {
644                        output[pos1] =  ( ( video[pos1] * ( 0xFF-osd_alpha[pos] ) ) + ( osd[pos1] * osd_alpha[pos] ) ) >>8;
645                        pos1++;
646                        output[pos1] =  ( ( video[pos1] * ( 0xFF-osd_alpha[pos] ) ) + ( osd[pos1] * osd_alpha[pos] ) ) >>8;
647                        pos1++;
648                        output[pos1] =  ( ( video[pos1] * ( 0xFF-osd_alpha[pos] ) ) + ( osd[pos1] * osd_alpha[pos] ) ) >>8;
649                        pos1++;
650                        pos++;
651                }
652        }
653}
654
655// helpers
656
657char* upcase(char* mixedstr)
658{
659        int j;
660        for (j=0; j< strlen(mixedstr); ++j)
661        {
662                mixedstr[j]=toupper(mixedstr[j]);
663        }
664        return mixedstr;
665}
Note: See TracBrowser for help on using the repository browser.