When using this BinaryBuilder with a zip archive, it will extract the entries and build a hierarchical namespace with constants declared for each zip entry. Very handy.
/*
==============================================================================
Utility to turn a bunch of binary files into a .cpp file and .h file full of
data so they can be built directly into an executable.
Use this code at your own risk! It carries no warranty!
==============================================================================
*/
#include "../JuceLibraryCode/JuceHeader.h"
class StringStack : private StringArray {
public:
int level() const
{
return this->size();
}
bool isEmpty() const
{
return (level() == 0);
}
void push(const String& value)
{
this->add(value);
}
void pop()
{
if (!isEmpty())
{
this->remove(this->size() - 1);
}
}
const String& top() const {
if (isEmpty())
return String::empty;
return this->operator [] (this->size() - 1);
}
const String& operator [] (const int index) const {
return StringArray::operator [] (index);
}
};
static String indentation(const int n) {
return String::repeatedString(" ", n);
}
//==============================================================================
static int addFile (const File& file,
const String& classname,
OutputStream& headerStream,
OutputStream& cppStream)
{
MemoryBlock mb;
file.loadFileAsData (mb);
const String name (file.getFileName().toLowerCase()
.replaceCharacter (' ', '_')
.replaceCharacter ('.', '_')
.retainCharacters ("abcdefghijklmnopqrstuvwxyz_0123456789"));
std::cout << "Adding " << name << ": "
<< (int) mb.getSize() << " bytes" << std::endl;
// Add a namespace for the file entry
headerStream << indentation(1) << "namespace " << name << "\r\n"
<< indentation(1) << "{\r\n";
headerStream << indentation(2) << "extern const char* data;\r\n"
<< indentation(2) << "const int size = " << (int) mb.getSize() << ";\r\n\r\n";
static int tempNum = 0;
cppStream << "static const unsigned char temp" << ++tempNum << "[] = {";
size_t i = 0;
const uint8* const data = (const uint8*) mb.getData();
while (i < mb.getSize() - 1)
{
if ((i % 40) != 39)
cppStream << (int) data[i] << ",";
else
cppStream << (int) data[i] << ",\r\n ";
++i;
}
cppStream << (int) data[i] << ",0,0};\r\n";
cppStream << "const char* " << classname << "::" << name << "::data"
<< " = (const char*) temp" << tempNum << ";\r\n\r\n";
// If zip file, add all entries as an enum, makes it easy (and "code safe") to use
// with ZipArchive
if (file.getFileExtension().endsWithIgnoreCase("zip"))
{
ZipFile zip(file);
zip.sortEntriesByFilename();
if (zip.getNumEntries() > 0)
{
headerStream << indentation(2) << "// Adding easy access constants for sorted zip file entries\r\n";
StringStack stack;
for (int i = 0; i < zip.getNumEntries(); ++i)
{
const ZipFile::ZipEntry* pEntry = zip.getEntry(i);
const StringArray pathEntries = StringArray::fromTokens(pEntry->filename, "/", String::empty);
for (int j = 0; j < pathEntries.size() - 1; ++j)
{
const String entry (pathEntries[j]
.replaceCharacter (' ', '_')
.replaceCharacter ('.', '_')
.retainCharacters ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789"));
stack_loop:
if (stack.level() < (j + 1))
{
// Add namespace
headerStream << indentation(stack.level() + 2) << "namespace " << entry << "\r\n";
headerStream << indentation(stack.level() + 2) << "{\r\n";
stack.push(entry);
}
else if (stack[j] != entry)
{
while (!stack.isEmpty() && stack.top() != entry)
{
// Remove namespace
stack.pop();
headerStream << indentation(stack.level() + 2) << "}\r\n";
goto stack_loop;
}
}
}
while (stack.level() > pathEntries.size() - 1)
{
// Remove namespace
stack.pop();
headerStream << indentation(stack.level() + 2) << "}\r\n";
}
const String fileNameEntry = pathEntries[pathEntries.size()-1].replaceCharacters(" .-", "___");
if (fileNameEntry.isNotEmpty())
{
headerStream << indentation(stack.level() + 2) << "const unsigned " << fileNameEntry << " = " << i << ";\r\n";
}
}
while (!stack.isEmpty())
{
// Remove namespace
stack.pop();
headerStream << indentation(stack.level() + 2) << "}\r\n";
}
}
}
// Exit namespace for the file entry
headerStream << indentation(1) << "}\r\n";
return mb.getSize();
}
static bool isHiddenFile (const File& f, const File& root)
{
return f.getFileName().endsWithIgnoreCase (".scc")
|| f.getFileName() == ".svn"
|| f.getFileName().startsWithChar ('.')
|| (f.getSize() == 0 && ! f.isDirectory())
|| (f.getParentDirectory() != root && isHiddenFile (f.getParentDirectory(), root));
}
//==============================================================================
int main (int argc, char* argv[])
{
std::cout << "\n BinaryBuilder! Copyright 2007 by Julian Storer - www.juce.com\n\n";
if (argc < 4 || argc > 5)
{
std::cout << " Usage: BinaryBuilder sourcedirectory targetdirectory targetclassname [optional wildcard pattern]\n\n"
" BinaryBuilder will find all files in the source directory, and encode them\n"
" into two files called (targetclassname).cpp and (targetclassname).h, which it\n"
" will write into the target directory supplied.\n\n"
" Any files in sub-directories of the source directory will be put into the\n"
" resultant class, but #ifdef'ed out using the name of the sub-directory (hard to\n"
" explain, but obvious when you try it...)\n";
return 0;
}
const File sourceDirectory (File::getCurrentWorkingDirectory()
.getChildFile (String (argv[1]).unquoted()));
if (! sourceDirectory.isDirectory())
{
std::cout << "Source directory doesn't exist: "
<< sourceDirectory.getFullPathName()
<< std::endl << std::endl;
return 0;
}
const File destDirectory (File::getCurrentWorkingDirectory()
.getChildFile (String (argv[2]).unquoted()));
if (! destDirectory.isDirectory())
{
// No need to fail because destination directory doesn't exist, only if creating it
// fails.
if (! descDirectory.createDirectory())
{
std::cout << "Destination directory doesn't exist: "
<< destDirectory.getFullPathName() << std::endl << std::endl;
return 0;
}
}
String className (argv[3]);
className = className.trim();
const File headerFile (destDirectory.getChildFile (className).withFileExtension (".h"));
const File cppFile (destDirectory.getChildFile (className).withFileExtension (".cpp"));
std::cout << "Creating " << headerFile.getFullPathName()
<< " and " << cppFile.getFullPathName()
<< " from files in " << sourceDirectory.getFullPathName()
<< "..." << std::endl << std::endl;
Array <File> files;
sourceDirectory.findChildFiles (files, File::findFiles, true,
(argc > 4) ? argv[4] : "*");
if (files.size() == 0)
{
std::cout << "Didn't find any source files in: "
<< sourceDirectory.getFullPathName() << std::endl << std::endl;
return 0;
}
headerFile.deleteFile();
cppFile.deleteFile();
ScopedPointer <OutputStream> header (headerFile.createOutputStream());
if (header == 0)
{
std::cout << "Couldn't open "
<< headerFile.getFullPathName() << " for writing" << std::endl << std::endl;
return 0;
}
ScopedPointer <OutputStream> cpp (cppFile.createOutputStream());
if (cpp == 0)
{
std::cout << "Couldn't open "
<< cppFile.getFullPathName() << " for writing" << std::endl << std::endl;
return 0;
}
*header << "/* (Auto-generated binary data file). */\r\n\r\n"
"#ifndef BINARY_" << className.toUpperCase() << "_H\r\n"
"#define BINARY_" << className.toUpperCase() << "_H\r\n\r\n"
"namespace " << className << "\r\n"
"{\r\n";
*cpp << "/* (Auto-generated binary data file). */\r\n\r\n"
"#include \"" << className << ".h\"\r\n\r\n";
int totalBytes = 0;
for (int i = 0; i < files.size(); ++i)
{
const File file (files[i]);
// (avoid source control files and hidden files..)
if (! isHiddenFile (file, sourceDirectory))
{
if (file.getParentDirectory() != sourceDirectory)
{
*header << " #ifdef " << file.getParentDirectory().getFileName().toUpperCase() << "\r\n";
*cpp << "#ifdef " << file.getParentDirectory().getFileName().toUpperCase() << "\r\n";
totalBytes += addFile (file, className, *header, *cpp);
*header << " #endif\r\n";
*cpp << "#endif\r\n";
}
else
{
totalBytes += addFile (file, className, *header, *cpp);
}
}
}
*header << "}\r\n\r\n"
"#endif\r\n";
header = 0;
cpp = 0;
std::cout << std::endl << " Total size of binary data: " << totalBytes << " bytes" << std::endl;
return 0;
}
