The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

289 lines
12KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce::build_tools
  19. {
  20. static const char* resourceFileIdentifierString = "JUCER_BINARY_RESOURCE";
  21. //==============================================================================
  22. void ResourceFile::setClassName (const String& name)
  23. {
  24. className = name;
  25. }
  26. void ResourceFile::addFile (const File& file)
  27. {
  28. files.add (file);
  29. auto variableNameRoot = makeBinaryDataIdentifierName (file);
  30. auto variableName = variableNameRoot;
  31. int suffix = 2;
  32. while (variableNames.contains (variableName))
  33. variableName = variableNameRoot + String (suffix++);
  34. variableNames.add (variableName);
  35. }
  36. String ResourceFile::getDataVariableFor (const File& file) const
  37. {
  38. const auto index = files.indexOf (file);
  39. jassert (index >= 0);
  40. return variableNames[index];
  41. }
  42. String ResourceFile::getSizeVariableFor (const File& file) const
  43. {
  44. return getDataVariableFor (file) + "Size";
  45. }
  46. int64 ResourceFile::getTotalDataSize() const
  47. {
  48. return std::accumulate (files.begin(),
  49. files.end(),
  50. int64 { 0 },
  51. [] (int64 acc, const File& f) { return acc + f.getSize(); });
  52. }
  53. static void writeComment (MemoryOutputStream& mo)
  54. {
  55. mo << newLine << newLine
  56. << " This is an auto-generated file: Any edits you make may be overwritten!" << newLine
  57. << newLine
  58. << "*/" << newLine
  59. << newLine;
  60. }
  61. Result ResourceFile::writeHeader (MemoryOutputStream& header)
  62. {
  63. header << "/* =========================================================================================";
  64. writeComment (header);
  65. header << "#pragma once" << newLine
  66. << newLine
  67. << "namespace " << className << newLine
  68. << "{" << newLine;
  69. for (int i = 0; i < files.size(); ++i)
  70. {
  71. auto& file = files.getReference (i);
  72. if (! file.existsAsFile())
  73. return Result::fail ("Can't open resource file: " + file.getFullPathName());
  74. auto dataSize = file.getSize();
  75. auto variableName = variableNames[i];
  76. FileInputStream fileStream (file);
  77. if (fileStream.openedOk())
  78. {
  79. header << " extern const char* " << variableName << ";" << newLine;
  80. header << " const int " << variableName << "Size = " << (int) dataSize << ";" << newLine << newLine;
  81. }
  82. }
  83. header << " // Number of elements in the namedResourceList and originalFileNames arrays." << newLine
  84. << " const int namedResourceListSize = " << files.size() << ";" << newLine
  85. << newLine
  86. << " // Points to the start of a list of resource names." << newLine
  87. << " extern const char* namedResourceList[];" << newLine
  88. << newLine
  89. << " // Points to the start of a list of resource filenames." << newLine
  90. << " extern const char* originalFilenames[];" << newLine
  91. << newLine
  92. << " // If you provide the name of one of the binary resource variables above, this function will" << newLine
  93. << " // return the corresponding data and its size (or a null pointer if the name isn't found)." << newLine
  94. << " const char* getNamedResource (const char* resourceNameUTF8, int& dataSizeInBytes);" << newLine
  95. << newLine
  96. << " // If you provide the name of one of the binary resource variables above, this function will" << newLine
  97. << " // return the corresponding original, non-mangled filename (or a null pointer if the name isn't found)." << newLine
  98. << " const char* getNamedResourceOriginalFilename (const char* resourceNameUTF8);" << newLine
  99. << "}" << newLine;
  100. return Result::ok();
  101. }
  102. Result ResourceFile::writeCpp (MemoryOutputStream& cpp, const File& headerFile, int& i, const int maxFileSize)
  103. {
  104. bool isFirstFile = (i == 0);
  105. cpp << "/* ==================================== " << resourceFileIdentifierString << " ====================================";
  106. writeComment (cpp);
  107. cpp << "#include <cstring>" << newLine
  108. << newLine
  109. << "namespace " << className << newLine
  110. << "{" << newLine;
  111. while (i < files.size())
  112. {
  113. auto& file = files.getReference (i);
  114. auto variableName = variableNames[i];
  115. FileInputStream fileStream (file);
  116. if (fileStream.openedOk())
  117. {
  118. auto tempVariable = "temp_binary_data_" + String (i);
  119. cpp << newLine << "//================== " << file.getFileName() << " ==================" << newLine
  120. << "static const unsigned char " << tempVariable << "[] =" << newLine;
  121. {
  122. MemoryBlock data;
  123. fileStream.readIntoMemoryBlock (data);
  124. writeDataAsCppLiteral (data, cpp, true, true);
  125. }
  126. cpp << newLine << newLine
  127. << "const char* " << variableName << " = (const char*) " << tempVariable << ";" << newLine;
  128. }
  129. ++i;
  130. if (cpp.getPosition() > maxFileSize)
  131. break;
  132. }
  133. if (isFirstFile)
  134. {
  135. if (i < files.size())
  136. {
  137. cpp << newLine
  138. << "}" << newLine
  139. << newLine
  140. << "#include \"" << headerFile.getFileName() << "\"" << newLine
  141. << newLine
  142. << "namespace " << className << newLine
  143. << "{";
  144. }
  145. cpp << newLine
  146. << newLine
  147. << "const char* getNamedResource (const char* resourceNameUTF8, int& numBytes);" << newLine
  148. << "const char* getNamedResource (const char* resourceNameUTF8, int& numBytes)" << newLine
  149. << "{" << newLine;
  150. StringArray returnCodes;
  151. for (auto& file : files)
  152. {
  153. auto dataSize = file.getSize();
  154. returnCodes.add ("numBytes = " + String (dataSize) + "; return " + variableNames[files.indexOf (file)] + ";");
  155. }
  156. createStringMatcher (cpp, "resourceNameUTF8", variableNames, returnCodes, 4);
  157. cpp << " numBytes = 0;" << newLine
  158. << " return nullptr;" << newLine
  159. << "}" << newLine
  160. << newLine;
  161. cpp << "const char* namedResourceList[] =" << newLine
  162. << "{" << newLine;
  163. for (int j = 0; j < files.size(); ++j)
  164. cpp << " " << variableNames[j].quoted() << (j < files.size() - 1 ? "," : "") << newLine;
  165. cpp << "};" << newLine << newLine;
  166. cpp << "const char* originalFilenames[] =" << newLine
  167. << "{" << newLine;
  168. for (auto& f : files)
  169. cpp << " " << f.getFileName().quoted() << (files.indexOf (f) < files.size() - 1 ? "," : "") << newLine;
  170. cpp << "};" << newLine << newLine;
  171. cpp << "const char* getNamedResourceOriginalFilename (const char* resourceNameUTF8);" << newLine
  172. << "const char* getNamedResourceOriginalFilename (const char* resourceNameUTF8)" << newLine
  173. << "{" << newLine
  174. << " for (unsigned int i = 0; i < (sizeof (namedResourceList) / sizeof (namedResourceList[0])); ++i)" << newLine
  175. << " if (strcmp (namedResourceList[i], resourceNameUTF8) == 0)" << newLine
  176. << " return originalFilenames[i];" << newLine
  177. << newLine
  178. << " return nullptr;" << newLine
  179. << "}" << newLine
  180. << newLine;
  181. }
  182. cpp << "}" << newLine;
  183. return Result::ok();
  184. }
  185. ResourceFile::WriteResult ResourceFile::write (int maxFileSize,
  186. String projectLineFeed,
  187. File headerFile,
  188. std::function<File (int)> getCppFile)
  189. {
  190. Array<File> filesCreated;
  191. {
  192. MemoryOutputStream mo;
  193. mo.setNewLineString (projectLineFeed);
  194. auto r = writeHeader (mo);
  195. if (r.failed())
  196. return { r, {} };
  197. if (! overwriteFileWithNewDataIfDifferent (headerFile, mo))
  198. return { Result::fail ("Can't write to file: " + headerFile.getFullPathName()), {} };
  199. filesCreated.add (headerFile);
  200. }
  201. int i = 0;
  202. int fileIndex = 0;
  203. for (;;)
  204. {
  205. auto cpp = getCppFile (fileIndex);
  206. MemoryOutputStream mo;
  207. mo.setNewLineString (projectLineFeed);
  208. auto r = writeCpp (mo, headerFile, i, maxFileSize);
  209. if (r.failed())
  210. return { r, std::move (filesCreated) };
  211. if (! overwriteFileWithNewDataIfDifferent (cpp, mo))
  212. return { Result::fail ("Can't write to file: " + cpp.getFullPathName()), std::move (filesCreated) };
  213. filesCreated.add (cpp);
  214. ++fileIndex;
  215. if (i >= files.size())
  216. break;
  217. }
  218. return { Result::ok(), std::move (filesCreated) };
  219. }
  220. } // namespace juce::build_tools