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.

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