1 // -*- coding: utf-8 -*-
2 //
3 // fgrcc.hxx --- Simple resource compiler for FlightGear
4 // Copyright (C) 2017  Florent Rougon
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License along
17 // with this program; if not, write to the Free Software Foundation, Inc.,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 
20 #ifndef _FGRCC_HXX_
21 #define _FGRCC_HXX_
22 
23 #include <ios>
24 #include <iosfwd>
25 #include <vector>
26 #include <array>
27 #include <cstddef>              // std::size_t
28 
29 #include <simgear/misc/sg_path.hxx>
30 #include <simgear/xml/easyxml.hxx>
31 #include <simgear/embedded_resources/EmbeddedResource.hxx>
32 
33 // Simple class to hold data gathered by the parser. Each instance corresponds
34 // to a resource declaration (i.e., to a 'file' element in the XML resource
35 // declaration file).
36 struct ResourceDeclaration
37 {
38   ResourceDeclaration(
39     const SGPath& virtualPath, const SGPath& realPath,
40     const std::string& language,
41     simgear::AbstractEmbeddedResource::CompressionType compressionType);
42 
43   bool isCompressed() const;
44 
45   SGPath virtualPath;
46   SGPath realPath;
47   std::string language;
48   simgear::AbstractEmbeddedResource::CompressionType compressionType;
49 };
50 
51 // Class for turning a byte stream into a stream of escape sequences suitable
52 // for use inside a C++ string literal. This class could be changed so as to
53 // be *derived* from std::istream, however this is not needed here.
54 class CPPEncoder
55 {
56 public:
57   explicit CPPEncoder(std::istream& inputStream);
58 
59   // Used to implement operator<<
60   virtual std::size_t write(std::ostream& oStream);
61 
62 private:
63   // errorNumber: should be the 'errno' value set by the failing call.
64   [[ noreturn ]] static void handleWriteError(int errorNumber);
65 
66   std::istream& _inputStream;
67 };
68 
69 std::ostream& operator<<(std::ostream&, CPPEncoder&);
70 
71 // Generate all the C++ code needed to initialize a set of resources with the
72 // EmbeddedResourceManager.
73 class ResourceCodeGenerator
74 {
75 public:
76   explicit ResourceCodeGenerator(
77     const std::vector<ResourceDeclaration>& resourceDeclarations,
78     std::ostream& outputStream,
79     const SGPath& outputCppFile,
80     const std::string& initFuncName,
81     const SGPath& outputHeaderFile,
82     const std::string& headerIdentifier,
83     std::size_t inBufSize = 262144,
84     std::size_t outBufSize = 242144);
85 
86   void writeCode() const;       // write the main C++ file
87   void writeHeaderFile() const; // write the associated header file
88 
89 private:
90   // Return a string representing the compression type of a resource
91   static std::string resourceClass(
92     simgear::AbstractEmbeddedResource::CompressionType compressionType);
93   // Encode an integral index in a way that can be safely used as part of a
94   // C++ variable name. This is needed because, for instance, 'resource10' is
95   // not a valid C++ variable name.
96   static std::string encodeResourceIndex(std::size_t index);
97   std::size_t writeEncodedResourceContents(const ResourceDeclaration& resDecl)
98     const;
99 
100   const std::vector<ResourceDeclaration>& _resDecl;
101   std::ostream& _outputStream;
102   const SGPath _outputCppFile;         // SGPath() if stdout, otherwise the path
103   const std::string _initFuncName;     // in UTF-8 encoding
104   const SGPath _outputHeaderFile;      // SGPath() if header not requested
105   const std::string _headerIdentifier; // in UTF-8 encoding
106 
107   const std::size_t _compInBufSize;
108   const std::size_t _compOutBufSize;
109   std::unique_ptr<char[]> _compressionInBuf;
110   std::unique_ptr<char[]> _compressionOutBuf;
111 };
112 
113 // Parser class for XML resource declaration files
114 class ResourceBuilderXMLVisitor: public XMLVisitor
115 {
116 public:
117   explicit ResourceBuilderXMLVisitor(const SGPath& rootDir);
118 
119   // Give access to the structured data obtained once parsing is finished
120   const std::vector<ResourceDeclaration>& getResourceDeclarations() const;
121 
122 protected:
123   void startElement(const char *name, const XMLAttributes &atts) override;
124   void endElement(const char *name) override;
125   void data(const char *s, int len) override;
126   void warning(const char *message, int line, int column) override;
127 
128 private:
129   void startQResourceElement(const XMLAttributes &atts);
130   void startFileElement(const XMLAttributes &atts);
131   void error(const char *message, int line, int column);
132 
133   enum class XMLTagType {
134     START = 0,
135     END
136   };
137 
138   static const std::array<std::string, 2> _tagTypeStr;
139 
140   enum class ParserState {
141     START = 0,
142     INSIDE_FGRCC_ELT,
143     INSIDE_QRESOURCE_ELT,
144     INSIDE_FILE_ELT,
145     END
146   };
147 
148   static const std::array<std::string, 5> _parserStateStr;
149 
150   static bool readBoolean(const std::string& s);
151 
152   static simgear::AbstractEmbeddedResource::CompressionType
153   determineCompressionType(const SGPath& resourceFilePath,
154                            const std::string& compression);
155 
156   [[ noreturn ]] void unexpectedTagError(
157     XMLTagType tagType, const std::string& found,
158     const std::string& expected = std::string());
159 
160   ParserState _parserState = ParserState::START;
161   // Value obtained from the --root command line option
162   const SGPath _rootDir;
163 
164   // 'prefix' attribute of the current 'qresource' element
165   std::string _currentPrefix;
166   // 'lang' attribute of the current 'qresource' element
167   std::string _currentLanguage;
168   // 'alias' attribute of the current 'file' element
169   std::string _currentAlias;
170   // This attribute is not part of the v1.0 QRC format, it's a FlightGear
171   // extension. It tells whether, and if so, how the current <file> should be
172   // compressed in the generated code.
173   std::string _currentCompressionTypeStr;
174   // String used to assemble resource file paths declared in 'file' elements
175   // (the contents of an element can be provided in several chunks by
176   // XMLVisitor::data())
177   std::string _resourceFile;
178 
179   // Holds the resulting structured data once the parsing is finished
180   std::vector<ResourceDeclaration> _resourceDeclarations;
181 };
182 
183 #endif  // _FGRCC_HXX_
184