1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4     Rosegarden
5     A sequencer and musical notation editor.
6     Copyright 2000-2021 the Rosegarden development team.
7     See the AUTHORS file for more details.
8 
9     This program is free software; you can redistribute it and/or
10     modify it under the terms of the GNU General Public License as
11     published by the Free Software Foundation; either version 2 of the
12     License, or (at your option) any later version.  See the file
13     COPYING included with this distribution for more information.
14 */
15 
16 #define RG_MODULE_STRING "[LSCPPatchExtractor]"
17 
18 #include "LSCPPatchExtractor.h"
19 
20 #include <QFile>
21 #include <QTextStream>
22 #include <QStringList>
23 #include "misc/Debug.h"
24 
25 class QFile;
26 class QTextStream;
27 class QString;
28 
29 namespace Rosegarden
30 {
31 
32 bool
33 LSCPPatchExtractor::isLSCPFile(const QString& fileName)
34 {
35     QFile file(fileName);
36     if (!file.open(QFile::ReadOnly)) {
37         return false;
38     } else {
39         QTextStream check(&file);
40         while (!check.atEnd()) {
41             QString currentLine = check.readLine();
42             if (currentLine.contains("sfArk")) {
43 
44                 // Crappy, but I have no better ideas.  We know for a FACT if
45                 // you try to import programs from a .sfArk file, Rosegarden
46                 // will end up running isLSCPFile() on it, and it will otherwise
47                 // test true, and result in a crash.  I could set up some
48                 // QProcess thing with a call to file and grep or whatever, but
49                 // I'm just going to hard code this function to reject sfArk
50                 // files.  Be my guest to engineer a much more elegant solution,
51                 // because one is certainly possible!
52                 RG_WARNING << "Some doofus tried to import a .sfArk file.  I'm aborting so we avoid crashing later.";
53                 return false;
54 
55 
56             } else if (currentLine.contains("map", Qt::CaseInsensitive)) { //CaseInsensitive - you could never be sure
57 
58                 // This attempt to determine a valid file apparently returns
59                 // true with completely invalid binary files, but not knowing
60                 // anything about the LSCP format, I have no better ideas.  This
61                 // thing must have come in as a patch I committed on someone's
62                 // behalf, because I certainly didn't write this patch
63                 // extractor.
64                 RG_DEBUG << "MAP string found!";
65                 return true;
66             }
67         }
68         RG_WARNING << "Has extension, but it will not be useful!";
69         return false;
70     }
71 }
72 
73 LSCPPatchExtractor::Device
74 LSCPPatchExtractor::extractContent(const QString& fileName)
75 {
76 /////// Works for single device midi mappings!
77 
78     Device device;
79 
80     QFile lscpFile(fileName);
81     QTextStream inStream(&lscpFile);
82 
83     QStringList splitLine;
84 
85     // unsigned int bank, program;
86     std::string programName;
87     std::string bankName;
88     std::string tempDeviceName, tempBankName;
89 
90     if (!lscpFile.open(QFile::ReadOnly)) {
91         return device;
92     } else {
93         while (!inStream.atEnd()) {
94             QString currentLine = inStream.readLine();
95             currentLine = currentLine.simplified();
96 
97             Bank_Prog currentDevice;
98 
99             if (!currentLine.isEmpty() && currentLine.startsWith("add", Qt::CaseInsensitive)) {
100 
101 ///// Useless for now. Will be needed if someone dedicates time to implement Device import!
102 //                std::cout << "Usao sam u ADD!";
103 //
104 //                splitLine = splitQuotedString(currentLine);
105 //                tempDeviceName = splitLine.at(2).latin1();
106 //                //debug
107 //                for (int i = 0; i < splitLine.size(); i++) std::cout << splitLine.at(i) << std::endl;
108 //                std::cout << "  " << tempDeviceName << std::endl;
109 
110             } else if (!currentLine.isEmpty() && currentLine.startsWith("map", Qt::CaseInsensitive)) {
111 
112 //                std::cout << "Usao sam u MAP!";
113 
114                 unsigned int positionOfBankElement = 3; //position of element in QStringList if NON_MODAL element is absent
115                 unsigned int positionOfProgramElement = 4; //similar to above
116                 unsigned int positionOfPatchFileName = 6; //we extract bank name from filesystem's file name.
117 
118                 splitLine = splitQuotedString(currentLine);
119 
120                 if (splitLine.at(2) == "NON_MODAL") {
121                     // Shifting position of elements if optional element (NON MODAL) is present
122                     positionOfBankElement = 4;
123                     positionOfProgramElement = 5;
124                     positionOfPatchFileName = 7;
125                 }
126 
127                 //Getting bank name HACK!!! Not in specification, so we use filename!
128                 QString patchFileName = splitLine.at(positionOfPatchFileName);
129                 QStringList splitPatchFileName = patchFileName.split("/");
130                 QString temp = splitPatchFileName.at(splitPatchFileName.size()-1);
131                 temp = temp.replace("x20"," ");
132                 temp = temp.remove(".gig");
133 
134                 currentDevice.bankName = temp.toStdString();
135                 currentDevice.bankNumber = splitLine.at(positionOfBankElement).toInt();
136                 currentDevice.programNumber = splitLine.at(positionOfProgramElement).toInt();
137 
138                 QString quotedName = splitLine.at(splitLine.size() - 1);
139                 // Chacking for another optional element. This one is even more strange - program name may be absent as well!
140                 if (quotedName.isEmpty() ||
141                     quotedName.contains("ON_DEMAND") ||
142                     quotedName.contains("ON_DEMAND_HOLD") ||
143                     quotedName.contains("PERSISTENT")) {
144                     currentDevice.programName = "Unnamed instrument";
145                 } else {
146                     currentDevice.programName = quotedName.toStdString();
147                 }
148 
149                 device.push_back(currentDevice);
150             }
151 
152         }
153         return device;
154     }
155 }
156 
157 }
158