Fixed stupid typo in an audio plugin
[mupen64plus-pandora.git] / source / mupen64launcher / src / cprofile.cpp
CommitLineData
8b5037a6 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
25CProfile::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
44CProfile::~CProfile()
45{
46}
47
48int8_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
156int8_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
243int8_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";
339dbc9c 281 one.So="notaz_audio";
8b5037a6 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
332int8_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
505int8_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
555int16_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
589int8_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
739int8_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
838void 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
881int8_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
1083int16_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
1101void 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
1113void 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
1128bool 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
1156void 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
1178void 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
1221void 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
1240string 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
1298int8_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
1308int8_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
1318void 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}