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.

283 lines
11KB

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