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.

281 lines
11KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 6 technical preview.
  4. Copyright (c) 2020 - 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 this technical preview, this file is not subject to commercial licensing.
  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 << "namespace " << className << newLine
  105. << "{" << newLine;
  106. while (i < files.size())
  107. {
  108. auto& file = files.getReference(i);
  109. auto variableName = variableNames[i];
  110. FileInputStream fileStream (file);
  111. if (fileStream.openedOk())
  112. {
  113. auto tempVariable = "temp_binary_data_" + String (i);
  114. cpp << newLine << "//================== " << file.getFileName() << " ==================" << newLine
  115. << "static const unsigned char " << tempVariable << "[] =" << newLine;
  116. {
  117. MemoryBlock data;
  118. fileStream.readIntoMemoryBlock (data);
  119. writeDataAsCppLiteral (data, cpp, true, true);
  120. }
  121. cpp << newLine << newLine
  122. << "const char* " << variableName << " = (const char*) " << tempVariable << ";" << newLine;
  123. }
  124. ++i;
  125. if (cpp.getPosition() > maxFileSize)
  126. break;
  127. }
  128. if (isFirstFile)
  129. {
  130. if (i < files.size())
  131. {
  132. cpp << newLine
  133. << "}" << newLine
  134. << newLine
  135. << "#include \"" << headerFile.getFileName() << "\"" << newLine
  136. << newLine
  137. << "namespace " << className << newLine
  138. << "{";
  139. }
  140. cpp << newLine
  141. << newLine
  142. << "const char* getNamedResource (const char* resourceNameUTF8, int& numBytes)" << newLine
  143. << "{" << newLine;
  144. StringArray returnCodes;
  145. for (auto& file : files)
  146. {
  147. auto dataSize = file.getSize();
  148. returnCodes.add ("numBytes = " + String (dataSize) + "; return " + variableNames[files.indexOf (file)] + ";");
  149. }
  150. createStringMatcher (cpp, "resourceNameUTF8", variableNames, returnCodes, 4);
  151. cpp << " numBytes = 0;" << newLine
  152. << " return nullptr;" << newLine
  153. << "}" << newLine
  154. << newLine;
  155. cpp << "const char* namedResourceList[] =" << newLine
  156. << "{" << newLine;
  157. for (int j = 0; j < files.size(); ++j)
  158. cpp << " " << variableNames[j].quoted() << (j < files.size() - 1 ? "," : "") << newLine;
  159. cpp << "};" << newLine << newLine;
  160. cpp << "const char* originalFilenames[] =" << newLine
  161. << "{" << newLine;
  162. for (auto& f : files)
  163. cpp << " " << f.getFileName().quoted() << (files.indexOf (f) < files.size() - 1 ? "," : "") << newLine;
  164. cpp << "};" << newLine << newLine;
  165. cpp << "const char* getNamedResourceOriginalFilename (const char* resourceNameUTF8)" << newLine
  166. << "{" << newLine
  167. << " for (unsigned int i = 0; i < (sizeof (namedResourceList) / sizeof (namedResourceList[0])); ++i)" << newLine
  168. << " {" << newLine
  169. << " if (namedResourceList[i] == resourceNameUTF8)" << newLine
  170. << " return originalFilenames[i];" << newLine
  171. << " }" << newLine
  172. << newLine
  173. << " return nullptr;" << newLine
  174. << "}" << newLine
  175. << newLine;
  176. }
  177. cpp << "}" << newLine;
  178. return Result::ok();
  179. }
  180. ResourceFile::WriteResult ResourceFile::write (int maxFileSize,
  181. String projectLineFeed,
  182. File headerFile,
  183. std::function<File (int)> getCppFile)
  184. {
  185. Array<File> filesCreated;
  186. {
  187. MemoryOutputStream mo;
  188. mo.setNewLineString (projectLineFeed);
  189. auto r = writeHeader (mo);
  190. if (r.failed())
  191. return { r };
  192. if (! overwriteFileWithNewDataIfDifferent (headerFile, mo))
  193. return { Result::fail ("Can't write to file: " + headerFile.getFullPathName()) };
  194. filesCreated.add (headerFile);
  195. }
  196. int i = 0;
  197. int fileIndex = 0;
  198. for (;;)
  199. {
  200. auto cpp = getCppFile (fileIndex);
  201. MemoryOutputStream mo;
  202. mo.setNewLineString (projectLineFeed);
  203. auto r = writeCpp (mo, headerFile, i, maxFileSize);
  204. if (r.failed())
  205. return { r, std::move (filesCreated) };
  206. if (! overwriteFileWithNewDataIfDifferent (cpp, mo))
  207. return { Result::fail ("Can't write to file: " + cpp.getFullPathName()), std::move (filesCreated) };
  208. filesCreated.add (cpp);
  209. ++fileIndex;
  210. if (i >= files.size())
  211. break;
  212. }
  213. return { Result::ok(), std::move (filesCreated) };
  214. }
  215. }
  216. }