Launcher, based on PickleLauncher
[mupen64plus-pandora.git] / source / mupen64launcher / src / cprofile.cpp
1 /**
2  *  @section LICENSE
3  *
4  *  PickleLauncher
5  *  Copyright (C) 2010-2011 Scott Smith
6  *
7  *  This program is free software: you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation, either version 3 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  *
20  *  @section LOCATION
21  */
22
23 #include "cprofile.h"
24
25 CProfile::CProfile() : CBase(),
26     LaunchableDirs      (false),
27     LauncherPath        (""),
28     LauncherName        (""),
29     FilePath            (""),
30     TargetApp           (""),
31     ZipFile             (""),
32     EntryFilter         (""),
33     Commands            (),
34     Extensions          (),
35     Entries             (),
36     AlphabeticIndices   (),
37     Minizip             (),
38     Plugins             (),
39     Rom                 ("")
40 {
41     AlphabeticIndices.resize(TOTAL_LETTERS, 0);
42 }
43
44 CProfile::~CProfile()
45 {
46 }
47
48 int8_t CProfile::Load( const string& location, const string& delimiter )
49 {
50     bool            readline;
51     string          line;
52     ifstream        fin;
53
54     fin.open(location.c_str(), ios_base::in);
55
56     if (!fin)
57     {
58         Log( "Error: Failed to open profile\n" );
59         return 1;
60     }
61
62     // Read in the profile
63     if (fin.is_open())
64     {
65         readline = true;
66         while (!fin.eof())
67         {
68             if (readline == true)
69             {
70                 getline(fin,line);
71             }
72
73             if (line.length() > 0)
74             {
75                 // Common options
76                 if (UnprefixString( TargetApp, line, PROFILE_TARGETAPP ) == true)
77                 {
78                     readline = true;
79                 }
80                 else
81                 if (UnprefixString( FilePath, line, PROFILE_FILEPATH ) == true)
82                 {
83                     CheckPath(FilePath);
84                     readline = true;
85                 }
86                 else    // Commands
87                 if (line.at(0) == '<' && line.at(line.length()-1) == '>')
88                 {
89                     if (LoadCmd(fin, line, delimiter))
90                     {
91                         Log( "Error: Loading command from %s\n", location.c_str() );
92                         return 1;
93                     }
94                     readline = false;
95                 }
96                 else    // Extensions
97                 if (line.at(0) == '[' && line.at(line.length()-1) == ']')
98                 {
99                     if (LoadExt(fin, line, delimiter))
100                     {
101                         Log( "Error: Loading extension from %s\n", location.c_str() );
102                         return 1;
103                     }
104                     readline = false;
105                 }
106                 else    // Entries
107                 if (line.at(0) == '{' && line.at(line.length()-1) == '}')
108                 {
109                     if (LoadEntry(fin, line, delimiter))
110                     {
111                         Log( "Error: Loading entry from %s\n", location.c_str() );
112                         return 1;
113                     }
114                     readline = false;
115                 }
116                 else
117                 {
118                     readline = true;
119                 }
120             }
121             else
122             {
123                 readline = true;
124             }
125         }
126         fin.close();
127     }
128     else
129     {
130         Log( "Error: Failed to open profile\n" );
131         return 1;
132     }
133
134     // Load plugins
135     if (LoadPlugins())
136     {
137        Log( "Error: Loading plugin from %s\n", location.c_str() );
138        return 1;
139     }
140
141     // Sanity checks
142     if (FilePath.length()<=0)
143     {
144         Log( "Error: file path was not read from profile\n" );
145         return 1;
146     }
147     if (Extensions.size()<=0)
148     {
149         Log( "Error: no extensions were read from profile\n" );
150         return 1;
151     }
152
153     return 0;
154 }
155
156 int8_t CProfile::LoadCmd( ifstream& fin, string& line, const string& delimiter )
157 {
158     uint8_t         i;
159     uint8_t         count;
160     command_t       cmd;
161     argument_t      arg;
162     vector<string>  parts;
163
164     // Command name
165     cmd.Name = line.substr( 1, line.length()-2 );
166
167     // Command location and script
168     getline(fin,line);
169     if (UnprefixString( line, line, PROFILE_CMDPATH ) == true)
170     {
171         cmd.Command = line.substr( line.find_last_of('/')+1 );
172         cmd.Path    = line.substr( 0, line.find_last_of('/')+1 );
173
174         if (cmd.Command.compare("./") == 0)
175         {
176             cmd.Command = string(getenv("PWD"))+"/";
177         }
178         CheckPath(cmd.Path);
179
180         getline(fin,line);
181     }
182     else
183     {
184         Log( "Error: %s not found\n", PROFILE_CMDPATH );
185         return 1;
186     }
187
188     // Command arguments and values
189     count = 0;
190     while (UnprefixString( line, line, PROFILE_CMDARG ) == true)
191     {
192         SplitString( delimiter, line, parts );
193
194         if (parts.size() >= ARG_MIN_COUNT)
195         {
196             arg.Flag    = parts.at(0);
197             arg.Default = a_to_i( parts.at(1) );
198
199             arg.Names.clear();
200             arg.Values.clear();
201             for (i=2; i<parts.size(); i++)
202             {
203                 arg.Names.push_back( parts.at(i) );
204                 i++;
205                 if (i<parts.size())
206                 {
207                     arg.Values.push_back( parts.at(i) );
208                 }
209                 else
210                 {
211                     Log( "Error: Uneven number of argument names to values\n" );
212                     return 1;
213                 }
214             }
215
216             if (arg.Default >= arg.Values.size())
217             {
218                 arg.Default = arg.Values.size()-1;
219             }
220
221             cmd.Arguments.push_back( arg );
222
223             count++;
224             getline( fin, line );
225         }
226         else
227         {
228             Log( "Error: Not enough argument parts detected\n" );
229             return 1;
230         }
231     }
232
233     if (count<1)
234     {
235         Log( "Error: %s not found at least once\n", PROFILE_CMDARG );
236         return 1;
237     }
238
239     Commands.push_back(cmd);
240     return 0;
241 }
242
243 int8_t CProfile::LoadPlugins()
244 {
245     plugin_t         plugin;
246     oneplugin_t      one;
247     string           line;
248
249     Plugins.clear();
250         // all hardcoded for now!
251         // init video plugins
252         plugin.Name=string("Video Plugin");
253         plugin.TextFile=string("videodriver");
254 //        plugin.TextFile+=string(".txt");
255         // videos plugins
256         one.Name="Glide GLES2";
257         one.So="mupen64plus-video-glide64mk2";
258         plugin.Plugins.push_back(one);
259         one.Name="Rice GLES2";
260         one.So="mupen64plus-video-rice";
261         plugin.Plugins.push_back(one);
262         one.Name="Rice GLES1.1";
263         one.So="mupen64plus-video-ricees1";
264         plugin.Plugins.push_back(one);
265         one.Name="GLES2N64";
266         one.So="mupen64plus-video-gles2n64";
267         plugin.Plugins.push_back(one);
268         one.Name="Arachnoid GLES1.1";
269         one.So="mupen64plus-video-arachnoid";
270         plugin.Plugins.push_back(one);
271         
272         Plugins.push_back(plugin);
273         
274         plugin.Plugins.clear();
275         // init audio plugins
276         plugin.Name="Audio Plugin";
277         plugin.TextFile="audiodriver";
278 //        plugin.TextFile+=".txt";
279         // audios plugins
280         one.Name="Notaz Audio";
281         one.So="notaz-audio";
282         plugin.Plugins.push_back(one);
283         one.Name="SDL Audio";
284         one.So="mupen64plus-audio-sdl";
285         plugin.Plugins.push_back(one);
286         
287         Plugins.push_back(plugin);
288         
289         plugin.Plugins.clear();
290         // init rsp plugins
291         plugin.Name="RSP Plugin";
292         plugin.TextFile="rspdriver";
293 //        plugin.TextFile+=".txt";
294         // audios plugins
295         one.Name="Default HLE RSP";
296         one.So="mupen64plus-rsp-hle";
297         plugin.Plugins.push_back(one);
298         one.Name="Z64 LLE RSP with HLE Video";
299         one.So="mupen64plus-rsp-z64-hlevideo";
300         plugin.Plugins.push_back(one);
301         one.Name="Z64 LLE RSP";
302         one.So="mupen64plus-rsp-z64";
303         plugin.Plugins.push_back(one);
304         
305         Plugins.push_back(plugin);
306    //Get default values
307    ifstream        fplug;
308    int             index, i;
309    for (index=0; index<Plugins.size(); index++) {
310        fplug.open((Plugins.at(index).TextFile+".txt").c_str(), ios_base::in);
311
312        Plugins.at(index).Which=0;
313
314        // Read in the default value
315        if (fplug && fplug.is_open())
316        {
317             getline(fplug,line);
318             if (line.length() > 0)
319             {
320                //search for a match
321                for (i=0; i<Plugins[index].Plugins.size(); i++) 
322                   if (lowercase(Plugins[index].Plugins[i].So).compare(lowercase(line))==0)
323                    Plugins[index].Which=i;
324             }
325             fplug.close();
326
327         }
328     }
329     return 0;
330 }
331
332 int8_t CProfile::LoadExt( ifstream& fin, string& line, const string& delimiter )
333 {
334     uint8_t         i;
335     uint8_t         count;
336     vector<string>  parts;
337     extension_t     ext;
338     argument_t      arg;
339     argforce_t      argforce;
340     exeforce_t      exeforce;
341     string          extensions;
342
343     // Extension names
344     extensions = line.substr(1, line.length()-2);
345     SplitString( delimiter, extensions, parts );
346     ext.extName.clear();
347     for (i=0; i<parts.size(); i++)
348     {
349         ext.extName.push_back( lowercase(parts.at(i)) );
350     }
351
352     if (ext.extName.size() == 0)
353     {
354         Log( "Error: no extensions detected\n" );
355         return 1;
356     }
357
358     // Extension executable
359     getline(fin,line);
360     if (UnprefixString( line, line, PROFILE_EXEPATH ) == true)
361     {
362         ext.exeName = line.substr( line.find_last_of('/')+1 );
363         ext.exePath = line.substr( 0, line.find_last_of('/')+1 );
364
365         if (ext.exePath.compare("./") == 0)
366         {
367             ext.exePath = string(getenv("PWD"))+"/";
368         }
369
370         getline(fin,line);
371     }
372     else
373     {
374         Log( "Error: %s not found\n", PROFILE_EXEPATH );
375         return 1;
376     }
377
378     // Extension blacklist
379     if (UnprefixString( line, line, PROFILE_BLACKLIST ) == true)
380     {
381         SplitString( delimiter, line, ext.Blacklist );
382
383         getline(fin,line);
384     }
385
386     // Extension arguments
387     count = 0;
388     while (UnprefixString( line, line, PROFILE_EXTARG ) == true)
389     {
390         SplitString( delimiter, line, parts );
391
392         if (parts.size() >= ARG_MIN_COUNT)
393         {
394             arg.Flag    = parts.at(0);
395             arg.Default = a_to_i( parts.at(1) );
396
397             arg.Names.clear();
398             arg.Values.clear();
399             for (i=2; i<parts.size(); i++)
400             {
401                 arg.Names.push_back( parts.at(i) );
402                 i++;
403                 if (i<parts.size())
404                 {
405                     arg.Values.push_back( parts.at(i) );
406                 }
407                 else
408                 {
409                     Log( "Error: Uneven number of argument names to values\n" );
410                     return 1;
411                 }
412             }
413
414             if (arg.Default >= arg.Values.size())
415             {
416                 arg.Default = arg.Values.size()-1;
417             }
418
419             ext.Arguments.push_back(arg);
420
421             count++;
422             getline(fin,line);
423         }
424         else
425         {
426             Log( "Error: Not enough argument parts detected\n" );
427             return 1;
428         }
429     }
430
431     if (count<1)
432     {
433         Log( "Error: %s not found at least once\n", PROFILE_EXTARG );
434         return 1;
435     }
436
437     // Extension argforces
438     while (UnprefixString( line, line, PROFILE_ARGFORCE ) == true)
439     {
440         SplitString( delimiter, line, parts );
441
442         if (parts.size() == ARGFORCE_COUNT)
443         {
444             argforce.Path = parts.at(0);
445             CheckPath(argforce.Path);
446
447             argforce.Argument   = a_to_i( parts.at(1) );
448             argforce.Value      = parts.at(2);
449             ext.ArgForces.push_back( argforce );
450         }
451         else
452         {
453             Log( "Error: %s wrong number of parts actual: %d expected: %s\n", PROFILE_ARGFORCE, parts.size(), ARGFORCE_COUNT );
454             return 1;
455         }
456         getline( fin, line );
457     }
458
459     // Exe path forces
460     while (UnprefixString( line, line, PROFILE_EXEFORCE ) == true)
461     {
462         SplitString( delimiter, line, parts );
463
464         if (parts.size()>=EXEFORCE_COUNT)
465         {
466             exeforce.exeName = parts.at(0).substr( line.find_last_of('/')+1 );
467             exeforce.exePath = parts.at(0).substr( 0, line.find_last_of('/')+1 );
468
469             if (exeforce.exePath.compare("./") == 0)
470             {
471                 exeforce.exePath = string(getenv("PWD"))+"/";
472             }
473
474             exeforce.Files.clear();
475             for (i=1; i<parts.size(); i++)
476             {
477                 exeforce.Files.push_back( parts.at(i) );
478             }
479             ext.ExeForces.push_back( exeforce );
480         }
481         else
482         {
483             Log( "Error: %s wrong number of parts actual: %d expected: %s\n", PROFILE_EXEFORCE, parts.size(), EXEFORCE_COUNT );
484             return 1;
485         }
486         getline( fin, line );
487     }
488     Extensions.push_back(ext);
489
490     // Check for directory exe
491     if (ext.exeName.length() > 0)
492     {
493         for (i=0; i<ext.extName.size(); i++)
494         {
495             if (CheckExtension( ext.extName.at(i), EXT_DIRS) >= 0)
496             {
497                 LaunchableDirs = true;
498             }
499         }
500     }
501
502     return 0;
503 }
504
505 int8_t CProfile::LoadEntry( ifstream& fin, string& line, const string& delimiter )
506 {
507     uint16_t            i;
508     string::size_type   pos1,pos2;
509     entry_t             entry;
510     vector<string>      parts;
511
512     // Extension name
513     pos1 = line.find_last_of('/');
514     pos2 = line.find_last_of(delimiter);
515     entry.Path  = line.substr( 1, pos1 );
516     entry.Name  = line.substr( pos1+1, pos2-pos1-1 );
517     entry.Alias = line.substr( pos2+1, line.length()-pos2-2 );
518     entry.Custom = true;
519
520     // Extension executable
521     getline(fin,line);
522     if (UnprefixString( line, line, PROFILE_ENTRY_CMDS ) == true)
523     {
524         SplitString( delimiter, line, parts );
525         for (i=0; i<parts.size(); i++)
526         {
527             entry.CmdValues.push_back( a_to_i( parts.at(i) ) );
528         }
529     }
530     else
531     {
532         Log( "Error: %s not found\n", PROFILE_ENTRY_CMDS );
533         return 1;
534     }
535
536     getline(fin,line);
537     if (UnprefixString( line, line, PROFILE_ENTRY_ARGS ) == true)
538     {
539         SplitString( delimiter, line, parts );
540         for (i=0; i<parts.size(); i++)
541         {
542             entry.ArgValues.push_back( a_to_i( parts.at(i) ) );
543         }
544     }
545     else
546     {
547         Log( "Error: %s not found\n", PROFILE_ENTRY_ARGS );
548         return 1;
549     }
550     Entries.push_back( entry );
551
552     return 0;
553 }
554
555 int16_t CProfile::AddEntry( listoption_t& argument, const string& name )
556 {
557     uint16_t i, j;
558     entry_t entry;
559
560     entry.Name      = name;
561     entry.Path      = FilePath;
562     entry.Custom    = true;
563     entry.CmdValues.clear();
564     for (i=0; i<Commands.size(); i++)
565     {
566         for (j=0; j<Commands.size(); j++)
567         {
568             entry.CmdValues.push_back(Commands.at(i).Arguments.at(j).Default);
569         }
570     }
571     entry.ArgValues.clear();
572     if (CheckRange( argument.Extension, Extensions.size() ))
573     {
574         for (i=0; i<Extensions.at(argument.Extension).Arguments.size(); i++)
575         {
576             entry.ArgValues.push_back(Extensions.at(argument.Extension).Arguments.at(i).Default);
577         }
578     }
579     else
580     {
581         Log( "Warning: AddEntry argument.Extension out of range for Extensions\n" );
582         return -1;
583     }
584     Entries.push_back(entry);
585
586     return Entries.size()-1;
587 }
588
589 int8_t CProfile::Save( const string& location, const string& delimiter )
590 {
591     uint16_t index, i, j;
592     ofstream   fout;
593
594     fout.open( location.c_str(), ios_base::trunc );
595
596     if (!fout)
597     {
598         Log( "Failed to open profile\n" );
599         return 1;
600     }
601
602     // Write out the profile
603     if (fout.is_open())
604     {
605         fout << "# Global Settings" << endl;
606         fout << PROFILE_TARGETAPP << TargetApp << endl;
607         fout << PROFILE_FILEPATH << FilePath << endl;
608         // Commands
609         fout << endl << "# Command Settings" << endl;
610         for (index=0; index<Commands.size(); index++)
611         {
612             fout << "<" << Commands.at(index).Name << ">" << endl;
613             fout << PROFILE_CMDPATH << Commands.at(index).Path << Commands.at(index).Command << endl;
614             // Arguments
615             for (i=0; i<Commands.at(index).Arguments.size(); i++)
616             {
617                 fout << PROFILE_CMDARG << Commands.at(index).Arguments.at(i).Flag
618                                        << delimiter
619                                        << i_to_a(Commands.at(index).Arguments.at(i).Default);
620                 for (j=0; j<Commands.at(index).Arguments.at(i).Values.size(); j++)
621                 {
622                     fout << delimiter << Commands.at(index).Arguments.at(i).Names.at(j);
623                     fout << delimiter << Commands.at(index).Arguments.at(i).Values.at(j);
624                 }
625                 fout << endl;
626             }
627         }
628
629         // Extensions
630         fout << endl << "# Extension Settings" << endl;
631         for (index=0; index<Extensions.size(); index++)
632         {
633             fout << "[";
634             for ( i=0; i<Extensions.at(index).extName.size(); i++ )
635             {
636                 if (i>0)
637                 {
638                     fout << delimiter;
639                 }
640                 fout << Extensions.at(index).extName.at(i);
641             }
642             fout << "]" << endl;
643
644             // Executables
645             fout << PROFILE_EXEPATH + Extensions.at(index).exePath + Extensions.at(index).exeName << endl;
646
647             // Blacklist
648             if (Extensions.at(index).Blacklist.size()>0)
649             {
650                 fout << PROFILE_BLACKLIST;
651                 for (i=0; i<Extensions.at(index).Blacklist.size(); i++)
652                 {
653                     if (i>0)
654                     {
655                         fout << delimiter;
656                     }
657                     fout << Extensions.at(index).Blacklist.at(i);
658                 }
659                 fout << endl;
660             }
661             // Arguments
662             for (i=0; i<Extensions.at(index).Arguments.size(); i++)
663             {
664                 fout << PROFILE_EXTARG << Extensions.at(index).Arguments.at(i).Flag
665                                        << delimiter
666                                        << i_to_a(Extensions.at(index).Arguments.at(i).Default);
667                 for (j=0; j<Extensions.at(index).Arguments.at(i).Values.size(); j++)
668                 {
669                     fout << delimiter << Extensions.at(index).Arguments.at(i).Names.at(j);
670                     fout << delimiter << Extensions.at(index).Arguments.at(i).Values.at(j);
671                 }
672                 fout << endl;
673             }
674             // Argument forces
675             for (i=0; i<Extensions.at(index).ArgForces.size(); i++)
676             {
677                 fout << PROFILE_ARGFORCE << Extensions.at(index).ArgForces.at(i).Path
678                                          << delimiter << i_to_a(Extensions.at(index).ArgForces.at(i).Argument)
679                                          << delimiter << Extensions.at(index).ArgForces.at(i).Value << endl;
680             }
681             // Exe forces
682             for (i=0; i<Extensions.at(index).ExeForces.size(); i++)
683             {
684                 fout << PROFILE_EXEFORCE << Extensions.at(index).ExeForces.at(i).exePath
685                                          << Extensions.at(index).ExeForces.at(i).exeName;
686                 for (j=0; j<Extensions.at(index).ExeForces.at(i).Files.size(); j++)
687                 {
688                     fout << delimiter << Extensions.at(index).ExeForces.at(i).Files.at(j);
689                 }
690                 fout << endl;
691             }
692         }
693
694         // Entries
695         fout << endl << "# Custom Entries Settings" << endl;
696         for (index=0; index<Entries.size(); index++)
697         {
698             if (Entries.at(index).Custom == true)
699             {
700                 // Entry path, name, and alias
701                 fout << "{" << Entries.at(index).Path << Entries.at(index).Name
702                             << delimiter
703                             << Entries.at(index).Alias << "}" << endl;
704                 // Entry command values
705                 fout << PROFILE_ENTRY_CMDS;
706                 for (i=0; i<Entries.at(index).CmdValues.size(); i++)
707                 {
708                     if (i>0)
709                     {
710                         fout << delimiter;
711                     }
712                     fout << Entries.at(index).CmdValues.at(i);
713                 }
714                 fout << endl;
715                 // Entry argument values
716                 fout << PROFILE_ENTRY_ARGS;
717                 for (i=0; i<Entries.at(index).ArgValues.size(); i++)
718                 {
719                     if (i>0)
720                     {
721                         fout << delimiter;
722                     }
723                     fout << Entries.at(index).ArgValues.at(i);
724                 }
725                 fout << endl;
726             }
727         }
728
729         fout.close();
730     }
731     else
732     {
733         Log( "Failed to open profile\n" );
734         return 1;
735     }
736     return 0;
737 }
738
739 int8_t CProfile::ScanEntry( listitem_t& item, vector<listoption_t>& items )
740 {
741     int16_t ext_index;
742     uint16_t i, j;
743     listoption_t option;
744
745     items.clear();
746
747     // Find the extension
748     if (item.Type == TYPE_DIR && LaunchableDirs == true)
749     {
750         ext_index = FindExtension(EXT_DIRS);
751     }
752     else
753     {
754         ext_index = FindExtension(item.Name);
755     }
756
757     if (CheckRange( ext_index, Extensions.size()))
758     {
759         option.Extension = ext_index;
760
761         // Scan for command arguments
762         for (i=0; i<Commands.size(); i++)
763         {
764             for (j=0; j<Commands.at(i).Arguments.size(); j++)
765             {
766                 option.Name = Commands.at(i).Name + " " + Commands.at(i).Arguments.at(j).Flag + " ";
767                 if (CheckRange( item.Entry, Entries.size() ))
768                 {
769                     if (Entries.at(item.Entry).CmdValues.at(j) < Commands.at(i).Arguments.at(j).Names.size())
770                     {
771                         option.Name += Commands.at(i).Arguments.at(j).Names.at( Entries.at(item.Entry).CmdValues.at(j) );
772                     }
773                     else
774                     {
775                         option.Name += "Error: ScanEntry Entry out of range";
776                     }
777                 }
778                 else
779                 {
780                     if (CheckRange( Commands.at(i).Arguments.at(j).Default, Commands.at(i).Arguments.at(j).Names.size() ))
781                     {
782                         option.Name += Commands.at(i).Arguments.at(j).Names.at( Commands.at(i).Arguments.at(j).Default );
783                     }
784                     else
785                     {
786                         option.Name += "Error: ScanEntry Default out of range";
787                     }
788                 }
789                 option.Command = i;
790                 option.Argument = j;
791                 items.push_back(option);
792             }
793         }
794
795         // Scan for extension arguments
796         for (i=0; i<Extensions.at(ext_index).Arguments.size(); i++)
797         {
798             option.Name = Extensions.at(ext_index).Arguments.at(i).Flag + " ";
799             if (CheckRange( item.Entry, Entries.size() ))
800             {
801                 if (CheckRange( Entries.at(item.Entry).ArgValues.at(i), Extensions.at(ext_index).Arguments.at(i).Names.size()) &&
802                     CheckRange( Entries.at(item.Entry).ArgValues.at(i), Extensions.at(ext_index).Arguments.at(i).Values.size())    )
803                 {
804                     option.Name += Extensions.at(ext_index).Arguments.at(i).Names.at( Entries.at(item.Entry).ArgValues.at(i) ) + " ";
805                     option.Name += Extensions.at(ext_index).Arguments.at(i).Values.at( Entries.at(item.Entry).ArgValues.at(i) );
806                 }
807                 else
808                 {
809                     option.Name += "Error: bad custom entry";
810                 }
811             }
812             else
813             {
814                 if (CheckRange( Extensions.at(ext_index).Arguments.at(i).Default, Extensions.at(ext_index).Arguments.at(i).Names.size()) &&
815                     CheckRange( Extensions.at(ext_index).Arguments.at(i).Default, Extensions.at(ext_index).Arguments.at(i).Values.size())    )
816                 {
817                     option.Name += Extensions.at(ext_index).Arguments.at(i).Names.at( Extensions.at(ext_index).Arguments.at(i).Default ) + " ";
818                     option.Name += Extensions.at(ext_index).Arguments.at(i).Values.at( Extensions.at(ext_index).Arguments.at(i).Default );
819                 }
820                 else
821                 {
822                     option.Name += "Error: index not found";
823                 }
824             }
825             option.Command = -1;
826             option.Argument = i;
827             items.push_back(option);
828         }
829     }
830     else
831     {
832         Log( "Error ScanEntry ext_index out of range\n" );
833         return -1;
834     }
835     return ext_index;
836 }
837
838 void CProfile::ScanArgument( listoption_t& item, vector<string>& values )
839 {
840     uint16_t index;
841
842     values.clear();
843
844     if (CheckRange( item.Command, Commands.size() ))
845     {
846         if (CheckRange(item.Argument, Commands.at(item.Command).Arguments.size() ))
847         {
848             for (index=0; index<Commands.at(item.Command).Arguments.at(item.Argument).Values.size(); index++)
849             {
850                 values.push_back( Commands.at(item.Command).Arguments.at(item.Argument).Values.at(index) );
851             }
852         }
853         else
854         {
855             Log( "Error: ScanArgument Commands item.Argument out of range\n" );
856             values.push_back( "Error: Check log" );
857         }
858     }
859     else if (CheckRange( item.Extension, Extensions.size() ))
860     {
861         if (CheckRange( item.Argument, Extensions.at(item.Extension).Arguments.size() ))
862         {
863             for (index=0; index<Extensions.at(item.Extension).Arguments.at(item.Argument).Values.size(); index++)
864             {
865                 values.push_back( Extensions.at(item.Extension).Arguments.at(item.Argument).Values.at(index) );
866             }
867         }
868         else
869         {
870             Log( "Error: ScanArgument Extensions item.Argument out of range\n" );
871             values.push_back( "Error: Check log" );
872         }
873     }
874     else
875     {
876         Log( "Error: ScanArgument item type undefined\n" );
877         values.push_back( "Error: Check log" );
878     }
879 }
880
881 int8_t CProfile::ScanDir( string location, bool showhidden, bool showzip, vector<listitem_t>& items )
882 {
883     DIR *dp;
884     struct dirent *dirp;
885     string filename;
886     bool found;
887     int16_t alpha_index;
888     int16_t ext_index;
889     uint16_t file, i;
890     listitem_t item;
891     entry_t entry;
892     vector<string> dirs;
893     vector<string> files;
894     vector<listitem_t>::iterator sort_index;
895
896     dirs.clear();
897     files.clear();
898     items.clear();
899
900     if (ZipFile.length() == 0)
901     {
902         if((dp = opendir(location.c_str())) == NULL)
903         {
904             Log( "Failed to open dir path %s\n", location.c_str() );
905             return 1;
906         }
907
908         while ((dirp = readdir(dp)) != NULL)
909         {
910             filename = string(dirp->d_name);
911
912             if (filename.length() > 0)
913             {
914                 // Skip . and ..
915                 if (filename.compare(".") == 0 || filename.compare("..") == 0)
916                     continue;
917
918                 // Skip hidden files and folders
919                 if (showhidden == false && filename.at(0) == '.')
920                     continue;
921
922                 item.Entry  = -1;
923                 item.Name   = filename;
924                 if (dirp->d_type == DT_DIR)       // Directories
925                 {
926                     // Filter out by blacklist
927                     ext_index = FindExtension( EXT_DIRS );
928                     found = false;
929                     if (CheckRange( ext_index, Extensions.size() ))
930                     {
931                         for (i=0; i<Extensions.at(ext_index).Blacklist.size(); i++)
932                         {
933                             if (Extensions.at(ext_index).Blacklist.at(i).compare(filename) == 0)
934                             {
935                                 found = true;
936                                 break;
937                             }
938                         }
939                     }
940
941                     if (found == false)
942                     {
943                         item.Type   = TYPE_DIR;
944                         items.push_back(item);
945                     }
946                 }
947                 else // Files
948                 {
949                     files.push_back(filename);
950                 }
951             }
952             else
953             {
954                 Log( "Error: filename length was 0\n" );
955             }
956         }
957         closedir(dp);
958     }
959     else
960     {
961         Minizip.ListFiles( FilePath + ZipFile, files );
962     }
963
964     // Filter
965     for (file=0; file<files.size(); file++)
966     {
967         found = false;
968         if (CheckExtension( files.at(file), ZIP_EXT) < 0 ||                       // Any non-zip ext should be filtered
969            (CheckExtension( files.at(file), ZIP_EXT) >= 0 && showzip == false))   // only filter zip if internal support is off
970         {
971             // Filter out files by extension
972             ext_index = FindExtension( files.at(file) );
973
974             // Filter out by blacklist
975             found = false;
976             if (CheckRange( ext_index, Extensions.size() ))
977             {
978                 for (i=0; i<Extensions.at(ext_index).Blacklist.size(); i++)
979                 {
980                     if (Extensions.at(ext_index).Blacklist.at(i).compare(files.at(file)) == 0)
981                     {
982                         found = true;
983                         break;
984                     }
985                 }
986             }
987             else
988             {
989                 found = true;
990             }
991         }
992
993         // Filter by search string
994         if (found != true && EntryFilter.length() > 0)
995         {
996             if (lowercase(files.at(file)).find( lowercase(EntryFilter), 0) != string::npos)
997             {
998                 found = false;
999             }
1000             else
1001             {
1002                 found = true;
1003             }
1004         }
1005
1006         // If here then item is valid and determine if an entry exists
1007         if (found == false)
1008         {
1009             // Add to display list
1010             item.Name = files.at(file);
1011             // Check for zip file
1012             if (CheckExtension( files.at(file), ZIP_EXT) >= 0)
1013             {
1014                 item.Type = TYPE_ZIP;
1015             }
1016             else
1017             {
1018                 item.Type = TYPE_FILE;
1019             }
1020
1021             // Find if an entry has been defined
1022             item.Entry = -1;
1023             for (i=0; i<Entries.size(); i++)
1024             {
1025                 if (Entries.at(i).Name.compare(files.at(file)) == 0 &&
1026                     Entries.at(i).Path.compare(location) == 0)
1027                 {
1028
1029                     item.Entry = i;
1030                     break;
1031                 }
1032             }
1033
1034             items.push_back(item);
1035         }
1036     }
1037
1038     // Sort
1039     sort( items.begin(), items.end(), CompareItems );
1040
1041     // Build alphabetic indices
1042     AlphabeticIndices.clear();
1043     AlphabeticIndices.resize(TOTAL_LETTERS, 0);
1044     for (i=0; i<items.size(); i++)
1045     {
1046         if (items.at(i).Type != TYPE_DIR)
1047         {
1048             if (items.at(i).Name.length() > 0 )
1049             {
1050                 alpha_index = tolower(items.at(i).Name.at(0))-'a';
1051                 if (alpha_index < 'a' || alpha_index > 'z')
1052                 {
1053                     alpha_index = TOTAL_LETTERS-1;
1054                 }
1055
1056                 if (CheckRange( alpha_index, AlphabeticIndices.size() ))
1057                 {
1058                     if (AlphabeticIndices.at(alpha_index) == 0)
1059                     {
1060                         AlphabeticIndices.at(alpha_index) = i;
1061                     }
1062                 }
1063                 else
1064                 {
1065                     Log( "Error: Scandir alpha_index out of range\n" );
1066                     return 1;
1067                 }
1068             }
1069         }
1070     }
1071     alpha_index = 0;
1072     for (i=0; i<AlphabeticIndices.size(); i++)
1073     {
1074         if (AlphabeticIndices.at(i) == 0)
1075         {
1076             AlphabeticIndices.at(i) = alpha_index;
1077         }
1078         alpha_index = AlphabeticIndices.at(i);
1079     }
1080     return 0;
1081 }
1082
1083 int16_t CProfile::FindExtension( const string& ext )
1084 {
1085     uint16_t i, j;
1086
1087     for (i=0; i<Extensions.size(); i++)
1088     {
1089         for (j=0; j<Extensions.at(i).extName.size(); j++)
1090         {
1091             if (CheckExtension( ext, Extensions.at(i).extName.at(j)) >= 0)
1092             {
1093                 return i;
1094             }
1095         }
1096     }
1097
1098     return -1;
1099 }
1100
1101 void CProfile::ScanDefPlugins( vector<listoption_t>& items )
1102 {
1103    items.clear();
1104    listoption_t itm;
1105    for (int i=0; i<Plugins.size(); i++)
1106    {
1107       itm.Extension = i;
1108       itm.Name = Plugins.at(i).Name;
1109       items.push_back(itm);
1110    }
1111 }
1112
1113 void CProfile::ScanDef1Plugins( int8_t which, vector<listitem_t>& items )
1114 {
1115    items.clear();
1116    listitem_t itm;
1117    for (int i=0; i<Plugins.at(which).Plugins.size(); i++)
1118    {
1119       itm.Type = (i==Plugins[which].Which)?TYPE_DIR:TYPE_FILE;
1120       itm.Entry = i;
1121       itm.Name = Plugins.at(which).Plugins.at(i).Name;
1122       items.push_back(itm);
1123    }
1124 }
1125
1126
1127 // decide is a > b
1128 bool CompareItems( listitem_t a, listitem_t b )
1129 {
1130     // Folders should be above files
1131     if (a.Type == TYPE_DIR && b.Type >= TYPE_FILE)
1132     {
1133         return true;
1134     }
1135     else if (a.Type >= TYPE_FILE && b.Type == TYPE_DIR)
1136     {
1137         return false;
1138     }
1139     else
1140     {
1141         // Convert to lower cases, so that upper case files are sorted with lower case files
1142         transform( a.Name.begin(), a.Name.end(), a.Name.begin(), (int (*)(int))tolower );
1143         transform( b.Name.begin(), b.Name.end(), b.Name.begin(), (int (*)(int))tolower );
1144
1145         if (a.Name.compare(b.Name) >= 0)
1146         {
1147             return false;
1148         }
1149         else
1150         {
1151             return true;
1152         }
1153     }
1154 }
1155
1156 void  CProfile::SaveDef1Plugin( int8_t which, int8_t index )
1157 {
1158     ofstream   fout;
1159
1160     fout.open( (Plugins.at(which).TextFile+".txt").c_str(), ios_base::trunc );
1161     if (!fout)
1162     {
1163         Log( "Failed to open profile\n" );
1164         return;
1165     }
1166
1167     // Write out the profile
1168     if (fout.is_open())
1169     {
1170         fout << Plugins.at(which).Plugins.at(index).So << endl;
1171         fout.close();
1172     }
1173     // Change the default
1174     Plugins[which].Which = index;
1175     return;
1176 }
1177
1178 void CProfile::ScanRomPlugins( const string& name,  vector<listoption_t>& items )
1179 {
1180    string line;
1181    vector<string> parts;
1182
1183    Rom = RomCRC(name);
1184
1185    items.clear();
1186    listoption_t itm;
1187    for (int i=0; i<Plugins.size(); i++)
1188    {
1189       itm.Extension = i;
1190       itm.Name = Plugins.at(i).Name;
1191       items.push_back(itm);
1192       Plugins.at(i).Rom = -1;
1193    }
1194
1195    //look for value in pandora/crc file
1196    if (Rom.compare("")!=0)
1197    {
1198       ifstream   fin;
1199
1200       fin.open( (string("pandora/")+Rom).c_str(), ios_base::in );
1201
1202       if (fin && fin.is_open())
1203       {
1204         // file exist; read and interpret
1205         while (!fin.eof())
1206         {
1207             getline(fin, line);
1208             SplitString( "=", line, parts );
1209             if (parts.size()==2)
1210             {
1211                int8_t plug = FindPlugin(parts[0]);
1212                if (plug>=0)
1213                   Plugins[plug].Rom = Find1Plugin(plug, parts[1]);
1214             }
1215         }
1216      } 
1217    }
1218 }
1219
1220
1221 void CProfile::ScanRom1Plugins( int8_t which, vector<listitem_t>& items )
1222 {
1223    items.clear();
1224    listitem_t itm;
1225
1226    itm.Entry = -1;
1227    itm.Type = (itm.Entry == Plugins[which].Rom)?TYPE_DIR:TYPE_FILE;
1228    itm.Name = "default";
1229    items.push_back(itm);
1230    for (int i=0; i<Plugins[which].Plugins.size(); i++)
1231    {
1232       itm.Type = TYPE_FILE;
1233       itm.Entry = i;
1234       itm.Type = (itm.Entry == Plugins[which].Rom)?TYPE_DIR:TYPE_FILE;
1235       itm.Name = Plugins[which].Plugins.at(i).Name;
1236       items.push_back(itm);
1237    }
1238 }
1239
1240 string CProfile::RomCRC(const string& name)
1241 {
1242    char rom_header[0x40];       // rom header
1243
1244    FILE *f;
1245    f = fopen(name.c_str(), "rb");
1246    if (!f) {
1247       Log("Unable to open file\n");
1248       return string("");
1249    }
1250    fread(rom_header, 0x40, 1, f);
1251    fclose(f);
1252         
1253    char temp;
1254
1255    int i;
1256
1257    // now, check header to unswap things...
1258    /* Btyeswap if .v64 image. */
1259    if(rom_header[0]==0x37)
1260    {
1261       for (i = 0; i < 0x40; i+=2)
1262       {
1263          temp=rom_header[i];
1264          rom_header[i]=rom_header[i+1];
1265          rom_header[i+1]=temp;
1266       }
1267    }
1268    /* Wordswap if .n64 image. */
1269    else if(rom_header[0]==0x40)
1270    {
1271       for (i = 0; i < 0x40; i+=4)
1272       {
1273          temp=rom_header[i];
1274          rom_header[i]=rom_header[i+3];
1275          rom_header[i+3]=temp;
1276          temp=rom_header[i+1];
1277          rom_header[i+1]=rom_header[i+2];
1278          rom_header[i+2]=temp;
1279       }
1280    }
1281    // check it's a Valid rom
1282    if ((rom_header[0]!=0x80)||(rom_header[1]!=0x37)||(rom_header[2]!=0x12)||(rom_header[3]!=0x40)) 
1283    {
1284       Log("Unable to open file\n");
1285       return string("");
1286    }
1287
1288
1289    char buff[32];
1290    sprintf(buff, "%02x%02x%02x%02x%02x%02x%02x%02x", 
1291                  rom_header[0x13], rom_header[0x12], rom_header[0x11], rom_header[0x10],
1292                  rom_header[0x17], rom_header[0x16], rom_header[0x15], rom_header[0x14] );
1293
1294         
1295    return string(buff);
1296 }
1297
1298 int8_t CProfile::FindPlugin(const string& name)
1299 {
1300    int i;
1301    for (i=0; i<Plugins.size(); i++)
1302       if (Plugins[i].TextFile.compare(name)==0)
1303          return i;
1304
1305    return -1;
1306 }
1307
1308 int8_t CProfile::Find1Plugin(int8_t which, const string& name)
1309 {
1310    int i;
1311    for (i=0; i<Plugins[which].Plugins.size(); i++)
1312       if (Plugins[which].Plugins[i].So.compare(name)==0)
1313          return i;
1314
1315    return -1;
1316 }
1317
1318 void  CProfile::SaveRom1Plugin( int8_t which, int8_t index )
1319 {
1320    if (Plugins[which].Rom == index)
1321       return;
1322
1323    Plugins[which].Rom = index;
1324    //check if we want all default (i.e delete conf.) or not
1325    bool conf=false;
1326    for (int i=0; i<Plugins.size(); i++)
1327       if (Plugins[i].Rom!=-1)
1328          conf=true;
1329
1330    if (!conf)
1331    {
1332       remove((string("pandora/")+Rom).c_str());
1333       return;
1334    }
1335
1336     ofstream   fout;
1337
1338     fout.open( (string("pandora/")+Rom).c_str(), ios_base::trunc );
1339     if (!fout)
1340     {
1341         Log( "Failed to open Rom special conf\n" );
1342         return;
1343     }
1344
1345     // Write out the profile
1346     if (fout.is_open())
1347     {
1348         for (int i=0; i<Plugins.size(); i++)
1349            if (Plugins[i].Rom!=-1)
1350               fout << Plugins[i].TextFile << "=" << Plugins[i].Plugins[Plugins[i].Rom].So << endl;
1351         fout.close();
1352     }
1353 }